AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
i386.c
Go to the documentation of this file.
1
17
18#include <core/arch.h>
19#include <core/panic.h>
20#include <core/printk.h>
21#include <core/string.h>
22#include <core/system.h>
23#include <cpu/cpu.h>
24#include <sys/cpu.h>
25
26#include <mm/pmap.h>
27#include <mm/buddy.h>
28#include <mm/mm.h>
29#include <mm/vm.h>
30
31MALLOC_DEFINE(M_PMAP, "pmap", "physical memory map structure");
32
33#define PG_PRESENT 0x001
34#define PG_WRITE 0x002
35#define PG_USER 0x004
36#define PG_PWT 0x008 /* write-through */
37#define PG_PCD 0x010 /* cache disabled */
38
39#define PG_ADDR(e) ((e) & ~(uint32_t) PAGE_MASK)
40
41#define VDIR(va) (((va) >> 22) & 0x3FF)
42#define VTBL(va) (((va) >> 12) & 0x3FF)
43
44/* the directory's first entry for the kernel's half, at 0xC0000000 */
45#define KERNEL_PDE 768
46
47static struct pmap k_pmap;
48
49/* the running CPU's page tables, which its CR3 has */
50#define cur_pmap (curcpu()->arch.pmap)
51
53static inline uint32_t *table(paddr_t paddr)
54{
55 return PHYS2VIRT(paddr);
56}
57
60static paddr_t table_alloc(void)
61{
62 struct vm_page *vm_page = mm_page_alloc();
63
64 if (!vm_page)
65 return 0;
66
68 return vm_page->paddr;
69}
70
74{
76
77 if (!paddr)
78 panic("no memory for the kernel's page tables");
79
80 return paddr;
81}
82
85{
87}
88
92static uint32_t *pte_get(struct pmap *pmap, vaddr_t va, int make)
93{
94 uint32_t *pde = &table(pmap->map)[VDIR(va)];
95
96 if (!(*pde & PG_PRESENT)) {
97 paddr_t t;
98
99 if (!make || !(t = table_alloc()))
100 return NULL;
101
102 *pde = t | PG_PRESENT | PG_WRITE | (VDIR(va) < KERNEL_PDE ? PG_USER : 0);
103 }
104
105 return &table(PG_ADDR(*pde))[VTBL(va)];
106}
107
108/*
109 * Switches the running CPU to an address space's page tables.
110 */
111struct pmap *pmap_switch(struct pmap *pmap)
112{
113 if (!pmap)
114 panic("pmap?");
115
116 struct pmap *ret = cur_pmap;
117
118 if (pmap != cur_pmap) {
120 write_cr3(pmap->map);
121 cur_pmap = pmap;
122 }
123
124 return ret;
125}
126
127/*
128 * Takes over the boot page tables as the kernel's, at boot, and makes
129 * every page table of the kernel's half, so that those entries never
130 * change and every address space can share them.
131 */
132void pmap_init(void)
133{
134 /* The boot directory (boot/init.c) becomes the kernel's. It maps the
135 * direct map, and the same memory at 0, which is removed. */
137 k_pmap.ref = 1;
140 cur_pmap = &k_pmap;
141
142 uint32_t *dir = table(k_pmap.map);
143
144 for (int i = 0; i < KERNEL_PDE; ++i)
145 dir[i] = 0;
146
147 /* make every page table of the kernel's half now, before there is
148 * any other directory to share them */
149 for (int i = KERNEL_PDE; i < 1024; ++i)
150 if (!(dir[i] & PG_PRESENT))
151 dir[i] = table_alloc_boot() | PG_PRESENT | PG_WRITE;
152
153 tlb_flush();
154}
155
157static struct pmap *pmap_alloc(void)
158{
159 return kmalloc(sizeof(struct pmap), &M_PMAP, 0);
160}
161
163static void pmap_release(struct pmap *pmap)
164{
165 /* make sure that no CPU has it loaded anymore */
166 tlb_shootdown(pmap, 0, 0, 1);
167
168 if (pmap->map)
170
171 kfree(pmap);
172}
173
174/*
175 * Makes the page tables of a new address space: an empty user half, and
176 * the kernel's half, which every address space shares.
177 */
178struct pmap *pmap_create(void)
179{
180 struct pmap *pmap = pmap_alloc();
181
182 if (pmap == NULL)
183 return NULL;
184
185 memset(pmap, 0, sizeof(struct pmap));
186
187 if (!(pmap->map = table_alloc())) {
188 kfree(pmap);
189 return NULL;
190 }
191
192 pmap->ref = 1;
193
194 /* the kernel's half, the same in every directory */
195 uint32_t *dir = table(pmap->map), *kdir = table(k_pmap.map);
196
197 for (int i = KERNEL_PDE; i < 1024; ++i)
198 dir[i] = kdir[i];
199
200 return pmap;
201}
202
203/*
204 * Adds a reference to an address space's page tables.
205 */
206void pmap_incref(struct pmap *pmap)
207{
208 pmap->ref++;
209}
210
211/*
212 * Drops a reference to an address space's page tables, freeing them with
213 * the last one.
214 */
215void pmap_decref(struct pmap *pmap)
216{
217 if (--pmap->ref == 0)
219}
220
223{
224 uint32_t pte = PG_PRESENT;
225
226 if (flags & (VM_KW | VM_UW))
227 pte |= PG_WRITE;
228
229 if (flags & VM_URWX)
230 pte |= PG_USER;
231
232 /* device memory: uncached */
233 if (flags & VM_NOCACHE)
234 pte |= PG_PCD | PG_PWT;
235
236 return pte;
237}
238
239/*
240 * Maps a page in an address space, replacing what was mapped there.
241 */
242int pmap_add(struct pmap *pmap, vaddr_t va, paddr_t pa, uint32_t flags)
243{
244 if ((va & PAGE_MASK) || (pa & PAGE_MASK))
245 return -EINVAL;
246
247 uint32_t *pte = pte_get(pmap, va, 1);
248
249 /* there was no memory for its page table */
250 if (!pte)
251 return -ENOMEM;
252
253 uint32_t old = *pte;
254 *pte = pa | pte_flags(flags);
255
256 /* another page was mapped there: flush it from the TLBs */
257 if (old & PG_PRESENT)
258 tlb_shootdown(pmap, va, va + PAGE_SIZE, 0);
259
260 return 0;
261}
262
263/*
264 * Unmaps the pages of a range of an address space, and flushes them from
265 * the TLBs that may have them.
266 */
267void pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
268{
269 if ((sva & PAGE_MASK) || (eva & PAGE_MASK))
270 return;
271
272 vaddr_t first = eva, last = sva;
273
274 for (vaddr_t va = sva; va < eva; va += PAGE_SIZE) {
275 uint32_t *pte = pte_get(pmap, va, 0);
276
277 if (pte && (*pte & PG_PRESENT)) {
278 *pte = 0;
279 first = MIN(first, va);
280 last = va + PAGE_SIZE;
281 }
282 }
283
284 if (first < last)
285 tlb_shootdown(pmap, first, last, 0);
286}
287
288/*
289 * Frees the page tables of an address space's user half. The pages they
290 * map lose the reference that the page fault handler gave them
291 * (`mm_page_incref()`).
292 */
294{
295 uint32_t *dir = table(pmap->map);
296
297 for (int i = 0; i < KERNEL_PDE; ++i) {
298 if (!(dir[i] & PG_PRESENT))
299 continue;
300
301 uint32_t *t = table(PG_ADDR(dir[i]));
302
303 for (int j = 0; j < 1024; ++j)
304 if (t[j] & PG_PRESENT)
305 mm_page_decref(PG_ADDR(t[j]));
306
307 table_free(PG_ADDR(dir[i]));
308 dir[i] = 0;
309 }
310
312}
313
314/*
315 * Changes the permissions of the mapped pages of a range of an address
316 * space.
317 */
318void pmap_protect(struct pmap *pmap, vaddr_t sva, vaddr_t eva, uint32_t prot)
319{
320 if (sva & PAGE_MASK)
321 return;
322
323 vaddr_t first = eva, last = sva;
324
325 for (vaddr_t va = sva; va < eva; va += PAGE_SIZE) {
326 uint32_t *pte = pte_get(pmap, va, 0);
327
328 if (pte && (*pte & PG_PRESENT)) {
329 *pte = PG_ADDR(*pte) | pte_flags(prot);
330 first = MIN(first, va);
331 last = va + PAGE_SIZE;
332 }
333 }
334
335 if (first < last)
336 tlb_shootdown(pmap, first, last, 0);
337}
338
340void pmap_copy(struct pmap *dst_map, struct pmap *src_map, vaddr_t dst_addr, size_t len,
341 vaddr_t src_addr)
342{
343 return;
344}
345
352void pmap_update(struct pmap *pmap)
353{
354 return;
355}
356
357/*
358 * Copies a page of physical memory to another, through the direct map.
359 */
361{
362 memcpy(PHYS2VIRT(dst), PHYS2VIRT(src), PAGE_SIZE);
363}
364
371void pmap_page_protect(struct vm_page *pg, uint32_t flags)
372{
373 return;
374}
375
383{
384 return -1;
385}
386
394{
395 return -1;
396}
397
405{
406 return -1;
407}
408
416{
417 return -1;
418}
419
420/*
421 * Finds the page mapped at a virtual address of an address space.
422 */
424{
425 uint32_t *pte = pte_get(pmap, vaddr, 0);
426
427 return pte && (*pte & PG_PRESENT) ? PG_ADDR(*pte) : 0;
428}
429
437{
438 if (vaddr & PAGE_MASK)
439 return -EINVAL;
440
441 pmap_remove(cur_pmap, vaddr, vaddr + PAGE_SIZE);
442 return 0;
443}
444
445/*
446 * Handles a page fault: turns the CPU's error code into the kernel's
447 * `PF_*` flags, and calls `mm_page_fault()`.
448 */
449int arch_mm_page_fault(vaddr_t vaddr, int err)
450{
451 int flags = 0;
452
453 flags |= err & 0x01? PF_PRESENT : 0;
454 flags |= err & 0x02? PF_WRITE : PF_READ;
455 flags |= err & 0x04? PF_USER : 0;
456 flags |= err & 0x10? PF_EXEC : 0;
457
458 return mm_page_fault(vaddr, flags);
459}
460
461/*
462 * Reads from a page of physical memory, through the direct map.
463 */
464int pmap_page_read(paddr_t paddr, off_t off, size_t size, void *buf)
465{
466 if (off < 0 || off >= PAGE_SIZE)
467 return -EINVAL;
468
469 memcpy(buf, (char *) PHYS2VIRT(paddr) + off, MIN(size, PAGE_SIZE - (size_t) off));
470 return 0;
471}
472
473/*
474 * Writes to a page of physical memory, through the direct map.
475 */
476int pmap_page_write(paddr_t paddr, off_t off, size_t size, void *buf)
477{
478 if (off < 0 || off >= PAGE_SIZE)
479 return -EINVAL;
480
481 memcpy((char *) PHYS2VIRT(paddr) + off, buf, MIN(size, PAGE_SIZE - (size_t) off));
482 return 0;
483}
484
485/* where the window for the memory past the direct map has room next */
486static vaddr_t phys_window_next = ARCH_PHYS_WINDOW_BASE;
487
491static void *phys_window_map(uintptr_t paddr, size_t size, uint32_t flags)
492{
493 paddr_t start = PAGE_ALIGN(paddr);
494 size_t len = PAGE_ROUND(paddr + size) - start;
495
496 if (phys_window_next + len > ARCH_PHYS_WINDOW_BASE + ARCH_PHYS_WINDOW_SIZE)
497 return NULL; /* no room left in the window */
498
500
501 /* if there's no memory for a page table, unmap what was mapped */
502 for (size_t off = 0; off < len; off += PAGE_SIZE) {
503 if (pmap_add(&k_pmap, vaddr + off, start + off, flags)) {
504 pmap_remove(&k_pmap, vaddr, vaddr + off);
505 return NULL;
506 }
507 }
508
509 phys_window_next += len;
510
511 return (void *) (vaddr + (paddr - start));
512}
513
514/*
515 * Returns a kernel address for a range of physical memory: its address
516 * in the direct map if it's there, else a mapping made in the window.
517 */
518void *arch_phys_map(uintptr_t paddr, size_t size)
519{
520 if (paddr + size <= ARCH_DMAP_SIZE)
521 return PHYS2VIRT(paddr);
522
523 return phys_window_map(paddr, size, VM_KRW);
524}
525
526/*
527 * Maps a device's registers in the kernel's address space, uncached.
528 */
529void *arch_mmio_map(uintptr_t paddr, size_t size)
530{
531 return phys_window_map(paddr, size, VM_KRW | VM_NOCACHE);
532}
#define USER_STACK
Definition arch.h:60
#define PAGE_SIZE
Definition system.h:7
#define PAGE_MASK
Definition system.h:8
uint64_t paddr_t
Definition mm.h:39
#define PHYS2VIRT(paddr)
Definition mm.h:26
static void tlb_flush(void)
Flushes every translation from this CPU's TLB.
Definition mm.h:43
uint64_t vaddr_t
Definition mm.h:40
void pmap_loaded(struct pmap *from, struct pmap *to)
Definition pmap.c:157
void tlb_shootdown(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int leave)
Definition pmap.c:130
#define panic(s)
Definition panic.h:15
static uintptr_t read_cr3(void)
Definition cpu.h:201
int arch_mm_page_fault(vaddr_t vaddr, int err)
Handles a page fault: turns the CPU's error code into the kernel's PF_* flags, and calls mm_page_faul...
Definition i386.c:449
#define PG_PRESENT
Definition smp.c:63
#define PG_WRITE
Definition smp.c:64
#define PG_USER
Definition i386.c:35
#define PG_PCD
Definition i386.c:37
#define VDIR(va)
Definition i386.c:41
int pmap_is_referenced(struct vm_page *pg)
Not implemented on x86, and nothing uses it.
Definition i386.c:415
#define PG_PWT
Definition i386.c:36
static vaddr_t phys_window_next
Definition i386.c:486
void pmap_page_protect(struct vm_page *pg, uint32_t flags)
Not implemented on x86 (it does nothing), and nothing uses it.
Definition i386.c:371
static struct pmap * pmap_alloc(void)
Allocates a struct pmap.
Definition i386.c:157
#define KERNEL_PDE
Definition i386.c:45
void pmap_update(struct pmap *pmap)
Makes the changes to an address space's page tables take effect.
Definition i386.c:352
int pmap_clear_modify(struct vm_page *pg)
Not implemented on x86, and nothing uses it.
Definition i386.c:382
#define VTBL(va)
Definition i386.c:42
static paddr_t table_alloc_boot(void)
Allocates a page table that the kernel can't boot without: it panics if there is no memory.
Definition i386.c:73
void pmap_copy(struct pmap *dst_map, struct pmap *src_map, vaddr_t dst_addr, size_t len, vaddr_t src_addr)
Does nothing: the generic pmap_copy() isn't used on x86.
Definition i386.c:340
int pmap_is_modified(struct vm_page *pg)
Not implemented on x86, and nothing uses it.
Definition i386.c:404
int arch_page_unmap(vaddr_t vaddr)
Unmaps a page of the running CPU's address space.
Definition i386.c:436
#define PG_ADDR(e)
Definition i386.c:39
int pmap_clear_reference(struct vm_page *pg)
Not implemented on x86, and nothing uses it.
Definition i386.c:393
static void * phys_window_map(uintptr_t paddr, size_t size, uint32_t flags)
Maps a range of physical memory in the window, with flags.
Definition i386.c:491
#define MIN(a, b)
Definition system.h:11
#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 PAGE_ROUND(ptr)
Definition mm.h:10
#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
#define PF_USER
Definition mm.h:20
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
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
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 * memcpy(void *dst, const void *src, size_t n)
Copies n bytes; dst and src must not overlap.
Definition libc.c:11
int pmap_add(struct pmap *pmap, vaddr_t va, paddr_t pa, uint32_t flags)
Maps a page in an address space, replacing what was mapped there.
Definition pmap.c:307
#define cur_pmap
Definition pmap.c:69
static void table_free(paddr_t paddr)
Frees a page table.
Definition pmap.c:91
paddr_t arch_page_get_mapping(struct pmap *pmap, vaddr_t vaddr)
Finds the page mapped at a virtual address of an address space.
Definition pmap.c:431
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
int pmap_page_read(paddr_t paddr, off_t off, size_t size, void *buf)
Reads from a page of physical memory, through the kernel's mapping.
Definition pmap.c:440
void pmap_init(void)
Takes over the boot page tables as the kernel's, at boot, and makes the page tables of the kernel's h...
Definition pmap.c:195
static uint64_t * table(paddr_t paddr)
Returns a page table, through the direct map.
Definition pmap.c:72
static void pmap_release(struct pmap *pmap)
Frees a pmap whose last reference went.
Definition pmap.c:228
void * arch_phys_map(uintptr_t paddr, size_t size)
Returns a kernel address for a range of physical memory: its address in the direct map if it's there,...
Definition pmap.c:497
void pmap_decref(struct pmap *pmap)
Drops a reference to an address space's page tables, freeing them with the last one.
Definition pmap.c:271
static struct pmap k_pmap
Definition pmap.c:59
struct pmap * pmap_switch(struct pmap *pmap)
Switches this CPU to an address space's page tables.
Definition pmap.c:164
static uint64_t pte_flags(uint32_t flags)
Returns the descriptor bits for a page with the VM_* flags flags.
Definition pmap.c:279
int pmap_page_write(paddr_t paddr, off_t off, size_t size, void *buf)
Writes to a page of physical memory, through the kernel's mapping.
Definition pmap.c:452
void pmap_incref(struct pmap *pmap)
Adds a reference to an address space's page tables.
Definition pmap.c:263
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
struct pmap * pmap_create(void)
Makes the page tables of a new address space: an empty user half, and the kernel's half,...
Definition pmap.c:242
static uint64_t * pte_get(struct pmap *pmap, vaddr_t va, int make)
Returns the page table entry that maps va, in pmap or in the kernel's half.
Definition pmap.c:100
void pmap_remove_all(struct pmap *pmap)
Unmaps the whole user half of an address space, and frees its page tables.
Definition pmap.c:382
static paddr_t table_alloc(void)
Allocates an empty page table.
Definition pmap.c:79
void * arch_mmio_map(uintptr_t paddr, size_t size)
Maps a device's registers into the kernel's address space, uncached (memory-mapped I/O).
Definition pmap.c:508
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned int uint32_t
Definition stddef.h:22
An address space's page tables, on x86.
Definition pmap.h:9
paddr_t map
Definition pmap.h:10
size_t ref
Definition pmap.h:11
A page of physical memory (pages[] has one for every page of RAM).
Definition vm.h:150
paddr_t paddr
its physical address
Definition vm.h:151
struct pmap * pmap
its page tables (the architecture's)
Definition vm.h:27
long int off_t
Definition types.h:10
#define VM_URWX
user read/write/execute
Definition vm.h:56
#define VM_KW
kernel write
Definition vm.h:39
#define VM_NOCACHE
not cached (device memory)
Definition vm.h:45
struct vm_space kvm_space
Definition vmm.c:17
#define VM_KRW
kernel read/write
Definition vm.h:49
#define VM_UW
user write
Definition vm.h:42