JOS LAB2,Memory and Paging.PART 2
Posted on Wed, 24 Nov 2010 10:17:05 -1100在PART1说完分页机制后,我们开始讨论具体的实现代码吧 !
第一个函数:
void i386_vm_init(void) { pde_t* pgdir; uint32_t cr0; size_t n; ////////////////////////////////////////////////////////////////////// // create initial page directory. pgdir = boot_alloc(PGSIZE, PGSIZE); memset(pgdir, 0, PGSIZE); boot_pgdir = pgdir; boot_cr3 = PADDR(pgdir); ////////////////////////////////////////////////////////////////////// // Recursively insert PD in itself as a page table, to form // a virtual page table at virtual address VPT. // (For now, you don't have understand the greater purpose of the // following two lines.) // Permissions: kernel RW, user NONE pgdir[PDX(VPT)] = PADDR(pgdir)|PTE_W|PTE_P; // same for UVPT // Permissions: kernel R, user R pgdir[PDX(UVPT)] = PADDR(pgdir)|PTE_U|PTE_P; ////////////////////////////////////////////////////////////////////// // Allocate an array of npage 'struct Page's and store it in 'pages'. // The kernel uses this array to keep track of physical pages: for // each physical page, there is a corresponding struct Page in this // array. 'npage' is the number of physical pages in memory. // User-level programs will get read-only access to the array as well. // Your code goes here: //***************** //SUNUS,Nov 15,2010 pages = boot_alloc(npage * sizeof(struct Page), PGSIZE); ROUNDUP(pages,PGSIZE); cprintf("pages is %08x\n",PADDR(pages)); /* I don't know how to deal with the permission stuffs,so leave it ! - SUNUS*/ ////////////////////////////////////////////////////////////////////// // Now that we've allocated the initial kernel data structures, we set // up the list of free physical pages. Once we've done so, all further // memory management will go through the page_* functions. In // particular, we can now map memory using boot_map_segment or page_insert page_init(); check_page_alloc(); page_check(); ////////////////////////////////////////////////////////////////////// // Now we set up virtual memory ////////////////////////////////////////////////////////////////////// // Map 'pages' read-only by the user at linear address UPAGES // Permissions: // - the new image at UPAGES -- kernel R, user R // (ie. perm = PTE_U | PTE_P) // - pages itself -- kernel RW, user NONE // Your code goes here: //SUNUS,Nov,23,2010 size_t actual_ptsize; actual_ptsize = ROUNDUP(npage * sizeof(struct Page),PGSIZE); boot_map_segment(pgdir, UPAGES, actual_ptsize, PADDR(pages), (PTE_W|PTE_U)); ////////////////////////////////////////////////////////////////////// // Use the physical memory that 'bootstack' refers to as the kernel // stack. The kernel stack grows down from virtual address KSTACKTOP. // We consider the entire range from [KSTACKTOP-PTSIZE, KSTACKTOP) // to be the kernel stack, but break this into two pieces: // * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory // * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed; so if // the kernel overflows its stack, it will fault rather than // overwrite memory. Known as a "guard page". // Permissions: kernel RW, user NONE // Your code goes here: //SUNUS,Nov,23,2010 boot_map_segment(pgdir, KSTACKTOP - KSTKSIZE, KSTKSIZE, PADDR(bootstack),PTE_W|PTE_P); boot_map_segment(pgdir, KSTACKTOP - PTSIZE, PTSIZE - KSTKSIZE, 0, 0 ); // I don't know if it's necessary -sunus// ////////////////////////////////////////////////////////////////////// // Map all of physical memory at KERNBASE. // Ie. the VA range [KERNBASE, 2^32) should map to // the PA range [0, 2^32 - KERNBASE) // We might not have 2^32 - KERNBASE bytes of physical memory, but // we just set up the mapping anyway. // Permissions: kernel RW, user NONE // Your code goes here: boot_map_segment(pgdir, KERNBASE, 0x0fffffff + 1, 0, PTE_W|PTE_P); // Check that the initial page directory has been set up correctly. check_boot_pgdir(); ////////////////////////////////////////////////////////////////////// // On x86, segmentation maps a VA to a LA (linear addr) and // paging maps the LA to a PA. I.e. VA => LA => PA. If paging is // turned off the LA is used as the PA. Note: there is no way to // turn off segmentation. The closest thing is to set the base // address to 0, so the VA => LA mapping is the identity. // Current mapping: VA KERNBASE+x => PA x. // (segmentation base=-KERNBASE and paging is off) // From here on down we must maintain this VA KERNBASE + x => PA x // mapping, even though we are turning on paging and reconfiguring // segmentation. // Map VA 0:4MB same as VA KERNBASE, i.e. to PA 0:4MB. // (Limits our kernel to <4MB) pgdir[0] = pgdir[PDX(KERNBASE)]; // Install page table. lcr3(boot_cr3); // Turn on paging. cr0 = rcr0(); cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_TS|CR0_EM|CR0_MP; cr0 &= ~(CR0_TS|CR0_EM); lcr0(cr0); // Current mapping: KERNBASE+x => x => x. // (x < 4MB so uses paging pgdir[0]) // Reload all segment registers. asm volatile("lgdt gdt_pd"); asm volatile("movw %%ax,%%gs" :: "a" (GD_UD|3)); asm volatile("movw %%ax,%%fs" :: "a" (GD_UD|3)); asm volatile("movw %%ax,%%es" :: "a" (GD_KD)); asm volatile("movw %%ax,%%ds" :: "a" (GD_KD)); asm volatile("movw %%ax,%%ss" :: "a" (GD_KD)); asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (GD_KT)); // reload cs asm volatile("lldt %%ax" :: "a" (0)); // Final mapping: KERNBASE+x => KERNBASE+x => x. // This mapping was only used after paging was turned on but // before the segment registers were reloaded. pgdir[0] = 0; // Flush the TLB for good measure, to kill the pgdir[0] mapping. lcr3(boot_cr3); }
这是开始的对内存初始化的函数,我们先来到
static void* boot_alloc(uint32_t n, uint32_t align) { extern char end[]; void *v; // Initialize boot_freemem if this is the first time. // 'end' is a magic symbol automatically generated by the linker, // which points to the end of the kernel's bss segment - // i.e., the first virtual address that the linker // did _not_ assign to any kernel code or global variables. if (boot_freemem == 0) boot_freemem = end; // LAB 2: Your code here: // Step 1: round boot_freemem up to be aligned properly // (hint: look in types.h for some handy macros) // Step 2: save current value of boot_freemem as allocated chunk // Step 3: increase boot_freemem to record allocation // Step 4: return allocated chunk // SUNUS Nov,14,2010 boot_freemem = ROUNDUP(boot_freemem, align); v = boot_freemem; boot_freemem += n; if(PADDR(boot_freemem) > (maxpa)) { cprintf("v is %08x , mapa is %08x\n",PADDR(boot_freemem),maxpa); cprintf("Memory Not Enough!\n"); return NULL; } return v; }
这里的 ROUNDUP是向上取整函数,这里的作用是让boot_freemem按4KB内存对齐。这样可以让寻址的效率达到最大。
该函数作用则是分配n字节大小的内存,且起始地址按align方式对齐。
而分配的方式,其实则是让boot_freemem指针不断自增,从而让boot_freemem的值 之前,end之后的地址为“已使用内存”.并返回下一个可使用内存地址。maxpa为最大物理地址,在之前的函数中已经被赋值,这里先不深究。
这里来到了
page_init(void),这里注意的是对于一个页是否可用的标志是pages[x].pp->ref,pp->ref位对页面引用的计数器,若等于0,则可用
对于初始化,要注意系统的有几个页面是不可用的(系统预留或处于安全原因):
pages[0],IOPHYSMEM~EXTPHYSMEM预留的IO HOLE还有内核位置[KERNBASE~boot_freemem]
void page_init(void) { /*Nov 16,2010 Sunus */ int i; pages[0].pp_ref = 1; LIST_INIT(&page_free_list); for(i = 1 ; i < npage ; i++) { pages[i].pp_ref = 0; if((i >= IOPHYSMEM/PGSIZE && i < EXTPHYSMEM/PGSIZE) /*IO HOLE*/ || (i >= PADDR(KERNBASE)/PGSIZE && i < (size_t)ROUNDUP(PADDR(boot_freemem),PGSIZE)/PGSIZE)) /*KERNBASE-boot_freemem*/ { pages[i].pp_ref = 1; continue; } LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link); } }