Rewrite page allocator to support multi-page allocations.
[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_page (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   ASSERT (pd != base_page_dir);
32   for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
33     if (*pde & PG_P) 
34       {
35         uint32_t *pt = pde_get_pt (*pde);
36         uint32_t *pte;
37         
38         for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
39           if (*pte & PG_P) 
40             palloc_free_page (pte_get_page (*pte));
41         palloc_free_page (pt);
42       }
43   palloc_free_page (pd);
44 }
45
46 /* Returns the mapping of user virtual address UADDR in page
47    directory PD into a kernel virtual address.
48    If UADDR is unmapped, behavior varies based on CREATE:
49    if CREATE is true, then a new, zeroed page is created and a
50    pointer into it is returned,
51    otherwise a null pointer is returned.
52    Also returns a null pointer if UADDR is a kernel address. */
53 static uint32_t *
54 lookup_page (uint32_t *pd, void *uaddr, bool create)
55 {
56   uint32_t *pt, *pde;
57
58   ASSERT (pd != NULL);
59
60   /* Make sure it's a user address. */
61   if (uaddr >= PHYS_BASE)
62     return NULL;
63
64   /* Check for a page table for UADDR.
65      If one is missing, create one if requested. */
66   pde = pd + pd_no (uaddr);
67   if (*pde == 0) 
68     {
69       if (create)
70         {
71           pt = palloc_get_page (PAL_ZERO);
72           if (pt == NULL) 
73             return NULL; 
74       
75           *pde = pde_create (pt);
76         }
77       else
78         return NULL;
79     }
80
81   /* Return the page table entry. */
82   pt = pde_get_pt (*pde);
83   return &pt[pt_no (uaddr)];
84 }
85
86 /* Adds a mapping from user virtual address UPAGE to kernel
87    virtual address KPAGE in page directory PD.
88    UPAGE must not already be mapped.
89    If WRITABLE is true, the new page is read/write;
90    otherwise it is read-only.
91    Returns true if successful, false if memory allocation
92    failed. */
93 bool
94 pagedir_set_page (uint32_t *pd, void *upage, void *kpage,
95                   bool writable) 
96 {
97   uint32_t *pte;
98
99   ASSERT (pg_ofs (upage) == 0);
100   ASSERT (pg_ofs (kpage) == 0);
101   ASSERT (upage < PHYS_BASE);
102   ASSERT (pagedir_get_page (pd, upage) == NULL);
103
104   pte = lookup_page (pd, upage, true);
105   if (pte != NULL) 
106     {
107       *pte = pte_create_user (kpage, writable);
108       return true;
109     }
110   else
111     return false;
112 }
113
114 /* Returns the kernel virtual address that user virtual address
115    UADDR is mapped to in PD, or a null pointer if there is no
116    mapping. */
117 void *
118 pagedir_get_page (uint32_t *pd, const void *uaddr) 
119 {
120   uint32_t *pte = lookup_page (pd, (void *) uaddr, false);
121   return pte != NULL && *pte != 0 ? pte_get_page (*pte) : NULL;
122 }
123
124 /* Loads page directory PD into the CPU's page directory base
125    register. */
126 void
127 pagedir_activate (uint32_t *pd) 
128 {
129   if (pd == NULL)
130     pd = base_page_dir;
131   asm volatile ("movl %0,%%cr3" :: "r" (vtop (pd)));
132 }