JOS的物理页表分配实现

March 4th, 2014 by JasonLe's Tech Leave a reply »

最近将JOS的启动流程学习了一遍,细化了系统从real mode到32-bit的转变。
但是在系统真正要接管内存,分配真正页表之前,要有一个简单的页表将kernel code载入到内存中。

我们先使用一个static void i386_detect_memory(void) 函数来探测物理内存一共有多少pages,一个page为4K.
另外我们看一下JOS的memery map就可以发现我们做的工作,写这部分一定要有好的大局观。知道每部分初始化位于内存的哪个位置。


我先画了一个JOS初始化的内存page分配图。
Unnamed QQ Screenshot20140304213235

下面这个函数是在没有建立系统分页机制前,一个临时简易的物理页表,直接是在物理内存上进行的,用的也是虚拟地址,比如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;
}