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