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