高半部分内核初始化 [英] Higher half kernel initialization

查看:73
本文介绍了高半部分内核初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

这些问题是为什么到目前为止,我避免使用上半个内核.有没有人有一个很好的系统来解决这些依赖关系?也许对此GDT技巧进行了一些改动,既可以访问位于其链接/虚拟地址的内核,也可以访问位于其物理地址的multiboot表,或者使用某种预定义的页表来避免上述问题,可能涉及PSE?

解决方案

这是我解决此问题的方法:

我的内核映像是由GRUB加载的(物理)地址0x01000000(16MB,位于ISA DMA区域的正上方).该图像主要由两部分组成:

  1. 早期初始化"部分.本节包含为准备跳转到上半部内核而执行的代码.在本节中,我还将为准备过程中使用的堆栈和堆保留一些空间.本节中的所有代码都链接到(虚拟)地址0x01000000.
  2. 图像的其余部分包含上半部分内核的代码和数据.此部分中的所有代码都链接到(虚拟)地址0xc0000000(3GB).

由于早期init部分中的代码与加载时的地址链接在一起,因此GRUB可以毫无问题地跳转到该代码中.此早期初始化代码执行以下步骤:

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

在这一点上,其余的初始化是从上半部分代码完成的.这包括设置GDT,IDT,内存管理等.请注意,MBI已重定位到一个知名位置,因此您不必担心会用自己的数据结构覆盖它.

关于物理内存管理器的一句话:我要做的是计算我的数据结构所需的页面数量,从内核映像之后的第一页开始分配这些结构,并从这些数据结构之后开始处理页面. /p>

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

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?

解决方案

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.

这篇关于高半部分内核初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆