CS318 - Pintos
Pintos source browser for JHU CS318 course
pagedir.c
Go to the documentation of this file.
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/pte.h"
7 #include "threads/palloc.h"
8 
9 static uint32_t *active_pd (void);
10 static void invalidate_pagedir (uint32_t *);
11 
12 /** Creates a new page directory that has mappings for kernel
13  virtual addresses, but none for user virtual addresses.
14  Returns the new page directory, or a null pointer if memory
15  allocation fails. */
16 uint32_t *
18 {
19  uint32_t *pd = palloc_get_page (0);
20  if (pd != NULL)
22  return pd;
23 }
24 
25 /** Destroys page directory PD, freeing all the pages it
26  references. */
27 void
29 {
30  uint32_t *pde;
31 
32  if (pd == NULL)
33  return;
34 
35  ASSERT (pd != init_page_dir);
36  for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
37  if (*pde & PTE_P)
38  {
39  uint32_t *pt = pde_get_pt (*pde);
40  uint32_t *pte;
41 
42  for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
43  if (*pte & PTE_P)
45  palloc_free_page (pt);
46  }
47  palloc_free_page (pd);
48 }
49 
50 /** Returns the address of the page table entry for virtual
51  address VADDR in page directory PD.
52  If PD does not have a page table for VADDR, behavior depends
53  on CREATE. If CREATE is true, then a new page table is
54  created and a pointer into it is returned. Otherwise, a null
55  pointer is returned. */
56 static uint32_t *
57 lookup_page (uint32_t *pd, const void *vaddr, bool create)
58 {
59  uint32_t *pt, *pde;
60 
61  ASSERT (pd != NULL);
62 
63  /* Shouldn't create new kernel virtual mappings. */
64  ASSERT (!create || is_user_vaddr (vaddr));
65 
66  /* Check for a page table for VADDR.
67  If one is missing, create one if requested. */
68  pde = pd + pd_no (vaddr);
69  if (*pde == 0)
70  {
71  if (create)
72  {
74  if (pt == NULL)
75  return NULL;
76 
77  *pde = pde_create (pt);
78  }
79  else
80  return NULL;
81  }
82 
83  /* Return the page table entry. */
84  pt = pde_get_pt (*pde);
85  return &pt[pt_no (vaddr)];
86 }
87 
88 /** Adds a mapping in page directory PD from user virtual page
89  UPAGE to the physical frame identified by kernel virtual
90  address KPAGE.
91  UPAGE must not already be mapped.
92  KPAGE should probably be a page obtained from the user pool
93  with palloc_get_page().
94  If WRITABLE is true, the new page is read/write;
95  otherwise it is read-only.
96  Returns true if successful, false if memory allocation
97  failed. */
98 bool
99 pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool writable)
100 {
101  uint32_t *pte;
102 
103  ASSERT (pg_ofs (upage) == 0);
104  ASSERT (pg_ofs (kpage) == 0);
105  ASSERT (is_user_vaddr (upage));
106  ASSERT (vtop (kpage) >> PTSHIFT < init_ram_pages);
107  ASSERT (pd != init_page_dir);
108 
109  pte = lookup_page (pd, upage, true);
110 
111  if (pte != NULL)
112  {
113  ASSERT ((*pte & PTE_P) == 0);
114  *pte = pte_create_user (kpage, writable);
115  return true;
116  }
117  else
118  return false;
119 }
120 
121 /** Looks up the physical address that corresponds to user virtual
122  address UADDR in PD. Returns the kernel virtual address
123  corresponding to that physical address, or a null pointer if
124  UADDR is unmapped. */
125 void *
126 pagedir_get_page (uint32_t *pd, const void *uaddr)
127 {
128  uint32_t *pte;
129 
130  ASSERT (is_user_vaddr (uaddr));
131 
132  pte = lookup_page (pd, uaddr, false);
133  if (pte != NULL && (*pte & PTE_P) != 0)
134  return pte_get_page (*pte) + pg_ofs (uaddr);
135  else
136  return NULL;
137 }
138 
139 /** Marks user virtual page UPAGE "not present" in page
140  directory PD. Later accesses to the page will fault. Other
141  bits in the page table entry are preserved.
142  UPAGE need not be mapped. */
143 void
144 pagedir_clear_page (uint32_t *pd, void *upage)
145 {
146  uint32_t *pte;
147 
148  ASSERT (pg_ofs (upage) == 0);
149  ASSERT (is_user_vaddr (upage));
150 
151  pte = lookup_page (pd, upage, false);
152  if (pte != NULL && (*pte & PTE_P) != 0)
153  {
154  *pte &= ~PTE_P;
155  invalidate_pagedir (pd);
156  }
157 }
158 
159 /** Returns true if the PTE for virtual page VPAGE in PD is dirty,
160  that is, if the page has been modified since the PTE was
161  installed.
162  Returns false if PD contains no PTE for VPAGE. */
163 bool
164 pagedir_is_dirty (uint32_t *pd, const void *vpage)
165 {
166  uint32_t *pte = lookup_page (pd, vpage, false);
167  return pte != NULL && (*pte & PTE_D) != 0;
168 }
169 
170 /** Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
171  in PD. */
172 void
173 pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty)
174 {
175  uint32_t *pte = lookup_page (pd, vpage, false);
176  if (pte != NULL)
177  {
178  if (dirty)
179  *pte |= PTE_D;
180  else
181  {
182  *pte &= ~(uint32_t) PTE_D;
183  invalidate_pagedir (pd);
184  }
185  }
186 }
187 
188 /** Returns true if the PTE for virtual page VPAGE in PD has been
189  accessed recently, that is, between the time the PTE was
190  installed and the last time it was cleared. Returns false if
191  PD contains no PTE for VPAGE. */
192 bool
193 pagedir_is_accessed (uint32_t *pd, const void *vpage)
194 {
195  uint32_t *pte = lookup_page (pd, vpage, false);
196  return pte != NULL && (*pte & PTE_A) != 0;
197 }
198 
199 /** Sets the accessed bit to ACCESSED in the PTE for virtual page
200  VPAGE in PD. */
201 void
202 pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed)
203 {
204  uint32_t *pte = lookup_page (pd, vpage, false);
205  if (pte != NULL)
206  {
207  if (accessed)
208  *pte |= PTE_A;
209  else
210  {
211  *pte &= ~(uint32_t) PTE_A;
212  invalidate_pagedir (pd);
213  }
214  }
215 }
216 
217 /** Loads page directory PD into the CPU's page directory base
218  register. */
219 void
221 {
222  if (pd == NULL)
223  pd = init_page_dir;
224 
225  /* Store the physical address of the page directory into CR3
226  aka PDBR (page directory base register). This activates our
227  new page tables immediately. See [IA32-v2a] "MOV--Move
228  to/from Control Registers" and [IA32-v3a] 3.7.5 "Base
229  Address of the Page Directory". */
230  asm volatile ("movl %0, %%cr3" : : "r" (vtop (pd)) : "memory");
231 }
232 
233 /** Returns the currently active page directory. */
234 static uint32_t *
235 active_pd (void)
236 {
237  /* Copy CR3, the page directory base register (PDBR), into
238  `pd'.
239  See [IA32-v2a] "MOV--Move to/from Control Registers" and
240  [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
241  uintptr_t pd;
242  asm volatile ("movl %%cr3, %0" : "=r" (pd));
243  return ptov (pd);
244 }
245 
246 /** Seom page table changes can cause the CPU's translation
247  lookaside buffer (TLB) to become out-of-sync with the page
248  table. When this happens, we have to "invalidate" the TLB by
249  re-activating it.
250 
251  This function invalidates the TLB if PD is the active page
252  directory. (If PD is not active then its entries are not in
253  the TLB, so there is no need to invalidate anything.) */
254 static void
256 {
257  if (active_pd () == pd)
258  {
259  /* Re-activating PD clears the TLB. See [IA32-v3a] 3.12
260  "Translation Lookaside Buffers (TLBs)". */
261  pagedir_activate (pd);
262  }
263 }
pagedir_create
uint32_t * pagedir_create(void)
Creates a new page directory that has mappings for kernel virtual addresses, but none for user virtua...
Definition: pagedir.c:17
pagedir_get_page
void * pagedir_get_page(uint32_t *pd, const void *uaddr)
Looks up the physical address that corresponds to user virtual address UADDR in PD.
Definition: pagedir.c:126
pagedir_set_dirty
void pagedir_set_dirty(uint32_t *pd, const void *vpage, bool dirty)
Set the dirty bit to DIRTY in the PTE for virtual page VPAGE in PD.
Definition: pagedir.c:173
PAL_ZERO
Zero page contents.
Definition: palloc.h:10
PHYS_BASE
#define PHYS_BASE
Base address of the 1:1 physical-to-virtual mapping.
Definition: vaddr.h:53
vtop
static uintptr_t vtop(const void *vaddr)
Returns physical address at which kernel virtual address VADDR is mapped.
Definition: vaddr.h:82
NULL
#define NULL
Definition: stddef.h:4
string.h
PTSHIFT
#define PTSHIFT
Functions and macros for working with x86 hardware page tables.
Definition: pte.h:21
init_ram_pages
uint32_t init_ram_pages
Amount of physical memory, in 4 kB pages.
memcpy
void * memcpy(void *dst_, const void *src_, size_t size)
Copies SIZE bytes from SRC to DST, which must not overlap.
Definition: string.c:7
ptov
static void * ptov(uintptr_t paddr)
Returns kernel virtual address at which physical address PADDR is mapped.
Definition: vaddr.h:72
PTE_P
#define PTE_P
1=present, 0=not present.
Definition: pte.h:64
PTE_D
#define PTE_D
1=dirty, 0=not dirty (PTEs only).
Definition: pte.h:68
stdbool.h
PGSIZE
#define PGSIZE
Bytes in a page.
Definition: vaddr.h:20
pte.h
lookup_page
static uint32_t * lookup_page(uint32_t *pd, const void *vaddr, bool create)
Returns the address of the page table entry for virtual address VADDR in page directory PD.
Definition: pagedir.c:57
palloc_get_page
void * palloc_get_page(enum palloc_flags flags)
Obtains a single free page and returns its kernel virtual address.
Definition: palloc.c:111
active_pd
static uint32_t * active_pd(void)
Returns the currently active page directory.
Definition: pagedir.c:235
pagedir_is_dirty
bool pagedir_is_dirty(uint32_t *pd, const void *vpage)
Returns true if the PTE for virtual page VPAGE in PD is dirty, that is, if the page has been modified...
Definition: pagedir.c:164
pagedir_set_accessed
void pagedir_set_accessed(uint32_t *pd, const void *vpage, bool accessed)
Sets the accessed bit to ACCESSED in the PTE for virtual page VPAGE in PD.
Definition: pagedir.c:202
pt_no
static unsigned pt_no(const void *va)
Obtains page table index from a virtual address.
Definition: pte.h:32
uint32_t
unsigned int uint32_t
Definition: stdint.h:26
init.h
pagedir_is_accessed
bool pagedir_is_accessed(uint32_t *pd, const void *vpage)
Returns true if the PTE for virtual page VPAGE in PD has been accessed recently, that is,...
Definition: pagedir.c:193
PTE_A
#define PTE_A
1=accessed, 0=not acccessed.
Definition: pte.h:67
pagedir_set_page
bool pagedir_set_page(uint32_t *pd, void *upage, void *kpage, bool writable)
Adds a mapping in page directory PD from user virtual page UPAGE to the physical frame identified by ...
Definition: pagedir.c:99
invalidate_pagedir
static void invalidate_pagedir(uint32_t *)
Seom page table changes can cause the CPU's translation lookaside buffer (TLB) to become out-of-sync ...
Definition: pagedir.c:255
palloc.h
pte_get_page
static void * pte_get_page(uint32_t pte)
Returns a pointer to the page that page table entry PTE points to.
Definition: pte.h:102
pde_get_pt
static uint32_t * pde_get_pt(uint32_t pde)
Returns a pointer to the page table that page directory entry PDE, which must "present",...
Definition: pte.h:78
ASSERT
#define ASSERT(CONDITION)
This is outside the header guard so that debug.h may be included multiple times with different settin...
Definition: debug.h:31
pagedir.h
palloc_free_page
void palloc_free_page(void *page)
Frees the page at PAGE.
Definition: palloc.c:146
pd_no
static uintptr_t pd_no(const void *va)
Obtains page directory index from a virtual address.
Definition: pte.h:37
pagedir_activate
void pagedir_activate(uint32_t *pd)
Loads page directory PD into the CPU's page directory base register.
Definition: pagedir.c:220
is_user_vaddr
static bool is_user_vaddr(const void *vaddr)
Returns true if VADDR is a user virtual address.
Definition: vaddr.h:57
init_page_dir
uint32_t * init_page_dir
Page directory with kernel mappings only.
Definition: init.c:42
pte_create_user
static uint32_t pte_create_user(void *page, bool writable)
Returns a PTE that points to PAGE.
Definition: pte.h:96
pde_create
static uint32_t pde_create(uint32_t *pt)
Returns a PDE that points to page table PT.
Definition: pte.h:71
pagedir_destroy
void pagedir_destroy(uint32_t *pd)
Destroys page directory PD, freeing all the pages it references.
Definition: pagedir.c:28
uintptr_t
uint32_t uintptr_t
Definition: stdint.h:36
pg_ofs
static unsigned pg_ofs(const void *va)
Offset within a page.
Definition: vaddr.h:24
stddef.h
create
bool create(const char *file, unsigned initial_size)
Definition: syscall.c:91
pagedir_clear_page
void pagedir_clear_page(uint32_t *pd, void *upage)
Marks user virtual page UPAGE "not present" in page directory PD.
Definition: pagedir.c:144