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