· 5 years ago · Apr 05, 2020, 12:36 PM
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#define MEM_ENTRY_ADDR 0x7000
13
14// These variables are set by i386_detect_memory()
15size_t npages; // Amount of physical memory (in pages)
16static size_t npages_basemem; // Amount of base memory (in pages)
17
18// These variables are set in mem_init()
19pde_t *kern_pgdir; // Kernel's initial page directory
20struct PageInfo *pages; // Physical page state array
21static struct PageInfo *page_free_list; // Free list of physical pages
22
23
24// --------------------------------------------------------------
25// Detect machine's physical memory setup.
26// --------------------------------------------------------------
27
28static int
29nvram_read(int r)
30{
31 return mc146818_read(r) | (mc146818_read(r + 1) << 8);
32}
33
34static void
35i386_detect_memory(void)
36{
37 size_t basemem, extmem, ext16mem, totalmem;
38
39 // Use CMOS calls to measure available base & extended memory.
40 // (CMOS calls return results in kilobytes.)
41 basemem = nvram_read(NVRAM_BASELO);
42 extmem = nvram_read(NVRAM_EXTLO);
43 ext16mem = nvram_read(NVRAM_EXT16LO) * 64;
44
45 // Calculate the number of physical pages available in both base
46 // and extended memory.
47 if (ext16mem)
48 totalmem = 16 * 1024 + ext16mem;
49 else if (extmem)
50 totalmem = 1 * 1024 + extmem;
51 else
52 totalmem = basemem;
53
54 npages = totalmem / (PGSIZE / 1024);
55 npages_basemem = basemem / (PGSIZE / 1024);
56
57 cprintf("Physical memory: %uK available, base = %uK, extended = %uK\n",
58 totalmem, basemem, totalmem - basemem);
59}
60
61
62// --------------------------------------------------------------
63// Set up memory mappings above UTOP.
64// --------------------------------------------------------------
65
66static void boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm);
67static void check_page_free_list(bool only_low_memory);
68static void check_page_alloc(void);
69static void check_kern_pgdir(void);
70static physaddr_t check_va2pa(pde_t *pgdir, uintptr_t va);
71static void check_page(void);
72static void check_page_installed_pgdir(void);
73
74// This simple physical memory allocator is used only while JOS is setting
75// up its virtual memory system. page_alloc() is the real allocator.
76//
77// If n>0, allocates enough pages of contiguous physical memory to hold 'n'
78// bytes. Doesn't initialize the memory. Returns a kernel virtual address.
79//
80// If n==0, returns the address of the next free page without allocating
81// anything.
82//
83// If we're out of memory, boot_alloc should panic.
84// This function may ONLY be used during initialization,
85// before the page_free_list list has been set up.
86static void *
87boot_alloc(uint32_t n)
88{
89 static char *nextfree; // virtual address of next byte of free memory
90 char *result;
91
92 // Initialize nextfree if this is the first time.
93 // 'end' is a magic symbol automatically generated by the linker,
94 // which points to the end of the kernel's bss segment:
95 // the first virtual address that the linker did *not* assign
96 // to any kernel code or global variables.
97 if (!nextfree) {
98 extern char end[];
99 nextfree = ROUNDUP((char *) end, PGSIZE);
100 }
101
102 // Allocate a chunk large enough to hold 'n' bytes, then update
103 // nextfree. Make sure nextfree is kept aligned
104 // to a multiple of PGSIZE.
105 //
106 // LAB 2: Your code here.
107
108 result = nextfree;
109 nextfree += (uint32_t)ROUNDUP((char *) n, PGSIZE);
110 //check if we are out of memory, if yes, send an error
111 if ((uint32_t) nextfree > (npages * PGSIZE + KERNBASE)){
112 panic("boot_alloc: we're out of memory to allocate!\n");
113 }
114
115 return result;
116}
117
118// Set up a two-level page table:
119// kern_pgdir is its linear (virtual) address of the root
120//
121// This function only sets up the kernel part of the address space
122// (ie. addresses >= UTOP). The user part of the address space
123// will be set up later.
124//
125// From UTOP to ULIM, the user is allowed to read but not write.
126// Above ULIM the user cannot read or write.
127void
128mem_init(void)
129{
130 uint32_t cr0;
131 size_t n;
132
133 // Find out how much memory the machine has (npages & npages_basemem).
134 i386_detect_memory();
135
136 // Remove this line when you're ready to test this function.
137 //panic("mem_init: This function is not finished\n");
138
139 //////////////////////////////////////////////////////////////////////
140 // create initial page directory.
141 kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
142 memset(kern_pgdir, 0, PGSIZE);
143
144 //////////////////////////////////////////////////////////////////////
145 // Recursively insert PD in itself as a page table, to form
146 // a virtual page table at virtual address UVPT.
147 // (For now, you don't have understand the greater purpose of the
148 // following line.)
149
150 // Permissions: kernel R, user R
151 kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;
152
153 //////////////////////////////////////////////////////////////////////
154 // Allocate an array of npages 'struct PageInfo's and store it in 'pages'.
155 // The kernel uses this array to keep track of physical pages: for
156 // each physical page, there is a corresponding struct PageInfo in this
157 // array. 'npages' is the number of physical pages in memory. Use memset
158 // to initialize all fields of each struct PageInfo to 0.
159 // Your code goes here:
160
161 uint32_t mysize = npages * sizeof(struct PageInfo);
162 pages = (struct PageInfo *) boot_alloc(mysize);
163 memset(pages, 0, mysize);
164
165 //////////////////////////////////////////////////////////////////////
166 // Now that we've allocated the initial kernel data structures, we set
167 // up the list of free physical pages. Once we've done so, all further
168 // memory management will go through the page_* functions. In
169 // particular, we can now map memory using boot_map_region
170 // or page_insert
171 page_init();
172
173 check_page_free_list(1);
174 check_page_alloc();
175 check_page();
176
177 //////////////////////////////////////////////////////////////////////
178 // Now we set up virtual memory
179
180 //////////////////////////////////////////////////////////////////////
181 // Map 'pages' read-only by the user at linear address UPAGES
182 // Permissions:
183 // - the new image at UPAGES -- kernel R, user R
184 // (ie. perm = PTE_U | PTE_P)
185 // - pages itself -- kernel RW, user NONE
186 // Your code goes here:
187
188 //To map paqes, we will use boot_map_region function
189 //boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
190 //at linear address UPAGES
191 uint32_t pa = PADDR(pages);
192 // Size is a multiple of PGSIZE, hence we use the size of page table
193 size_t custom_page_size = PTSIZE;
194 boot_map_region(kern_pgdir, UPAGES, custom_page_size, pa, (PTE_U | PTE_P));
195
196 //////////////////////////////////////////////////////////////////////
197 // Use the physical memory that 'bootstack' refers to as the kernel
198 // stack. The kernel stack grows down from virtual address KSTACKTOP.
199 // We consider the entire range from [KSTACKTOP-PTSIZE, KSTACKTOP)
200 // to be the kernel stack, but break this into two pieces:
201 // * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
202 // * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed; so if
203 // the kernel overflows its stack, it will fault rather than
204 // overwrite memory. Known as a "guard page".
205 // Permissions: kernel RW, user NONE
206 // Your code goes here:
207 pa = PADDR(bootstack);
208 custom_page_size = KSTKSIZE; //CPU's kernel stack size
209 //[KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
210 boot_map_region(kern_pgdir, (KSTACKTOP-KSTKSIZE), custom_page_size, pa, (PTE_W | PTE_P));
211
212 //////////////////////////////////////////////////////////////////////
213 // Map all of physical memory at KERNBASE.
214 // Ie. the VA range [KERNBASE, 2^32) should map to
215 // the PA range [0, 2^32 - KERNBASE)
216 // We might not have 2^32 - KERNBASE bytes of physical memory, but
217 // we just set up the mapping anyway.
218 // Permissions: kernel RW, user NONE
219 // Your code goes here:
220
221 // Map all of physical memory at KERNBASE.
222 //2^32 is 0xFFFFFFFF in hex, so 0xFFFFFFFF-KERNBASE
223 custom_page_size = 0xFFFFFFFF-KERNBASE;
224 boot_map_region(kern_pgdir, KERNBASE, custom_page_size, 0, PTE_W | PTE_P);
225
226
227 // Check that the initial page directory has been set up correctly.
228 check_kern_pgdir();
229
230 // Switch from the minimal entry page directory to the full kern_pgdir
231 // page table we just created. Our instruction pointer should be
232 // somewhere between KERNBASE and KERNBASE+4MB right now, which is
233 // mapped the same way by both page tables.
234 //
235 // If the machine reboots at this point, you've probably set up your
236 // kern_pgdir wrong.
237 lcr3(PADDR(kern_pgdir));
238
239 check_page_free_list(0);
240
241 // entry.S set the really important flags in cr0 (including enabling
242 // paging). Here we configure the rest of the flags that we care about.
243 cr0 = rcr0();
244 cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_MP;
245 cr0 &= ~(CR0_TS|CR0_EM);
246 lcr0(cr0);
247
248 // Some more checks, only possible after kern_pgdir is installed.
249 check_page_installed_pgdir();
250}
251
252// --------------------------------------------------------------
253// Tracking of physical pages.
254// The 'pages' array has one 'struct PageInfo' entry per physical page.
255// Pages are reference counted, and free pages are kept on a linked list.
256// --------------------------------------------------------------
257
258//
259// Initialize page structure and memory free list.
260// After this is done, NEVER use boot_alloc again. ONLY use the page
261// allocator functions below to allocate and deallocate physical
262// memory via the page_free_list.
263//
264void
265page_init(void)
266{
267 // The example code here marks all physical pages as free.
268 // However this is not truly the case. What memory is free?
269 // 1) Mark physical page 0 as in use.
270 // This way we preserve the real-mode IDT and BIOS structures
271 // in case we ever need them. (Currently we don't, but...)
272 // 2) The rest of base memory, [PGSIZE, npages_basemem * PGSIZE)
273 // is free.
274 // 3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM), which must
275 // never be allocated.
276 // 4) Then extended memory [EXTPHYSMEM, ...).
277 // Some of it is in use, some is free. Where is the kernel
278 // in physical memory? Which pages are already in use for
279 // page tables and other data structures?
280 //
281 // Change the code to reflect this.
282 // NB: DO NOT actually touch the physical memory corresponding to
283 // free pages!
284 size_t i;
285 //We will refer to memlayout to understand the layout of our memory and the bifurcations
286 uint32_t kern_size = (uint32_t)boot_alloc(0) - KERNBASE;
287 uint32_t kern_pages_cnt = kern_size/PGSIZE;
288
289 int a1 = 0, b1 = 0;
290 for (i = 0; i < npages; i++) {
291 pages[i].pp_link = NULL;
292
293 //pages[i].pp_link = page_free_list;
294 if ((i==0) || ( i < EXTPHYSMEM/PGSIZE + kern_pages_cnt && i >= npages_basemem )){
295 a1 += 1;
296 pages[i].pp_ref = 1;
297 } else {
298 b1 += 1;
299 //cprintf("Checkpoint B, ");
300 pages[i].pp_ref = 0;
301 pages->pp_link = page_free_list;
302 page_free_list = &pages[i];
303 }
304 }
305 cprintf("page_init succeeded, npages = %d\n", npages);
306 cprintf("a1, b1= %d | %d\n", a1, b1);
307 cprintf("Size of page_free_list = %d\n", sizeof(page_free_list));
308}
309
310
311
312struct PageInfo *
313page_alloc(int alloc_flags)
314{
315 //
316// Allocates a physical page. If (alloc_flags & ALLOC_ZERO), fills the entire
317// returned physical page with '\0' bytes. Does NOT increment the reference
318// count of the page - the caller must do these if necessary (either explicitly
319// or via page_insert).
320//
321// Be sure to set the pp_link field of the allocated page to NULL so
322// page_free can check for double-free bugs.
323//
324// Returns NULL if out of free memory.
325//
326// Hint: use page2kva and memset
327
328 struct PageInfo *pp = page_free_list;
329
330 if (pp)
331 {
332 //Check if pointers to the page is 0.
333 assert (pp->pp_ref == 0);
334 //next page on the free list. transfer the value to free list and clear
335 page_free_list = pp->pp_link;
336 pp->pp_link = NULL;
337
338
339 if(alloc_flags & ALLOC_ZERO){
340 //page2kva(struct PageInfo *pp)
341 memset (page2kva(pp), 0, PGSIZE);
342 }
343 return pp;
344
345 } else {
346 return NULL;
347 }
348}
349
350//
351// Return a page to the free list.
352// (This function should only be called when pp->pp_ref reaches 0.)
353//
354void
355page_free(struct PageInfo *pp)
356{
357 // Fill this function in
358 // Hint: You may want to panic if pp->pp_ref is nonzero or
359 // pp->pp_link is not NULL.
360
361 //assert creates panic if it is false
362 assert (pp->pp_link == NULL);
363 assert (pp->pp_ref == 0);
364
365 pp->pp_link = page_free_list;
366 page_free_list = pp;
367
368}
369
370//
371// Decrement the reference count on a page,
372// freeing it if there are no more refs.
373//
374void
375page_decref(struct PageInfo* pp)
376{
377 if (--pp->pp_ref == 0)
378 page_free(pp);
379}
380
381// Given 'pgdir', a pointer to a page directory, pgdir_walk returns
382// a pointer to the page table entry (PTE) for linear address 'va'.
383// This requires walking the two-level page table structure.
384//
385// The relevant page table page might not exist yet.
386// If this is true, and create == false, then pgdir_walk returns NULL.
387// Otherwise, pgdir_walk allocates a new page table page with page_alloc.
388// - If the allocation fails, pgdir_walk returns NULL.
389// - Otherwise, the new page's reference count is incremented,
390// the page is cleared,
391// and pgdir_walk returns a pointer into the new page table page.
392//
393// Hint 1: you can turn a PageInfo * into the physical address of the
394// page it refers to with page2pa() from kern/pmap.h.
395//
396// Hint 2: the x86 MMU checks permission bits in both the page directory
397// and the page table, so it's safe to leave permissions in the page
398// directory more permissive than strictly necessary.
399//
400// Hint 3: look at inc/mmu.h for useful macros that manipulate page
401// table and page directory entries.
402//
403pte_t *
404pgdir_walk(pde_t *pgdir, const void *va, int create)
405{
406 //pde_t returns the the page directory of a pointer
407 // The PDX, PTX, PGOFF, and PGNUM macros decompose linear addresses as shown.
408// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la),
409// use PGADDR(PDX(la), PTX(la), PGOFF(la)).
410 // Fill this function in
411
412 const uint32_t temp_va = PDX(va);
413 //pgdir is our page directory address
414 pte_t *entry_addr = pgdir + temp_va;
415 struct PageInfo *pp;
416 if(*entry_addr & PTE_P){
417 } else {
418 //The directory entry does not exists
419 if(!create){
420 return NULL;
421 } else {
422 pp = page_alloc(0); //To crate a page and memset 0, refer to page_alloc
423 if(!pp){
424 return NULL;
425 } else {
426 pp->pp_ref = pp->pp_ref+1;
427 *entry_addr = page2pa(pp); //Converting into physical address
428 //As per hint 2, we need to set the permission bits too. Executable, Valid, and Writable should be sufficient
429 *entry_addr += PTE_P + PTE_W + PTE_U;
430 //Update the if condition above now to check if the directory entry exists
431 }
432 }
433 }
434 //pgdir_walk returns a pointer to the page table entry
435 //https://pdos.csail.mit.edu/6.828/2014/lec/l-josmem.html
436 //The PDX part of the address indexes into the page directory to give you a page table.
437 //The PTX part indexes into the page table to give you a page, and then you add the low bits in.
438 pde_t *return_addr = KADDR(PTE_ADDR(*entry_addr))+PTX(va); //Getting the kernel address for the kernel
439 return return_addr;
440}
441
442static void
443boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
444{
445
446//
447// Map [va, va+size) of virtual address space to physical [pa, pa+size)
448// in the page table rooted at pgdir. Size is a multiple of PGSIZE, and
449// va and pa are both page-aligned.
450// Use permission bits perm|PTE_P for the entries.
451//
452// This function is only intended to set up the ``static'' mappings
453// above UTOP. As such, it should *not* change the pp_ref field on the
454// mapped pages.
455//
456// Hint: the TA solution uses pgdir_walk
457
458 // Fill this function in
459 pde_t *pgdir_addr;
460 for(size_t i = 0; i < size; i += PGSIZE){
461 //create pages in the page directory
462 pgdir_addr = pgdir_walk(pgdir, (void*)va + i, 1);
463 *pgdir_addr = (pa + i) + (perm | PTE_P); //Get the physical address along with the permission and replace the value
464 }
465
466}
467
468
469int
470page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
471{
472 //
473// Map the physical page 'pp' at virtual address 'va'.
474// The permissions (the low 12 bits) of the page table entry
475// should be set to 'perm|PTE_P'.
476//
477// Requirements
478// - If there is already a page mapped at 'va', it should be page_remove()d.
479// - If necessary, on demand, a page table should be allocated and inserted
480// into '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// Corner-case hint: Make sure to consider what happens when the same
485// pp is re-inserted at the same virtual address in the same pgdir.
486// However, try not to distinguish this case in your code, as this
487// frequently leads to subtle bugs; there's an elegant way to handle
488// everything in one code path.
489//
490// RETURNS:
491// 0 on success
492// -E_NO_MEM, if page table couldn't be allocated
493//
494// Hint: The TA solution is implemented using pgdir_walk, page_remove,
495// and page2pa.
496//
497
498 // Fill this function in
499 //return 0;
500 //We will do a pgdir_walk as suggested and create a pgdir if does not exist.
501 pte_t *mypte = pgdir_walk(pgdir, va, 1); //Page directory created
502
503 //Given if this fails, we need to return -E_NO_MEM;
504 if(!mypte){
505 return -E_NO_MEM;
506 } else {
507 pp->pp_ref = pp->pp_ref+1;
508 }
509
510 //To avoid duplication we can check if the permisions are already set, if yes, then it is a duplicate.
511 if(*mypte & PTE_P){
512 page_remove(pgdir, va); //got this idea from the hint
513 }
514
515 //page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
516 // The permissions (the low 12 bits) of the page table entry
517 // should be set to 'perm|PTE_P'.
518 *mypte = page2pa(pp); //Converting to physical address, took the inspiration from pgdir_walk
519 //Changing the permission of the page table entry
520 *mypte += perm | PTE_P;
521 //Change the permission of the complete page in the directory
522 const uint32_t page_addr = PDX(va);
523 pgdir[page_addr] = pgdir[page_addr] | perm; //Set the required permissions if not already set
524 return 0;
525}
526
527
528struct PageInfo *
529page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
530{
531 //
532// Return the page mapped at virtual address 'va'.
533// If pte_store is not zero, then we store in it the address
534// of the pte for this page. This is used by page_remove and
535// can be used to verify page permissions for syscall arguments,
536// but should not be used by most callers.
537//
538// Return NULL if there is no page mapped at va.
539//
540// Hint: the TA solution uses pgdir_walk and pa2page.
541//
542
543 // Fill this function in
544 pde_t *pgdir_addr = pgdir_walk(pgdir, va, 0);;
545
546 if (!pgdir_addr || !(*pgdir_addr & PTE_P)){
547 return NULL;
548 }
549 if (pte_store){
550 *pte_store = pgdir_addr;
551 }
552 // Address in page table or page directory entry PTE_ADDR
553 return pa2page(PTE_ADDR(*pgdir_addr));
554
555}
556
557void
558page_remove(pde_t *pgdir, void *va)
559{
560
561//
562// Unmaps the physical page at virtual address 'va'.
563// If there is no physical page at that address, silently does nothing.
564//
565// Details:
566// - The ref count on the physical page should decrement.
567// - The physical page should be freed if the refcount reaches 0.
568// - The pg table entry corresponding to 'va' should be set to 0.
569// (if such a PTE exists)
570// - The TLB must be invalidated if you remove an entry from
571// the page table.
572//
573// Hint: The TA solution is implemented using page_lookup,
574// tlb_invalidate, and page_decref.
575//
576
577 // Fill this function in
578
579 //Since we do not have a pte_store to send to page lookup, we will initiatlize one.
580 //The hints have significantly declared the answer lol.
581
582 pte_t *mypte;
583
584 struct PageInfo *pp;
585 pp = page_lookup(pgdir, va, &mypte);
586
587 if (pp)
588 {
589 *mypte = 0;
590 page_decref(pp);
591 tlb_invalidate(pgdir, va);
592
593
594 }
595}
596
597//
598// Invalidate a TLB entry, but only if the page tables being
599// edited are the ones currently in use by the processor.
600//
601void
602tlb_invalidate(pde_t *pgdir, void *va)
603{
604 // Flush the entry only if we're modifying the current address space.
605 // For now, there is only one address space, so always invalidate.
606 invlpg(va);
607}
608
609
610// --------------------------------------------------------------
611// Checking functions.
612// --------------------------------------------------------------
613
614//
615// Check that the pages on the page_free_list are reasonable.
616//
617static void
618check_page_free_list(bool only_low_memory)
619{
620 struct PageInfo *pp;
621 unsigned pdx_limit = only_low_memory ? 1 : NPDENTRIES;
622 int nfree_basemem = 0, nfree_extmem = 0;
623 char *first_free_page;
624
625 if (!page_free_list)
626 panic("'page_free_list' is a null pointer!");
627
628 if (only_low_memory) {
629 // Move pages with lower addresses first in the free
630 // list, since entry_pgdir does not map all pages.
631 struct PageInfo *pp1, *pp2;
632 struct PageInfo **tp[2] = { &pp1, &pp2 };
633 for (pp = page_free_list; pp; pp = pp->pp_link) {
634 int pagetype = PDX(page2pa(pp)) >= pdx_limit;
635 *tp[pagetype] = pp;
636 tp[pagetype] = &pp->pp_link;
637 }
638 *tp[1] = 0;
639 *tp[0] = pp2;
640 page_free_list = pp1;
641 cprintf("only_low_memory called\n");
642 }
643 cprintf("Size of page_free_list = %d\n", sizeof(page_free_list));
644 // if there's a page that shouldn't be on the free list,
645 // try to make sure it eventually causes trouble.
646 for (pp = page_free_list; pp; pp = pp->pp_link)
647 if (PDX(page2pa(pp)) < pdx_limit)
648 memset(page2kva(pp), 0x97, 128);
649
650 first_free_page = (char *) boot_alloc(0);
651 for (pp = page_free_list; pp; pp = pp->pp_link) {
652 cprintf("Size of pp = %d, page_free_list = %d\n", sizeof(pp), sizeof(page_free_list));
653 // check that we didn't corrupt the free list itself
654 assert(pp >= pages);
655 assert(pp < pages + npages);
656 assert(((char *) pp - (char *) pages) % sizeof(*pp) == 0);
657
658 // check a few pages that shouldn't be on the free list
659 assert(page2pa(pp) != 0);
660 assert(page2pa(pp) != IOPHYSMEM);
661 assert(page2pa(pp) != EXTPHYSMEM - PGSIZE);
662 assert(page2pa(pp) != EXTPHYSMEM);
663 assert(page2pa(pp) < EXTPHYSMEM || (char *) page2kva(pp) >= first_free_page);
664
665 cprintf("value of EXTPHYSMEM and page2pa(pp): %08x | %08x | %08x\n", EXTPHYSMEM, page2pa(pp));
666 if (page2pa(pp) < EXTPHYSMEM)
667 ++nfree_basemem;
668 else
669 ++nfree_extmem;
670 }
671 cprintf("value of nfree_basemem and nfree_extmem: %d | %d\n", nfree_basemem, nfree_extmem);
672 assert(nfree_basemem > 0);
673 assert(nfree_extmem > 0);
674
675
676 cprintf("check_page_free_list() succeeded!\n");
677}
678
679//
680// Check the physical page allocator (page_alloc(), page_free(),
681// and page_init()).
682//
683static void
684check_page_alloc(void)
685{
686 struct PageInfo *pp, *pp0, *pp1, *pp2;
687 int nfree;
688 struct PageInfo *fl;
689 char *c;
690 int i;
691
692 if (!pages)
693 panic("'pages' is a null pointer!");
694
695 // check number of free pages
696 for (pp = page_free_list, nfree = 0; pp; pp = pp->pp_link)
697 ++nfree;
698
699 // should be able to allocate three pages
700 pp0 = pp1 = pp2 = 0;
701 assert((pp0 = page_alloc(0)));
702 assert((pp1 = page_alloc(0)));
703 assert((pp2 = page_alloc(0)));
704
705 assert(pp0);
706 assert(pp1 && pp1 != pp0);
707 assert(pp2 && pp2 != pp1 && pp2 != pp0);
708 assert(page2pa(pp0) < npages*PGSIZE);
709 assert(page2pa(pp1) < npages*PGSIZE);
710 assert(page2pa(pp2) < npages*PGSIZE);
711
712 // temporarily steal the rest of the free pages
713 fl = page_free_list;
714 page_free_list = 0;
715
716 // should be no free memory
717 assert(!page_alloc(0));
718
719 // free and re-allocate?
720 page_free(pp0);
721 page_free(pp1);
722 page_free(pp2);
723 pp0 = pp1 = pp2 = 0;
724 assert((pp0 = page_alloc(0)));
725 assert((pp1 = page_alloc(0)));
726 assert((pp2 = page_alloc(0)));
727 assert(pp0);
728 assert(pp1 && pp1 != pp0);
729 assert(pp2 && pp2 != pp1 && pp2 != pp0);
730 assert(!page_alloc(0));
731
732 // test flags
733 memset(page2kva(pp0), 1, PGSIZE);
734 page_free(pp0);
735 assert((pp = page_alloc(ALLOC_ZERO)));
736 assert(pp && pp0 == pp);
737 c = page2kva(pp);
738 for (i = 0; i < PGSIZE; i++)
739 assert(c[i] == 0);
740
741 // give free list back
742 page_free_list = fl;
743
744 // free the pages we took
745 page_free(pp0);
746 page_free(pp1);
747 page_free(pp2);
748
749 // number of free pages should be the same
750 for (pp = page_free_list; pp; pp = pp->pp_link)
751 --nfree;
752 assert(nfree == 0);
753
754 cprintf("check_page_alloc() succeeded!\n");
755}
756
757//
758// Checks that the kernel part of virtual address space
759// has been set up roughly correctly (by mem_init()).
760//
761// This function doesn't test every corner case,
762// but it is a pretty good sanity check.
763//
764
765static void
766check_kern_pgdir(void)
767{
768 uint32_t i, n;
769 pde_t *pgdir;
770
771 pgdir = kern_pgdir;
772
773 // check pages array
774 n = ROUNDUP(npages*sizeof(struct PageInfo), PGSIZE);
775 for (i = 0; i < n; i += PGSIZE)
776 assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i);
777
778
779 // check phys mem
780 for (i = 0; i < npages * PGSIZE; i += PGSIZE)
781 assert(check_va2pa(pgdir, KERNBASE + i) == i);
782
783 // check kernel stack
784 for (i = 0; i < KSTKSIZE; i += PGSIZE)
785 assert(check_va2pa(pgdir, KSTACKTOP - KSTKSIZE + i) == PADDR(bootstack) + i);
786 assert(check_va2pa(pgdir, KSTACKTOP - PTSIZE) == ~0);
787
788 // check PDE permissions
789 for (i = 0; i < NPDENTRIES; i++) {
790 switch (i) {
791 case PDX(UVPT):
792 case PDX(KSTACKTOP-1):
793 case PDX(UPAGES):
794 assert(pgdir[i] & PTE_P);
795 break;
796 default:
797 if (i >= PDX(KERNBASE)) {
798 assert(pgdir[i] & PTE_P);
799 assert(pgdir[i] & PTE_W);
800 } else
801 assert(pgdir[i] == 0);
802 break;
803 }
804 }
805 cprintf("check_kern_pgdir() succeeded!\n");
806}
807
808// This function returns the physical address of the page containing 'va',
809// defined by the page directory 'pgdir'. The hardware normally performs
810// this functionality for us! We define our own version to help check
811// the check_kern_pgdir() function; it shouldn't be used elsewhere.
812
813static physaddr_t
814check_va2pa(pde_t *pgdir, uintptr_t va)
815{
816 pte_t *p;
817
818 pgdir = &pgdir[PDX(va)];
819 if (!(*pgdir & PTE_P))
820 return ~0;
821 p = (pte_t*) KADDR(PTE_ADDR(*pgdir));
822 if (!(p[PTX(va)] & PTE_P))
823 return ~0;
824 return PTE_ADDR(p[PTX(va)]);
825}
826
827
828// check page_insert, page_remove, &c
829static void
830check_page(void)
831{
832 struct PageInfo *pp, *pp0, *pp1, *pp2;
833 struct PageInfo *fl;
834 pte_t *ptep, *ptep1;
835 void *va;
836 int i;
837 extern pde_t entry_pgdir[];
838
839 // should be able to allocate three pages
840 pp0 = pp1 = pp2 = 0;
841 assert((pp0 = page_alloc(0)));
842 assert((pp1 = page_alloc(0)));
843 assert((pp2 = page_alloc(0)));
844
845 assert(pp0);
846 assert(pp1 && pp1 != pp0);
847 assert(pp2 && pp2 != pp1 && pp2 != pp0);
848
849 // temporarily steal the rest of the free pages
850 fl = page_free_list;
851 page_free_list = 0;
852
853 // should be no free memory
854 assert(!page_alloc(0));
855
856 // there is no page allocated at address 0
857 assert(page_lookup(kern_pgdir, (void *) 0x0, &ptep) == NULL);
858
859 // there is no free memory, so we can't allocate a page table
860 assert(page_insert(kern_pgdir, pp1, 0x0, PTE_W) < 0);
861
862 // free pp0 and try again: pp0 should be used for page table
863 page_free(pp0);
864 assert(page_insert(kern_pgdir, pp1, 0x0, PTE_W) == 0);
865 assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0));
866 assert(check_va2pa(kern_pgdir, 0x0) == page2pa(pp1));
867 assert(pp1->pp_ref == 1);
868 assert(pp0->pp_ref == 1);
869
870 // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
871 assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0);
872 assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2));
873 assert(pp2->pp_ref == 1);
874
875 // should be no free memory
876 assert(!page_alloc(0));
877
878 // should be able to map pp2 at PGSIZE because it's already there
879 assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0);
880 assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2));
881 assert(pp2->pp_ref == 1);
882
883 // pp2 should NOT be on the free list
884 // could happen in ref counts are handled sloppily in page_insert
885 assert(!page_alloc(0));
886
887 // check that pgdir_walk returns a pointer to the pte
888 ptep = (pte_t *) KADDR(PTE_ADDR(kern_pgdir[PDX(PGSIZE)]));
889 assert(pgdir_walk(kern_pgdir, (void*)PGSIZE, 0) == ptep+PTX(PGSIZE));
890
891 // should be able to change permissions too.
892 assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W|PTE_U) == 0);
893 assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp2));
894 assert(pp2->pp_ref == 1);
895 assert(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U);
896 assert(kern_pgdir[0] & PTE_U);
897
898 // should be able to remap with fewer permissions
899 assert(page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W) == 0);
900 assert(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_W);
901 assert(!(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U));
902
903 // should not be able to map at PTSIZE because need free page for page table
904 assert(page_insert(kern_pgdir, pp0, (void*) PTSIZE, PTE_W) < 0);
905
906 // insert pp1 at PGSIZE (replacing pp2)
907 assert(page_insert(kern_pgdir, pp1, (void*) PGSIZE, PTE_W) == 0);
908 assert(!(*pgdir_walk(kern_pgdir, (void*) PGSIZE, 0) & PTE_U));
909
910 // should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
911 assert(check_va2pa(kern_pgdir, 0) == page2pa(pp1));
912 assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp1));
913 // ... and ref counts should reflect this
914 assert(pp1->pp_ref == 2);
915 assert(pp2->pp_ref == 0);
916
917 // pp2 should be returned by page_alloc
918 assert((pp = page_alloc(0)) && pp == pp2);
919
920 // unmapping pp1 at 0 should keep pp1 at PGSIZE
921 page_remove(kern_pgdir, 0x0);
922 assert(check_va2pa(kern_pgdir, 0x0) == ~0);
923 assert(check_va2pa(kern_pgdir, PGSIZE) == page2pa(pp1));
924 assert(pp1->pp_ref == 1);
925 assert(pp2->pp_ref == 0);
926
927 // test re-inserting pp1 at PGSIZE
928 assert(page_insert(kern_pgdir, pp1, (void*) PGSIZE, 0) == 0);
929 assert(pp1->pp_ref);
930 assert(pp1->pp_link == NULL);
931
932 // unmapping pp1 at PGSIZE should free it
933 page_remove(kern_pgdir, (void*) PGSIZE);
934 assert(check_va2pa(kern_pgdir, 0x0) == ~0);
935 assert(check_va2pa(kern_pgdir, PGSIZE) == ~0);
936 assert(pp1->pp_ref == 0);
937 assert(pp2->pp_ref == 0);
938
939 // so it should be returned by page_alloc
940 assert((pp = page_alloc(0)) && pp == pp1);
941
942 // should be no free memory
943 assert(!page_alloc(0));
944
945 // forcibly take pp0 back
946 assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0));
947 kern_pgdir[0] = 0;
948 assert(pp0->pp_ref == 1);
949 pp0->pp_ref = 0;
950
951 // check pointer arithmetic in pgdir_walk
952 page_free(pp0);
953 va = (void*)(PGSIZE * NPDENTRIES + PGSIZE);
954 ptep = pgdir_walk(kern_pgdir, va, 1);
955 ptep1 = (pte_t *) KADDR(PTE_ADDR(kern_pgdir[PDX(va)]));
956 assert(ptep == ptep1 + PTX(va));
957 kern_pgdir[PDX(va)] = 0;
958 pp0->pp_ref = 0;
959
960 // check that new page tables get cleared
961 memset(page2kva(pp0), 0xFF, PGSIZE);
962 page_free(pp0);
963 pgdir_walk(kern_pgdir, 0x0, 1);
964 ptep = (pte_t *) page2kva(pp0);
965 for(i=0; i<NPTENTRIES; i++)
966 assert((ptep[i] & PTE_P) == 0);
967 kern_pgdir[0] = 0;
968 pp0->pp_ref = 0;
969
970 // give free list back
971 page_free_list = fl;
972
973 // free the pages we took
974 page_free(pp0);
975 page_free(pp1);
976 page_free(pp2);
977
978 cprintf("check_page() succeeded!\n");
979}
980
981// check page_insert, page_remove, &c, with an installed kern_pgdir
982static void
983check_page_installed_pgdir(void)
984{
985 struct PageInfo *pp, *pp0, *pp1, *pp2;
986 struct PageInfo *fl;
987 pte_t *ptep, *ptep1;
988 uintptr_t va;
989 int i;
990
991 // check that we can read and write installed pages
992 pp1 = pp2 = 0;
993 assert((pp0 = page_alloc(0)));
994 assert((pp1 = page_alloc(0)));
995 assert((pp2 = page_alloc(0)));
996 page_free(pp0);
997 memset(page2kva(pp1), 1, PGSIZE);
998 memset(page2kva(pp2), 2, PGSIZE);
999 page_insert(kern_pgdir, pp1, (void*) PGSIZE, PTE_W);
1000 assert(pp1->pp_ref == 1);
1001 assert(*(uint32_t *)PGSIZE == 0x01010101U);
1002 page_insert(kern_pgdir, pp2, (void*) PGSIZE, PTE_W);
1003 assert(*(uint32_t *)PGSIZE == 0x02020202U);
1004 assert(pp2->pp_ref == 1);
1005 assert(pp1->pp_ref == 0);
1006 *(uint32_t *)PGSIZE = 0x03030303U;
1007 assert(*(uint32_t *)page2kva(pp2) == 0x03030303U);
1008 page_remove(kern_pgdir, (void*) PGSIZE);
1009 assert(pp2->pp_ref == 0);
1010
1011 // forcibly take pp0 back
1012 assert(PTE_ADDR(kern_pgdir[0]) == page2pa(pp0));
1013 kern_pgdir[0] = 0;
1014 assert(pp0->pp_ref == 1);
1015 pp0->pp_ref = 0;
1016
1017 // free the pages we took
1018 page_free(pp0);
1019
1020 cprintf("check_page_installed_pgdir() succeeded!\n");
1021}