1/* @title: Memory Descriptor */
2#pragma once
3#include <compiler/wrapper.h>
4#include <mem/vma_range.h>
5#include <structures/rbit.h>
6#include <sync/rwlock.h>
7#include <types/refcount.h>
8
9enum mm_map_flags {
10 MM_MAP_FIXED = 1 << 0,
11 MM_MAP_ANON = 1 << 1,
12 /* TODO: MM_MAP_SHARED / file-backed... */
13};
14
15enum mm_fault_flags {
16 MM_FAULT_WRITE = 1 << 0, /* error_code & 0x02 */
17 MM_FAULT_USER = 1 << 1, /* error_code & 0x04 */
18 MM_FAULT_INSN = 1 << 2, /* error_code & 0x10 */
19};
20
21enum mm_fault_result {
22 MM_FAULT_OK,
23 MM_FAULT_RETRY,
24 MM_FAULT_SIGSEGV,
25 MM_FAULT_OOM
26};
27
28/* TODO: hold 6 per-cpu, switch between them with
29 * generation counters. we can look towards linux
30 * once we go and implement this thing for ASID */
31struct mm_context {
32 size_t ctx_id;
33 atomic_size_t tlb_gen;
34 struct cpu_mask cpus;
35};
36
37struct mm {
38 struct vas *vas;
39 struct rbit
40 vma_range_tree; /* VMAs keyed by [start, end-1], augmented `max` */
41 struct rwlock lock; /* top of the lock order */
42 paddr_t pml4; /* physical root of the page tables */
43 vaddr_t mmap_cursor; /* next-fit hint: where the last gap search ended */
44 refcount_t users; /* threads sharing this address space */
45 refcount_t refcount; /* keep the struct alive past last user */
46};
47
48struct mm *mm_alloc(void) cw_alloc();
49void mm_free(struct mm *mm);
50struct mm *mm_fork(struct mm *src); /* clone VMAs + anon_vma_fork each */
51
52void mm_exit(struct mm *mm); /* exit_mmap: unmap + tear down every VMA,
53 * but leave the struct/pml4 standing */
54
55void mm_vma_range_insert(struct mm *mm,
56 struct vma_range *vma_range); /* assert no overlap */
57void mm_vma_range_remove(struct mm *mm, struct vma_range *vma_range);
58struct vma_range *
59mm_vma_range_find(struct mm *mm,
60 vaddr_t addr); /* first vma_range, end > addr */
61struct vma_range *mm_vma_range_find_intersection(struct mm *mm, vaddr_t s,
62 vaddr_t e);
63
64/* lowest gap >= len in [low, high) */
65vaddr_t mm_vma_range_find_gap(struct mm *mm, size_t len, size_t align,
66 vaddr_t low, vaddr_t high);
67
68/* find a gap (or honor a hint), build a vma_range, insert */
69vaddr_t mm_map(struct mm *mm, vaddr_t hint, size_t len,
70 enum vma_range_protection prot, enum mm_map_flags flags);
71
72/* tear down [start, start+len): split VMAs at the boundaries, unmap their
73 * PTEs, drop rmap, free/trim the VMAs */
74enum err mm_unmap(struct mm *mm, vaddr_t start, size_t len);
75
76/* change protection over a range: split at boundaries, vma_range_set_prot each,
77 * re-protect the live PTEs */
78enum err mm_protect(struct mm *mm, vaddr_t start, size_t len,
79 enum vma_range_protection prot);
80
81enum err mm_pgtable_init(struct mm *mm); /* vmm_make_user_pml4() -> mm->pml4 */
82
83void mm_pgtable_free(struct mm *mm); /* free user PT pages; never the
84 * shared kernel higher-half */
85
86void mm_activate(struct mm *mm); /* load CR3 = mm->pml4 on switch */
87
88enum err mm_map_page(struct mm *mm, vaddr_t va, paddr_t pa, uint64_t pflags);
89void mm_unmap_page(struct mm *mm, vaddr_t va);
90paddr_t mm_query(struct mm *mm, vaddr_t va, uint64_t *pflags_out);
91
92enum mm_fault_result mm_fault(struct mm *mm, vaddr_t addr,
93 enum mm_fault_flags flags);
94
95enum mm_fault_result mm_do_anon_fault(struct vma_range *vma_range, vaddr_t addr,
96 enum mm_fault_flags);
97enum mm_fault_result mm_do_cow_fault(struct vma_range *vma_range, vaddr_t addr,
98 enum mm_fault_flags);
99
100static inline bool mm_getref(struct mm *mm) {
101 return refcount_inc_not_zero(rc: &mm->refcount);
102}
103
104static inline void mm_putref(struct mm *mm) {
105 if (refcount_dec_and_test(rc: &mm->refcount))
106 mm_free(mm);
107}
108
109/* a new thread joins the address space */
110static inline bool mm_users_getref(struct mm *mm) {
111 return refcount_inc_not_zero(rc: &mm->users);
112}
113
114/* last user -> mm_exit(mm); then mm_putref() */
115static inline void mm_users_putref(struct mm *mm) {
116 if (refcount_dec_and_test(rc: &mm->users)) {
117 mm_exit(mm);
118 mm_putref(mm); /* release the mm_refcount that mm_users held */
119 }
120}
121