最近将JOS的启动流程学习了一遍,细化了系统从real mode到32-bit的转变。
但是在系统真正要接管内存,分配真正页表之前,要有一个简单的页表将kernel code载入到内存中。
我们先使用一个static void i386_detect_memory(void) 函数来探测物理内存一共有多少pages,一个page为4K.
另外我们看一下JOS的memery map就可以发现我们做的工作,写这部分一定要有好的大局观。知道每部分初始化位于内存的哪个位置。
下面这个函数是在没有建立系统分页机制前,一个临时简易的物理页表,直接是在物理内存上进行的,用的也是虚拟地址,比如kernel code 起始代码的虚拟内存在0xf0100000,实际位置要剪掉一个一个KERNBASE的宏(0xf0000000).这个部分就要有初始的物理页表进行管理。
boot_alloc(uint32_t n) { static char *nextfree; char *result = NULL; if (!nextfree) { extern char end[]; nextfree = ROUNDUP((char *) end, PGSIZE); // cprintf("end = %p\n ",end); // cprintf("nextfree = %p\n ",nextfree); } result = nextfree; if((uint32_t)(result - KERNBASE+n) > (uint32_t)(npages*PGSIZE)) { panic("boot_alloc():out of physical memory\n"); } nextfree = ROUNDUP(result+n,PGSIZE); //这里使用两个指针来标记将要使用的空间和下一次要分配的空间, //其中result代表将要使用的n个byte空间 ,nextfree是指下次要使用的空间的首地址 // cprintf("n = %d\n",n); // cprintf("result = %p\n",result); // cprintf("nextfree = %p\n\n",nextfree); return result; }
然后我们要建立一个映射整条物理内存的页表,前面我们已经说明。因为我们已经得到了所有的npages,然后针对每个pages对应一个pages,这样就实现了所有物理内存的映射。
通过上面的图,我们发现已被占用的物理内存有两块:
1. 0至4KB(第0页);
2. 640KB开始,至当前可用物理地址(利用boot_alloc(0)获得,小心要通过PADDR转化成物理地址);
另外,我们认为boot loader、Kernel可执行代码的ELF文件头这两块内存的内容是可以覆盖的。
所以我们初始寻找出IOPHYSMEM EXTPHYSEME。这部分代码主要是BIOS,CMOS的固件代码,不能覆盖,需要跳过。
但是首先我们需要找到IOPHYSMEM EXTPHYSEME对应的pages页。然后跳过,将空余的pages链接到page_free_list 。如果之后有需要,就要从page_free_list 上面取下一个page,进行分配,说白了就是链表节点的链接与删除。
void page_init(void) { size_t i; size_t IOPHYSMEM_begin = ROUNDDOWN(IOPHYSMEM,PGSIZE)/PGSIZE; size_t EXTPHYSEME_end = PADDR(boot_alloc(0)) / PGSIZE; // cprintf("IO = %d EXT = %d \n",IOPHYSMEM_begin,EXTPHYSEME_end); // cprintf("npages_basemem:%d\n",npages_basemem); // cprintf("PGNUM : %d\n",PGNUM(PADDR(boot_alloc(0) ) ) ); // cprintf("npages = %d\n",npages); page_free_list = NULL; for (i = 0; i < npages; i++) { pages[i].pp_ref = 0; if(0 == i || ( (IOPHYSMEM_begin <= i) && ( i < EXTPHYSEME_end ) ) ) { // cprintf("IOHOLE = %p\n",&pages[i]); } else { pages[i].pp_link = page_free_list; page_free_list = &pages[i];//等于链表实现 //cprintf("Normal = %p\n",page_free_list); } } } struct PageInfo * page_alloc(int alloc_flags) { // Fill this function in struct PageInfo *page_return; if(page_free_list != NULL) { page_return = page_free_list; page_free_list = (struct PageInfo *)page_free_list->pp_link; if(alloc_flags & ALLOC_ZERO) { memset(page2kva(page_return),'\0',PGSIZE); } } else page_return =NULL; return page_return; } void page_free(struct PageInfo *pp) { // Fill this function in pp->pp_link = page_free_list; page_free_list = pp; return; }