Make lookup_page() just return null for uaddr >= PHYS_BASE.
[pintos-anon] / src / userprog / pagedir.c
1 #include "userprog/pagedir.h"
2 #include <stdbool.h>
3 #include <stddef.h>
4 #include <string.h>
5 #include "threads/init.h"
6 #include "threads/mmu.h"
7 #include "threads/palloc.h"
8
9 /* Creates a new page directory that has mappings for kernel
10    virtual addresses, but none for user virtual addresses.
11    Returns the new page directory, or a null pointer if memory
12    allocation fails. */
13 uint32_t *
14 pagedir_create (void) 
15 {
16   uint32_t *pd = palloc_get (0);
17   memcpy (pd, base_page_dir, PGSIZE);
18   return pd;
19 }
20
21 /* Destroys page directory PD, freeing all the pages it
22    references. */
23 void
24 pagedir_destroy (uint32_t *pd) 
25 {
26   uint32_t *pde;
27
28   if (pd == NULL)
29     return;
30
31   for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
32     if (*pde & PG_P) 
33       {
34         uint32_t *pt = pde_get_pt (*pde);
35         uint32_t *pte;
36         
37         for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
38           if (*pte & PG_P) 
39             palloc_free (pte_get_page (*pte));
40         palloc_free (pt);
41       }
42   palloc_free (pd);
43 }
44
45 /* Returns the mapping of user virtual address UADDR in page
46    directory PD into a kernel virtual address.
47    If UADDR is unmapped, behavior varies based on CREATE:
48    if CREATE is true, then a new, zeroed page is created and a
49    pointer into it is returned,
50    otherwise a null pointer is returned.
51    Also returns a null pointer if UADDR is a kernel address. */
52 static uint32_t *
53 lookup_page (uint32_t *pd, void *uaddr, bool create)
54 {
55   uint32_t *pt, *pde;
56
57   ASSERT (pd != NULL);
58
59   /* Make sure it's a user address. */
60   if (uaddr >= PHYS_BASE)
61     return NULL;
62
63   /* Check for a page table for UADDR.
64      If one is missing, create one if requested. */
65   pde = pd + pd_no (uaddr);
66   if (*pde == 0) 
67     {
68       if (create)
69         {
70           pt = palloc_get (PAL_ZERO);
71           if (pt == NULL) 
72             return NULL; 
73       
74           *pde = pde_create (pt);
75         }
76       else
77         return NULL;
78     }
79
80   /* Return the page table entry. */
81   pt = pde_get_pt (*pde);
82   return &pt[pt_no (uaddr)];
83 }
84
85 /* Adds a mapping from user virtual address UPAGE to kernel
86    virtual address KPAGE in page directory PD.
87    UPAGE must not already be mapped.
88    If WRITABLE is true, the new page is read/write;
89    otherwise it is read-only.
90    Returns true if successful, false if memory allocation
91    failed. */
92 bool
93 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
94                   bool writable) 
95 {
96   uint32_t *pte;
97
98   ASSERT (pg_ofs (upage) == 0);
99   ASSERT (pg_ofs (kpage) == 0);
100   ASSERT (upage < PHYS_BASE);
101   ASSERT (lookup_page (pd, upage, false) == NULL);
102
103   pte = lookup_page (pd, upage, true);
104   if (pte != NULL) 
105     {
106       *pte = pte_create_user (kpage, writable);
107       return true;
108     }
109   else
110     return false;
111 }
112
113 /* Returns the kernel virtual address that user virtual address
114    UADDR is mapped to in PD, or a null pointer if there is no
115    mapping. */
116 void *
117 pagedir_get_page (uint32_t *pd, const void *uaddr) 
118 {
119   uint32_t *pte = lookup_page (pd, (void *) uaddr, false);
120   return pte != NULL && *pte != 0 ? pte_get_page (*pte) : NULL;
121 }
122
123 /* Loads page directory PD into the CPU's page directory base
124    register. */
125 void
126 pagedir_activate (uint32_t *pd) 
127 {
128   if (pd == NULL)
129     pd = base_page_dir;
130   asm volatile ("movl %0,%%cr3" :: "r" (vtop (pd)));
131 }