PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
space.h
Go to the documentation of this file.
1#pragma once
2
4#include <kernel/fs/path.h>
6#include <kernel/sync/lock.h>
7
8#include <boot/boot_info.h>
9
10#include <sys/bitmap.h>
11#include <sys/list.h>
12#include <sys/proc.h>
13
14/**
15 * @brief Address Space handling.
16 * @defgroup kernel_mem_space Space
17 * @ingroup kernel_mem
18 *
19 * @{
20 */
21
22/**
23 * @brief Flags for space initialization.
24 * @enum space_flags_t
25 */
26typedef enum
27{
29 /**
30 * Use the PMM bitmap to allocate the page table, this is really only for the kernel page table as it
31 * must be within a 32 bit boundary because the smp trampoline loads it as a dword.
32 */
34 SPACE_MAP_KERNEL_BINARY = 1 << 1, ///< Map the kernel binary into the address space.
35 SPACE_MAP_KERNEL_HEAP = 1 << 2, ///< Map the kernel heap into the address space.
36 SPACE_MAP_IDENTITY = 1 << 3, ///< Map the identity mapped physical memory into the address space.
38
39/**
40 * @brief Space callback function.
41 */
42typedef void (*space_callback_func_t)(void* data);
43
44/**
45 * @brief Space callback structure.
46 * @struct vmm_callback_t
47 */
54
55/**
56 * @brief Pinned page structure.
57 * @struct space_pinned_page_t
58 *
59 * Stored in the `pinnedPages` map in `space_t`.
60 */
61typedef struct
62{
64 uint64_t pinCount; ///< The number of times this page is pinned, will be unpinned when it reaches 0.
66
67/**
68 * @brief Virtual address space structure.
69 * @struct space_t
70 *
71 * The `space_t` structure represents a virtual address space.
72 *
73 * Note that the actual pin depth, if its is greater than 1, is tracked in the `pinnedPages` map, the page table only
74 * tracks if a page is pinned or not for faster access and to avoid having to access the map even when just pinning a
75 * page once.
76 */
77typedef struct space
78{
79 page_table_t pageTable; ///< The page table associated with the address space.
80 map_t pinnedPages; ///< Map of pages with a pin depth greater than 1.
81 uintptr_t startAddress; ///< The start address for allocations in this address space.
82 uintptr_t endAddress; ///< The end address for allocations in this address space.
83 uintptr_t freeAddress; ///< The next available free virtual address in this address space.
85 /**
86 * Array of callbacks for this address space, indexed by the callback ID.
87 *
88 * Lazily initialized to a size equal to the largest used callback ID.
89 */
91 uint64_t callbacksLength; ///< Length of the `callbacks` array.
92 BITMAP_DEFINE(callbackBitmap, PML_MAX_CALLBACK); ///< Bitmap to track available callback IDs.
93 BITMAP_DEFINE(cpus, CPU_MAX); ///< Bitmap to track which CPUs are using this space.
94 atomic_uint16_t shootdownAcks;
96} space_t;
97
98/**
99 * @brief The maximum time to wait for the acknowledgements from other CPU's before panicking.
100 */
101#define SPACE_TLB_SHOOTDOWN_TIMEOUT (CLOCKS_PER_SEC)
102
103/**
104 * @brief Initializes a virtual address space.
105 *
106 * @param space The address space to initialize.
107 * @param startAddress The starting address for allocations in this address space.
108 * @param endAddress The ending address for allocations in this address space.
109 * @param flags Flags to control the initialization behavior.
110 * @return On success, `0`. On failure, `ERR` and `errno` is set to:
111 * - `EINVAL`: Invalid parameters.
112 * - `ENOMEM`: Not enough memory to initialize the address space.
113 */
114uint64_t space_init(space_t* space, uintptr_t startAddress, uintptr_t endAddress, space_flags_t flags);
115
116/**
117 * @brief Deinitializes a virtual address space.
118
119 * @param space The address space to deinitialize.
120 */
121void space_deinit(space_t* space);
122
123/**
124 * @brief Pins pages within a region of the address space.
125 *
126 * Used to prevent TOCTOU attacks, where a system call provides some user space region, the kernel then checks that its
127 * mapped and after that check a seperate thread in the user space process unmaps or modifies that regions mappings
128 * while the kernel is still using it.
129 *
130 * Our solution is to pin any user space pages that are accessed or modified during the syscall, meaning that a special
131 * flag is set in the address spaces page tables that prevent those pages from being unmapped or modified until they are
132 * unpinned which happens when the syscall is finished in `space_unpin()`.
133 *
134 * If the region is not fully mapped, or the region is not within the spaces `startAddress` and `endAddress` range, the
135 * function will fail.
136 *
137 * If any page in the region is already at its maximum pin depth, the calling thread will block until the page is
138 * unpinned by another thread.
139 *
140 * If a user stack is provided and the region to pin is both unmapped and within the stack region, memory will be
141 * allocated and mapped to the relevant region in the user stack. This is needed as its possible for a user space
142 * process to pass an address to a system call that is in its user stack but not yet mapped. For example, it could
143 * create a big buffer on its stack then pass it to a syscall without first accessing it, meaning no page fault would
144 * have occurred to map the pages.
145 *
146 * @param space The target address space.
147 * @param address The address to pin, can be `NULL` if length is 0.
148 * @param length The length of the region pointed to by `address`, in bytes.
149 * @param userStack Pointer to the user stack of the calling thread, can be `NULL, see above.
150 * @return On success, `0`. On failure, `ERR` and `errno` is set to:
151 * - `EINVAL`: Invalid parameters.
152 * - `EOVERFLOW`: Address overflow.
153 * - `EFAULT`: The region is not fully mapped or within the provided user stack.
154 * - `ENOMEM`: Not enough memory.
155 */
156uint64_t space_pin(space_t* space, const void* address, size_t length, stack_pointer_t* userStack);
157
158/**
159 * @brief Pins a region of memory terminated by a terminator value.
160 *
161 * Pins pages in the address space starting from `address` up to `maxSize` bytes or until the specified
162 * terminator is found.
163 *
164 * Used for null-terminated strings or other buffers that have a specific terminator.
165 *
166 * @param space The target address space.
167 * @param address The starting address of the region to pin.
168 * @param terminator The terminator value to search for.
169 * @param objectSize The size of each object to compare against the terminator, in bytes.
170 * @param maxCount The maximum number of objects to scan before failing.
171 * @param userStack Pointer to the user stack of the calling thread, can be `NULL`, see `space_pin()`.
172 * @return On success, the number of bytes pinned, not including the terminator. On failure, `ERR` and `errno` is set
173 * to:
174 * - `EINVAL`: Invalid parameters.
175 * - `EOVERFLOW`: Address overflow.
176 * - `EFAULT`: The region is not fully mapped or within the provided user stack.
177 * - `ENOMEM`: Not enough memory.
178 */
179uint64_t space_pin_terminated(space_t* space, const void* address, const void* terminator, size_t objectSize,
180 size_t maxCount, stack_pointer_t* userStack);
181
182/**
183 * @brief Unpins pages in a region previously pinned with `space_pin()` or `space_pin_string()`.
184 *
185 * Will wake up any threads waiting to pin the same pages.
186 *
187 * @param space The target address space.
188 * @param address The address of the region to unpin, can be `NULL` if length is 0.
189 * @param length The length of the region pointed to by `address`, in bytes.
190 */
191void space_unpin(space_t* space, const void* address, size_t length);
192
193/**
194 * @brief Checks if a virtual memory region is within the allowed address range of the space.
195 *
196 * Checks that the given memory region is within the `startAddress` and `endAddress` range of the space, really only
197 * used in system calls that might access unmapped user space memory for example `mmap()`, in such cases we dont want to
198 * pin the "buffer" since we expect that it is not yet mapped.
199 *
200 * @param space The target address space.
201 * @param addr The starting address of the memory region, can be `NULL` if length is 0.
202 * @param length The length of the memory region, in bytes.
203 * @return On success, `0`. On failure, `ERR` and `errno` is set to:
204 * - `EINVAL`: Invalid parameters.
205 * - `EOVERFLOW`: Address overflow.
206 * - `EFAULT`: The region is outside the allowed address range.
207 */
208uint64_t space_check_access(space_t* space, const void* addr, size_t length);
209
210/**
211 * @brief Helper structure for managing address space mappings.
212 * @struct space_mapping_t
213 */
221
222/**
223 * @brief Prepare for changes to the address space mappings.
224 *
225 * Will return with the spaces mutex acquired for writing, which must be released by calling
226 * `space_mapping_end()`.
227 *
228 * If `flags & PML_USER` then the addresses must be in the user space range.
229 *
230 * @note Handling page faults to grow stacks requires mapping memory, this means that if we were to run out of memory
231 * while executing this function, it could lead to a deadlock. To avoid this, this function will call
232 * `stack_pointer_poke()` to ensure that sufficient stack space is available.
233 *
234 * @param space The target address space.
235 * @param mapping Will be filled with parsed information about the mapping.
236 * @param virtAddr The virtual address the mapping will apply to. Can be `NULL` to let the kernel choose an address.
237 * @param physAddr The physical address to map from. Can be `PHYS_ADDR_INVALID`.
238 * @param length The length of the virtual memory region to modify, in bytes.
239 * @param alignment The required alignment for the virtual memory region in bytes.
240 * @param flags The page table flags for the mapping.
241 * @return On success, `0`. On failure, `ERR` and `errno` is set to:
242 * - `EINVAL`: Invalid parameters.
243 * - `EOVERFLOW`: Address overflow.
244 * - `EFAULT`: The addresses are outside the allowed range.
245 * - `ENOMEM`: Not enough memory.
246 */
247uint64_t space_mapping_start(space_t* space, space_mapping_t* mapping, void* virtAddr, phys_addr_t physAddr,
248 size_t length, size_t alignment, pml_flags_t flags);
249
250/**
251 * @brief Allocate a callback.
252 *
253 * Must be called between `space_mapping_start()` and `space_mapping_end()`.
254 *
255 * When `pageAmount` number of pages with this callback ID are unmapped or the address space is freed,
256 * the callback function will be called with the provided private data.
257 *
258 * @param space The target address space.
259 * @param pageAmount The number of pages the callback is responsible for.
260 * @param func The callback function.
261 * @param private Private data to pass to the callback function.
262 * @return On success, returns the callback ID. On failure, returns `PML_MAX_CALLBACK`.
263 */
264pml_callback_id_t space_alloc_callback(space_t* space, size_t pageAmount, space_callback_func_t func, void* data);
265
266/**
267 * @brief Free a callback.
268 *
269 * Must be called between `space_mapping_start()` and `space_mapping_end()`.
270 *
271 * Allows the callback ID to be reused. The callback function will not be called.
272 *
273 * @param space The target address space.
274 * @param callbackId The callback ID to free.
275 */
276void space_free_callback(space_t* space, pml_callback_id_t callbackId);
277
278/**
279 * @brief Performs cleanup after changes to the address space mappings.
280 *
281 * Must be called after `space_mapping_start()`.
282 *
283 * @param space The target address space.
284 * @param mapping The parsed information about the mapping.
285 * @param err The error code, if 0 then no error.
286 * @return If `err` is `EOK`, returns the virtual address of the mapping. If `err` is not `EOK`, returns `NULL` and
287 * `errno` is set to `err`.
288 */
289void* space_mapping_end(space_t* space, space_mapping_t* mapping, errno_t err);
290
291/**
292 * @brief Checks if a virtual memory region is fully mapped.
293 *
294 * @param space The target address space.
295 * @param virtAddr The virtual address of the memory region.
296 * @param length The length of the memory region, in bytes.
297 * @return `true` if the entire region is mapped, `false` otherwise.
298 */
299bool space_is_mapped(space_t* space, const void* virtAddr, size_t length);
300
301/**
302 * @brief Get the number of user pages allocated in the address space.
303 *
304 * Will count the number of pages with the `PML_OWNED` flag set in user space.
305 *
306 * @param space The target address space.
307 * @return The number of user pages mapped.
308 */
310
311/**
312 * @brief Translate a virtual address to a physical address in the address space.
313 *
314 * @param space The target address space.
315 * @param virtAddr The virtual address to translate.
316 * @return On success, `0`. On failure, `ERR` and `errno` is set to:
317 * - `EINVAL`: Invalid parameters.
318 * - `EFAULT`: The virtual address is not mapped.
319 */
320phys_addr_t space_virt_to_phys(space_t* space, const void* virtAddr);
321
322/** @} */
static fd_t data
Definition dwm.c:21
int errno_t
Definition errno_t.h:4
#define CPU_MAX
Maximum number of CPUs supported.
Definition cpu.h:50
static uintptr_t address
Mapped virtual address of the HPET registers.
Definition hpet.c:96
uintptr_t phys_addr_t
Physical address type.
uint8_t pml_callback_id_t
Callback ID type.
#define PML_MAX_CALLBACK
Maximum number of callbacks that can be registered for a page table.
uint64_t space_pin_terminated(space_t *space, const void *address, const void *terminator, size_t objectSize, size_t maxCount, stack_pointer_t *userStack)
Pins a region of memory terminated by a terminator value.
Definition space.c:330
void * space_mapping_end(space_t *space, space_mapping_t *mapping, errno_t err)
Performs cleanup after changes to the address space mappings.
Definition space.c:630
bool space_is_mapped(space_t *space, const void *virtAddr, size_t length)
Checks if a virtual memory region is fully mapped.
Definition space.c:650
void space_unpin(space_t *space, const void *address, size_t length)
Unpins pages in a region previously pinned with space_pin() or space_pin_string().
Definition space.c:418
void space_free_callback(space_t *space, pml_callback_id_t callbackId)
Free a callback.
Definition space.c:612
uint64_t space_mapping_start(space_t *space, space_mapping_t *mapping, void *virtAddr, phys_addr_t physAddr, size_t length, size_t alignment, pml_flags_t flags)
Prepare for changes to the address space mappings.
Definition space.c:494
void(* space_callback_func_t)(void *data)
Space callback function.
Definition space.h:42
space_flags_t
Flags for space initialization.
Definition space.h:27
pml_callback_id_t space_alloc_callback(space_t *space, size_t pageAmount, space_callback_func_t func, void *data)
Allocate a callback.
Definition space.c:571
uint64_t space_user_page_count(space_t *space)
Get the number of user pages allocated in the address space.
Definition space.c:657
phys_addr_t space_virt_to_phys(space_t *space, const void *virtAddr)
Translate a virtual address to a physical address in the address space.
Definition space.c:670
uint64_t space_pin(space_t *space, const void *address, size_t length, stack_pointer_t *userStack)
Pins pages within a region of the address space.
Definition space.c:281
uint64_t space_check_access(space_t *space, const void *addr, size_t length)
Checks if a virtual memory region is within the allowed address range of the space.
Definition space.c:444
void space_deinit(space_t *space)
Deinitializes a virtual address space.
Definition space.c:121
uint64_t space_init(space_t *space, uintptr_t startAddress, uintptr_t endAddress, space_flags_t flags)
Initializes a virtual address space.
Definition space.c:64
@ SPACE_NONE
Definition space.h:28
@ SPACE_MAP_KERNEL_HEAP
Map the kernel heap into the address space.
Definition space.h:35
@ SPACE_USE_PMM_BITMAP
Definition space.h:33
@ SPACE_MAP_IDENTITY
Map the identity mapped physical memory into the address space.
Definition space.h:36
@ SPACE_MAP_KERNEL_BINARY
Map the kernel binary into the address space.
Definition space.h:34
static const path_flag_t flags[]
Definition path.c:47
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
A simple ticket lock implementation.
Definition lock.h:44
Map entry structure.
Definition map.h:69
Hash map structure.
Definition map.h:90
A page table structure.
A entry in a page table without a specified address or callback ID.
uint64_t pageAmount
Definition space.h:52
space_callback_func_t func
Definition space.h:50
void * data
Definition space.h:51
Helper structure for managing address space mappings.
Definition space.h:215
phys_addr_t physAddr
Definition space.h:217
size_t pageAmount
Definition space.h:218
pml_flags_t flags
Definition space.h:219
void * virtAddr
Definition space.h:216
Pinned page structure.
Definition space.h:62
uint64_t pinCount
The number of times this page is pinned, will be unpinned when it reaches 0.
Definition space.h:64
map_entry_t mapEntry
Definition space.h:63
Virtual address space structure.
Definition space.h:78
BITMAP_DEFINE(callbackBitmap, PML_MAX_CALLBACK)
Bitmap to track available callback IDs.
lock_t lock
Definition space.h:95
map_t pinnedPages
Map of pages with a pin depth greater than 1.
Definition space.h:80
uintptr_t startAddress
The start address for allocations in this address space.
Definition space.h:81
uint64_t callbacksLength
Length of the callbacks array.
Definition space.h:91
uintptr_t endAddress
The end address for allocations in this address space.
Definition space.h:82
BITMAP_DEFINE(cpus, CPU_MAX)
Bitmap to track which CPUs are using this space.
page_table_t pageTable
The page table associated with the address space.
Definition space.h:79
space_flags_t flags
Definition space.h:84
uintptr_t freeAddress
The next available free virtual address in this address space.
Definition space.h:83
atomic_uint16_t shootdownAcks
Definition space.h:94
space_callback_t * callbacks
Definition space.h:90
Structure to define a stack in memory.