AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
fault.c
Go to the documentation of this file.
1
16
17#include <core/system.h>
18#include <core/panic.h>
19#include <core/arch.h>
20
21#include <mm/pmap.h>
22#include <mm/vm.h>
23
24#include <sys/sched.h>
25#include <sys/signal.h>
26
28struct pf {
29 int flags;
31
34
35 size_t off; /* the page's offset in the mapping's object or anon */
36 hash_t hash; /* `off` hashed, to look it up */
37};
38
41static inline int check_violation(int flags, int vm_flags)
42{
43 return
44 ((flags & PF_READ) && !(vm_flags & VM_UR)) ||
45 ((flags & PF_WRITE) && !(vm_flags & VM_UW)) ||
46 ((flags & PF_EXEC) && !(vm_flags & VM_UX));
47}
48
58static inline int pf_present(struct pf *pf)
59{
60 /* the page is mapped, but without the permission the access needed */
61 struct vm_entry *vm_entry = pf->vm_entry;
63
64 struct pmap *pmap = pf->vm_space->pmap;
65
66 /* with no anon, or a shared one, this step can't handle it: the
67 * next steps do */
68 if (!vm_anon || vm_anon->ref != 1)
69 return 0;
70
71 /* only this process has the anon */
72 struct hashmap_node *hash_node = hashmap_lookup(vm_anon->arefs, pf->hash, &pf->off);
73 struct vm_aref *vm_aref = (struct vm_aref *) (hash_node? hash_node->entry : NULL);
74
75 if (!vm_aref || vm_aref->ref != 1)
76 return 0;
77
78 if (vm_aref->flags & VM_COPY) {
79 /* copy the page */
80 struct vm_page *new_page = mm_page_alloc();
81
82 if (!new_page)
83 return -ENOMEM;
84
85 new_page->off = vm_aref->vm_page->off;
86 new_page->ref = 1;
87 new_page->vm_object = NULL;
88
90
92
93 vm_aref->vm_page = new_page;
95
96 /* if there's no memory for a page table, the copy is the aref's
97 * already, and the next fault maps it */
98 int err = mm_page_map(pmap, pf->addr, new_page->paddr, pf->vm_entry->flags & VM_PERM);
99
100 if (err)
101 return err;
102 } else {
103 /* only this process has the page: just change its permissions */
105 }
106
107 return 1;
108}
109
119static inline int pf_anon(struct pf *pf)
120{
121 struct vm_entry *vm_entry = pf->vm_entry;
122 struct vm_anon *vm_anon = vm_entry->vm_anon;
123
124 struct pmap *pmap = pf->vm_space->pmap;
125
126 if (vm_anon->flags & VM_COPY) {
127
128 if (vm_anon->ref > 1) {
129 struct vm_anon *new_anon = vm_anon_copy(vm_anon);
130
131 if (!new_anon)
132 return -ENOMEM;
133
135 vm_entry->vm_anon = new_anon;
136 vm_anon = new_anon;
137 }
138
140 }
141
142 struct hashmap_node *aref_node = hashmap_lookup(vm_anon->arefs, pf->hash, &pf->off);
143
144 if (!aref_node || !aref_node->entry)
145 return 0;
146
147 struct vm_aref *aref = (struct vm_aref *) aref_node->entry;
148
149 if (!aref->vm_page)
150 panic("aref has no page");
151
152 if (!(pf->flags & PF_WRITE)) {
153 /* a read: map it read-only */
154 struct vm_page *vm_page = aref->vm_page;
155
156 uint32_t perm = (vm_entry->flags & VM_PERM) & ~(VM_UW|VM_KW);
157 int err = mm_page_map(pmap, pf->addr, vm_page->paddr, perm);
158
159 if (err)
160 return err;
161
163 return 1;
164 }
165
166 /* a write */
167
168 if (aref->ref == 1) {
169 /* only this process has the page: just map it */
170
171 struct vm_page *vm_page = aref->vm_page;
172
173 uint32_t perm = vm_entry->flags & VM_PERM;
174 int err = mm_page_map(pmap, pf->addr, vm_page->paddr, perm);
175
176 if (err)
177 return err;
178
180 return 1;
181 }
182
183 /* the page is shared: copy it, and map the copy read-write */
184
185 /* get the copy and its aref first, so that if there's no memory for
186 * them, nothing has changed */
187 struct vm_aref *new_aref = kmalloc(sizeof(struct vm_aref), &M_VM_AREF, M_ZERO);
188 struct vm_page *new_page = new_aref ? mm_page_alloc() : NULL;
189
190 if (!new_page) {
191 kfree(new_aref);
192 return -ENOMEM;
193 }
194
195 struct vm_page *vm_page = aref->vm_page;
196
197 new_page->off = vm_page->off;
198 new_page->ref = 1;
199 new_page->vm_object = NULL;
200
201 pmap_page_copy(vm_page->paddr, new_page->paddr);
202
203 new_aref->ref = 1;
204 new_aref->flags = aref->flags;
205 new_aref->vm_page = new_page;
206
207 /* the anon's page at this offset is the copy now (the key is the
208 * same, so its hashmap node takes the new aref) */
209 aref_node->entry = new_aref;
210 aref->ref--;
211
212 /* if there's no memory for a page table, the copy is the anon's
213 * already, and the next fault maps it */
214 int err = mm_page_map(pmap, pf->addr, new_page->paddr, vm_entry->flags & VM_PERM);
215
216 return err ? err : 1;
217}
218
222static inline struct vm_page *vm_object_page(struct vm_object *vm_object, hash_t hash, size_t off)
223{
224 struct hashmap_node *hash_node = hashmap_lookup(vm_object->pages, hash, &off);
225 struct vm_page *vm_page = NULL;
226
227 if (hash_node) {
228 /* the object has the page */
229 vm_page = (struct vm_page *) hash_node->entry;
230 } else {
231 /* it doesn't: page it in */
232 if (vm_object->pager && vm_object->pager->in)
234 else
235 printk("vm: an object with no pager, page %p missing\n", (void *) off);
236 }
237
238 return vm_page;
239}
240
249static inline int pf_object(struct pf *pf)
250{
251 struct vm_entry *vm_entry = pf->vm_entry;
252
254 struct vm_page *vm_page = NULL;
255 struct pmap *pmap = pf->vm_space->pmap;
256
257 /* the object's page, read in if needed */
259
260 if (!vm_page)
261 return -EIO;
262
263 if (!(vm_entry->flags & VM_UW)) {
264 /* a read-only mapping: map the object's page */
266
267 if (err)
268 return err;
269
271 return 1;
272 }
273
274 /* a writable mapping: the process gets its own copy, in its anon */
275
276 /* make the anon if there's none yet */
277 if (!vm_entry->vm_anon) {
278 if (!(vm_entry->vm_anon = vm_anon_new()))
279 return -ENOMEM;
280
281 vm_entry->vm_anon->ref = 1;
282 }
283
284 struct vm_aref *vm_aref = kmalloc(sizeof(struct vm_aref), &M_VM_AREF, M_ZERO);
285
286 if (!vm_aref)
287 return -ENOMEM;
288
289 vm_aref->ref = 1;
290
291 /* copy the page */
292 struct vm_page *new_page = mm_page_alloc();
293
294 if (!new_page) {
295 kfree(vm_aref);
296 return -ENOMEM;
297 }
298
299 new_page->off = vm_page->off;
300 new_page->ref = 1;
301 new_page->vm_object = NULL;
302
303 pmap_page_copy(vm_page->paddr, new_page->paddr);
304
305 vm_aref->vm_page = new_page;
306
307 if (hashmap_insert(vm_entry->vm_anon->arefs, pf->hash, vm_aref)) {
308 mm_page_dealloc(new_page->paddr);
309 kfree(vm_aref);
310 return -ENOMEM;
311 }
312
313 /* if there's no memory for a page table, the copy is the anon's
314 * already, and the next fault maps it */
315 int err = mm_page_map(pmap, pf->addr, new_page->paddr, vm_entry->flags & VM_PERM);
316
317 return err ? err : 1;
318}
319
326static inline int pf_zero(struct pf *pf)
327{
328 struct vm_entry *vm_entry = pf->vm_entry;
329 struct pmap *pmap = pf->vm_space->pmap;
330
331 if (!vm_entry->vm_anon) {
332 if (!(vm_entry->vm_anon = vm_anon_new()))
333 return -ENOMEM;
334
335 vm_entry->vm_anon->ref = 1;
336 }
337
338 struct vm_aref *vm_aref = kmalloc(sizeof(struct vm_aref), &M_VM_AREF, M_ZERO);
339
340 if (!vm_aref)
341 return -ENOMEM;
342
343 struct vm_page *new_page = mm_page_alloc();
344
345 if (!new_page) {
346 kfree(vm_aref);
347 return -ENOMEM;
348 }
349
350 new_page->off = pf->off;
351 new_page->ref = 1;
352
353 vm_aref->vm_page = new_page;
354 vm_aref->ref = 1;
355
356 /* zero the page through its mapping (writable by the kernel only, for
357 * now) before the anon has it: a page that isn't zeroed yet is never
358 * any process's, so another process's old data can't leak */
359 int err = mm_page_map(pmap, pf->addr, new_page->paddr, VM_KW);
360
361 if (!err) {
362 memset((void *) pf->addr, 0, PAGE_SIZE);
364 err = hashmap_insert(vm_entry->vm_anon->arefs, pf->hash, vm_aref);
365
366 if (err)
368 }
369
370 if (err) {
371 mm_page_dealloc(new_page->paddr);
372 kfree(vm_aref);
373 return err;
374 }
375
376 return 1;
377}
378
395int mm_page_fault(vaddr_t vaddr, int flags)
396{
397 vaddr_t addr = PAGE_ALIGN(vaddr);
398
399 struct vm_space *vm_space = &curproc->vm_space;
400 struct pmap *pmap = vm_space->pmap;
401 struct vm_entry *vm_entry = NULL;
402
403 /* the mapping the address is in */
405
406 /* no mapping, or one that doesn't allow the access: SIGSEGV */
408 goto sigsegv;
409
410 /* the page's offset in the mapping's object (and anon) */
411 size_t off = addr - vm_entry->base + vm_entry->off;
412
413 hash_t hash = hashmap_digest(&off, sizeof(off));
414
415 struct pf pf = {
416 .flags = flags,
417 .addr = addr,
418 .vm_space = vm_space,
419 .vm_entry = vm_entry,
420 .off = off,
421 .hash = hash,
422 };
423
424 /* each step returns 1 if the page is mapped now, 0 if the fault isn't
425 * that step's to handle, or a negative errno if it can't be resolved
426 * (no memory, or the file's page couldn't be read), which is then
427 * like an address that isn't the process's */
428 int ret = 0;
429
430 /* mapped, without the permission: a write to a shared page */
431 if (flags & PF_PRESENT && (ret = pf_present(&pf)))
432 goto done;
433
434 /* a page the process's anon has */
435 if (vm_entry->vm_anon && (ret = pf_anon(&pf)))
436 goto done;
437
438 /* a page of the object the mapping is of */
439 if (vm_entry->vm_object && (ret = pf_object(&pf)))
440 goto done;
441
442 /* a new page, zeroed */
443 ret = pf_zero(&pf);
444
445done:
446 if (ret > 0)
447 return 0;
448
449 if (ret < 0)
450 printk("[%d:%d] %s: page fault at %p not resolved: error %d\n",
451 curproc->pid, curthread->tid, curproc->name, (void *) vaddr, -ret);
452
453 /* out of memory: the process can't go on (the access would only fault
454 * again), so it's killed; if the fault came from a system call's copy
455 * from or to its memory, the copy fails (EFAULT), and the process is
456 * killed on its way back to user mode */
457 if (ret == -ENOMEM)
458 signal_proc_send(curproc, SIGKILL);
459
460sigsegv:
461 curthread->fault_addr = vaddr;
462 return -EFAULT;
463}
#define PAGE_SIZE
Definition system.h:7
uint64_t vaddr_t
Definition mm.h:40
#define panic(s)
Definition panic.h:15
static int pf_anon(struct pf *pf)
Handles a fault on a page that the process's anon has (a page it wrote to before).
Definition fault.c:119
static struct vm_page * vm_object_page(struct vm_object *vm_object, hash_t hash, size_t off)
Returns an object's page at offset off, reading it in with the object's pager if the object doesn't h...
Definition fault.c:222
static int pf_zero(struct pf *pf)
Handles a fault on a page that nothing has yet: a new page, zeroed, in the process's anon.
Definition fault.c:326
static int pf_object(struct pf *pf)
Handles a fault on a page of the object the mapping is of (a file's page).
Definition fault.c:249
static int check_violation(int flags, int vm_flags)
Returns whether an access of kind flags (PF_*) is one the mapping's permissions vm_flags don't allow.
Definition fault.c:41
static int pf_present(struct pf *pf)
Handles a fault on a page that is mapped, but not with the permission the access needed: a write to a...
Definition fault.c:58
uintptr_t hash_t
Definition hashmap.h:22
#define PF_READ
Definition mm.h:17
void mm_page_decref(paddr_t paddr)
Drops a reference to a page of RAM.
Definition mm.c:50
#define PAGE_ALIGN(ptr)
Definition mm.h:9
#define PF_EXEC
Definition mm.h:19
#define PF_WRITE
Definition mm.h:18
struct vm_page * mm_page_alloc(void)
Allocates a free page of physical memory.
Definition mm.c:78
int mm_page_fault(vaddr_t vaddr, int flags)
Handles a page fault in the current process's address space.
Definition fault.c:395
int mm_page_map(struct pmap *pmap, vaddr_t vaddr, paddr_t paddr, int flags)
Maps a page into an address space.
Definition mm.c:122
void mm_page_dealloc(paddr_t paddr)
Frees a page of physical memory, back to the zone it came from.
Definition mm.c:103
#define PF_PRESENT
Definition mm.h:16
void mm_page_incref(paddr_t paddr)
Adds a reference to a page of RAM.
Definition mm.c:39
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
#define M_ZERO
Definition kvmem.h:26
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
void pmap_page_copy(paddr_t src, paddr_t dst)
Copies a page of physical memory to another.
Definition pmap.c:423
void pmap_protect(struct pmap *pmap, vaddr_t sva, vaddr_t eva, uint32_t prot)
Changes the permissions of the mapped pages of a range of an address space.
Definition pmap.c:399
void pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
Unmaps the pages of a range of an address space, and flushes them from the TLBs that may have them.
Definition pmap.c:337
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
#define curproc
Definition sched.h:13
#define curthread
Definition sched.h:12
int signal_proc_send(struct proc *proc, int signal)
Sends a signal to a process.
Definition signal.c:310
#define NULL
Definition stddef.h:27
unsigned int uint32_t
Definition stddef.h:22
A hash table's node: an entry, with its key's hash.
Definition hashmap.h:43
hash_t hash
Definition hashmap.h:44
void * entry
Definition hashmap.h:45
What a page fault is about, passed to each of the steps below.
Definition fault.c:28
int flags
Definition fault.c:29
struct vm_entry * vm_entry
Definition fault.c:33
size_t off
Definition fault.c:35
struct vm_space * vm_space
Definition fault.c:32
vaddr_t addr
Definition fault.c:30
hash_t hash
Definition fault.c:36
An address space's page tables, on x86.
Definition pmap.h:9
An anon: a mapping's private pages, those the process wrote to.
Definition vm.h:119
struct hashmap * arefs
its arefs, by the offset of their page in the mapping
Definition vm.h:121
int flags
its flags (VM_COPY: to be copied before it's written to)
Definition vm.h:127
size_t ref
how many mappings share the anon
Definition vm.h:124
An anon's reference to a page (an aref).
Definition vm.h:107
uint32_t flags
its flags (VM_COPY)
Definition vm.h:115
size_t ref
how many anons have the aref
Definition vm.h:112
struct vm_page * vm_page
the page
Definition vm.h:109
A mapping (map entry): a range of an address space, its permissions, and where its pages come from.
Definition vm.h:66
struct vm_anon * vm_anon
its anon: the pages the process wrote to
Definition vm.h:81
uint32_t flags
its permissions and flags (VM_*)
Definition vm.h:78
size_t off
where the mapping starts in its object
Definition vm.h:87
vaddr_t base
where the mapping starts in its address space
Definition vm.h:72
struct vm_object * vm_object
its object: what its other pages come from (a file)
Definition vm.h:84
A vm object: what a mapping's pages come from (a file), and its pages that have been read in.
Definition vm.h:132
struct vm_pager * pager
its pager
Definition vm.h:143
struct hashmap * pages
its pages that have been read in, by their offset
Definition vm.h:134
A page of physical memory (pages[] has one for every page of RAM).
Definition vm.h:150
size_t off
its offset in its object or anon
Definition vm.h:155
size_t ref
how many references it has
Definition vm.h:157
struct vm_object * vm_object
the object it belongs to, or NULL
Definition vm.h:153
paddr_t paddr
its physical address
Definition vm.h:151
struct vm_page *(*) in(struct vm_object *vm_object, size_t off)
reads in the page at off, or returns NULL
Definition vm.h:96
An address space: its mappings, and its page tables.
Definition vm.h:25
struct pmap * pmap
its page tables (the architecture's)
Definition vm.h:27
#define VM_KW
kernel write
Definition vm.h:39
#define VM_COPY
to be copied before it's written to
Definition vm.h:47
#define VM_PERM
permissions mask
Definition vm.h:44
#define VM_UR
user read
Definition vm.h:41
struct vm_anon * vm_anon_new(void)
Makes a new, empty anon.
Definition vm_anon.c:36
#define VM_UX
user execute
Definition vm.h:43
struct vm_anon * vm_anon_copy(struct vm_anon *vm_anon)
Makes a copy of an anon that shares its arefs, and so its pages, when a mapping's shared anon is writ...
Definition vm_anon.c:174
void vm_anon_decref(struct vm_anon *vm_anon)
Drops a reference to an anon, and frees it with the last one.
Definition vm_anon.c:130
#define VM_UW
user write
Definition vm.h:42
struct vm_entry * vm_space_find(struct vm_space *vm_space, vaddr_t vaddr)
Finds the map entry that an address is in.
Definition vm_space.c:99