AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
pmap.c
Go to the documentation of this file.
1
18
19#include <core/arch.h>
20#include <core/panic.h>
21#include <core/printk.h>
22#include <core/string.h>
23#include <core/system.h>
24#include <boot/boot.h>
25#include <cpu/cpu.h>
26#include <sys/cpu.h>
27
28#include <mm/pmap.h>
29#include <mm/buddy.h>
30#include <mm/mm.h>
31#include <mm/vm.h>
32
33MALLOC_DEFINE(M_PMAP, "pmap", "physical memory map structure");
34
35extern struct boot *__kboot;
36
37/* The descriptors' bits. A valid descriptor at levels 0 to 2 is a table,
38 * or at levels 1 and 2 a block; at level 3 it is a page. */
39#define D_VALID (1ULL << 0)
40#define D_TABLE (1ULL << 1) /* a table, not a block (levels 0-2); a page (3) */
41#define D_ATTR(i) ((uint64_t) (i) << 2) /* MAIR_EL1's index: 0 normal, 1 device */
42#define D_AP_EL0 (1ULL << 6) /* user mode may access it */
43#define D_AP_RO (1ULL << 7) /* read only */
44#define D_SH_INNER (3ULL << 8) /* inner shareable */
45#define D_AF (1ULL << 10) /* accessed, set here so that no fault sets it */
46#define D_NG (1ULL << 11) /* not global: a process's mapping */
47#define D_PXN (1ULL << 53) /* the kernel doesn't execute it */
48#define D_UXN (1ULL << 54) /* user mode doesn't execute it */
49
50#define D_ADDR(e) ((e) & 0x0000FFFFFFFFF000ULL)
51
52/* The index of `va` in its table at `level`, counted from the bottom: 3
53 * is the top table (level 0), and 0 the table of pages (level 3). Each
54 * level takes 9 bits, as on x86-64. */
55#define IDX(va, level) (((va) >> (12 + 9 * (level))) & 0x1FF)
56
57#define KERNEL_HALF(va) ((va) >= ARCH_DMAP_BASE)
58
59static struct pmap k_pmap;
60
61/* The kernel half's top table, TTBR1_EL1's (head.S's `boot_l0_hi`). */
63
64/* The direct map's end: the end of RAM, and at least what the boot tables
65 * map (`ARCH_BOOT_MAPPED`). */
67
68/* The running CPU's pmap, the one in TTBR0_EL1. */
69#define cur_pmap (curcpu()->arch.pmap)
70
72static inline uint64_t *table(paddr_t paddr)
73{
74 return PHYS2VIRT(paddr);
75}
76
79static paddr_t table_alloc(void)
80{
81 struct vm_page *vm_page = mm_page_alloc();
82
83 if (!vm_page)
84 return 0;
85
87 return vm_page->paddr;
88}
89
92{
94}
95
100static uint64_t *pte_get(struct pmap *pmap, vaddr_t va, int make)
101{
102 uint64_t *t = table(KERNEL_HALF(va) ? k_root : pmap->map);
103
104 for (int level = 3; level > 0; --level) {
105 uint64_t *e = &t[IDX(va, level)];
106
107 if (!(*e & D_VALID)) {
108 paddr_t t_new;
109
110 if (!make || !(t_new = table_alloc()))
111 return NULL;
112
113 *e = t_new | D_VALID | D_TABLE;
114 }
115
116 /* the direct map's blocks aren't the pmap's to change */
117 if (!(*e & D_TABLE))
118 return NULL;
119
120 t = table(D_ADDR(*e));
121 }
122
123 return &t[IDX(va, 0)];
124}
125
126/*
127 * Removes a range of a pmap from this CPU's TLB, if the pmap is loaded. It
128 * doesn't reach other CPUs.
129 */
130void tlb_shootdown(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int leave)
131{
132 if (leave) {
133 if (cur_pmap == pmap)
135 return;
136 }
137
138 if (pmap != &k_pmap && pmap != cur_pmap)
139 return;
140
141 asm volatile("dsb ishst" ::: "memory");
142
143 if (eva - sva > 32 * PAGE_SIZE) {
144 asm volatile("tlbi vmalle1" ::: "memory");
145 } else {
146 for (vaddr_t va = sva; va < eva; va += PAGE_SIZE)
147 asm volatile("tlbi vaae1, %0" :: "r"((va >> 12) & 0xFFFFFFFFFFFULL) : "memory");
148 }
149
150 asm volatile("dsb ish; isb" ::: "memory");
151}
152
153/*
154 * Tells the pmap code that a CPU switched pmaps. There's nothing to do on
155 * aarch64.
156 */
157void pmap_loaded(struct pmap *from, struct pmap *to)
158{
159}
160
161/*
162 * Loads a pmap's user half on this CPU, and flushes the TLB.
163 */
164struct pmap *pmap_switch(struct pmap *pmap)
165{
166 if (!pmap)
167 panic("pmap?");
168
169 struct pmap *ret = cur_pmap;
170
171 if (pmap != cur_pmap) {
172 write_sysreg(ttbr0_el1, pmap->map);
173 isb();
174 tlb_flush();
175 cur_pmap = pmap;
176 }
177
178 return ret;
179}
180
182static paddr_t ram_end(void)
183{
184 paddr_t end = 0;
185
186 for (int i = 0; i < __kboot->mmap_count; ++i)
187 if (__kboot->mmap[i].type == MMAP_USABLE && __kboot->mmap[i].end > end)
188 end = __kboot->mmap[i].end;
189
190 return end;
191}
192
193/* Takes over the boot page tables, maps the rest of RAM in the direct
194 * map, and loads the kernel's pmap. */
195void pmap_init(void)
196{
197 extern char boot_l0_hi[], boot_l1[];
198
199 /* the boot tables' kernel half becomes the kernel's */
200 k_root = LMA((uintptr_t) boot_l0_hi);
201
202 /* map RAM past what the boot tables map in the direct map too, as
203 * 1 GiB blocks of normal memory in the level 1 table they share */
204 uint64_t *l1 = (uint64_t *) boot_l1;
205 paddr_t end = (ram_end() + (1ULL << 30) - 1) & ~((1ULL << 30) - 1);
206
207 for (paddr_t pa = ARCH_BOOT_MAPPED; pa < end && pa < (512ULL << 30); pa += 1ULL << 30)
208 l1[pa >> 30] = pa | D_VALID | D_ATTR(0) | D_SH_INNER | D_AF;
209
211
212 /* the kernel's own user half is empty (the boot tables' identity map
213 * goes with this) */
214 if (!(k_pmap.map = table_alloc()))
215 panic("no memory for the kernel's page tables");
216
217 k_pmap.ref = 1;
219
220 cur_pmap = NULL;
222
223 printk("aarch64: %ld MiB in the direct map\n", (uint64_t) (dmap_end >> 20));
224}
225
228static void pmap_release(struct pmap *pmap)
229{
230 /* make sure this CPU doesn't have it loaded anymore */
231 tlb_shootdown(pmap, 0, 0, 1);
232
233 if (pmap->map)
235
236 kfree(pmap);
237}
238
239/*
240 * Makes a pmap with an empty user half.
241 */
242struct pmap *pmap_create(void)
243{
244 struct pmap *pmap = kmalloc(sizeof(struct pmap), &M_PMAP, M_ZERO);
245
246 if (pmap == NULL)
247 return NULL;
248
249 /* only the user half: the kernel's is TTBR1_EL1's */
250 if (!(pmap->map = table_alloc())) {
251 kfree(pmap);
252 return NULL;
253 }
254
255 pmap->ref = 1;
256
257 return pmap;
258}
259
260/*
261 * Takes a reference to a pmap.
262 */
263void pmap_incref(struct pmap *pmap)
264{
265 pmap->ref++;
266}
267
268/*
269 * Drops a reference to a pmap, and frees it with the last one.
270 */
271void pmap_decref(struct pmap *pmap)
272{
273 if (--pmap->ref == 0)
275}
276
280{
281 uint64_t pte = D_VALID | D_TABLE | D_AF;
282 int user = flags & VM_URWX, write = flags & (VM_KW | VM_UW);
283
284 /* EL1 always has access; EL0's access is AP[1], and read only is
285 * AP[2] */
286 if (user)
287 pte |= D_AP_EL0 | D_NG;
288 if (!write)
289 pte |= D_AP_RO;
290
291 /* the kernel never runs user pages, and user mode runs only what says
292 * so */
293 if (user || !(flags & VM_KX))
294 pte |= D_PXN;
295 if (!(flags & VM_UX))
296 pte |= D_UXN;
297
298 /* device memory, which never runs, or normal memory */
299 pte |= flags & VM_NOCACHE ? D_ATTR(1) | D_PXN | D_UXN : D_ATTR(0) | D_SH_INNER;
300
301 return pte;
302}
303
304/*
305 * Maps a page.
306 */
307int pmap_add(struct pmap *pmap, vaddr_t va, paddr_t pa, uint32_t flags)
308{
309 if ((va & PAGE_MASK) || (pa & PAGE_MASK))
310 return -EINVAL;
311
312 /* the direct map's blocks aren't changed page by page */
313 if (va >= ARCH_DMAP_BASE && va < ARCH_DMAP_BASE + dmap_end)
314 return -EINVAL;
315
316 uint64_t *pte = pte_get(pmap, va, 1);
317
318 /* there was no memory for a table on the way */
319 if (!pte)
320 return -ENOMEM;
321
322 uint64_t old = *pte;
323 *pte = pa | pte_flags(flags);
324
325 /* if another page was there, flush it from the TLB */
326 if (old & D_VALID)
327 tlb_shootdown(KERNEL_HALF(va) ? &k_pmap : pmap, va, va + PAGE_SIZE, 0);
328 else
329 asm volatile("dsb ishst; isb" ::: "memory");
330
331 return 0;
332}
333
334/*
335 * Unmaps a range. The pages lose no references.
336 */
337void pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
338{
339 if ((sva & PAGE_MASK) || (eva & PAGE_MASK))
340 return;
341
342 vaddr_t first = eva, last = sva;
343
344 for (vaddr_t va = sva; va < eva; va += PAGE_SIZE) {
345 uint64_t *pte = pte_get(pmap, va, 0);
346
347 if (pte && (*pte & D_VALID)) {
348 *pte = 0;
349 first = MIN(first, va);
350 last = va + PAGE_SIZE;
351 }
352 }
353
354 if (first < last)
355 tlb_shootdown(KERNEL_HALF(sva) ? &k_pmap : pmap, first, last, 0);
356}
357
361static void tables_free(paddr_t paddr, int level)
362{
363 uint64_t *t = table(paddr);
364
365 for (int i = 0; i < 512; ++i) {
366 if (!(t[i] & D_VALID))
367 continue;
368
369 if (level > 1)
370 tables_free(D_ADDR(t[i]), level - 1);
371 else
372 mm_page_decref(D_ADDR(t[i]));
373 }
374
375 table_free(paddr);
376}
377
378/*
379 * Unmaps a pmap's whole user half, and frees its tables, all but the top
380 * one. The pages lose a reference.
381 */
383{
384 uint64_t *l0 = table(pmap->map);
385
386 for (int i = 0; i < 512; ++i) {
387 if (l0[i] & D_VALID) {
388 tables_free(D_ADDR(l0[i]), 3);
389 l0[i] = 0;
390 }
391 }
392
394}
395
396/*
397 * Changes a range's permissions. Pages that aren't mapped stay unmapped.
398 */
399void pmap_protect(struct pmap *pmap, vaddr_t sva, vaddr_t eva, uint32_t prot)
400{
401 if (sva & PAGE_MASK)
402 return;
403
404 vaddr_t first = eva, last = sva;
405
406 for (vaddr_t va = sva; va < eva; va += PAGE_SIZE) {
407 uint64_t *pte = pte_get(pmap, va, 0);
408
409 if (pte && (*pte & D_VALID)) {
410 *pte = D_ADDR(*pte) | pte_flags(prot);
411 first = MIN(first, va);
412 last = va + PAGE_SIZE;
413 }
414 }
415
416 if (first < last)
417 tlb_shootdown(KERNEL_HALF(sva) ? &k_pmap : pmap, first, last, 0);
418}
419
420/*
421 * Copies a physical page.
422 */
424{
425 memcpy(PHYS2VIRT(dst), PHYS2VIRT(src), PAGE_SIZE);
426}
427
428/*
429 * Finds the physical page that a virtual address is mapped to.
430 */
432{
433 uint64_t *pte = pte_get(pmap, vaddr, 0);
434 return pte && (*pte & D_VALID) ? D_ADDR(*pte) : 0;
435}
436
437/*
438 * Reads from a physical page, up to its end.
439 */
440int pmap_page_read(paddr_t paddr, off_t off, size_t size, void *buf)
441{
442 if (off < 0 || off >= PAGE_SIZE)
443 return -EINVAL;
444
445 memcpy(buf, (char *) PHYS2VIRT(paddr) + off, MIN(size, PAGE_SIZE - (size_t) off));
446 return 0;
447}
448
449/*
450 * Writes to a physical page, up to its end.
451 */
452int pmap_page_write(paddr_t paddr, off_t off, size_t size, void *buf)
453{
454 if (off < 0 || off >= PAGE_SIZE)
455 return -EINVAL;
456
457 memcpy((char *) PHYS2VIRT(paddr) + off, buf, MIN(size, PAGE_SIZE - (size_t) off));
458 return 0;
459}
460
461/* The start of the free part of the window for devices' registers. The
462 * window only grows: nothing mapped there is unmapped. */
464
467static void *window_map(uintptr_t paddr, size_t size, uint32_t flags)
468{
469 paddr_t start = PAGE_ALIGN(paddr);
470 size_t len = PAGE_ROUND(paddr + size) - start;
471
473 return NULL; /* there's no room left in the window */
474
475 vaddr_t vaddr = mmio_next;
476
477 /* if there's no memory for a page table, unmap what was mapped */
478 for (size_t off = 0; off < len; off += PAGE_SIZE) {
479 if (pmap_add(&k_pmap, vaddr + off, start + off, flags)) {
480 pmap_remove(&k_pmap, vaddr, vaddr + off);
481 return NULL;
482 }
483 }
484
485 mmio_next += len;
486
487 return (void *) (vaddr + (paddr - start));
488}
489
490/*
491 * Maps physical memory into the kernel's address space: RAM is in the
492 * direct map already, and anything else is mapped in the window. RAM is
493 * taken to start at 1 GiB, as on QEMU's virt machine: the direct map's
494 * first GiB is its devices', mapped as device memory (boot/head.S), not
495 * to be used for memory. Another platform would say where its RAM is.
496 */
497void *arch_phys_map(uintptr_t paddr, size_t size)
498{
499 if (paddr >= (1ULL << 30) && paddr + size <= dmap_end)
500 return PHYS2VIRT(paddr);
501
502 return window_map(paddr, size, VM_KRW);
503}
504
505/*
506 * Maps a device's registers, uncached (as device memory).
507 */
508void *arch_mmio_map(uintptr_t paddr, size_t size)
509{
510 return window_map(paddr, size, VM_KRW | VM_NOCACHE);
511}
512
513/* Sets up the page tables, at boot. */
515{
516 pmap_init();
517}
struct boot * __kboot
Definition init.c:19
#define USER_STACK
Definition arch.h:60
#define PAGE_SIZE
Definition system.h:7
#define PAGE_MASK
Definition system.h:8
#define isb()
Definition cpu.h:45
#define write_sysreg(r, v)
Definition cpu.h:39
uint64_t paddr_t
Definition mm.h:39
#define ARCH_BOOT_MAPPED
Definition mm.h:23
#define PHYS2VIRT(paddr)
Definition mm.h:26
#define LMA(obj)
Definition mm.h:37
#define ARCH_DMAP_BASE
Definition mm.h:14
#define ARCH_MMIO_BASE
Definition mm.h:19
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
#define ARCH_MMIO_SIZE
Definition mm.h:20
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
@ MMAP_USABLE
Definition boot.h:23
#define MIN(a, b)
Definition system.h:11
#define MAX(a, b)
Definition system.h:10
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
struct vm_page * mm_page_alloc(void)
Allocates a free page of physical memory.
Definition mm.c:78
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 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
#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 * memcpy(void *dst, const void *src, size_t n)
Copies n bytes; dst and src must not overlap.
Definition libc.c:11
#define D_AF
Definition pmap.c:45
#define D_ATTR(i)
Definition pmap.c:41
#define D_VALID
Definition pmap.c:39
static paddr_t k_root
Definition pmap.c:62
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 D_ADDR(e)
Definition pmap.c:50
#define cur_pmap
Definition pmap.c:69
static void table_free(paddr_t paddr)
Frees a page table.
Definition pmap.c:91
#define D_AP_RO
Definition pmap.c:43
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
#define D_NG
Definition pmap.c:46
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
#define D_SH_INNER
Definition pmap.c:44
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
static paddr_t dmap_end
Definition pmap.c:66
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 paddr_t ram_end(void)
Returns the end of the RAM that the boot loader found.
Definition pmap.c:182
#define D_UXN
Definition pmap.c:48
void arch_mm_setup(void)
Finishes setting up memory at boot, once mm_setup() has set up the page structures and the buddy allo...
Definition pmap.c:514
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
static void tables_free(paddr_t paddr, int level)
Frees a table of the user half at level and the tables under it.
Definition pmap.c:361
static void * window_map(uintptr_t paddr, size_t size, uint32_t flags)
Maps physical memory in the window, with flags.
Definition pmap.c:467
#define KERNEL_HALF(va)
Definition pmap.c:57
#define D_PXN
Definition pmap.c:47
static vaddr_t mmio_next
Definition pmap.c:463
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
#define D_TABLE
Definition pmap.c:40
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
#define D_AP_EL0
Definition pmap.c:42
#define IDX(va, level)
Definition pmap.c:55
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
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned int uint32_t
Definition stddef.h:22
unsigned long long int uint64_t
Definition stddef.h:23
Definition boot.h:35
int mmap_count
Definition boot.h:40
mmap_t * mmap
Definition boot.h:42
uintptr_t end
Definition boot.h:31
enum mmap_type type
Definition boot.h:29
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_UX
user execute
Definition vm.h:43
#define VM_KRW
kernel read/write
Definition vm.h:49
#define VM_KX
kernel execute
Definition vm.h:40
#define VM_UW
user write
Definition vm.h:42