高半内核初始化

发布于 2024-11-13 06:48:42 字数 600 浏览 3 评论 0原文

当初始化我的内核时,我需要做一些事情:1)需要启用分页,2)物理内存管理器需要从 grub 解析内存映射,3)各种启动代码需要访问需要的数据留在那里供以后使用(例如 GDT、IDT、内存管理结构)。

这些步骤之间的依赖性让我发疯。对于上半部分,内核在其虚拟地址处链接,因此我提出的选项是 1)在汇编中启用分页,这将涉及跟踪所有多重引导指针(在汇编中),以便它们仍然可以访问到物理内存管理器,然后取消它们全部的映射,2)将启动代码链接到其物理地址,然后执行一些指针操作以访问其物理地址的内核结构,或者3)不使用高半部分核心。

还涉及到在编译时不知道物理内存量的情况下引导物理内存管理器。我很确定我必须在分配第一个结构时小心地避免所有多重引导结构,或者首先使用它们,然后不用担心覆盖它们(尽管我仍然需要处理模块,并且这种方法可能涉及在设置物理内存管理器时将多重引导表复制到我需要的已知位置)。

这些问题是我迄今为止一直避免使用较高半内核的原因。有人有一个好的系统来解决这些依赖关系吗?也许这个 GDT 技巧有一些变化,可以访问链接/虚拟地址的内核和物理地址的多重引导表,或者使用某种预定义的页表来避免上述问题,也许涉及 PSE?

When initializing my kernel, I have a few things that need to happen: 1) paging needs to be enabled, 2) the physical memory manager needs to parse the memory map from grub, and 3) assorted startup code needs to access data that needs to stay there for later (e.g. the GDT, IDT, memory management structures).

The dependencies between these steps are driving me crazy. With higher-half, the kernel is linked at its virtual address and so the options I've come up with are 1) enable paging in assembly, which would involve following all the multiboot pointers (in assembly) so they'll still be accessible to the physical memory manager and then later unmapping them all, 2) link the startup code at its physical address and then do some pointer manipulation to access kernel structures at their physical addresses as well, or 3) don't use a higher-half kernel.

Also involved is bootstrapping the physical memory manager without knowing the amount of physical memory at compile time. I'm pretty sure I have to either carefully avoid all the multiboot structures when allocating the first structures, or use them all first and then don't worry about overwriting them (although I'd still have to deal with modules and this approach probably involves copying the multiboot tables to a known location as I need them while setting up the physical memory manager).

These problems are why I've avoided a higher half kernel up to now. Does anyone have a good system for resolving these dependencies? Maybe some variation on this GDT trick to access both the kernel at its linked/virtual address and the multiboot tables at their physical address, or using some kind of pre-defined page tables that avoid the problems above, maybe involving PSE?

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

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

发布评论

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

评论(1

小姐丶请自重 2024-11-20 06:48:42

这就是我解决这个问题的方法:

我的内核映像由 GRUB 在(物理)地址 0x01000000(16MB,位于 ISA DMA 区域上方)加载。该映像基本上由两部分组成:

  1. “early init”部分。本节包含为准备跳转到上半内核而执行的代码。我还在本节中为准备过程中使用的堆栈和堆预留了一些空间。本节中的所有代码都链接在(虚拟)地址 0x01000000 处。
  2. 映像的其余部分包含属于上半内核一部分的代码和数据。这部分中的所有代码都链接在(虚拟)地址 0xc0000000 (3GB)。

由于早期 init 部分中的代码链接到与其加载位置相同的地址,因此 GRUB 可以毫无问题地跳转到该代码。此早期 init 代码执行以下步骤:

  1. 重新定位 GRUB 传递给内核的 MBI 结构。早期初始化部分内的堆用于此目的。
  2. 标识映射从物理地址 0x0 开始到早期 init 部分使用的最后一个页面的物理地址的所有页面。身份映射意味着虚拟地址与物理地址相同。这确保了启用分页后,早期 init 部分中的代码仍然可以执行。
  3. 将上半部内核映射到虚拟地址 0xc0000000。
  4. 启用分页。
  5. 跳入上半内核。

此时,其余的初始化是由上半部分代码完成的。这包括设置 GDT、IDT、内存管理……请注意,MBI 已重新定位到众所周知的位置,因此您不必担心用自己的数据结构覆盖它。

关于物理内存管理器的简短说明:我所做的是计算数据结构所需的页面数量,从内核映像之后的第一页开始分配这些结构,并从这些数据结构之后开始处理页面。

我希望这个解释是清楚的。如果不是,请告诉我。如果您愿意,我还可以为您提供我的内核的副本。

This is how I tackled this problem:

My kernel image is loaded by GRUB at (physical) address 0x01000000 (16MB, just above the ISA DMA region). This image basically consists of two parts:

  1. An "early init" section. This section contains code that is executed to prepare to jump to the higher half kernel. I also reserve some space in this section for a stack and a heap used during this preparation. All code in this section is linked at (virtual) address 0x01000000.
  2. The rest of the image contains code and data that is part of the higher half kernel. All code in this part is linked at (virtual) address 0xc0000000 (3GB).

Since the code in the early init section is linked at the same address as where it is loaded, GRUB can jump into this code without any problems. This early init code performs the following steps:

  1. Relocate the MBI structure that GRUB passes to the kernel. The heap inside the early init section is used for this.
  2. Identity map all pages starting at physical address 0x0 up to the physical address of the last page used by the early init section. Identity mapping means that the virtual addresses are the same as the physical addresses. This makes sure that the code in the early init section can still be executed after paging is enabled.
  3. Map the higher half kernel at virtual address 0xc0000000.
  4. Enable paging.
  5. Jump into the higher half kernel.

At this point the rest of the initialization is done from higher half code. This includes setting up the GDT, IDT, memory management,... Note that the MBI is relocated to a well-known location so you shouldn't have to worry about overwriting it with your own data structures.

A small word about the physical memory manager: What I do is calculate the amount of pages needed for my data structures, allocate these structures starting at the first page after the kernel image and start dealing pages starting after these data structures.

I hope this explanation is clear. If it's not, please let me know. I could also provide you with a copy of my kernel if you'd like that.

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