2009-12-06

奇怪了,内存哪里设置的有错?

我在cr4里面设置了PSE选项,那么应该是2M的页面大小

/\*\* 启用PAE \*/
 movl %cr4,%eax                  \# Get CR4
 orl  $(CR4\_PAE | CR4\_PSE), %eax
 movl %eax,%cr4

cr3里面的东西是这么设置的:

#define PG_V    0x001 
#define PG_RW   0x002 
#define PG_U    0x004 
#define PG_PS   0x080 //最后一级页表项这里必须设置

        .align 4096 // the others may not be needed, but this one MUST BE THERE 
        .globl pml4_base        
        // PML4 
pml4_base: //都指向PT3的第一项 
        .rept (512) 
        .quad (PT3 + PG_V + PG_RW + PG_U) 
        .endr 
        .globl  PT3 
PT3: //都指向PT2的第一项 
        .rept (512) 
        .quad (PT2 + PG_V + PG_RW + PG_U) 
        .endr 
PT2: //一个页面2M,512个就是1G 
        i=0 
        .rept (512) 
        .quad  (i<<21) + PG_V + PG_RW + PG_PS + PG_U 
        i=i+1 
        .endr

理论上来讲,我将1G的内存映射满了整个64位地址空间(因高16位产生的空洞除外)。之后我在ia32-e long mode下从0x100000开始写操作,写到0x102fff的时候,我的机器就重启了。纳闷了很久我才想起来,kernel就是从物理地址的0x100000开始映射的,然后我用objdump/nm看了下,PML4就是从0x102000开始的。我猜是因为TLB有缓存,所以它在写完第一个4K页,开始写PT3的时候,才被CPU报错重启。

freebsd下,elf64_exec这个函数通过bootinfo64.c的bi_load64函数把elf载入进来,并得到modulep/kernendp两个偏移量,他们最终会被传递给kernel。然后elf64_exec初始化页表,它采用和我前面同样的方式,不同的是它是用memset的方式在运行时初始化页表数据。然后elf64_exec通过amd64_tramp函数从32位跳转到64位long mode。进入kernel的entry point。
kernel的entry point是btext,在locore.S里。
hammer_time(u_int64_t modulep, u_int64_t physfree)的这两个参数其实是从bi_load64那里得来的。physfree是加载完kernel的elf镜像的终止地址,也就是可用内存区的起始地址。

我不知道该怎么从grub2的grub_multiboot_info中得到kernend信息。mem_lower/mem_upper只报告了内存的大小。grub_multiboot_mmap_entry只报告了内存映射情况,我就是从它这里得知从0x100000开始都是可用内存区,才去memset的。

比较神奇的是为什么可用内存区被分为了3段?1M以下一段,1M以上一段。然后从0x3ff00000开始还有1M的可用内存区。前两个我明白,最后这一段是干嘛的?

此博客中的热门博文

少写代码,多读别人写的代码

在windows下使用llvm+clang

tensorflow distributed runtime初窥