Professional Documents
Culture Documents
*/
#include <inc/x86.h>
#include <inc/mmu.h>
#include <inc/error.h>
#include <inc/string.h>
#include <inc/assert.h>
#include <kern/pmap.h>
#include <kern/kclock.h>
static int
nvram_read(int r)
{
static void
i386_detect_memory(void)
{
size_t npages_extmem;
// --------------------------------------------------------------
static void boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa,
int perm);
static void check_page_free_list(bool only_low_memory);
static void check_page_alloc(void);
static void check_kern_pgdir(void);
static physaddr_t check_va2pa(pde_t *pgdir, uintptr_t va);
static void check_page(void);
static void check_page_installed_pgdir(void);
// This simple physical memory allocator is used only while JOS is setting
// up its virtual memory system. page_alloc() is the real allocator.
//
// If n>0, allocates enough pages of contiguous physical memory to hold 'n'
// bytes. Doesn't initialize the memory. Returns a kernel virtual address.
//
// If n==0, returns the address of the next free page without allocating
// anything.
//
// If we're out of memory, boot_alloc should panic.
// This function may ONLY be used during initialization,
// before the page_free_list list has been set up.
static void *
boot_alloc(uint32_t n)
{
static char *nextfree;
char *result;
size_t numPgs=0;
if (n == 0) {
result=nextfree;
}
result = next;
}
//TODO
return result;
}
//
// This function only sets up the kernel part of the address space
// (ie. addresses >= UTOP). The user part of the address space
// will be setup later.
//
// From UTOP to ULIM, the user is allowed to read but not write.
// Above ULIM the user cannot read or write.
void
mem_init(void)
{
uint32_t cr0;
size_t n;
// Find out how much memory the machine has (npages & npages_basemem).
i386_detect_memory();
//////////////////////////////////////////////////////////////////////
// create initial page directory.
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
memset(kern_pgdir, 0, PGSIZE);
//////////////////////////////////////////////////////////////////////
// Recursively insert PD in itself as a page table, to form
// a virtual page table at virtual address UVPT.
// (For now, you don't have understand the greater purpose of the
// following line.)
//////////////////////////////////////////////////////////////////////
// Allocate an array of npages 'struct PageInfo'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 PageInfo in this
// array. 'npages' is the number of physical pages in memory. Use memset
// to initialize all fields of each struct PageInfo to 0.
// Your code goes here:
//////////////////////////////////////////////////////////////////////
// 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_region
// or page_insert
page_init();
check_page_free_list(1);
check_page_alloc();
check_page();
//////////////////////////////////////////////////////////////////////
// Now we set up virtual memory
//////////////////////////////////////////////////////////////////////
// Map 'pages' read-only by the user at linear address UPAGES
// Permissions:
//
//
//
//////////////////////////////////////////////////////////////////////
// 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)
//
//
//
//
//////////////////////////////////////////////////////////////////////
// Map all of physical memory at KERNBASE.
// Ie. the VA range [KERNBASE, 2^32) should map to
//
// Check that the initial page directory has been set up correctly.
check_kern_pgdir();
// Switch from the minimal entry page directory to the full kern_pgdir
// page table we just created.
check_page_free_list(0);
//
// Initialize page structure and memory free list.
// After this is done, NEVER use boot_alloc again. ONLY use the page
//
is free.
never be allocated.
//
//
//
// Change the code to reflect this.
// NB: DO NOT actually touch the physical memory corresponding to
// free pages!
size_t i;
for (i = 1; i < npages; i++) {
pages[i].pp_ref = 0;
pages[i].pp_link = page_free_list;
page_free_list = &pages[i];
//TODO
cprintf("(PADDR(boot_alloc(0)) %d\n", PADDR(boot_alloc(0)));
cprintf("PGNUM(PADDR(boot_alloc(0))) %d\n", PGNUM(PADDR(boot_alloc(0))));
//
// Allocates a physical page. If (alloc_flags & ALLOC_ZERO), fills the entire
// returned physical page with '\0' bytes. Does NOT increment the reference
// count of the page - the caller must do these if necessary (either explicitly
// or via page_insert).
//
// Be sure to set the pp_link field of the allocated page to NULL so
// page_free can check for double-free bugs.
//
// Returns NULL if out of free memory.
//
// Hint: use page2kva and memset
struct PageInfo *
page_alloc(int alloc_flags)
{
//
// Return a page to the free list.
// (This function should only be called when pp->pp_ref reaches 0.)
//
void
page_free(struct PageInfo *pp)
{
//TODO
// Fill this function in
// Hint: You may want to panic if pp->pp_ref is nonzero or
// pp->pp_link is not NULL.
if(pp->pp_ref!=0)
panic("pp_ref is nonzero");
if(pp->pp_link)
panic("pp_link is not NULL");
pp->pp_link=page_free_list;
page_free_list=pp;
//
// Decrement the reference count on a page,
// freeing it if there are no more refs.
//
void
page_decref(struct PageInfo* pp)
{
if (--pp->pp_ref == 0)
page_free(pp);
}
//
//
//
and pgdir_walk returns a pointer into the new page table page.
//
// Hint 1: you can turn a Page * into the physical address of the
// page it refers to with page2pa() from kern/pmap.h.
//
// Hint 2: the x86 MMU checks permission bits in both the page directory
// and the page table, so it's safe to leave permissions in the page
// directory more permissive than strictly necessary.
//
// Hint 3: look at inc/mmu.h for useful macros that mainipulate page
// table and page directory entries.
//
pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)
{
//TODO
pde_t * pde; //va(virtual address) point to pa(physical address)
pte_t * pgtable; //same as pde
struct Page *pp;
pp->pp_ref++;
*pde = PADDR(pgtable) | PTE_P | PTE_W | PTE_U;
}
return &pgtable[PTX(va)];
// Fill this function in
//
// Map [va, va+size) of virtual address space to physical [pa, pa+size)
// in the page table rooted at pgdir. Size is a multiple of PGSIZE, and
// va and pa are both page-aligned.
// Use permission bits perm|PTE_P for the entries.
//
// This function is only intended to set up the ``static'' mappings
// above UTOP. As such, it should *not* change the pp_ref field on the
// mapped pages.
//
// Hint: the TA solution uses pgdir_walk
static void
boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{
//TODO ovo su provjere neke provjeriti da li radi sve i bez njih
assert(pgdir);
assert(ROUNDDOWN(va, PGSIZE) == va);
assert(ROUNDDOWN(size, PGSIZE) == size);
assert(ROUNDDOWN(pa, PGSIZE) == pa);
// Next page.
va += PGSIZE;
pa += PGSIZE;
size -= PGSIZE;
}
//
// Map the physical page 'pp' at virtual address 'va'.
// The permissions (the low 12 bits) of the page table entry
// should be set to 'perm|PTE_P'.
//
// Requirements
// - If there is already a page mapped at 'va', it should be page_remove()d.
// - If necessary, on demand, a page table should be allocated and inserted
//
into 'pgdir'.
//
// Corner-case hint: Make sure to consider what happens when the same
// pp is re-inserted at the same virtual address in the same pgdir.
// However, try not to distinguish this case in your code, as this
// frequently leads to subtle bugs; there's an elegant way to handle
// everything in one code path.
//
// RETURNS:
// 0 on success
// -E_NO_MEM, if page table couldn't be allocated
//
// Hint: The TA solution is implemented using pgdir_walk, page_remove,
// and page2pa.
//
int
page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
{
// Fill this function in
return 0;
}
//
// Return the page mapped at virtual address 'va'.
// If pte_store is not zero, then we store in it the address
// of the pte for this page. This is used by page_remove and
// can be used to verify page permissions for syscall arguments,
// but should not be used by most callers.
//
//
// Unmaps the physical page at virtual address 'va'.
// If there is no physical page at that address, silently does nothing.
//
// Details:
// - The ref count on the physical page should decrement.
// - The physical page should be freed if the refcount reaches 0.
// - The pg table entry corresponding to 'va' should be set to 0.
//
//
// Hint: The TA solution is implemented using page_lookup,
//
//
void
page_remove(pde_t *pgdir, void *va)
{
// Fill this function in
}
//
// Invalidate a TLB entry, but only if the page tables being
// edited are the ones currently in use by the processor.
//
void
tlb_invalidate(pde_t *pgdir, void *va)
{
// Flush the entry only if we're modifying the current address space.
// For now, there is only one address space, so always invalidate.
invlpg(va);
}
//
// Check that the pages on the page_free_list are reasonable.
//
static void
check_page_free_list(bool only_low_memory)
{
struct PageInfo *pp;
if (!page_free_list)
panic("'page_free_list' is a null pointer!");
if (only_low_memory) {
// Move pages with lower addresses first in the free
// list, since entry_pgdir does not map all pages.
struct PageInfo *pp1, *pp2;
struct PageInfo **tp[2] = { &pp1, &pp2 };
for (pp = page_free_list; pp; pp = pp->pp_link) {
int pagetype = PDX(page2pa(pp)) >= pdx_limit;
*tp[pagetype] = pp;
tp[pagetype] = &pp->pp_link;
}
*tp[1] = 0;
*tp[0] = pp2;
page_free_list = pp1;
}
//
// Check the physical page allocator (page_alloc(), page_free(),
// and page_init()).
//
static void
check_page_alloc(void)
{
struct PageInfo *pp, *pp0, *pp1, *pp2;
int nfree;
struct PageInfo *fl;
char *c;
int i;
if (!pages)
panic("'pages' is a null pointer!");
assert(pp0);
assert(pp1 && pp1 != pp0);
assert(pp2 && pp2 != pp1 && pp2 != pp0);
assert(page2pa(pp0) < npages*PGSIZE);
assert(page2pa(pp1) < npages*PGSIZE);
// test flags
memset(page2kva(pp0), 1, PGSIZE);
page_free(pp0);
assert((pp = page_alloc(ALLOC_ZERO)));
assert(pp && pp0 == pp);
c = page2kva(pp);
cprintf("check_page_alloc() succeeded!\n");
}
//
// Checks that the kernel part of virtual address space
// has been setup roughly correctly (by mem_init()).
//
// This function doesn't test every corner case,
// but it is a pretty good sanity check.
//
static void
check_kern_pgdir(void)
{
uint32_t i, n;
pde_t *pgdir;
pgdir = kern_pgdir;
case PDX(UPAGES):
assert(pgdir[i] & PTE_P);
break;
default:
if (i >= PDX(KERNBASE)) {
assert(pgdir[i] & PTE_P);
assert(pgdir[i] & PTE_W);
} else
assert(pgdir[i] == 0);
break;
}
}
cprintf("check_kern_pgdir() succeeded!\n");
}
// This function returns the physical address of the page containing 'va',
// defined by the page directory 'pgdir'. The hardware normally performs
// this functionality for us! We define our own version to help check
// the check_kern_pgdir() function; it shouldn't be used elsewhere.
static physaddr_t
check_va2pa(pde_t *pgdir, uintptr_t va)
{
pte_t *p;
pgdir = &pgdir[PDX(va)];
if (!(*pgdir & PTE_P))
return ~0;
p = (pte_t*) KADDR(PTE_ADDR(*pgdir));
if (!(p[PTX(va)] & PTE_P))
return ~0;
return PTE_ADDR(p[PTX(va)]);
}
assert(pp0);
assert(pp1 && pp1 != pp0);
assert(pp2 && pp2 != pp1 && pp2 != pp0);
// free pp0 and try again: pp0 should be used for page table
page_free(pp0);
assert(page_insert(kern_pgdir, pp1, 0x0, PTE_W) == 0);
assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0));
assert(check_va2pa(kern_pgdir, 0x0) == page2pa(pp1));
assert(pp1->pp_ref == 1);
assert(pp0->pp_ref == 1);
// should be able to map pp2 at PGSIZE because pp0 is already allocated for
page table
assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0);
assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2));
assert(pp2->pp_ref == 1);
assert(!page_alloc(0));
// should not be able to map at PTSIZE because need free page for page table
cprintf("check_page() succeeded!\n");
}
uintptr_t va;
int i;
cprintf("check_page_installed_pgdir() succeeded!\n");
}