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);
}
}
