text: /* See COPYRIGHT for copyright information. */
  
  #include <inc/x86.h>
  #include <inc/mmu.h>
  #include <inc/error.h>
  #include <inc/string.h>
  #include <inc/assert.h>
  #include <inc/debug.h>
  
  
  #include <kern/pmap.h>
  #include <kern/kclock.h>
  #include <kern/env.h>
  
  // These variables are set by i386_detect_memory()
  size_t npages;			// Amount of physical memory (in pages)
  static size_t npages_basemem;	// Amount of base memory (in pages)
  
  // These variables are set in mem_init()
  pde_t *kern_pgdir;		// Kernel's initial page directory
  struct PageInfo *pages;		// Physical page state array
  static struct PageInfo *page_free_list;	// Free list of physical pages
  
  
  // --------------------------------------------------------------
  // Detect machine's physical memory setup.
  // --------------------------------------------------------------
  
  static int
  nvram_read(int r)
  {
  	return mc146818_read(r) | (mc146818_read(r + 1) << 8);
  }
  
  static void
  i386_detect_memory(void)
  {
  	size_t npages_extmem;
  
  	// Use CMOS calls to measure available base & extended memory.
  	// (CMOS calls return results in kilobytes.)
  	npages_basemem = (nvram_read(NVRAM_BASELO) * 1024) / PGSIZE;
  	npages_extmem = (nvram_read(NVRAM_EXTLO) * 1024) / PGSIZE;
  
  	// Calculate the number of physical pages available in both base
  	// and extended memory.
  	if (npages_extmem)
  		npages = (EXTPHYSMEM / PGSIZE) + npages_extmem;
  	else
  		npages = npages_basemem;
  
  	cprintf("Physical memory: %uK available, base = %uK, extended = %uK\n",
  		npages * PGSIZE / 1024,
  		npages_basemem * PGSIZE / 1024,
  		npages_extmem * PGSIZE / 1024);
  }
  
  
  // --------------------------------------------------------------
  // Set up memory mappings above UTOP.
  // --------------------------------------------------------------
  
  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;	// virtual address of next byte of free memory
  	char *result;
  
  	// Initialize nextfree 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:
  	// the first virtual address that the linker did *not* assign
  	// to any kernel code or global variables.
  	if (!nextfree) {
  		extern char end[];
  		nextfree = ROUNDUP((char *) end, PGSIZE);
  	}
  
  	// Allocate a chunk large enough to hold 'n' bytes, then update
  	// nextfree.  Make sure nextfree is kept aligned
  	// to a multiple of PGSIZE.
  	//
  	// LAB 2: Your code here.
      result = nextfree;
      nextfree = ROUNDUP(nextfree + n, PGSIZE);
  
  	return result;
  }
  
  // Set up a two-level page table:
  //    kern_pgdir is its linear (virtual) address of the root
  //
  // 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();
  
  	// Remove this line when you're ready to test this function.
  	// panic("mem_init: This function is not finished\n");
  
  	//////////////////////////////////////////////////////////////////////
  	// 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.)
  
  	// Permissions: kernel R, user R
  	kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;
  
  	//////////////////////////////////////////////////////////////////////
  	// 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:
  
      pages = (struct PageInfo*)boot_alloc(sizeof(struct PageInfo)*npages);
      memset(pages, 0, sizeof(struct PageInfo)*npages);
  
  	//////////////////////////////////////////////////////////////////////
  	// Make 'envs' point to an array of size 'NENV' of 'struct Env'.
  	// LAB 3: Your code here.
  
  	envs = (struct Env*)boot_alloc(sizeof(struct Env)*NENV);
  	memset(envs, 0, sizeof(struct Env)*NENV);
  
  	//////////////////////////////////////////////////////////////////////
  	// 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:
  	//    - 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:
  
     boot_map_region(kern_pgdir, 
                      UPAGES, 
                      ROUNDUP((sizeof(struct PageInfo)*npages), PGSIZE),
                      PADDR(pages), 
                      (PTE_U | PTE_P));
  
  	//////////////////////////////////////////////////////////////////////
  	// 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:
      boot_map_region(kern_pgdir, 
                      (KSTACKTOP-KSTKSIZE), 
                      KSTKSIZE,
                      PADDR(bootstack), 
                      (PTE_W | PTE_P));
  
  	//////////////////////////////////////////////////////////////////////
  	// 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_region(kern_pgdir, 
                      KERNBASE, 
                      ROUNDUP((0xFFFFFFFF-KERNBASE), PGSIZE),
                      0, 
                      (PTE_W | PTE_P));
  
  	//////////////////////////////////////////////////////////////////////
  	// Map 'envs' read-only by the user at linear address UENVS
  	// Permissions:
  	//    - the new image at UPAGES -- kernel R, user R
  	//      (ie. perm = PTE_U | PTE_P)
  	//    - pages itself -- kernel RW, user NONE
      boot_map_region(kern_pgdir, 
                      UENVS, 
                      ROUNDUP((sizeof(struct Env)*NENV), PGSIZE),
                      PADDR(envs), 
                      (PTE_U | PTE_P));
  
  
  	// 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.	Our instruction pointer should be
  	// somewhere between KERNBASE and KERNBASE+4MB right now, which is
  	// mapped the same way by both page tables.
  	//
  	// If the machine reboots at this point, you've probably set up your
  	// kern_pgdir wrong.
  	lcr3(PADDR(kern_pgdir));
  
  	check_page_free_list(0);
  
  	// entry.S set the really important flags in cr0 (including enabling
  	// paging).  Here we configure the rest of the flags that we care about.
  	cr0 = rcr0();
  	cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_MP;
  	cr0 &= ~(CR0_TS|CR0_EM);
  	lcr0(cr0);
  
  	// Some more checks, only possible after kern_pgdir is installed.
  	check_page_installed_pgdir();
  }
  
  // --------------------------------------------------------------
  // Tracking of physical pages.
  // The 'pages' array has one 'struct PageInfo' entry per physical page.
  // Pages are reference counted, and free pages are kept on a linked list.
  // --------------------------------------------------------------
  
  //
  // Initialize page structure and memory free list.
  // After this is done, NEVER use boot_alloc again.  ONLY use the page
  // allocator functions below to allocate and deallocate physical
  // memory via the page_free_list.
  //
  void
  page_init(void)
  {
  	// The example code here marks all physical pages as free.
  	// However this is not truly the case.  What memory is free?
  	//  1) Mark physical page 0 as in use.
  	//     This way we preserve the real-mode IDT and BIOS structures
  	//     in case we ever need them.  (Currently we don't, but...)
  	//  2) The rest of base memory, [PGSIZE, npages_basemem * PGSIZE)
  	//     is free.
  	//  3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM), which must
  	//     never be allocated.
  	//  4) Then extended memory [EXTPHYSMEM, ...).
  	//     Some of it is in use, some is free. Where is the kernel
  	//     in physical memory?  Which pages are already in use for
  	//     page tables and other data structures?
  	//
  	// Change the code to reflect this.
  	// NB: DO NOT actually touch the physical memory corresponding to
  	// free pages!
  	size_t i, first_free_page;
  	size_t first_free_byte;
  
      /*
  	for (i = 0; i < npages; i++) {
  		pages[i].pp_ref = 0;
  		pages[i].pp_link = page_free_list;
  		page_free_list = &pages[i];
  	}*/
  
      // Mark page 0 as in use
      pages[0].pp_ref = 1;
  
      // Mark base memory as free
  	for (i = 1; i < npages_basemem; i++)
  	{
  	    pages[i].pp_ref = 0;
  	    pages[i].pp_link = page_free_list;
  	    page_free_list = &pages[i];
  	}
  	
  	// IOPHYSMEM/PGSIZE == npages_basemem
  	// Mark IO hole
  	for (i = IOPHYSMEM/PGSIZE; i < EXTPHYSMEM/PGSIZE; i++)
  	{
  	    pages[i].pp_ref = 1;
  	}
      // kernel is loaded in physical memory 0x100000, the beginning of extended memory
      // page directory entry, and npages of PageInfo structure ares allocated by 
      // boot_alloc in mem_init(). next free byte is 
  
  
      first_free_byte = PADDR(boot_alloc(0));
      first_free_page = first_free_byte/PGSIZE;
  
      // mark kernel and page directory, PageInfo list as in use
      for (i = EXTPHYSMEM/PGSIZE; i < first_free_page; i++)
      {
          pages[i].pp_ref = 1;
      }
      // mark others as free
      for (i = first_free_page; i < npages; i++)
      {
          pages[i].pp_ref = 0;
          pages[i].pp_link = page_free_list;
  	    page_free_list = &pages[i];
  	}
  }
  
  //
  // 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)
  {
      struct PageInfo *pp;
      char *kva;
  
      if (!page_free_list)
      {
          return NULL;
      }
      pp = page_free_list;
      page_free_list = page_free_list->pp_link;
      pp->pp_link = NULL;
      if (alloc_flags & ALLOC_ZERO)
      {
          kva = page2kva(pp);
          memset(kva, '\0', PGSIZE);
      }
  
  	// Fill this function in
  	return pp;
  }
  
  //
  // 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)
  {
  	// 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 || pp->pp_link != NULL)
  	{
  	    panic("can't free page in use, or page in the free list");
  	}
  	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);
  }
  
  // Given 'pgdir', a pointer to a page directory, pgdir_walk returns
  // a pointer to the page table entry (PTE) for linear address 'va'.
  // This requires walking the two-level page table structure.
  //
  // The relevant page table page might not exist yet.
  // If this is true, and create == false, then pgdir_walk returns NULL.
  // Otherwise, pgdir_walk allocates a new page table page with page_alloc.
  //    - If the allocation fails, pgdir_walk returns NULL.
  //    - Otherwise, the new page's reference count is incremented,
  //	the page is cleared,
  //	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)
  {
  
      uint32_t pdx = PDX(va);
      uint32_t ptx = PTX(va);
      pde_t * pde;
      pte_t * pte;
      struct PageInfo *pp;
  
      pde = &pgdir[pdx];
      if(*pde & PTE_P)
      { 
          pte = (KADDR(PTE_ADDR(*pde)));
      }
      else
      {
          if (!create)
          {
              return NULL;
          }
          if(!(pp = page_alloc(ALLOC_ZERO)))
          {
  	return NULL;
  }
  
          pte = (pte_t *)page2kva(pp);
          pp->pp_ref++;
          *pde = PADDR(pte) | PTE_P | PTE_W | PTE_U;
      }   
  
      return &pte[ptx];
  }
  
  //
  // 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)
  {
      uintptr_t pva = va;
      physaddr_t ppa = pa;
      pte_t *pte;
      size_t i, np;
  
      np = size/PGSIZE;
      // can't use va+size as upper bound, may overflow    
      for (i = 0; i < np; i++)
      {
          pte = pgdir_walk(pgdir, (void *)pva, 1);
          if (!pte)
          {
              return;
          }
          *pte = PTE_ADDR(ppa) | perm;
          pva+=PGSIZE;
          ppa+=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'.
  //   - pp->pp_ref should be incremented if the insertion succeeds.
  //   - The TLB must be invalidated if a page was formerly present at 'va'.
  //
  // 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)
  {
      pte_t *pte = pgdir_walk(pgdir, va, 1);
      if (!pte)
      {
          return -E_NO_MEM;
      }
      if (*pte & PTE_P)
      {
          // reinsert same page to same va
          if (PTE_ADDR(*pte) == page2pa(pp))
          {
              *pte = page2pa(pp) | (perm|PTE_P);
              return 0;
          }
          else
          {
              page_remove(pgdir, va);
          }
      }
      *pte = page2pa(pp) | perm | PTE_P;
      pp->pp_ref++;
  
  	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.
  //
  // Return NULL if there is no page mapped at va.
  //
  // Hint: the TA solution uses pgdir_walk and pa2page.
  //
  struct PageInfo *
  page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
  {
      pte_t * pte = pgdir_walk(pgdir, va, 0);
  
      if (!pte)
      {
          return NULL;
      }
  
      if (pte_store)
      {
          *pte_store = pte;
      }
  
      return pa2page(PTE_ADDR(*pte));
  }
  
  //
  // 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.
  //     (if such a PTE exists)
  //   - The TLB must be invalidated if you remove an entry from
  //     the page table.
  //
  // Hint: The TA solution is implemented using page_lookup,
  // 	tlb_invalidate, and page_decref.
  //
  void
  page_remove(pde_t *pgdir, void *va)
  {
      pte_t *pte;
      pte_t **pte_store = &pte;
      struct PageInfo *pp = page_lookup(pgdir, va, pte_store);
      if(!pp)
          return;
  
      page_decref(pp);
  
      **pte_store = 0;
      tlb_invalidate(pgdir, va);
  }
  
  //
  // 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);
  }
  
  static uintptr_t user_mem_check_addr;
  
  //
  // Check that an environment is allowed to access the range of memory
  // [va, va+len) with permissions 'perm | PTE_P'.
  // Normally 'perm' will contain PTE_U at least, but this is not required.
  // 'va' and 'len' need not be page-aligned; you must test every page that
  // contains any of that range.  You will test either 'len/PGSIZE',
  // 'len/PGSIZE + 1', or 'len/PGSIZE + 2' pages.
  //
  // A user program can access a virtual address if (1) the address is below
  // ULIM, and (2) the page table gives it permission.  These are exactly
  // the tests you should implement here.
  //
  // If there is an error, set the 'user_mem_check_addr' variable to the first
  // erroneous virtual address.
  //
  // Returns 0 if the user program can access this range of addresses,
  // and -E_FAULT otherwise.
  //
  int
  user_mem_check(struct Env *env, const void *va, size_t len, int perm)
  {
  	// LAB 3: Your code here.
      pte_t * pte;
      void * end = (void *)va + len - 1;
      void * aligned_addr, *aligned_end;
  
      aligned_addr = ROUNDDOWN((void *)va, PGSIZE);
      aligned_end = ROUNDUP((void *)(va + len), PGSIZE);
  
      if (aligned_addr >= (void *)ULIM)
      {
          user_mem_check_addr = (uintptr_t)va;
          return -E_FAULT;
      }
  
      for (; aligned_addr < aligned_end; aligned_addr += PGSIZE) {
          pte = pgdir_walk(env->env_pgdir, aligned_addr, 0);
          if (!pte || !(*pte & PTE_P) || (*pte & perm) != perm)
          {
              if (aligned_addr < va)
              {
                  user_mem_check_addr = (uintptr_t)va;
              }
              else
              {
                  user_mem_check_addr = (uintptr_t)aligned_addr;
              }
              
              return -E_FAULT;
          }
      }
  
  	return 0;
  }
  
  //
  // Checks that environment 'env' is allowed to access the range
  // of memory [va, va+len) with permissions 'perm | PTE_U | PTE_P'.
  // If it can, then the function simply returns.
  // If it cannot, 'env' is destroyed and, if env is the current
  // environment, this function will not return.
  //
  void
  user_mem_assert(struct Env *env, const void *va, size_t len, int perm)
  {
  	if (user_mem_check(env, va, len, perm | PTE_U) < 0) {
  		cprintf("[%08x] user_mem_check assertion failure for "
  			"va %08x\n", env->env_id, user_mem_check_addr);
  		env_destroy(env);	// may not return
  	}
  }
  
  
  // --------------------------------------------------------------
  // Checking functions.
  // --------------------------------------------------------------
  
  //
  // Check that the pages on the page_free_list are reasonable.
  //
  static void
  check_page_free_list(bool only_low_memory)
  {
  	struct PageInfo *pp;
  	unsigned pdx_limit = only_low_memory ? 1 : NPDENTRIES;
  	int nfree_basemem = 0, nfree_extmem = 0;
  	char *first_free_page;
  
  	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;
  	}
  
  	// if there's a page that shouldn't be on the free list,
  	// try to make sure it eventually causes trouble.
  	for (pp = page_free_list; pp; pp = pp->pp_link)
  		if (PDX(page2pa(pp)) < pdx_limit)
  			memset(page2kva(pp), 0x97, 128);
  
  	first_free_page = (char *) boot_alloc(0);
  	for (pp = page_free_list; pp; pp = pp->pp_link) {
  		// check that we didn't corrupt the free list itself
  		assert(pp >= pages);
  		assert(pp < pages + npages);
  		assert(((char *) pp - (char *) pages) % sizeof(*pp) == 0);
  
  		// check a few pages that shouldn't be on the free list
  		assert(page2pa(pp) != 0);
  		assert(page2pa(pp) != IOPHYSMEM);
  		assert(page2pa(pp) != EXTPHYSMEM - PGSIZE);
  		assert(page2pa(pp) != EXTPHYSMEM);
  		assert(page2pa(pp) < EXTPHYSMEM || (char *) page2kva(pp) >= first_free_page);
  
  		if (page2pa(pp) < EXTPHYSMEM)
  			++nfree_basemem;
  		else
  			++nfree_extmem;
  	}
  
  	assert(nfree_basemem > 0);
  	assert(nfree_extmem > 0);
  }
  
  //
  // 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!");
  
  	// check number of free pages
  	for (pp = page_free_list, nfree = 0; pp; pp = pp->pp_link)
  		++nfree;
  
  	// should be able to allocate three pages
  	pp0 = pp1 = pp2 = 0;
  	assert((pp0 = page_alloc(0)));
  	assert((pp1 = page_alloc(0)));
  	assert((pp2 = page_alloc(0)));
  
  	assert(pp0);
  	assert(pp1 && pp1 != pp0);
  	assert(pp2 && pp2 != pp1 && pp2 != pp0);
  	assert(page2pa(pp0) < npages*PGSIZE);
  	assert(page2pa(pp1) < npages*PGSIZE);
  	assert(page2pa(pp2) < npages*PGSIZE);
  
  	// temporarily steal the rest of the free pages
  	fl = page_free_list;
  	page_free_list = 0;
  
  	// should be no free memory
  	assert(!page_alloc(0));
  
  	// free and re-allocate?
  	page_free(pp0);
  	page_free(pp1);
  	page_free(pp2);
  	pp0 = pp1 = pp2 = 0;
  	assert((pp0 = page_alloc(0)));
  	assert((pp1 = page_alloc(0)));
  	assert((pp2 = page_alloc(0)));
  	assert(pp0);
  	assert(pp1 && pp1 != pp0);
  	assert(pp2 && pp2 != pp1 && pp2 != pp0);
  	assert(!page_alloc(0));
  
  	// test flags
  	memset(page2kva(pp0), 1, PGSIZE);
  	page_free(pp0);
  	assert((pp = page_alloc(ALLOC_ZERO)));
  	assert(pp && pp0 == pp);
  	c = page2kva(pp);
  	for (i = 0; i < PGSIZE; i++)
  		assert(c[i] == 0);
  
  	// give free list back
  	page_free_list = fl;
  
  	// free the pages we took
  	page_free(pp0);
  	page_free(pp1);
  	page_free(pp2);
  
  	// number of free pages should be the same
  	for (pp = page_free_list; pp; pp = pp->pp_link)
  		--nfree;
  	assert(nfree == 0);
  
  	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;
  
  	// check pages array
  	n = ROUNDUP(npages*sizeof(struct PageInfo), PGSIZE);
  	for (i = 0; i < n; i += PGSIZE)
  		assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i);
  
  	// check envs array (new test for lab 3)
  	n = ROUNDUP(NENV*sizeof(struct Env), PGSIZE);
  	for (i = 0; i < n; i += PGSIZE)
  		assert(check_va2pa(pgdir, UENVS + i) == PADDR(envs) + i);
  
  	// check phys mem
  	for (i = 0; i < npages * PGSIZE; i += PGSIZE)
  		assert(check_va2pa(pgdir, KERNBASE + i) == i);
  
  	// check kernel stack
  	for (i = 0; i < KSTKSIZE; i += PGSIZE)
  		assert(check_va2pa(pgdir, KSTACKTOP - KSTKSIZE + i) == PADDR(bootstack) + i);
  	assert(check_va2pa(pgdir, KSTACKTOP - PTSIZE) == ~0);
  
  	// check PDE permissions
  	for (i = 0; i < NPDENTRIES; i++) {
  		switch (i) {
  		case PDX(UVPT):
  		case PDX(KSTACKTOP-1):
  		case PDX(UPAGES):
  		case PDX(UENVS):
  			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)]);
  }
  
  
  // check page_insert, page_remove, &c
  static void
  check_page(void)
  {
  	struct PageInfo *pp, *pp0, *pp1, *pp2;
  	struct PageInfo *fl;
  	pte_t *ptep, *ptep1;
  	void *va;
  	int i;
  	extern pde_t entry_pgdir[];
  
  	// should be able to allocate three pages
  	pp0 = pp1 = pp2 = 0;
  	assert((pp0 = page_alloc(0)));
  	assert((pp1 = page_alloc(0)));
  	assert((pp2 = page_alloc(0)));
  
  	assert(pp0);
  	assert(pp1 && pp1 != pp0);
  	assert(pp2 && pp2 != pp1 && pp2 != pp0);
  
  	// temporarily steal the rest of the free pages
  	fl = page_free_list;
  	page_free_list = 0;
  
  	// should be no free memory
  	assert(!page_alloc(0));
  
  	// there is no page allocated at address 0
  	assert(page_lookup(kern_pgdir, (void *) 0x0, &ptep) == NULL);
  
  	// there is no free memory, so we can't allocate a page table
  	assert(page_insert(kern_pgdir, pp1, 0x0, PTE_W) < 0);
  
  	// 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);
  
  	// should be no free memory
  	assert(!page_alloc(0));
  
  	// should be able to map pp2 at PGSIZE because it's already there
  	assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0);
  	assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2));
  	assert(pp2->pp_ref == 1);
  
  	// pp2 should NOT be on the free list
  	// could happen in ref counts are handled sloppily in page_insert
  	assert(!page_alloc(0));
  
  	// check that pgdir_walk returns a pointer to the pte
  	ptep = (pte_t *) KADDR(PTE_ADDR(kern_pgdir[PDX(PGSIZE)]));
  	assert(pgdir_walk(kern_pgdir, (void*)PGSIZE, 0) == ptep+PTX(PGSIZE));
  
  	// should be able to change permissions too.
  	assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W|PTE_U) == 0);
  	assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2));
  	assert(pp2->pp_ref == 1);
  	assert(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U);
  	assert(kern_pgdir[0] & PTE_U);
  
  	// should be able to remap with fewer permissions
  	assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0);
  	assert(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_W);
  	assert(!(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U));
  
  	// should not be able to map at PTSIZE because need free page for page table
  	assert(page_insert(kern_pgdir, pp0, (void*) PTSIZE, PTE_W) < 0);
  
  	// insert pp1 at PGSIZE (replacing pp2)
  	assert(page_insert(kern_pgdir, pp1, (void*) PGSIZE, PTE_W) == 0);
  	assert(!(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U));
  
  	// should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
  	assert(check_va2pa(kern_pgdir, 0) == page2pa(pp1));
  	assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp1));
  	// ... and ref counts should reflect this
  	assert(pp1->pp_ref == 2);
  	assert(pp2->pp_ref == 0);
  
  	// pp2 should be returned by page_alloc
  	assert((pp = page_alloc(0)) && pp == pp2);
  
  	// unmapping pp1 at 0 should keep pp1 at PGSIZE
  	page_remove(kern_pgdir, 0x0);
  	assert(check_va2pa(kern_pgdir, 0x0) == ~0);
  	assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp1));
  	assert(pp1->pp_ref == 1);
  	assert(pp2->pp_ref == 0);
  
  	// test re-inserting pp1 at PGSIZE
  	assert(page_insert(kern_pgdir, pp1, (void*) PGSIZE, 0) == 0);
  	assert(pp1->pp_ref);
  	assert(pp1->pp_link == NULL);
  
  	// unmapping pp1 at PGSIZE should free it
  	page_remove(kern_pgdir, (void*) PGSIZE);
  	assert(check_va2pa(kern_pgdir, 0x0) == ~0);
  	assert(check_va2pa(kern_pgdir, PGSIZE) == ~0);
  	assert(pp1->pp_ref == 0);
  	assert(pp2->pp_ref == 0);
  
  	// so it should be returned by page_alloc
  	assert((pp = page_alloc(0)) && pp == pp1);
  
  	// should be no free memory
  	assert(!page_alloc(0));
  
  	// forcibly take pp0 back
  	assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0));
  	kern_pgdir[0] = 0;
  	assert(pp0->pp_ref == 1);
  	pp0->pp_ref = 0;
  
  	// check pointer arithmetic in pgdir_walk
  	page_free(pp0);
  	va = (void*)(PGSIZE * NPDENTRIES + PGSIZE);
  	ptep = pgdir_walk(kern_pgdir, va, 1);
  	ptep1 = (pte_t *) KADDR(PTE_ADDR(kern_pgdir[PDX(va)]));
  	assert(ptep == ptep1 + PTX(va));
  	kern_pgdir[PDX(va)] = 0;
  	pp0->pp_ref = 0;
  
  	// check that new page tables get cleared
  	memset(page2kva(pp0), 0xFF, PGSIZE);
  	page_free(pp0);
  	pgdir_walk(kern_pgdir, 0x0, 1);
  	ptep = (pte_t *) page2kva(pp0);
  	for(i=0; i<NPTENTRIES; i++)
  		assert((ptep[i] & PTE_P) == 0);
  	kern_pgdir[0] = 0;
  	pp0->pp_ref = 0;
  
  	// give free list back
  	page_free_list = fl;
  
  	// free the pages we took
  	page_free(pp0);
  	page_free(pp1);
  	page_free(pp2);
  
  	cprintf("check_page() succeeded!\n");
  }
  
  // check page_insert, page_remove, &c, with an installed kern_pgdir
  static void
  check_page_installed_pgdir(void)
  {
  	struct PageInfo *pp, *pp0, *pp1, *pp2;
  	struct PageInfo *fl;
  	pte_t *ptep, *ptep1;
  	uintptr_t va;
  	int i;
  
  	// check that we can read and write installed pages
  	pp1 = pp2 = 0;
  	assert((pp0 = page_alloc(0)));
  	assert((pp1 = page_alloc(0)));
  	assert((pp2 = page_alloc(0)));
  	page_free(pp0);
  	memset(page2kva(pp1), 1, PGSIZE);
  	memset(page2kva(pp2), 2, PGSIZE);
  	page_insert(kern_pgdir, pp1, (void*) PGSIZE, PTE_W);
  	assert(pp1->pp_ref == 1);
  	assert(*(uint32_t *)PGSIZE == 0x01010101U);
  	page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W);
  	assert(*(uint32_t *)PGSIZE == 0x02020202U);
  	assert(pp2->pp_ref == 1);
  	assert(pp1->pp_ref == 0);
  	*(uint32_t *)PGSIZE = 0x03030303U;
  	assert(*(uint32_t *)page2kva(pp2) == 0x03030303U);
  	page_remove(kern_pgdir, (void*) PGSIZE);
  	assert(pp2->pp_ref == 0);
  
  	// forcibly take pp0 back
  	assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0));
  	kern_pgdir[0] = 0;
  	assert(pp0->pp_ref == 1);
  	pp0->pp_ref = 0;
  
  	// free the pages we took
  	page_free(pp0);
  
  	cprintf("check_page_installed_pgdir() succeeded!\n");
  }
  