Lab2 initial changes
[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         // CMOS tells us how many kilobytes there are
67         basemem = ROUNDDOWN(nvram_read(NVRAM_BASELO)*1024, PGSIZE);
68         extmem = ROUNDDOWN(nvram_read(NVRAM_EXTLO)*1024, PGSIZE);
69
70         // Calculate the maximum physical address based on whether
71         // or not there is any extended memory.  See comment in <inc/mmu.h>.
72         if (extmem)
73                 maxpa = EXTPHYSMEM + extmem;
74         else
75                 maxpa = basemem;
76
77         npage = maxpa / PGSIZE;
78
79         cprintf("Physical memory: %dK available, ", (int)(maxpa/1024));
80         cprintf("base = %dK, extended = %dK\n", (int)(basemem/1024), (int)(extmem/1024));
81 }
82
83 // --------------------------------------------------------------
84 // Set up initial memory mappings and turn on MMU.
85 // --------------------------------------------------------------
86
87 static void check_boot_pgdir(void);
88
89 //
90 // Allocate n bytes of physical memory aligned on an 
91 // align-byte boundary.  Align must be a power of two.
92 // Return kernel virtual address.  Returned memory is uninitialized.
93 //
94 // If we're out of memory, boot_alloc should panic.
95 // This function may ONLY be used during initialization,
96 // before the page_free_list has been set up.
97 // 
98 static void*
99 boot_alloc(uint32_t n, uint32_t align)
100 {
101         extern char end[];
102         void *v;
103
104         // Initialize boot_freemem if this is the first time.
105         // 'end' is a magic symbol automatically generated by the linker,
106         // which points to the end of the kernel's bss segment -
107         // i.e., the first virtual address that the linker
108         // did _not_ assign to any kernel code or global variables.
109         if (boot_freemem == 0)
110                 boot_freemem = end;
111
112         // LAB 2: Your code here:
113         //      Step 1: round boot_freemem up to be aligned properly
114         //      Step 2: save current value of boot_freemem as allocated chunk
115         //      Step 3: increase boot_freemem to record allocation
116         //      Step 4: return allocated chunk
117
118         return NULL;
119 }
120
121 //
122 // Given pgdir, a pointer to a page directory,
123 // walk the 2-level page table structure to find
124 // the page table entry (PTE) for linear address la.
125 // Return a pointer to this PTE.
126 //
127 // If the relevant page table doesn't exist in the page directory:
128 //      - If create == 0, return 0.
129 //      - Otherwise allocate a new page table, install it into pgdir,
130 //        and return a pointer into it.
131 //        (Questions: What data should the new page table contain?
132 //        And what permissions should the new pgdir entry have?
133 //        Note that we use the 486-only "WP" feature of %cr0, which
134 //        affects the way supervisor-mode writes are checked.)
135 //
136 // This function abstracts away the 2-level nature of
137 // the page directory by allocating new page tables
138 // as needed.
139 // 
140 // boot_pgdir_walk may ONLY be used during initialization,
141 // before the page_free_list has been set up.
142 // It should panic on failure.  (Note that boot_alloc already panics
143 // on failure.)
144 //
145 static pte_t*
146 boot_pgdir_walk(pde_t *pgdir, uintptr_t la, int create)
147 {
148         return 0;
149 }
150
151 //
152 // Map [la, la+size) of linear address space to physical [pa, pa+size)
153 // in the page table rooted at pgdir.  Size is a multiple of PGSIZE.
154 // Use permission bits perm|PTE_P for the entries.
155 //
156 // This function may ONLY be used during initialization,
157 // before the page_free_list has been set up.
158 //
159 static void
160 boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, physaddr_t pa, int perm)
161 {
162 }
163
164 // Set up a two-level page table:
165 //    boot_pgdir is its linear (virtual) address of the root
166 //    boot_cr3 is the physical adresss of the root
167 // Then turn on paging.  Then effectively turn off segmentation.
168 // (i.e., the segment base addrs are set to zero).
169 // 
170 // This function only sets up the kernel part of the address space
171 // (ie. addresses >= UTOP).  The user part of the address space
172 // will be setup later.
173 //
174 // From UTOP to ULIM, the user is allowed to read but not write.
175 // Above ULIM the user cannot read (or write). 
176 void
177 i386_vm_init(void)
178 {
179         pde_t* pgdir;
180         uint32_t cr0;
181         size_t n;
182
183         // Remove this line when you're ready to test this function.
184         panic("i386_vm_init: This function is not finished\n");
185
186         //////////////////////////////////////////////////////////////////////
187         // create initial page directory.
188         pgdir = boot_alloc(PGSIZE, PGSIZE);
189         memset(pgdir, 0, PGSIZE);
190         boot_pgdir = pgdir;
191         boot_cr3 = PADDR(pgdir);
192
193         //////////////////////////////////////////////////////////////////////
194         // Recursively insert PD in itself as a page table, to form
195         // a virtual page table at virtual address VPT.
196         // (For now, you don't have understand the greater purpose of the
197         // following two lines.)
198
199         // Permissions: kernel RW, user NONE
200         pgdir[PDX(VPT)] = PADDR(pgdir)|PTE_W|PTE_P;
201
202         // same for UVPT
203         // Permissions: kernel R, user R 
204         pgdir[PDX(UVPT)] = PADDR(pgdir)|PTE_U|PTE_P;
205
206         //////////////////////////////////////////////////////////////////////
207         // Map the kernel stack (symbol name "bootstack").  The complete VA
208         // range of the stack, [KSTACKTOP-PTSIZE, KSTACKTOP), breaks into two
209         // pieces:
210         //     * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
211         //     * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed => faults
212         //     Permissions: kernel RW, user NONE
213         // Your code goes here:
214
215         //////////////////////////////////////////////////////////////////////
216         // Map all of physical memory at KERNBASE. 
217         // Ie.  the VA range [KERNBASE, 2^32) should map to
218         //      the PA range [0, 2^32 - KERNBASE)
219         // We might not have 2^32 - KERNBASE bytes of physical memory, but
220         // we just set up the mapping anyway.
221         // Permissions: kernel RW, user NONE
222         // Your code goes here: 
223
224         //////////////////////////////////////////////////////////////////////
225         // Make 'pages' point to an array of size 'npage' of 'struct Page'.
226         // The kernel uses this structure to keep track of physical pages;
227         // 'npage' equals the number of physical pages in memory.  User-level
228         // programs get read-only access to the array as well.
229         // You must allocate the array yourself.
230         // Map this array read-only by the user at linear address UPAGES
231         // (ie. perm = PTE_U | PTE_P)
232         // Permissions:
233         //    - pages -- kernel RW, user NONE
234         //    - the read-only version mapped at UPAGES -- kernel R, user R
235         // Your code goes here: 
236
237         // Check that the initial page directory has been set up correctly.
238         check_boot_pgdir();
239
240         //////////////////////////////////////////////////////////////////////
241         // On x86, segmentation maps a VA to a LA (linear addr) and
242         // paging maps the LA to a PA.  I.e. VA => LA => PA.  If paging is
243         // turned off the LA is used as the PA.  Note: there is no way to
244         // turn off segmentation.  The closest thing is to set the base
245         // address to 0, so the VA => LA mapping is the identity.
246
247         // Current mapping: VA KERNBASE+x => PA x.
248         //     (segmentation base=-KERNBASE and paging is off)
249
250         // From here on down we must maintain this VA KERNBASE + x => PA x
251         // mapping, even though we are turning on paging and reconfiguring
252         // segmentation.
253
254         // Map VA 0:4MB same as VA KERNBASE, i.e. to PA 0:4MB.
255         // (Limits our kernel to <4MB)
256         pgdir[0] = pgdir[PDX(KERNBASE)];
257
258         // Install page table.
259         lcr3(boot_cr3);
260
261         // Turn on paging.
262         cr0 = rcr0();
263         cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_TS|CR0_EM|CR0_MP;
264         cr0 &= ~(CR0_TS|CR0_EM);
265         lcr0(cr0);
266
267         // Current mapping: KERNBASE+x => x => x.
268         // (x < 4MB so uses paging pgdir[0])
269
270         // Reload all segment registers.
271         asm volatile("lgdt gdt_pd");
272         asm volatile("movw %%ax,%%gs" :: "a" (GD_UD|3));
273         asm volatile("movw %%ax,%%fs" :: "a" (GD_UD|3));
274         asm volatile("movw %%ax,%%es" :: "a" (GD_KD));
275         asm volatile("movw %%ax,%%ds" :: "a" (GD_KD));
276         asm volatile("movw %%ax,%%ss" :: "a" (GD_KD));
277         asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (GD_KT));  // reload cs
278         asm volatile("lldt %%ax" :: "a" (0));
279
280         // Final mapping: KERNBASE+x => KERNBASE+x => x.
281
282         // This mapping was only used after paging was turned on but
283         // before the segment registers were reloaded.
284         pgdir[0] = 0;
285
286         // Flush the TLB for good measure, to kill the pgdir[0] mapping.
287         lcr3(boot_cr3);
288 }
289
290 //
291 // Checks that the kernel part of virtual address space
292 // has been setup roughly correctly(by i386_vm_init()).
293 //
294 // This function doesn't test every corner case,
295 // in fact it doesn't test the permission bits at all,
296 // but it is a pretty good sanity check. 
297 //
298 static physaddr_t check_va2pa(pde_t *pgdir, uintptr_t va);
299
300 static void
301 check_boot_pgdir(void)
302 {
303         uint32_t i, n;
304         pde_t *pgdir;
305
306         pgdir = boot_pgdir;
307
308         // check pages array
309         n = ROUNDUP(npage*sizeof(struct Page), PGSIZE);
310         for (i = 0; i < n; i += PGSIZE)
311                 assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i);
312         
313
314         // check phys mem
315         for (i = 0; KERNBASE + i != 0; i += PGSIZE)
316                 assert(check_va2pa(pgdir, KERNBASE + i) == i);
317
318         // check kernel stack
319         for (i = 0; i < KSTKSIZE; i += PGSIZE)
320                 assert(check_va2pa(pgdir, KSTACKTOP - KSTKSIZE + i) == PADDR(bootstack) + i);
321
322         // check for zero/non-zero in PDEs
323         for (i = 0; i < NPDENTRIES; i++) {
324                 switch (i) {
325                 case PDX(VPT):
326                 case PDX(UVPT):
327                 case PDX(KSTACKTOP-1):
328                 case PDX(UPAGES):
329                         assert(pgdir[i]);
330                         break;
331                 default:
332                         if (i >= PDX(KERNBASE))
333                                 assert(pgdir[i]);
334                         else
335                                 assert(pgdir[i] == 0);
336                         break;
337                 }
338         }
339         cprintf("check_boot_pgdir() succeeded!\n");
340 }
341
342 // This function returns the physical address of the page containing 'va',
343 // defined by the page directory 'pgdir'.  The hardware normally performs
344 // this functionality for us!  We define our own version to help check
345 // the check_boot_pgdir() function; it shouldn't be used elsewhere.
346
347 static physaddr_t
348 check_va2pa(pde_t *pgdir, uintptr_t va)
349 {
350         pte_t *p;
351
352         pgdir = &pgdir[PDX(va)];
353         if (!(*pgdir & PTE_P))
354                 return ~0;
355         p = (pte_t*) KADDR(PTE_ADDR(*pgdir));
356         if (!(p[PTX(va)] & PTE_P))
357                 return ~0;
358         return PTE_ADDR(p[PTX(va)]);
359 }
360                 
361 // --------------------------------------------------------------
362 // Tracking of physical pages.
363 // The 'pages' array has one 'struct Page' entry per physical page.
364 // Pages are reference counted, and free pages are kept on a linked list.
365 // --------------------------------------------------------------
366
367 //  
368 // Initialize page structure and memory free list.
369 // After this point, ONLY use the functions below
370 // to allocate and deallocate physical memory via the page_free_list,
371 // and NEVER use boot_alloc() or the related boot-time functions above.
372 //
373 void
374 page_init(void)
375 {
376         // The example code here marks all pages as free.
377         // However this is not truly the case.  What memory is free?
378         //  1) Mark page 0 as in use.
379         //     This way we preserve the real-mode IDT and BIOS structures
380         //     in case we ever need them.  (Currently we don't, but...)
381         //  2) Mark the rest of base memory as free.
382         //  3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM).
383         //     Mark it as in use so that it can never be allocated.      
384         //  4) Then extended memory [EXTPHYSMEM, ...).
385         //     Some of it is in use, some is free. Where is the kernel?
386         //     Which pages are used for page tables and other data structures?
387         //
388         // Change the code to reflect this.
389         int i;
390         LIST_INIT(&page_free_list);
391         for (i = 0; i < npage; i++) {
392                 pages[i].pp_ref = 0;
393                 LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link);
394         }
395 }
396
397 //
398 // Initialize a Page structure.
399 // The result has null links and 0 refcount.
400 // Note that the corresponding physical page is NOT initialized!
401 //
402 static void
403 page_initpp(struct Page *pp)
404 {
405         memset(pp, 0, sizeof(*pp));
406 }
407
408 //
409 // Allocates a physical page.
410 // Does NOT set the contents of the physical page to zero -
411 // the caller must do that if necessary.
412 //
413 // *pp_store -- is set to point to the Page struct of the newly allocated
414 // page
415 //
416 // RETURNS 
417 //   0 -- on success
418 //   -E_NO_MEM -- otherwise 
419 //
420 // Hint: use LIST_FIRST, LIST_REMOVE, and page_initpp
421 // Hint: pp_ref should not be incremented 
422 int
423 page_alloc(struct Page **pp_store)
424 {
425         // Fill this function in
426         return -E_NO_MEM;
427 }
428
429 //
430 // Return a page to the free list.
431 // (This function should only be called when pp->pp_ref reaches 0.)
432 //
433 void
434 page_free(struct Page *pp)
435 {
436         // Fill this function in
437 }
438
439 //
440 // Decrement the reference count on a page,
441 // freeing it if there are no more refs.
442 //
443 void
444 page_decref(struct Page* pp)
445 {
446         if (--pp->pp_ref == 0)
447                 page_free(pp);
448 }
449
450 // Given 'pgdir', a pointer to a page directory, pgdir_walk returns
451 // a pointer to the page table entry (PTE) for linear address 'va'.
452 // This requires walking the two-level page table structure.
453 //
454 // If the relevant page table doesn't exist in the page directory, then:
455 //    - If create == 0, pgdir_walk returns NULL.
456 //    - Otherwise, pgdir_walk tries to allocate a new page table
457 //      with page_alloc.  If this fails, pgdir_walk returns NULL.
458 //    - Otherwise, pgdir_walk returns a pointer into the new page table.
459 //
460 // This is boot_pgdir_walk, but using page_alloc() instead of boot_alloc().
461 // Unlike boot_pgdir_walk, pgdir_walk can fail.
462 //
463 // Hint: you can turn a Page * into the physical address of the
464 // page it refers to with page2pa() from kern/pmap.h.
465 pte_t *
466 pgdir_walk(pde_t *pgdir, const void *va, int create)
467 {
468         // Fill this function in
469         return NULL;
470 }
471
472 //
473 // Map the physical page 'pp' at virtual address 'va'.
474 // The permissions (the low 12 bits) of the page table
475 //  entry should be set to 'perm|PTE_P'.
476 //
477 // Details
478 //   - If there is already a page mapped at 'va', it is page_remove()d.
479 //   - If necessary, on demand, allocates a page table and inserts it into
480 //     'pgdir'.
481 //   - pp->pp_ref should be incremented if the insertion succeeds.
482 //   - The TLB must be invalidated if a page was formerly present at 'va'.
483 //
484 // RETURNS: 
485 //   0 on success
486 //   -E_NO_MEM, if page table couldn't be allocated
487 //
488 // Hint: The TA solution is implemented using pgdir_walk, page_remove,
489 // and page2pa.
490 //
491 int
492 page_insert(pde_t *pgdir, struct Page *pp, void *va, int perm) 
493 {
494         // Fill this function in
495         return 0;
496 }
497
498 //
499 // Return the page mapped at virtual address 'va'.
500 // If pte_store is not zero, then we store in it the address
501 // of the pte for this page.  This is used by page_remove
502 // but should not be used by other callers.
503 //
504 // Return 0 if there is no page mapped at va.
505 //
506 // Hint: the TA solution uses pgdir_walk and pa2page.
507 //
508 struct Page *
509 page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
510 {
511         // Fill this function in
512         return NULL;
513 }
514
515 //
516 // Unmaps the physical page at virtual address 'va'.
517 // If there is no physical page at that address, silently does nothing.
518 //
519 // Details:
520 //   - The ref count on the physical page should decrement.
521 //   - The physical page should be freed if the refcount reaches 0.
522 //   - The pg table entry corresponding to 'va' should be set to 0.
523 //     (if such a PTE exists)
524 //   - The TLB must be invalidated if you remove an entry from
525 //     the pg dir/pg table.
526 //
527 // Hint: The TA solution is implemented using page_lookup,
528 //      tlb_invalidate, and page_decref.
529 //
530 void
531 page_remove(pde_t *pgdir, void *va)
532 {
533         // Fill this function in
534 }
535
536 //
537 // Invalidate a TLB entry, but only if the page tables being
538 // edited are the ones currently in use by the processor.
539 //
540 void
541 tlb_invalidate(pde_t *pgdir, void *va)
542 {
543         // Flush the entry only if we're modifying the current address space.
544         // For now, there is only one address space, so always invalidate.
545         invlpg(va);
546 }
547
548 void
549 page_check(void)
550 {
551         struct Page *pp, *pp0, *pp1, *pp2;
552         struct Page_list fl;
553         pte_t *ptep;
554
555         // should be able to allocate three pages
556         pp0 = pp1 = pp2 = 0;
557         assert(page_alloc(&pp0) == 0);
558         assert(page_alloc(&pp1) == 0);
559         assert(page_alloc(&pp2) == 0);
560
561         assert(pp0);
562         assert(pp1 && pp1 != pp0);
563         assert(pp2 && pp2 != pp1 && pp2 != pp0);
564
565         // temporarily steal the rest of the free pages
566         fl = page_free_list;
567         LIST_INIT(&page_free_list);
568
569         // should be no free memory
570         assert(page_alloc(&pp) == -E_NO_MEM);
571
572         // there is no page allocated at address 0
573         assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
574
575         // there is no free memory, so we can't allocate a page table 
576         assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
577
578         // free pp0 and try again: pp0 should be used for page table
579         page_free(pp0);
580         assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
581         assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
582         assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
583         assert(pp1->pp_ref == 1);
584         assert(pp0->pp_ref == 1);
585
586         // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
587         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
588         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
589         assert(pp2->pp_ref == 1);
590
591         // should be no free memory
592         assert(page_alloc(&pp) == -E_NO_MEM);
593
594         // should be able to map pp2 at PGSIZE because it's already there
595         assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
596         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
597         assert(pp2->pp_ref == 1);
598
599         // pp2 should NOT be on the free list
600         // could happen in ref counts are handled sloppily in page_insert
601         assert(page_alloc(&pp) == -E_NO_MEM);
602
603         // should not be able to map at PTSIZE because need free page for page table
604         assert(page_insert(boot_pgdir, pp0, (void*) PTSIZE, 0) < 0);
605
606         // insert pp1 at PGSIZE (replacing pp2)
607         assert(page_insert(boot_pgdir, pp1, (void*) PGSIZE, 0) == 0);
608
609         // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
610         assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1));
611         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
612         // ... and ref counts should reflect this
613         assert(pp1->pp_ref == 2);
614         assert(pp2->pp_ref == 0);
615
616         // pp2 should be returned by page_alloc
617         assert(page_alloc(&pp) == 0 && pp == pp2);
618
619         // unmapping pp1 at 0 should keep pp1 at PGSIZE
620         page_remove(boot_pgdir, 0x0);
621         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
622         assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
623         assert(pp1->pp_ref == 1);
624         assert(pp2->pp_ref == 0);
625
626         // unmapping pp1 at PGSIZE should free it
627         page_remove(boot_pgdir, (void*) PGSIZE);
628         assert(check_va2pa(boot_pgdir, 0x0) == ~0);
629         assert(check_va2pa(boot_pgdir, PGSIZE) == ~0);
630         assert(pp1->pp_ref == 0);
631         assert(pp2->pp_ref == 0);
632
633         // so it should be returned by page_alloc
634         assert(page_alloc(&pp) == 0 && pp == pp1);
635
636         // should be no free memory
637         assert(page_alloc(&pp) == -E_NO_MEM);
638
639         // forcibly take pp0 back
640         assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
641         boot_pgdir[0] = 0;
642         assert(pp0->pp_ref == 1);
643         pp0->pp_ref = 0;
644
645         // give free list back
646         page_free_list = fl;
647
648         // free the pages we took
649         page_free(pp0);
650         page_free(pp1);
651         page_free(pp2);
652
653         cprintf("page_check() succeeded!\n");
654 }
655