CR0在Linux内核启动上包含PE/PG标志

发布于 2025-02-10 01:43:09 字数 1262 浏览 0 评论 0原文

我正在使用gnu grub版本2.04 bootloader和linux内核5.19-rc2

我正在调试Linux内核初始化,并期望CPU在内核启动时应在实际模式。实际上如Intel系统编程手册中指定的,第2章

所有Intel 64和IA-32处理器都输入实时地址模式。 加电或重置(请参阅第9章,“处理器管理和 初始化”)。然后,软件从实地启动开关 到受保护模式的模式。

因此,我在第一个Linux内核函数grub调用中设置了GDB中的断点(我相信的是 secondary_startup_64 在x86-64上),以在qemu中调试ubuntu 20.04。我期望pe pg cr0的标志将被禁用,因为在实际地址模式下没有分页,也没有受保护的模式。但这是我在gdb中得到的:

Thread 2 hit Breakpoint 1, secondary_startup_64 () at arch/x86/kernel/head_64.S:147
147             call verify_cpu
(gdb) p $cr0
$1 = [ PG AM WP NE ET MP PE ]

可以看到pgpe flags设置的标志,这不清楚为什么。我看到2种可能性:

  1. grub本身将CPU转移到受保护的模式中,并启用了分页
  2. secondary_startup_64 不是grub调用的Linux内核的第一个函数,并且之前执行了内核代码。 如果是这样,从grub到Linux内核的真实切入点是什么?

I'm using GNU GRUB version 2.04 bootloader and Linux Kernel 5.19-rc2

I'm debugging the Linux Kernel initialization and expected that right upon the Kernel startup the CPU should be in real mode. Actually as specified in the Intel System Programming Manual, Chapter 2:

All Intel 64 and IA-32 processors enter real-address mode following a
power-up or reset (see Chapter 9, “Processor Management and
Initialization”). Software then initiates the switch from real-address
mode to protected mode.

So I set breakpoint in GDB right at the first Linux Kernel function GRUB calls (which I beleive is secondary_startup_64 on x86-64) to debug Ubuntu 20.04 in QEMU. I expected PE and PG flags of CR0 to be disabled since there is no paging and no protected mode in real address mode. But here is what I got in gdb:

Thread 2 hit Breakpoint 1, secondary_startup_64 () at arch/x86/kernel/head_64.S:147
147             call verify_cpu
(gdb) p $cr0
$1 = [ PG AM WP NE ET MP PE ]

As can be seen PG and PE flags are set which is unclear why. I see 2 possibilities:

  1. GRUB itself transfers CPU into the protected mode and enabled paging
  2. secondary_startup_64 is not the first function of the Linux Kernel that GRUB calls and there is Kernel code executed before. If so what is the real entry point from GRUB to the Linux Kernel?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

所有Intel 64和IA-32处理器都在加电或重置后输入实地模式 ”,然后固件启动(使用一个CPU),然后IT启动所有其他CPU,所有CPU都将切换到受保护的模式或较长模式(以及初始化许多内容 - 内存,MTRR,ACPI表,...);除一个CPU外,所有CPU都重新入睡。

最终;如果固件是BIOS,则将CPU切换回真实模式并启动引导加载程序。如果固件是UEFI,它将以较长的模式离开CPU并启动启动加载程序。无论哪种方式,grub启动。

如果固件是BIOS,则grub将CPU切换为受保护的模式(然后将其从保护模式转换为实际模式,然后每次使用BIOS功能时将其切换为实际模式);做一堆东西;算出它正在加载64位Linux内核,然后切换到长模式。如果固件是UEFI,它将一直保持长期。请注意,Grub的代码主要是32位代码;并且受保护模式和延长模式都使您可以运行32位代码;因此,GRUB的大多数代码都适用于受保护模式和长模式。

甚至后来;在固件和grub完成了大量工作之后,可能在CPU模式发生了数千次之后; grub将控制权传递到Linux。

现在... Linux有几个不同的入口点,启动加载程序使用的任何条目最适合它。偶尔会发生这种变化 - 例如,Linux的非常古老的版本内置了一个软盘启动加载器(它们稍后放弃了),而Linux的现代版本可以在其中内置一个UEFI引导加载程序。还有一个实际模式入口点和一个受保护的模式入口点(较旧)和一个64位的入口点(更新)。如果固件为BIOS,则可能只使用32位入口点,如果固件为UEFI,则使用64位入口点。

这些入口点中的大多数(不包括UEFI引导加载器入口点)用作链条 - 如果使用了32位入口点,它将设置分页和较长模式,并跳到64位入口点。

您的断点位于/附近的64位入口点;因此,无论启动加载器使用的启动加载器(32位ONE或64位)无论哪个入口点,您都开始查看64位代码。 64位代码需要较长的模式,并且长度模式需要分页;因此必须设置PG和PE。

当然,(对于64位入口点)可能会配置分页,以便所有物理地址都映射到相同的虚拟地址,因此,即使启用了分页,它实际上也没有做任何事情。内核的64位启动代码将更改配置分页的方式(将内核映射到内核空间等)。

"All Intel 64 and IA-32 processors enter real-address mode following a power-up or reset", and then the firmware starts (using one CPU), then it starts all the other CPUs, and all the CPUs get switched to protected mode or long mode (along with initializing a lot of stuff - memory, MTRRs, ACPI tables, ...); and all the CPUs except one are put back to sleep.

Eventually; if the firmware is BIOS it switches the CPU back to real mode and starts a boot loader; and if the firmware is UEFI it leaves the CPU in long mode and starts a boot loader. Either way GRUB is started.

If the firmware was BIOS then GRUB switches the CPU to protected mode (and then switches it from protected mode to real mode and back to protected mode each time it wants to use BIOS functions); does a bunch of stuff; figures out it's loading a 64-bit Linux kernel and switches to long mode. If the firmware is UEFI it just stays in long mode the whole time. Note that GRUB's code is mostly 32-bit code; and both protected mode and long mode allow you to run 32-bit code; so most of GRUB's code works for both protected mode and long mode.

Even later; after a huge amount of stuff has been done by firmware and GRUB, and possibly after the CPU's mode has been changed a few thousand times; GRUB passes control to Linux.

Now... Linux has several different entry points, where the boot loader uses whichever entry point suits it the most. This changes occasionally - e.g. very old versions of Linux has a floppy disk boot loader built into it (they got rid of a bit later), and modern versions of Linux can have a UEFI boot loader built into it. There's also a real mode entry point and a protected mode entry point (which are older) and a 64-bit entry point (which is newer). Of these GRUB probably just uses the 32-bit entry point if the firmware was BIOS, and the 64-bit entry point if the firmware was UEFI.

Most of these entry points (excluding the UEFI boot loader entry point) work as a chain - e.g. if the real mode entry point is used it switches to protect mode and jumps to the 32-bit entry point; and if the 32-bit entry point is used it sets up paging and long mode and jumps to the 64-bit entry point.

Your breakpoint is at/near the 64-bit entry point; so regardless of which entry point the boot loader used (the 32-bit one or the 64-bit one) you start looking at 64-bit code. 64-bit code requires long mode, and long mode requires paging; so PG and PE must be set.

Of course (for the 64-bit entry point) paging is likely configured so that all physical addresses are mapped to the identical virtual addresses, so even though paging is enabled it doesn't actually do anything. Kernel's 64-bit startup code would change the way paging is configured (map kernel into kernel space, etc).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文