mit 6.828 lab 代码和笔记,以及中文注释源代码已放置在github中: https://github.com/yunwei37/xv6-labs
内核的链接地址(由objdump打印)与加载地址之间存在(相当大的)差异;操作系统内核通常喜欢被链接并在很高的虚拟地址(例如0xf0100000)上运行,以便将处理器虚拟地址空间的下部留给用户程序使用。
链接地址 f0100000加载地址 00100000许多机器在地址0xf0100000上没有任何物理内存,因此我们不能指望能够在其中存储内核;将使用处理器的内存管理硬件将虚拟地址0xf0100000(内核代码期望在其上运行的链接地址)映射到物理地址0x00100000(引导加载程序将内核加载到物理内存中)。
这样,尽管内核的虚拟地址足够高,可以为用户进程留出足够的地址空间,但是它将被加载到PC RAM中1MB点的BIOS ROM上方的物理内存中。
在这个阶段中,仅映射前4MB的物理内存;
映射:kern/entrypgdir.c 中手写,静态初始化的页面目录和页面表。 直到kern / entry.S设置了CR0_PG标志,内存引用才被视为物理地址。
将范围从0xf0000000到0xf0400000的虚拟地址转换为物理地址0x00000000到0x00400000
将虚拟地址0x00000000到0x00400000转换为物理地址0x00000000到0x00400000
kern/entrypgdir.c:
#include <inc/mmu.h> #include <inc/memlayout.h> pte_t entry_pgtable[NPTENTRIES]; // entry.S页面目录从虚拟地址KERNBASE开始映射前4MB的物理内存 // (也就是说,它映射虚拟地址 // 地址[KERNBASE,KERNBASE + 4MB)到物理地址[0,4MB) // 我们选择4MB,因为这就是我们可以在一页的空间中映射的表 // 这足以使我们完成启动的早期阶段。我们也映射 // 虚拟地址[0,4MB)到物理地址[0,4MB)这个 // 区域对于entry.S中的一些指令至关重要,然后我们 // 不再使用它。 // // 页面目录(和页面表)必须从页面边界开始, // 因此是“ __aligned__”属性。 另外,由于限制 // 与链接和静态初始化程序有关, 我们在这里使用“ x + PTE_P” // 而不是更标准的“ x | PTE_P”。 其他地方 // 您应该使用“ |”组合标志。 __attribute__((__aligned__(PGSIZE))) pde_t entry_pgdir[NPDENTRIES] = { // 将VA的[0,4MB)映射到PA的[0,4MB) [0] = ((uintptr_t)entry_pgtable - KERNBASE) + PTE_P, // 将VA的[KERNBASE,KERNBASE + 4MB)映射到PA的[0,4MB) [KERNBASE>>PDXSHIFT] = ((uintptr_t)entry_pgtable - KERNBASE) + PTE_P + PTE_W }; // 页表的条目0映射到物理页0,条目1映射到 // 物理页面1,依此类推 __attribute__((__aligned__(PGSIZE))) pte_t entry_pgtable[NPTENTRIES] = { 0x000000 | PTE_P | PTE_W, 0x001000 | PTE_P | PTE_W, 0x002000 | PTE_P | PTE_W, 0x003000 | PTE_P | PTE_W, 0x004000 | PTE_P | PTE_W, 0x005000 | PTE_P | PTE_W, ................ kern/entry.S /* See COPYRIGHT for copyright information. */ #include <inc/mmu.h> #include <inc/memlayout.h> # 逻辑右移 #define SRL(val, shamt) (((val) >> (shamt)) & ~(-1 << (32 - (shamt)))) ################################################################### # 内核(此代码)链接到地址〜(KERNBASE + 1 Meg), # 但引导加载程序会将其加载到地址〜1 Meg。 # # RELOC(x)将符号x从其链接地址映射到其在 # 物理内存中的实际位置(其加载地址)。 ################################################################### #define RELOC(x) ((x) - KERNBASE) #define MULTIBOOT_HEADER_MAGIC (0x1BADB002) #define MULTIBOOT_HEADER_FLAGS (0) #define CHECKSUM (-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)) ################################################################### # 进入点 ################################################################### .text # Multiboot标头 .align 4 .long MULTIBOOT_HEADER_MAGIC .long MULTIBOOT_HEADER_FLAGS .long CHECKSUM # '_start'指定ELF入口点。 既然当引导程序进入此代码时我们还没设置 # 虚拟内存,我们需要 # bootloader跳到入口点的*物理*地址。 .globl _start _start = RELOC(entry) .globl entry entry: movw $0x1234,0x472 # 热启动 # 我们尚未设置虚拟内存, 因此我们从 # 引导加载程序加载内核的物理地址为:1MB # (加上几个字节)处开始运行. 但是,C代码被链接为在 # KERNBASE+1MB 的位置运行。 我们建立了一个简单的页面目录, # 将虚拟地址[KERNBASE,KERNBASE + 4MB)转换为 # 物理地址[0,4MB)。 这4MB区域 # 直到我们在实验2 mem_init中设置真实页面表为止 # 是足够的。 # 将entry_pgdir的物理地址加载到cr3中。 entry_pgdir # 在entrypgdir.c中定义。 movl $(RELOC(entry_pgdir)),