· 8 years ago · Aug 12, 2018, 04:52 AM
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()
13static physaddr_t maxpa; // Maximum physical address
14size_t npage; // Amount of physical memory (in pages)
15static size_t basemem; // Amount of base memory (in bytes)
16static size_t extmem; // Amount of extended memory (in bytes)
17
18// These variables are set in i386_vm_init()
19pde_t* boot_pgdir; // Virtual address of boot time page directory
20physaddr_t boot_cr3; // Physical address of boot time page directory
21static char* boot_freemem; // Pointer to next byte of free mem
22
23struct Page* pages; // Virtual address of physical page array
24static 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//
32struct 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
53struct Pseudodesc gdt_pd = {
54 sizeof(gdt) - 1, (unsigned long) gdt
55};
56
57 static int
58nvram_read(int r)
59{
60 return mc146818_read(r) | (mc146818_read(r + 1) << 8);
61}
62
63 void
64i386_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
87static void check_boot_pgdir(void);
88static void check_page_alloc();
89static void page_check(void);
90static void boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, physaddr_t pa, int perm);
91
92//
93// A simple physical memory allocator, used only a few times
94// in the process of setting up the virtual memory system.
95// page_alloc() is the real allocator.
96//
97// Allocate n bytes of physical memory aligned on an
98// align-byte boundary. Align must be a power of two.
99// Return kernel virtual address. Returned memory is uninitialized.
100//
101// If we're out of memory, boot_alloc should panic.
102// This function may ONLY be used during initialization,
103// before the page_free_list has been set up.
104//
105 static void*
106boot_alloc(uint32_t n, uint32_t align)
107{
108 extern char end[];
109 void *v;
110
111 // Initialize boot_freemem if this is the first time.
112 // 'end' is a magic symbol automatically generated by the linker,
113 // which points to the end of the kernel's bss segment -
114 // i.e., the first virtual address that the linker
115 // did _not_ assign to any kernel code or global variables.
116 if (boot_freemem == 0)
117 boot_freemem = end;
118
119 // LAB 2: Your code here:
120 // Step 1: round boot_freemem up to be aligned properly
121 // (hint: look in types.h for some handy macros)
122 // Step 2: save current value of boot_freemem as allocated chunk
123 // Step 3: increase boot_freemem to record allocation
124 // Step 4: return allocated chunk
125
126 boot_freemem = (char*) ROUNDUP(boot_freemem,align);
127 v = boot_freemem;
128 boot_freemem = boot_freemem + n;
129 return v;
130}
131
132// Set up a two-level page table:
133// boot_pgdir is its linear (virtual) address of the root
134// boot_cr3 is the physical adresss of the root
135// Then turn on paging. Then effectively turn off segmentation.
136// (i.e., the segment base addrs are set to zero).
137//
138// This function only sets up the kernel part of the address space
139// (ie. addresses >= UTOP). The user part of the address space
140// will be setup later.
141//
142// From UTOP to ULIM, the user is allowed to read but not write.
143// Above ULIM the user cannot read (or write).
144 void
145i386_vm_init(void)
146{
147 pde_t* pgdir;
148 uint32_t cr0;
149 size_t n;
150
151 // Delete this line:
152 // panic("i386_vm_init: This function is not finished\n");
153
154 //////////////////////////////////////////////////////////////////////
155 // create initial page directory.
156 pgdir = boot_alloc(PGSIZE, PGSIZE);
157 memset(pgdir, 0, PGSIZE);
158 boot_pgdir = pgdir;
159 boot_cr3 = PADDR(pgdir);
160
161 //////////////////////////////////////////////////////////////////////
162 // Recursively insert PD in itself as a page table, to form
163 // a virtual page table at virtual address VPT.
164 // (For now, you don't have understand the greater purpose of the
165 // following two lines.)
166
167 // Permissions: kernel RW, user NONE
168 pgdir[PDX(VPT)] = PADDR(pgdir)|PTE_W|PTE_P;
169
170 // same for UVPT
171 // Permissions: kernel R, user R
172 pgdir[PDX(UVPT)] = PADDR(pgdir)|PTE_U|PTE_P;
173
174 //////////////////////////////////////////////////////////////////////
175 // Allocate an array of npage 'struct Page's and store it in 'pages'.
176 // The kernel uses this array to keep track of physical pages: for
177 // each physical page, there is a corresponding struct Page in this
178 // array. 'npage' is the number of physical pages in memory.
179 // User-level programs will get read-only access to the array as well.
180 // Your code goes here:
181 pages = boot_alloc(npage*sizeof(struct Page),PGSIZE);
182 //////////////////////////////////////////////////////////////////////
183 // Now that we've allocated the initial kernel data structures, we set
184 // up the list of free physical pages. Once we've done so, all further
185 // memory management will go through the page_* functions. In
186 // particular, we can now map memory using boot_map_segment or page_insert
187 page_init();
188
189 check_page_alloc();
190
191 page_check();
192
193 //////////////////////////////////////////////////////////////////////
194 // Now we set up virtual memory
195
196 //////////////////////////////////////////////////////////////////////
197 // Map 'pages' read-only by the user at linear address UPAGES
198 // Permissions:
199 // - the new image at UPAGES -- kernel R, user R
200 // (ie. perm = PTE_U | PTE_P)
201 // - pages itself -- kernel RW, user NONE
202 // Your code goes here:
203
204 //////////////////////////////////////////////////////////////////////
205 // Use the physical memory that 'bootstack' refers to as the kernel
206 // stack. The kernel stack grows down from virtual address KSTACKTOP.
207 // We consider the entire range from [KSTACKTOP-PTSIZE, KSTACKTOP)
208 // to be the kernel stack, but break this into two pieces:
209 // * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
210 // * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed; so if
211 // the kernel overflows its stack, it will fault rather than
212 // overwrite memory. Known as a "guard page".
213 // Permissions: kernel RW, user NONE
214 // Your code goes here:
215
216 //////////////////////////////////////////////////////////////////////
217 // Map all of physical memory at KERNBASE.
218 // Ie. the VA range [KERNBASE, 2^32) should map to
219 // the PA range [0, 2^32 - KERNBASE)
220 // We might not have 2^32 - KERNBASE bytes of physical memory, but
221 // we just set up the mapping anyway.
222 // Permissions: kernel RW, user NONE
223 // Your code goes here:
224
225 // Check that the initial page directory has been set up correctly.
226 check_boot_pgdir();
227
228 //////////////////////////////////////////////////////////////////////
229 // On x86, segmentation maps a VA to a LA (linear addr) and
230 // paging maps the LA to a PA. I.e. VA => LA => PA. If paging is
231 // turned off the LA is used as the PA. Note: there is no way to
232 // turn off segmentation. The closest thing is to set the base
233 // address to 0, so the VA => LA mapping is the identity.
234
235 // Current mapping: VA KERNBASE+x => PA x.
236 // (segmentation base=-KERNBASE and paging is off)
237
238 // From here on down we must maintain this VA KERNBASE + x => PA x
239 // mapping, even though we are turning on paging and reconfiguring
240 // segmentation.
241
242 // Map VA 0:4MB same as VA KERNBASE, i.e. to PA 0:4MB.
243 // (Limits our kernel to <4MB)
244 pgdir[0] = pgdir[PDX(KERNBASE)];
245
246 // Install page table.
247 lcr3(boot_cr3);
248
249 // Turn on paging.
250 cr0 = rcr0();
251 cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_TS|CR0_EM|CR0_MP;
252 cr0 &= ~(CR0_TS|CR0_EM);
253 lcr0(cr0);
254
255 // Current mapping: KERNBASE+x => x => x.
256 // (x < 4MB so uses paging pgdir[0])
257
258 // Reload all segment registers.
259 asm volatile("lgdt gdt_pd");
260 asm volatile("movw %%ax,%%gs" :: "a" (GD_UD|3));
261 asm volatile("movw %%ax,%%fs" :: "a" (GD_UD|3));
262 asm volatile("movw %%ax,%%es" :: "a" (GD_KD));
263 asm volatile("movw %%ax,%%ds" :: "a" (GD_KD));
264 asm volatile("movw %%ax,%%ss" :: "a" (GD_KD));
265 asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (GD_KT)); // reload cs
266 asm volatile("lldt %%ax" :: "a" (0));
267
268 // Final mapping: KERNBASE+x => KERNBASE+x => x.
269
270 // This mapping was only used after paging was turned on but
271 // before the segment registers were reloaded.
272 pgdir[0] = 0;
273
274 // Flush the TLB for good measure, to kill the pgdir[0] mapping.
275 lcr3(boot_cr3);
276}
277
278//
279// Check the physical page allocator (page_alloc(), page_free(),
280// and page_init()).
281//
282 static void
283check_page_alloc()
284{
285 struct Page *pp, *pp0, *pp1, *pp2;
286 struct Page_list fl;
287
288 // if there's a page that shouldn't be on
289 // the free list, try to make sure it
290 // eventually causes trouble.
291 LIST_FOREACH(pp0, &page_free_list, pp_link)
292 memset(page2kva(pp0), 0x97, 128);
293
294 LIST_FOREACH(pp0, &page_free_list, pp_link) {
295 // check that we didn't corrupt the free list itself
296 assert(pp0 >= pages);
297 assert(pp0 < pages + npage);
298
299 // check a few pages that shouldn't be on the free list
300 assert(page2pa(pp0) != 0);
301 assert(page2pa(pp0) != IOPHYSMEM);
302 assert(page2pa(pp0) != EXTPHYSMEM - PGSIZE);
303 assert(page2pa(pp0) != EXTPHYSMEM);
304 assert(page2kva(pp0) != ROUNDDOWN(boot_freemem - 1, PGSIZE));
305 }
306
307 // should be able to allocate three pages
308 pp0 = pp1 = pp2 = 0;
309 assert(page_alloc(&pp0) == 0);
310 assert(page_alloc(&pp1) == 0);
311 assert(page_alloc(&pp2) == 0);
312
313 assert(pp0);
314 assert(pp1 && pp1 != pp0);
315 assert(pp2 && pp2 != pp1 && pp2 != pp0);
316 assert(page2pa(pp0) < npage*PGSIZE);
317 assert(page2pa(pp1) < npage*PGSIZE);
318 assert(page2pa(pp2) < npage*PGSIZE);
319
320 // temporarily steal the rest of the free pages
321 fl = page_free_list;
322 LIST_INIT(&page_free_list);
323
324 // should be no free memory
325 assert(page_alloc(&pp) == -E_NO_MEM);
326
327 // free and re-allocate?
328 page_free(pp0);
329 page_free(pp1);
330 page_free(pp2);
331 pp0 = pp1 = pp2 = 0;
332 assert(page_alloc(&pp0) == 0);
333 assert(page_alloc(&pp1) == 0);
334 assert(page_alloc(&pp2) == 0);
335 assert(pp0);
336 assert(pp1 && pp1 != pp0);
337 assert(pp2 && pp2 != pp1 && pp2 != pp0);
338 assert(page_alloc(&pp) == -E_NO_MEM);
339
340 // give free list back
341 page_free_list = fl;
342
343 // free the pages we took
344 page_free(pp0);
345 page_free(pp1);
346 page_free(pp2);
347
348 cprintf("check_page_alloc() succeeded!\n");
349}
350
351//
352// Checks that the kernel part of virtual address space
353// has been setup roughly correctly(by i386_vm_init()).
354//
355// This function doesn't test every corner case,
356// in fact it doesn't test the permission bits at all,
357// but it is a pretty good sanity check.
358//
359static physaddr_t check_va2pa(pde_t *pgdir, uintptr_t va);
360
361 static void
362check_boot_pgdir(void)
363{
364 uint32_t i, n;
365 pde_t *pgdir;
366
367 pgdir = boot_pgdir;
368
369 // check pages array
370 n = ROUNDUP(npage*sizeof(struct Page), PGSIZE);
371 for (i = 0; i < n; i += PGSIZE)
372 assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i);
373
374
375 // check phys mem
376 for (i = 0; i < npage * PGSIZE; i += PGSIZE)
377 assert(check_va2pa(pgdir, KERNBASE + i) == i);
378
379 // check kernel stack
380 for (i = 0; i < KSTKSIZE; i += PGSIZE)
381 assert(check_va2pa(pgdir, KSTACKTOP - KSTKSIZE + i) == PADDR(bootstack) + i);
382 assert(check_va2pa(pgdir, KSTACKTOP - PTSIZE) == ~0);
383
384 // check for zero/non-zero in PDEs
385 for (i = 0; i < NPDENTRIES; i++) {
386 switch (i) {
387 case PDX(VPT):
388 case PDX(UVPT):
389 case PDX(KSTACKTOP-1):
390 case PDX(UPAGES):
391 assert(pgdir[i]);
392 break;
393 default:
394 if (i >= PDX(KERNBASE))
395 assert(pgdir[i]);
396 else
397 assert(pgdir[i] == 0);
398 break;
399 }
400 }
401 cprintf("check_boot_pgdir() succeeded!\n");
402}
403
404// This function returns the physical address of the page containing 'va',
405// defined by the page directory 'pgdir'. The hardware normally performs
406// this functionality for us! We define our own version to help check
407// the check_boot_pgdir() function; it shouldn't be used elsewhere.
408
409 static physaddr_t
410check_va2pa(pde_t *pgdir, uintptr_t va)
411{
412 pte_t *p;
413
414 pgdir = &pgdir[PDX(va)];
415 if (!(*pgdir & PTE_P))
416 return ~0;
417 p = (pte_t*) KADDR(PTE_ADDR(*pgdir));
418 if (!(p[PTX(va)] & PTE_P))
419 return ~0;
420 return PTE_ADDR(p[PTX(va)]);
421}
422
423// --------------------------------------------------------------
424// Tracking of physical pages.
425// The 'pages' array has one 'struct Page' entry per physical page.
426// Pages are reference counted, and free pages are kept on a linked list.
427// --------------------------------------------------------------
428
429//
430// Initialize page structure and memory free list.
431// After this is done, NEVER use boot_alloc again. ONLY use the page
432// allocator functions below to allocate and deallocate physical
433// memory via the page_free_list.
434//
435 void
436page_init(void)
437{
438 // The example code here marks all physical pages as free.
439 // However this is not truly the case. What memory is free?
440 // 1) Mark physical page 0 as in use.
441 // This way we preserve the real-mode IDT and BIOS structures
442 // in case we ever need them. (Currently we don't, but...)
443 // 2) The rest of base memory, [PGSIZE, basemem) is free.
444 // 3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM).
445 // Mark it as in use so that it can never be allocated.
446 // 4) Then extended memory [EXTPHYSMEM, ...).
447 // Some of it is in use, some is free. Where is the kernel
448 // in physical memory? Which pages are already in use for
449 // page tables and other data structures?
450 //
451 // Change the code to reflect this.
452 int i;
453 LIST_INIT(&page_free_list);
454 int io_hole_beginning = IOPHYSMEM >> PGSHIFT;
455 int io_hole_end = EXTPHYSMEM >> PGSHIFT;
456 for (i = 1; i < npage; i++) {
457 pages[i].pp_ref = 0;
458 LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link);
459 if ( i == io_hole_beginning - 1)
460 i = i + io_hole_end + 32;
461 }
462}
463
464//
465// Initialize a Page structure.
466// The result has null links and 0 refcount.
467// Note that the corresponding physical page is NOT initialized!
468//
469 static void
470page_initpp(struct Page *pp)
471{
472 memset(pp, 0, sizeof(*pp));
473}
474
475//
476// Allocates a physical page.
477// Does NOT set the contents of the physical page to zero, NOR does it
478// increment the reference count of the page - the caller must do
479// these if necessary.
480//
481// *pp_store -- is set to point to the Page struct of the newly allocated
482// page
483//
484// RETURNS
485// 0 -- on success
486// -E_NO_MEM -- otherwise
487//
488// Hint: use LIST_FIRST, LIST_REMOVE, and page_initpp
489 int
490page_alloc(struct Page **pp_store)
491{
492 // Fill this function in
493 if(!LIST_EMPTY(&page_free_list))
494 {
495 // page_initpp(*pp_store);
496
497 *pp_store = LIST_FIRST(&page_free_list);
498
499 LIST_REMOVE(*pp_store,pp_link);
500
501 page_initpp(*pp_store);
502
503 return 0;
504 }
505 else
506 return -E_NO_MEM;
507}
508
509//
510// Return a page to the free list.
511// (This function should only be called when pp->pp_ref reaches 0.)
512//
513 void
514page_free(struct Page *pp)
515{
516 // Fill this function in
517 if(pp->pp_ref == 0)
518 LIST_INSERT_HEAD(&page_free_list, pp, pp_link);
519}
520
521//
522// Decrement the reference count on a page,
523// freeing it if there are no more refs.
524//
525 void
526page_decref(struct Page* pp)
527{
528 if (--pp->pp_ref == 0)
529 page_free(pp);
530}
531
532// Given 'pgdir', a pointer to a page directory, pgdir_walk returns
533// a pointer to the page table entry (PTE) for linear address 'va'.
534// This requires walking the two-level page table structure.
535//
536// If the relevant page table doesn't exist in the page directory, then:
537// - If create == 0, pgdir_walk returns NULL.
538// - Otherwise, pgdir_walk tries to allocate a new page table
539// with page_alloc. If this fails, pgdir_walk returns NULL.
540// - pgdir_walk sets pp_ref to 1 for the new page table.
541// - pgdir_walk clears the new page table.
542// - Finally, pgdir_walk returns a pointer into the new page table.
543//
544// Hint: you can turn a Page * into the physical address of the
545// page it refers to with page2pa() from kern/pmap.h.
546//
547// Hint 2: the x86 MMU checks permission bits in both the page directory
548// and the page table, so it's safe to leave permissions in the page
549// more permissive than strictly necessary.
550 pte_t *
551pgdir_walk(pde_t *pgdir, const void *va, int create)
552{
553 // Fill this function in
554 pde_t *pg_dir_entry;
555 pte_t *pg_table_entry;
556 struct Page *pg;
557 pg_dir_entry = &pgdir[PDX(va)];
558 if( *pg_dir_entry & PTE_P )
559 pg_table_entry = (pte_t*)KADDR( PTE_ADDR(*pg_dir_entry));
560 else
561 {
562 if (create == 0)
563 return NULL;
564 if(page_alloc(&pg) == -E_NO_MEM)
565 return NULL;
566 pg->pp_ref=1;
567 pg_table_entry = (pte_t*)page2kva(pg);
568 *pg_dir_entry = page2pa(pg)|PTE_P|PTE_W|PTE_U;
569 }
570 return &pg_table_entry[PTX(va)];
571}
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// Requirements
579// - If there is already a page mapped at 'va', it should be page_remove()d.
580// - If necessary, on demand, a page table should be allocated and inserted
581// into '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//
585// Corner-case hint: Make sure to consider what happens when the same
586// pp is re-inserted at the same virtual address in the same pgdir.
587//
588// RETURNS:
589// 0 on success
590// -E_NO_MEM, if page table couldn't be allocated
591//
592// Hint: The TA solution is implemented using pgdir_walk, page_remove,
593// and page2pa.
594//
595 int
596page_insert(pde_t *pgdir, struct Page *pp, void *va, int perm)
597{
598 // Fill this function in
599 pte_t *pte;
600 pte = pgdir_walk(pgdir,va,1);
601 if(pte == NULL)
602 return -E_NO_MEM;
603 if(check_va2pa(pgdir, (uintptr_t)va) == page2pa(pp)){
604
605 *pte = page2pa(pp)|perm|PTE_P;
606 return 0;
607 }
608 if(*pte & PTE_P)
609 page_remove(pgdir,va);
610 pp->pp_ref++;
611 *pte = page2pa(pp)|perm|PTE_P;
612 return 0;
613}
614
615//
616// Map [la, la+size) of linear address space to physical [pa, pa+size)
617// in the page table rooted at pgdir. Size is a multiple of PGSIZE.
618// Use permission bits perm|PTE_P for the entries.
619//
620// This function is only intended to set up the ``static'' mappings
621// above UTOP. As such, it should *not* change the pp_ref field on the
622// mapped pages.
623//
624// Hint: the TA solution uses pgdir_walk
625 static void
626boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, physaddr_t pa, int perm)
627{
628 // Fill this function in
629 uintptr_t i;
630 for (i=0; i<size; i+=PGSIZE)
631 {
632 *pgdir_walk(pgdir,(uintptr_t *)(la+i), 1) = (pa+i)|perm|PTE_P;
633 }
634}
635
636//
637// Return the page mapped at virtual address 'va'.
638// If pte_store is not zero, then we store in it the address
639// of the pte for this page. This is used by page_remove and
640// can be used to verify page permissions for syscall arguments,
641// but should not be used by most callers.
642//
643// Return NULL if there is no page mapped at va.
644//
645// Hint: the TA solution uses pgdir_walk and pa2page.
646//
647 struct Page *
648page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
649{
650 // Fill this function in
651 pte_t *pte;
652 pte = pgdir_walk(pgdir, va, 0);
653 if(pte == NULL)
654 return NULL;
655 if(**pte_store != 0)
656 {
657 *pte_store = pte;
658 return pa2page(**pte_store);
659 }
660 else
661 return NULL;
662}
663
664//
665// Unmaps the physical page at virtual address 'va'.
666// If there is no physical page at that address, silently does nothing.
667//
668// Details:
669// - The ref count on the physical page should decrement.
670// - The physical page should be freed if the refcount reaches 0.
671// - The pg table entry corresponding to 'va' should be set to 0.
672// (if such a PTE exists)
673// - The TLB must be invalidated if you remove an entry from
674// the pg dir/pg table.
675//
676// Hint: The TA solution is implemented using page_lookup,
677// tlb_invalidate, and page_decref.
678//
679 void
680page_remove(pde_t *pgdir, void *va)
681{
682 // Fill this function in
683 struct Page *pg;
684 pte_t *pte;
685 pg = page_lookup(pgdir,va,&pte);
686 if( pg != NULL)
687 {
688 page_decref(pg);
689 if(*pte & PTE_P)
690 *pte = 0;
691 tlb_invalidate(pgdir,va);
692 }
693}
694
695//
696// Invalidate a TLB entry, but only if the page tables being
697// edited are the ones currently in use by the processor.
698//
699 void
700tlb_invalidate(pde_t *pgdir, void *va)
701{
702 // Flush the entry only if we're modifying the current address space.
703 // For now, there is only one address space, so always invalidate.
704 invlpg(va);
705}
706
707// check page_insert, page_remove, &c
708 static void
709page_check(void)
710{
711 struct Page *pp, *pp0, *pp1, *pp2;
712 struct Page_list fl;
713 pte_t *ptep, *ptep1;
714 void *va;
715 int i;
716
717 // should be able to allocate three pages
718 pp0 = pp1 = pp2 = 0;
719 assert(page_alloc(&pp0) == 0);
720 assert(page_alloc(&pp1) == 0);
721 assert(page_alloc(&pp2) == 0);
722
723 assert(pp0);
724 assert(pp1 && pp1 != pp0);
725 assert(pp2 && pp2 != pp1 && pp2 != pp0);
726
727 // temporarily steal the rest of the free pages
728 fl = page_free_list;
729 LIST_INIT(&page_free_list);
730
731 // should be no free memory
732 assert(page_alloc(&pp) == -E_NO_MEM);
733
734 // there is no page allocated at address 0
735 assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
736
737 // there is no free memory, so we can't allocate a page table
738 assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
739
740 // free pp0 and try again: pp0 should be used for page table
741 page_free(pp0);
742 assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
743 assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
744 assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
745 assert(pp1->pp_ref == 1);
746 assert(pp0->pp_ref == 1);
747
748 // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
749
750 assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
751 assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
752 assert(pp2->pp_ref == 1);
753
754 // should be no free memory
755 assert(page_alloc(&pp) == -E_NO_MEM);
756
757 // should be able to map pp2 at PGSIZE because it's already there
758 assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
759 assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
760 assert(pp2->pp_ref == 1);
761
762 // pp2 should NOT be on the free list
763 // could happen in ref counts are handled sloppily in page_insert
764 assert(page_alloc(&pp) == -E_NO_MEM);
765
766 // check that pgdir_walk returns a pointer to the pte
767 ptep = KADDR(PTE_ADDR(boot_pgdir[PDX(PGSIZE)]));
768 assert(pgdir_walk(boot_pgdir, (void*)PGSIZE, 0) == ptep+PTX(PGSIZE));
769
770 // should be able to change permissions too.
771 assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, PTE_U) == 0);
772 assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
773 assert(pp2->pp_ref == 1);
774 assert(*pgdir_walk(boot_pgdir, (void*) PGSIZE, 0) & PTE_U);
775 assert(boot_pgdir[0] & PTE_U);
776
777 // should not be able to map at PTSIZE because need free page for page table
778 assert(page_insert(boot_pgdir, pp0, (void*) PTSIZE, 0) < 0);
779
780 // insert pp1 at PGSIZE (replacing pp2)
781 assert(page_insert(boot_pgdir, pp1, (void*) PGSIZE, 0) == 0);
782 assert(!(*pgdir_walk(boot_pgdir, (void*) PGSIZE, 0) & PTE_U));
783
784 // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
785 assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1));
786 assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
787 // ... and ref counts should reflect this
788 assert(pp1->pp_ref == 2);
789 assert(pp2->pp_ref == 0);
790
791 // pp2 should be returned by page_alloc
792 assert(page_alloc(&pp) == 0 && pp == pp2);
793
794 // unmapping pp1 at 0 should keep pp1 at PGSIZE
795 page_remove(boot_pgdir, 0x0);
796 assert(check_va2pa(boot_pgdir, 0x0) == ~0);
797 assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
798 assert(pp1->pp_ref == 1);
799 assert(pp2->pp_ref == 0);
800
801 // unmapping pp1 at PGSIZE should free it
802 page_remove(boot_pgdir, (void*) PGSIZE);
803 assert(check_va2pa(boot_pgdir, 0x0) == ~0);
804 assert(check_va2pa(boot_pgdir, PGSIZE) == ~0);
805 assert(pp1->pp_ref == 0);
806 assert(pp2->pp_ref == 0);
807
808 // so it should be returned by page_alloc
809 assert(page_alloc(&pp) == 0 && pp == pp1);
810
811 // should be no free memory
812 assert(page_alloc(&pp) == -E_NO_MEM);
813
814#if 0
815 // should be able to page_insert to change a page
816 // and see the new data immediately.
817 memset(page2kva(pp1), 1, PGSIZE);
818 memset(page2kva(pp2), 2, PGSIZE);
819 page_insert(boot_pgdir, pp1, 0x0, 0);
820 assert(pp1->pp_ref == 1);
821 assert(*(int*)0 == 0x01010101);
822 page_insert(boot_pgdir, pp2, 0x0, 0);
823 assert(*(int*)0 == 0x02020202);
824 assert(pp2->pp_ref == 1);
825 assert(pp1->pp_ref == 0);
826 page_remove(boot_pgdir, 0x0);
827 assert(pp2->pp_ref == 0);
828#endif
829
830 // forcibly take pp0 back
831 assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
832 boot_pgdir[0] = 0;
833 assert(pp0->pp_ref == 1);
834 pp0->pp_ref = 0;
835
836 // check pointer arithmetic in pgdir_walk
837 page_free(pp0);
838 va = (void*)(PGSIZE * NPDENTRIES + PGSIZE);
839 ptep = pgdir_walk(boot_pgdir, va, 1);
840 ptep1 = KADDR(PTE_ADDR(boot_pgdir[PDX(va)]));
841 assert(ptep == ptep1 + PTX(va));
842 boot_pgdir[PDX(va)] = 0;
843 pp0->pp_ref = 0;
844
845 // check that new page tables get cleared
846 memset(page2kva(pp0), 0xFF, PGSIZE);
847 page_free(pp0);
848 pgdir_walk(boot_pgdir, 0x0, 1);
849 ptep = page2kva(pp0);
850 for(i=0; i<NPTENTRIES; i++)
851 assert((ptep[i] & PTE_P) == 0);
852 boot_pgdir[0] = 0;
853 pp0->pp_ref = 0;
854
855 // give free list back
856 page_free_list = fl;
857
858 // free the pages we took
859 page_free(pp0);
860 page_free(pp1);
861 page_free(pp2);
862
863 cprintf("page_check() succeeded!\n");
864}