Lab2, exercise 6, basic paging
[akaros.git] / kern / pmap.c
1 /* See COPYRIGHT for copyright information. */
2
3 #include <inc/x86.h>
4 #include <inc/mmu.h>
5 #include <inc/error.h>
6 #include <inc/string.h>
7 #include <inc/assert.h>
8
9 #include <kern/pmap.h>
10 #include <kern/kclock.h>
11
12 // These variables are set by i386_detect_memory()
13 static physaddr_t maxpa;        // Maximum physical address
14 size_t npage;                   // Amount of physical memory (in pages)
15 static size_t basemem;          // Amount of base memory (in bytes)
16 static size_t extmem;           // Amount of extended memory (in bytes)
17
18 // These variables are set in i386_vm_init()
19 pde_t* boot_pgdir;              // Virtual address of boot time page directory
20 physaddr_t boot_cr3;            // Physical address of boot time page directory
21 static char* boot_freemem;      // Pointer to next byte of free mem
22
23 struct Page* pages;             // Virtual address of physical page array
24 static struct Page_list page_free_list; // Free list of physical pages
25
26 // Global descriptor table.
27 //
28 // The kernel and user segments are identical (except for the DPL).
29 // To load the SS register, the CPL must equal the DPL.  Thus,
30 // we must duplicate the segments for the user and the kernel.
31 //
32 struct Segdesc gdt[] =
33 {
34         // 0x0 - unused (always faults -- for trapping NULL far pointers)
35         SEG_NULL,
36
37         // 0x8 - kernel code segment
38         [GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),
39
40         // 0x10 - kernel data segment
41         [GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0),
42
43         // 0x18 - user code segment
44         [GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),
45
46         // 0x20 - user data segment
47         [GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
48
49         // 0x28 - tss, initialized in idt_init()
50         [GD_TSS >> 3] = SEG_NULL
51 };
52
53 struct Pseudodesc gdt_pd = {
54         sizeof(gdt) - 1, (unsigned long) gdt
55 };
56
57 static int
58 nvram_read(int r)
59 {
60         return mc146818_read(r) | (mc146818_read(r + 1) << 8);
61 }
62
63 void
64 i386_detect_memory(void)
65 {
66         /* For shit BIOS reasons, this isn't seeing any more than 64MB,
67          * explained a little here: 
68          * http://exec.h1.ru/docs/os-devel-faq/os-faq-memory.html
69          */
70
71         // CMOS tells us how many kilobytes there are
72         basemem = ROUNDDOWN(nvram_read(NVRAM_BASELO)*1024, PGSIZE);
73         extmem = ROUNDDOWN(nvram_read(NVRAM_EXTLO)*1024, PGSIZE);
74
75         // Calculate the maximum physical address based on whether
76         // or not there is any extended memory.  See comment in <inc/memlayout.h>
77         if (extmem)
78                 maxpa = EXTPHYSMEM + extmem;
79         else
80                 maxpa = basemem;
81
82         npage = maxpa / PGSIZE;
83
84         cprintf("Physical memory: %dK available, ", (int)(maxpa/1024));
85         cprintf("base = %dK, extended = %dK\n", (int)(basemem/1024), (int)(extmem/1024));
86 }
87
88 // --------------------------------------------------------------
89 // Set up initial memory mappings and turn on MMU.
90 // --------------------------------------------------------------
91
92 static void check_boot_pgdir(void);
93
94 //
95 // Allocate n bytes of physical memory aligned on an 
96 // align-byte boundary.  Align must be a power of two.
97 // Return kernel virtual address.  Returned memory is uninitialized.
98 //
99 // If we're out of memory, boot_alloc should panic.
100 // This function may ONLY be used during initialization,
101 // before the page_free_list has been set up.
102 // 
103 static void*
104 boot_alloc(uint32_t n, uint32_t align)
105 {
106         extern char end[];
107         void *v;
108
109         // Initialize boot_freemem if this is the first time.
110         // 'end' is a magic symbol automatically generated by the linker,
111         // which points to the end of the kernel's bss segment -
112         // i.e., the first virtual address that the linker
113         // did _not_ assign to any kernel code or global variables.
114         if (boot_freemem == 0)
115                 boot_freemem = end;
116
117         //      Step 1: round boot_freemem up to be aligned properly
118         boot_freemem = ROUNDUP(boot_freemem, align);
119
120         //      Step 2: save current value of boot_freemem as allocated chunk
121         v = boot_freemem;
122         //  Step 2.5: check if we can alloc
123         if (PADDR(boot_freemem + n) > maxpa)
124                 panic("Out of memory in boot alloc, you fool!\n");
125         //      Step 3: increase boot_freemem to record allocation
126         boot_freemem += n;      
127         //      Step 4: return allocated chunk
128         return v;
129 }
130
131 //
132 // Given pgdir, a pointer to a page directory,
133 // walk the 2-level page table structure to find
134 // the page table entry (PTE) for linear address la.
135 // Return a pointer to this PTE.
136 //
137 // If the relevant page table doesn't exist in the page directory:
138 //      - If create == 0, return 0.
139 //      - Otherwise allocate a new page table, install it into pgdir,
140 //        and return a pointer into it.
141 //        (Questions: What data should the new page table contain?
142 //        And what permissions should the new pgdir entry have?
143 //        Note that we use the 486-only "WP" feature of %cr0, which
144 //        affects the way supervisor-mode writes are checked.)
145 //
146 // This function abstracts away the 2-level nature of
147 // the page directory by allocating new page tables
148 // as needed.
149 // 
150 // boot_pgdir_walk may ONLY be used during initialization,
151 // before the page_free_list has been set up.
152 // It should panic on failure.  (Note that boot_alloc already panics
153 // on failure.)
154 //
155 static pte_t*
156 boot_pgdir_walk(pde_t *pgdir, uintptr_t la, int create)
157 {
158         pde_t* the_pde = &pgdir[PDX(la)];
159         void* new_table;
160
161         if (*the_pde & PTE_P)
162                 return &((pde_t*)KADDR(PTE_ADDR(*the_pde)))[PTX(la)];
163         if (!create)
164                 return 0;
165         new_table = boot_alloc(PGSIZE, PGSIZE);
166         memset(new_table, 0, PGSIZE);
167         *the_pde = (pde_t)PADDR(new_table) | PTE_P | PTE_W;
168         return &((pde_t*)KADDR(PTE_ADDR(*the_pde)))[PTX(la)];
169 }
170
171 //
172 // Map [la, la+size) of linear address space to physical [pa, pa+size)
173 // in the page table rooted at pgdir.  Size is a multiple of PGSIZE.
174 // Use permission bits perm|PTE_P for the entries.
175 //
176 // This function may ONLY be used during initialization,
177 // before the page_free_list has been set up.
178 //
179 static void
180 boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, physaddr_t pa, int perm)
181 {
182         uintptr_t i;
183         pte_t *pte;
184         // la can be page unaligned, but weird things will happen
185         // unless pa has the same offset.  pa always truncates any
186         // possible offset.  will warn.  size can be weird too. 
187         if (PGOFF(la)) {
188                 warn("la not page aligned in boot_map_segment!");
189                 size += PGOFF(la);
190         }
191         // even though our maxpa doesn't go above 64MB yet...
192         if (pa + size > maxpa)
193                 warn("Attempting to map to physical memory beyond maxpa!");
194         // need to index with i instead of la + size, in case of wrap-around
195         for (i = 0; i < size; i += PGSIZE, la += PGSIZE, pa += PGSIZE) {
196                 pte = boot_pgdir_walk(pgdir, la, 1);
197                 *pte = PTE_ADDR(pa) | PTE_P | perm;
198         }
199 }
200
201 // Set up a two-level page table:
202 //    boot_pgdir is its linear (virtual) address of the root
203 //    boot_cr3 is the physical adresss of the root
204 // Then turn on paging.  Then effectively turn off segmentation.
205 // (i.e., the segment base addrs are set to zero).
206 // 
207 // This function only sets up the kernel part of the address space
208 // (ie. addresses >= UTOP).  The user part of the address space
209 // will be setup later.
210 //
211 // From UTOP to ULIM, the user is allowed to read but not write.
212 // Above ULIM the user cannot read (or write). 
213 void
214 i386_vm_init(void)
215 {
216         pde_t* pgdir;
217         uint32_t cr0;
218         size_t n;
219
220         //////////////////////////////////////////////////////////////////////
221         // create initial page directory.
222         pgdir = boot_alloc(PGSIZE, PGSIZE);
223         memset(pgdir, 0, PGSIZE);
224         boot_pgdir = pgdir;
225         boot_cr3 = PADDR(pgdir);
226
227         //////////////////////////////////////////////////////////////////////
228         // Recursively insert PD in itself as a page table, to form
229         // a virtual page table at virtual address VPT.
230         // (For now, you don't have understand the greater purpose of the
231         // following two lines.  Unless you are eagle-eyed, in which case you
232         // should already know.)
233
234         // Permissions: kernel RW, user NONE
235         pgdir[PDX(VPT)] = PADDR(pgdir)|PTE_W|PTE_P;
236
237         // same for UVPT
238         // Permissions: kernel R, user R 
239         pgdir[PDX(UVPT)] = PADDR(pgdir)|PTE_U|PTE_P;
240
241         //////////////////////////////////////////////////////////////////////
242         // Map the kernel stack (symbol name "bootstack").  The complete VA
243         // range of the stack, [KSTACKTOP-PTSIZE, KSTACKTOP), breaks into two
244         // pieces:
245         //     * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
246         //     * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed => faults
247         //     Permissions: kernel RW, user NONE
248         // Your code goes here:
249
250         // remember that the space for the kernel stack is allocated in the binary.
251         // bootstack and bootstacktop point to symbols in the data section, which 
252         // at this point are like 0xc010b000.  KSTACKTOP is the desired loc in VM
253         boot_map_segment(pgdir, (uintptr_t)KSTACKTOP - KSTKSIZE, 
254                          KSTKSIZE, PADDR(bootstack), PTE_W);
255
256         //////////////////////////////////////////////////////////////////////
257         // Map all of physical memory at KERNBASE. 
258         // Ie.  the VA range [KERNBASE, 2^32) should map to
259         //      the PA range [0, 2^32 - KERNBASE)
260         // We might not have 2^32 - KERNBASE bytes of physical memory, but
261         // we just set up the mapping anyway.
262         // Permissions: kernel RW, user NONE
263         // Your code goes here: 
264         
265         // this maps all of the possible phys memory
266         // note the use of unsigned underflow to get size = 0x40000000
267         //boot_map_segment(pgdir, KERNBASE, -KERNBASE, 0, PTE_W);
268         // but this only maps what is available, and saves memory.  every 4MB of
269         // mapped memory requires a 2nd level page: 2^10 entries, each covering 2^12
270         // need to modify tests below to account for this
271         boot_map_segment(pgdir, KERNBASE, maxpa, 0, PTE_W);
272
273         //////////////////////////////////////////////////////////////////////
274         // Make 'pages' point to an array of size 'npage' of 'struct Page'.
275         // The kernel uses this structure to keep track of physical pages;
276         // 'npage' equals the number of physical pages in memory.  User-level
277         // programs get read-only access to the array as well.
278         // You must allocate the array yourself.
279         // Map this array read-only by the user at linear address UPAGES
280         // (ie. perm = PTE_U | PTE_P)
281         // Permissions:
282         //    - pages -- kernel RW, user NONE
283         //    - the read-only version mapped at UPAGES -- kernel R, user R
284         // Your code goes here: 
285         
286         // round up to the nearest page
287         size_t page_array_size = ROUNDUP(npage*sizeof(struct Page), PGSIZE);
288         pages = (struct Page*)boot_alloc(page_array_size, PGSIZE);
289         memset(pages, 0, page_array_size);
290         if (page_array_size > PTSIZE) {
291                 warn("page_array_size bigger than PTSIZE, userland will not see all pages");
292                 page_array_size = PTSIZE;
293         }
294         boot_map_segment(pgdir, UPAGES, page_array_size, PADDR(pages), PTE_U);
295
296         // Check that the initial page directory has been set up correctly.
297         check_boot_pgdir();
298
299         //////////////////////////////////////////////////////////////////////
300         // On x86, segmentation maps a VA to a LA (linear addr) and
301         // paging maps the LA to a PA.  I.e. VA => LA => PA.  If paging is
302         // turned off the LA is used as the PA.  Note: there is no way to
303         // turn off segmentation.  The closest thing is to set the base
304         // address to 0, so the VA => LA mapping is the identity.
305
306         // Current mapping: VA KERNBASE+x => PA x.
307         //     (segmentation base=-KERNBASE and paging is off)
308
309         // From here on down we must maintain this VA KERNBASE + x => PA x
310         // mapping, even though we are turning on paging and reconfiguring
311         // segmentation.
312
313         // Map VA 0:4MB same as VA KERNBASE, i.e. to PA 0:4MB.
314         // (Limits our kernel to <4MB)
315         /* They mean linear address 0:4MB, and the kernel < 4MB is only until 
316          * segmentation is turned off.
317          * once we turn on paging, segmentation is still on, so references to
318          * KERNBASE+x will get mapped to linear address x, which we need to make 
319          * sure can map to phys addr x, until we can turn off segmentation and
320          * KERNBASE+x maps to LA KERNBASE+x, which maps to PA x, via paging
321          */
322         pgdir[0] = pgdir[PDX(KERNBASE)];
323
324         // Install page table.
325         lcr3(boot_cr3);
326
327         // Turn on paging.
328         cr0 = rcr0();
329         // not sure why they turned on TS and EM here.  or anything other 
330         // than PG and WP
331         cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_TS|CR0_EM|CR0_MP;
332         cr0 &= ~(CR0_TS|CR0_EM);
333         lcr0(cr0);
334
335         // Current mapping: KERNBASE+x => x => x.
336         // (x < 4MB so uses paging pgdir[0])
337
338         // Reload all segment registers.
339         asm volatile("lgdt gdt_pd");
340         asm volatile("movw %%ax,%%gs" :: "a" (GD_UD|3));
341         asm volatile("movw %%ax,%%fs" :: "a" (GD_UD|3));
342         asm volatile("movw %%ax,%%es" :: "a" (GD_KD));
343         asm volatile("movw %%ax,%%ds" :: "a" (GD_KD));
344         asm volatile("movw %%ax,%%ss" :: "a" (GD_KD));
345         asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (GD_KT));  // reload cs
346         asm volatile("lldt %%ax" :: "a" (0));
347
348         // Final mapping: KERNBASE+x => KERNBASE+x => x.
349
350         // This mapping was only used after paging was turned on but
351         // before the segment registers were reloaded.
352         pgdir[0] = 0;
353
354         // Flush the TLB for good measure, to kill the pgdir[0] mapping.
355         lcr3(boot_cr3);
356 }
357
358 //
359 // Checks that the kernel part of virtual address space
360 // has been setup roughly correctly(by i386_vm_init()).
361 //
362 // This function doesn't test every corner case,
363 // in fact it doesn't test the permission bits at all,
364 // but it is a pretty good sanity check. 
365 //
366 static physaddr_t check_va2pa(pde_t *pgdir, uintptr_t va);
367
368 static void
369 check_boot_pgdir(void)
370 {
371         uint32_t i, n;
372         pde_t *pgdir;
373
374         pgdir = boot_pgdir;
375
376         // check pages array
377         n = ROUNDUP(npage*sizeof(struct Page), PGSIZE);
378         for (i = 0; i < n; i += PGSIZE)
379                 assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i);
380
381         // check phys mem
382         //for (i = 0; KERNBASE + i != 0; i += PGSIZE)
383         // adjusted check to account for only mapping avail mem
384         for (i = 0; i < maxpa; i += PGSIZE)
385                 assert(check_va2pa(pgdir, KERNBASE + i) == i);
386
387         // check kernel stack
388         for (i = 0; i < KSTKSIZE; i += PGSIZE)
389                 assert(check_va2pa(pgdir, KSTACKTOP - KSTKSIZE + i) == PADDR(bootstack) + i);
390
391         // check for zero/non-zero in PDEs
392         for (i = 0; i < NPDENTRIES; i++) {
393                 switch (i) {
394                 case PDX(VPT):
395                 case PDX(UVPT):
396                 case PDX(KSTACKTOP-1):
397                 case PDX(UPAGES):
398                         assert(pgdir[i]);
399                         break;
400                 default:
401                         //if (i >= PDX(KERNBASE))
402                         // adjusted check to account for only mapping avail mem
403                         // and you can't KADDR maxpa (just above legal range)
404                         if (i >= PDX(KERNBASE) && i <= PDX(KADDR(maxpa-1)))
405                                 assert(pgdir[i]);
406                         else
407                                 assert(pgdir[i] == 0);
408                         break;
409                 }
410         }
411         cprintf("check_boot_pgdir() succeeded!\n");
412 }
413
414 // This function returns the physical address of the page containing 'va',
415 // defined by the page directory 'pgdir'.  The hardware normally performs
416 // this functionality for us!  We define our own version to help check
417 // the check_boot_pgdir() function; it shouldn't be used elsewhere.
418
419 static physaddr_t
420 check_va2pa(pde_t *pgdir, uintptr_t va)
421 {
422         pte_t *p;
423
424         pgdir = &pgdir[PDX(va)];
425         if (!(*pgdir & PTE_P))
426                 return ~0;
427         p = (pte_t*) KADDR(PTE_ADDR(*pgdir));
428         if (!(p[PTX(va)] & PTE_P))
429                 return ~0;
430         return PTE_ADDR(p[PTX(va)]);
431 }
432                 
433 // --------------------------------------------------------------
434 // Tracking of physical pages.
435 // The 'pages' array has one 'struct Page' entry per physical page.
436 // Pages are reference counted, and free pages are kept on a linked list.
437 // --------------------------------------------------------------
438
439 //  
440 // Initialize page structure and memory free list.
441 // After this point, ONLY use the functions below
442 // to allocate and deallocate physical memory via the page_free_list,
443 // and NEVER use boot_alloc() or the related boot-time functions above.
444 //
445 void
446 page_init(void)
447 {
448         // The example code here marks all pages as free.
449         // However this is not truly the case.  What memory is free?
450         //  1) Mark page 0 as in use.
451         //     This way we preserve the real-mode IDT and BIOS structures
452         //     in case we ever need them.  (Currently we don't, but...)
453         //  2) Mark the rest of base memory as free.
454         //  3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM).
455         //     Mark it as in use so that it can never be allocated.      
456         //  4) Then extended memory [EXTPHYSMEM, ...).
457         //     Some of it is in use, some is free. Where is the kernel?
458         //     Which pages are used for page tables and other data structures?
459         //
460         // Change the code to reflect this.
461         int i;
462         physaddr_t physaddr_after_kernel = PADDR(ROUNDUP(boot_freemem, PGSIZE));
463         LIST_INIT(&page_free_list);
464
465         pages[0].pp_ref = 1;
466         for (i = 1; i < PPN(IOPHYSMEM); i++) {
467                 pages[i].pp_ref = 0;
468                 LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link);
469         }
470         for (i = PPN(IOPHYSMEM); i < PPN(EXTPHYSMEM); i++) {
471                 pages[i].pp_ref = 1;
472         }
473         for (i = PPN(EXTPHYSMEM); i < PPN(physaddr_after_kernel); i++) {
474                 pages[i].pp_ref = 1;
475         }
476         for (i = PPN(physaddr_after_kernel); i < npage; i++) {
477                 pages[i].pp_ref = 0;
478                 LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link);
479         }
480 }
481
482 //
483 // Initialize a Page structure.
484 // The result has null links and 0 refcount.
485 // Note that the corresponding physical page is NOT initialized!
486 //
487 static void
488 page_initpp(struct Page *pp)
489 {
490         memset(pp, 0, sizeof(*pp));
491 }
492
493 //
494 // Allocates a physical page.
495 // Does NOT set the contents of the physical page to zero -
496 // the caller must do that if necessary.
497 //
498 // *pp_store -- is set to point to the Page struct of the newly allocated
499 // page
500 //
501 // RETURNS 
502 //   0 -- on success
503 //   -E_NO_MEM -- otherwise 
504 //
505 // Hint: use LIST_FIRST, LIST_REMOVE, and page_initpp
506 // Hint: pp_ref should not be incremented 
507 int
508 page_alloc(struct Page **pp_store)
509 {
510         if (LIST_EMPTY(&page_free_list))
511                 return -E_NO_MEM;
512         *pp_store = LIST_FIRST(&page_free_list);
513         LIST_REMOVE(*pp_store, pp_link);
514         page_initpp(*pp_store);
515         return 0;
516 }
517
518 //
519 // Return a page to the free list.
520 // (This function should only be called when pp->pp_ref reaches 0.)
521 //
522 void
523 page_free(struct Page *pp)
524 {
525         if (pp->pp_ref)
526                 panic("Attempting to free page with non-zero reference count!");
527         LIST_INSERT_HEAD(&page_free_list, pp, pp_link);
528 }
529
530 //
531 // Decrement the reference count on a page,
532 // freeing it if there are no more refs.
533 //
534 void
535 page_decref(struct Page* pp)
536 {
537         if (--pp->pp_ref == 0)
538                 page_free(pp);
539 }
540
541 // Given 'pgdir', a pointer to a page directory, pgdir_walk returns
542 // a pointer to the page table entry (PTE) for linear address 'va'.
543 // This requires walking the two-level page table structure.
544 //
545 // If the relevant page table doesn't exist in the page directory, then:
546 //    - If create == 0, pgdir_walk returns NULL.
547 //    - Otherwise, pgdir_walk tries to allocate a new page table
548 //      with page_alloc.  If this fails, pgdir_walk returns NULL.
549 //    - Otherwise, pgdir_walk returns a pointer into the new page table.
550 //
551 // This is boot_pgdir_walk, but using page_alloc() instead of boot_alloc().
552 // Unlike boot_pgdir_walk, pgdir_walk can fail.
553 //
554 // Hint: you can turn a Page * into the physical address of the
555 // page it refers to with page2pa() from kern/pmap.h.
556 pte_t*
557 pgdir_walk(pde_t *pgdir, const void *va, int create)
558 {
559         pde_t* the_pde = &pgdir[PDX(va)];
560         struct Page* new_table;
561
562         if (*the_pde & PTE_P)
563                 return &((pde_t*)KADDR(PTE_ADDR(*the_pde)))[PTX(va)];
564         if (!create)
565                 return NULL;
566         if (page_alloc(&new_table))
567                 return NULL;
568         new_table->pp_ref = 1;
569         memset(page2kva(new_table), 0, PGSIZE);
570         *the_pde = (pde_t)page2pa(new_table) | PTE_P | PTE_W;
571         return &((pde_t*)KADDR(PTE_ADDR(*the_pde)))[PTX(va)];
572 }
573 //
574 // Map the physical page 'pp' at virtual address 'va'.
575 // The permissions (the low 12 bits) of the page table
576 //  entry should be set to 'perm|PTE_P'.
577 //
578 // Details
579 //   - If there is already a page mapped at 'va', it is page_remove()d.
580 //   - If necessary, on demand, allocates a page table and inserts it into
581 //     'pgdir'.
582 //   - pp->pp_ref should be incremented if the insertion succeeds.
583 //   - The TLB must be invalidated if a page was formerly present at 'va'.
584 //     (this is handled in page_remove)
585 //
586 // RETURNS: 
587 //   0 on success
588 //   -E_NO_MEM, if page table couldn't be allocated
589 //
590 // Hint: The TA solution is implemented using pgdir_walk, page_remove,
591 // and page2pa.
592 //
593 int
594 page_insert(pde_t *pgdir, struct Page *pp, void *va, int perm) 
595 {
596         pte_t* pte = pgdir_walk(pgdir, va, 1);
597         if (!pte)
598                 return -E_NO_MEM;
599         // need to up the ref count in case pp is already mapped at va
600         // and we don't want to page_remove (which could free pp) and then 
601         // continue as if pp wasn't freed.  moral = up the ref asap
602         pp->pp_ref++;
603         if (*pte & PTE_P) {
604                 page_remove(pgdir, va);
605         }
606         *pte = page2pa(pp) | PTE_P | perm;
607         return 0;
608 }
609
610 //
611 // Return the page mapped at virtual address 'va'.
612 // If pte_store is not zero, then we store in it the address
613 // of the pte for this page.  This is used by page_remove
614 // but should not be used by other callers.
615 //
616 // Return 0 if there is no page mapped at va.
617 //
618 // Hint: the TA solution uses pgdir_walk and pa2page.
619 //
620 struct Page *
621 page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
622 {
623         pte_t* pte = pgdir_walk(pgdir, va, 0);
624         if (!pte || !(*pte & PTE_P))
625                 return 0;
626         if (pte_store)
627                 *pte_store = pte;
628         return pa2page(PTE_ADDR(*pte));
629 }
630
631 //
632 // Unmaps the physical page at virtual address 'va'.
633 // If there is no physical page at that address, silently does nothing.
634 //
635 // Details:
636 //   - The ref count on the physical page should decrement.
637 //   - The physical page should be freed if the refcount reaches 0.
638 //   - The pg table entry corresponding to 'va' should be set to 0.
639 //     (if such a PTE exists)
640 //   - The TLB must be invalidated if you remove an entry from
641 //     the pg dir/pg table.
642 //
643 // Hint: The TA solution is implemented using page_lookup,
644 //      tlb_invalidate, and page_decref.
645 //
646 void
647 page_remove(pde_t *pgdir, void *va)
648 {
649         pte_t* pte;
650         struct Page* page;
651         page = page_lookup(pgdir, va, &pte);
652         if (!page)
653                 return;
654         *pte = 0;
655         tlb_invalidate(pgdir, va);
656         page_decref(page);
657 }
658
659 //
660 // Invalidate a TLB entry, but only if the page tables being
661 // edited are the ones currently in use by the processor.
662 //
663 void
664 tlb_invalidate(pde_t *pgdir, void *va)
665 {
666         // Flush the entry only if we're modifying the current address space.
667         // For now, there is only one address space, so always invalidate.
668         invlpg(va);
669 }
670
671 void
672 page_check(void)
673 {
674         struct Page *pp, *pp0, *pp1, *pp2;
675         struct Page_list fl;
676         pte_t *ptep;
677
678         // should be able to allocate three pages
679         pp0 = pp1 = pp2 = 0;
680         assert(page_alloc(&pp0) == 0);
681         assert(page_alloc(&pp1) == 0);
682         assert(page_alloc(&pp2) == 0);
683
684         assert(pp0);
685         assert(pp1 && pp1 != pp0);
686         assert(pp2 && pp2 != pp1 && pp2 != pp0);
687
688         // temporarily steal the rest of the free pages
689         fl = page_free_list;
690         LIST_INIT(&page_free_list);
691
692         // should be no free memory
693         assert(page_alloc(&pp) == -E_NO_MEM);
694
695         // there is no page allocated at address 0
696         assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
697
698         // there is no free memory, so we can't allocate a page table 
699         assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
700
701         // free pp0 and try again: pp0 should be used for page table
702         page_free(pp0);
703         assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
704         assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
705         assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
706         assert(pp1->pp_ref == 1);
707         assert(pp0->pp_ref == 1);
708
709         // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
710         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
711         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
712         assert(pp2->pp_ref == 1);
713
714         // should be no free memory
715         assert(page_alloc(&pp) == -E_NO_MEM);
716
717         // should be able to map pp2 at PGSIZE because it's already there
718         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
719         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
720         assert(pp2->pp_ref == 1);
721
722         // pp2 should NOT be on the free list
723         // could happen in ref counts are handled sloppily in page_insert
724         assert(page_alloc(&pp) == -E_NO_MEM);
725
726         // should not be able to map at PTSIZE because need free page for page table
727         assert(page_insert(boot_pgdir, pp0, (void*) PTSIZE, 0) < 0);
728
729         // insert pp1 at PGSIZE (replacing pp2)
730         assert(page_insert(boot_pgdir, pp1, (void*) PGSIZE, 0) == 0);
731
732         // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
733         assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1));
734         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
735         // ... and ref counts should reflect this
736         assert(pp1->pp_ref == 2);
737         assert(pp2->pp_ref == 0);
738
739         // pp2 should be returned by page_alloc
740         assert(page_alloc(&pp) == 0 && pp == pp2);
741
742         // unmapping pp1 at 0 should keep pp1 at PGSIZE
743         page_remove(boot_pgdir, 0x0);
744         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
745         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
746         assert(pp1->pp_ref == 1);
747         assert(pp2->pp_ref == 0);
748
749         // unmapping pp1 at PGSIZE should free it
750         page_remove(boot_pgdir, (void*) PGSIZE);
751         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
752         assert(check_va2pa(boot_pgdir, PGSIZE) == ~0);
753         assert(pp1->pp_ref == 0);
754         assert(pp2->pp_ref == 0);
755
756         // so it should be returned by page_alloc
757         assert(page_alloc(&pp) == 0 && pp == pp1);
758
759         // should be no free memory
760         assert(page_alloc(&pp) == -E_NO_MEM);
761
762         // forcibly take pp0 back
763         assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
764         boot_pgdir[0] = 0;
765         assert(pp0->pp_ref == 1);
766         pp0->pp_ref = 0;
767
768         // give free list back
769         page_free_list = fl;
770
771         // free the pages we took
772         page_free(pp0);
773         page_free(pp1);
774         page_free(pp2);
775
776         cprintf("page_check() succeeded!\n");
777 }
778
779
780 /* 
781         // helpful if you want to manually walk with kvm / bochs
782         //cprintf("pgdir va = %08p, pgdir pa = %08p\n\n", pgdir, PADDR(pgdir));
783
784     // testing code for boot_pgdir_walk 
785         pte_t* temp;
786         temp = boot_pgdir_walk(pgdir, VPT + (VPT >> 10), 1);
787         cprintf("pgdir = %p\n", pgdir);
788         cprintf("test recursive walking pte_t* = %p\n", temp);
789         cprintf("test recursive walking entry = %p\n", PTE_ADDR(temp));
790         temp = boot_pgdir_walk(pgdir, 0xc0400000, 1);
791         cprintf("LA = 0xc0400000 = %p\n", temp);
792         temp = boot_pgdir_walk(pgdir, 0xc0400070, 1);
793         cprintf("LA = 0xc0400070 = %p\n", temp);
794         temp = boot_pgdir_walk(pgdir, 0xc0800000, 0);
795         cprintf("LA = 0xc0800000, no create = %p\n", temp);
796         temp = boot_pgdir_walk(pgdir, 0xc0600070, 1);
797         cprintf("LA = 0xc0600070 = %p\n", temp);
798         temp = boot_pgdir_walk(pgdir, 0xc0600090, 0);
799         cprintf("LA = 0xc0600090, nc = %p\n", temp);
800         temp = boot_pgdir_walk(pgdir, 0xc0608070, 0);
801         cprintf("LA = 0xc0608070, nc = %p\n", temp);
802         temp = boot_pgdir_walk(pgdir, 0xc0800070, 1);
803         cprintf("LA = 0xc0800070 = %p\n", temp);
804         temp = boot_pgdir_walk(pgdir, 0xc0b00070, 0);
805         cprintf("LA = 0xc0b00070, nc = %p\n", temp);
806         temp = boot_pgdir_walk(pgdir, 0xc0c00000, 0);
807         cprintf("LA = 0xc0c00000, nc = %p\n", temp);
808
809         // testing for boot_map_seg
810         cprintf("\n");
811         cprintf("before mapping 1 page to 0x00350000\n");
812         cprintf("0xc4000000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc4000000, 1));
813         cprintf("0xc4000000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc4000000, 1)));
814         boot_map_segment(pgdir, 0xc4000000, 4096, 0x00350000, PTE_W);
815         cprintf("after mapping\n");
816         cprintf("0xc4000000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc4000000, 1));
817         cprintf("0xc4000000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc4000000, 1)));
818
819         cprintf("\n");
820         cprintf("before mapping 3 pages to 0x00700000\n");
821         cprintf("0xd0000000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0000000, 1));
822         cprintf("0xd0000000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0000000, 1)));
823         cprintf("0xd0001000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0001000, 1));
824         cprintf("0xd0001000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0001000, 1)));
825         cprintf("0xd0002000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0002000, 1));
826         cprintf("0xd0002000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0002000, 1)));
827         boot_map_segment(pgdir, 0xd0000000, 4096*3, 0x00700000, 0);
828         cprintf("after mapping\n");
829         cprintf("0xd0000000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0000000, 1));
830         cprintf("0xd0000000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0000000, 1)));
831         cprintf("0xd0001000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0001000, 1));
832         cprintf("0xd0001000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0001000, 1)));
833         cprintf("0xd0002000's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xd0002000, 1));
834         cprintf("0xd0002000's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xd0002000, 1)));
835
836         cprintf("\n");
837         cprintf("before mapping 1 unaligned to 0x00500010\n");
838         cprintf("0xc8000010's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc8000010, 1));
839         cprintf("0xc8000010's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc8000010, 1)));
840         cprintf("0xc8001010's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc8001010, 1));
841         cprintf("0xc8001010's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc8001010, 1)));
842         boot_map_segment(pgdir, 0xc8000010, 4096, 0x00500010, PTE_W);
843         cprintf("after mapping\n");
844         cprintf("0xc8000010's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc8000010, 1));
845         cprintf("0xc8000010's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc8000010, 1)));
846         cprintf("0xc8001010's &pte: %08x\n",boot_pgdir_walk(pgdir, 0xc8001010, 1));
847         cprintf("0xc8001010's pte: %08x\n",*(boot_pgdir_walk(pgdir, 0xc8001010, 1)));
848
849         cprintf("\n");
850         boot_map_segment(pgdir, 0xe0000000, 4096, 0x10000000, PTE_W);
851
852 */