MOS Source Code
Loading...
Searching...
No Matches
mmap.c
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/io/io.h"
4#include "mos/mm/mm.h"
7#include "mos/syslog/printk.h"
8
9#include <mos/mm/cow.h>
10#include <mos/mm/mm_types.h>
11#include <mos/mm/mmap.h>
13#include <mos/mos_global.h>
14#include <mos/tasks/process.h>
16
23static bool mmap_verify_arguments(ptr_t *hint_addr, mmap_flags_t mmap_flags)
24{
25 if ((*hint_addr % MOS_PAGE_SIZE) != 0)
26 {
27 pr_warn("hint address must be page-aligned");
28 return false;
29 }
30 const bool shared = mmap_flags & MMAP_SHARED; // when forked, shared between parent and child
31 const bool private = mmap_flags & MMAP_PRIVATE; // when forked, make it Copy-On-Write
32
33 if (shared == private)
34 {
35 pr_warn("mmap_file: shared and private are mutually exclusive, and one of them must be specified");
36 return NULL;
37 }
38
39 if (mmap_flags & MMAP_EXACT)
40 {
41 // always use the hint address if MMAP_EXACT is specified
42 return true;
43 }
44 else
45 {
46 // if no hint address is specified, use the default
47 if (*hint_addr == 0)
48 *hint_addr = MOS_ADDR_USER_MMAP;
49 }
50
51 return true;
52}
53
54ptr_t mmap_anonymous(mm_context_t *ctx, ptr_t hint_addr, mmap_flags_t flags, vm_flags vm_flags, size_t n_pages)
55{
56 if (!mmap_verify_arguments(&hint_addr, flags))
57 return 0;
58
60
61 vmap_t *vmap = cow_allocate_zeroed_pages(ctx, n_pages, hint_addr, valloc_flags, vm_flags);
62
63 if (IS_ERR(vmap))
64 return PTR_ERR(vmap);
65
66 pr_dinfo2(vmm, "allocated %zd pages at " PTR_FMT, vmap->npages, vmap->vaddr);
67
69 vmap_finalise_init(vmap, VMAP_MMAP, type);
70 return vmap->vaddr;
71}
72
73ptr_t mmap_file(mm_context_t *ctx, ptr_t hint_addr, mmap_flags_t flags, vm_flags vm_flags, size_t n_pages, io_t *io, off_t offset)
74{
75 if (!mmap_verify_arguments(&hint_addr, flags))
76 return 0;
77
78 if (offset % MOS_PAGE_SIZE != 0)
79 {
80 pr_warn("mmap_file: offset must be page-aligned");
81 return 0;
82 }
83
86
88 vmap_t *vmap = mm_get_free_vaddr_locked(ctx, n_pages, hint_addr, valloc_flags);
90
91 if (IS_ERR(vmap))
92 {
93 pr_warn("mmap_file: no free virtual address space");
94 return 0;
95 }
96
97 vmap->vmflags = vm_flags;
98 vmap->type = type;
99
100 if (!io_mmap(io, vmap, offset))
101 {
102 vmap_destroy(vmap);
103 pr_warn("mmap_file: could not map the file: io_mmap() failed");
104 return 0;
105 }
106
107 vmap_finalise_init(vmap, VMAP_FILE, type);
108 return vmap->vaddr;
109}
110
111bool munmap(ptr_t addr, size_t size)
112{
113 spinlock_acquire(&current_process->mm->mm_lock);
114 vmap_t *const whole_map = vmap_obtain(current_process->mm, addr, NULL);
115 if (unlikely(!whole_map))
116 {
117 spinlock_release(&current_process->mm->mm_lock);
118 pr_warn("munmap: could not find the vmap");
119 return false;
120 }
121
122 // will unmap all pages containing the range, even if they are not fully contained
123 const ptr_t range_start = ALIGN_DOWN_TO_PAGE(addr);
124 const ptr_t range_end = ALIGN_UP_TO_PAGE(addr + size);
125
126 const size_t start_pgoff = (range_start - whole_map->vaddr) / MOS_PAGE_SIZE;
127 const size_t end_pgoff = (range_end - whole_map->vaddr) / MOS_PAGE_SIZE;
128
129 vmap_t *const range_map = vmap_split_for_range(whole_map, start_pgoff, end_pgoff);
130 if (unlikely(!range_map))
131 {
132 pr_warn("munmap: could not split the vmap");
133 spinlock_release(&current_process->mm->mm_lock);
134 spinlock_release(&whole_map->lock);
135 return false;
136 }
137
138 vmap_destroy(range_map);
139 spinlock_release(&current_process->mm->mm_lock);
140 spinlock_release(&whole_map->lock);
141 return true;
142}
143
144bool vm_protect(mm_context_t *mmctx, ptr_t addr, size_t size, vm_flags perm)
145{
146 MOS_ASSERT(addr % MOS_PAGE_SIZE == 0);
148
149 spinlock_acquire(&mmctx->mm_lock);
150 vmap_t *const first_part = vmap_obtain(mmctx, addr, NULL);
151 const size_t addr_pgoff = (addr - first_part->vaddr) / MOS_PAGE_SIZE;
152
153 //
154 // first | second | third
155 // ^ ^
156 // | |
157 // addr addr + size
158 //
159
160 vmap_t *const to_protect = __extension__({
161 vmap_t *vmap = first_part;
162 // if 'addr_pgoff' is 0, then the first part is the one we want to protect,
163 // otherwise we need to split it to get the vmap that starts at 'addr'
164 if (addr_pgoff)
165 {
166 vmap = vmap_split(first_part, addr_pgoff);
167 spinlock_release(&first_part->lock); // release the lock on the first part, we don't need it
168 }
169
170 vmap;
171 });
172
173 const size_t size_pgoff = size / MOS_PAGE_SIZE;
174 if (size_pgoff < to_protect->npages)
175 {
176 // if there is a third part
177 vmap_t *const part3 = vmap_split(to_protect, size_pgoff);
178 spinlock_release(&part3->lock); // release the lock on the third part, we don't need it
179 }
180
181 if (to_protect->io)
182 {
183 if (!io_mmap_perm_check(to_protect->io, perm, to_protect->type == VMAP_TYPE_PRIVATE))
184 {
185 spinlock_release(&to_protect->lock); // permission denied
186 spinlock_release(&mmctx->mm_lock);
187 return false;
188 }
189 }
190
191 const bool read_lost = to_protect->vmflags & VM_READ && !(perm & VM_READ); // if we lose read permission
192 const bool write_lost = to_protect->vmflags & VM_WRITE && !(perm & VM_WRITE); // if we lose write permission
193 const bool exec_lost = to_protect->vmflags & VM_EXEC && !(perm & VM_EXEC); // if we lose exec permission
194
195 vm_flags mask = 0;
196 if (read_lost)
197 {
198 mask |= VM_READ;
199 pr_warn("read permission lost, this is not supported yet");
200 }
201
202 if (write_lost)
203 mask |= VM_WRITE;
204 if (exec_lost)
205 mask |= VM_EXEC;
206
207 // remove permissions immediately
208 mm_do_mask_flags(mmctx->pgd, to_protect->vaddr, to_protect->npages, mask);
209
210 // do not add permissions immediately, we will let the page fault handler do it
211 // e.g. write permission granted only when the page is written to (and proper e.g. CoW)
212 // can be done.
213
214 // let page fault handler do the real flags update
215 to_protect->vmflags = perm | VM_USER;
216
217 spinlock_release(&to_protect->lock);
218 spinlock_release(&mmctx->mm_lock);
219 return true;
220}
#define MOS_ASSERT(cond)
Definition assert.h:14
#define MOS_PAGE_SIZE
Definition autoconf.h:6
#define MOS_ADDR_USER_MMAP
Definition autoconf.h:2
vmap_t * cow_allocate_zeroed_pages(mm_context_t *handle, size_t npages, ptr_t vaddr, valloc_flags hints, vm_flags flags)
Allocate zero-on-demand pages at a specific address.
Definition cow.c:79
void vmap_finalise_init(vmap_t *vmap, vmap_content_t content, vmap_type_t type)
Finalize the initialization of a vmap object.
Definition mm.c:257
vmap_type_t
Definition mm.h:30
vmap_t * vmap_obtain(mm_context_t *mmctx, ptr_t vaddr, size_t *out_offset)
Get the vmap object for a virtual address.
Definition mm.c:192
vmap_t * vmap_split(vmap_t *vmap, size_t split)
Split a vmap object into two, at the specified offset.
Definition mm.c:212
void mm_unlock_ctx_pair(mm_context_t *ctx1, mm_context_t *ctx2)
Definition mm.c:111
void vmap_destroy(vmap_t *vmap)
Destroy a vmap object, and unmmap the region.
Definition mm.c:171
void mm_lock_ctx_pair(mm_context_t *ctx1, mm_context_t *ctx2)
Lock and unlock a pair of mm_context_t objects.
Definition mm.c:95
vmap_t * vmap_split_for_range(vmap_t *vmap, size_t rstart_pgoff, size_t rend_pgoff)
Split a vmap to get a vmap object for a range of pages.
Definition mm.c:234
@ VMAP_TYPE_PRIVATE
Definition mm.h:31
@ VMAP_TYPE_SHARED
Definition mm.h:32
@ VMAP_MMAP
Definition mm.h:25
@ VMAP_FILE
Definition mm.h:24
valloc_flags
Definition paging.h:20
vmap_t * mm_get_free_vaddr_locked(mm_context_t *mmctx, size_t n_pages, ptr_t base_vaddr, valloc_flags flags)
Gets npages unmapped free pages from a page table.
Definition paging.c:18
@ VALLOC_EXACT
Allocate pages at the exact address.
Definition paging.h:22
@ VALLOC_DEFAULT
Default allocation flags.
Definition paging.h:21
bool io_mmap_perm_check(io_t *io, vm_flags flags, bool is_private)
Definition io.c:224
bool io_mmap(io_t *io, vmap_t *vmap, off_t offset)
Definition io.c:254
mmap_flags_t
Definition mm_types.h:15
@ MMAP_PRIVATE
Definition mm_types.h:17
@ MMAP_EXACT
Definition mm_types.h:16
@ MMAP_SHARED
Definition mm_types.h:18
ptr_t mmap_file(mm_context_t *ctx, ptr_t hint_addr, mmap_flags_t flags, vm_flags vm_flags, size_t n_pages, io_t *io, off_t offset)
Map a file into the current process's address space.
Definition mmap.c:73
bool munmap(ptr_t addr, size_t size)
Unmap a page from the current process's address space.
Definition mmap.c:111
ptr_t mmap_anonymous(mm_context_t *ctx, ptr_t hint_addr, mmap_flags_t flags, vm_flags vm_flags, size_t n_pages)
Map a page into the current process's address space.
Definition mmap.c:54
static bool mmap_verify_arguments(ptr_t *hint_addr, mmap_flags_t mmap_flags)
Check if the mmap flags are valid.
Definition mmap.c:23
bool vm_protect(mm_context_t *mmctx, ptr_t addr, size_t size, vm_flags perm)
Change the permissions of a mapping.
Definition mmap.c:144
#define ALIGN_UP_TO_PAGE(addr)
Definition mos_global.h:75
#define ALIGN_DOWN_TO_PAGE(addr)
Definition mos_global.h:76
#define unlikely(x)
Definition mos_global.h:40
#define NULL
Definition pb_syshdr.h:46
vm_flags
Definition platform.h:40
@ VM_READ
Definition platform.h:41
@ VM_EXEC
Definition platform.h:43
@ VM_WRITE
Definition platform.h:42
@ VM_USER
Definition platform.h:45
#define current_process
Definition platform.h:31
#define pr_warn(fmt,...)
Definition printk.h:38
#define pr_dinfo2(feat, fmt,...)
Definition printk.h:27
size_t size
Definition slab.c:30
#define spinlock_acquire(lock)
Definition spinlock.h:61
#define spinlock_release(lock)
Definition spinlock.h:62
Definition io.h:46
spinlock_t mm_lock
protects [pgd] and the [mmaps] list (the list itself, not the vmap_t objects)
Definition platform.h:81
pgd_t pgd
Definition platform.h:82
Definition mm.h:58
ptr_t vaddr
Definition mm.h:62
size_t npages
Definition mm.h:63
vm_flags vmflags
Definition mm.h:64
spinlock_t lock
Definition mm.h:60
io_t * io
Definition mm.h:67
vmap_type_t type
Definition mm.h:71
void mm_do_mask_flags(pgd_t max, ptr_t vaddr, size_t n_pages, vm_flags to_remove)
Definition table_ops.c:51
ssize_t off_t
Definition types.h:84
#define PTR_FMT
Definition types.h:33
unsigned long ptr_t
Definition types.h:25