1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#pragma once
4
5#include "mos/io/io.hpp"
6#include "mos/mm/mmstat.hpp"
7#include "mos/mm/physical/pmm.hpp"
8#include "mos/platform/platform.hpp"
9#include "mos/syslog/syslog.hpp"
10
11#include <mos/allocator.hpp>
12#include <mos/lib/structures/list.hpp>
13#include <mos/lib/sync/spinlock.hpp>
14#include <mos/mm/mm_types.h>
15
16/**
17 * @defgroup mm Memory Management
18 * @brief Memory management functions and structures.
19 * @{
20 */
21
22typedef enum
23{
24 VMAP_UNKNOWN = 0,
25 VMAP_STACK, // stack (user)
26 VMAP_FILE, // file mapping
27 VMAP_MMAP, // mmap mapping
28 VMAP_DMA, // DMA mapping
29} vmap_content_t;
30
31typedef enum
32{
33 VMAP_TYPE_PRIVATE = MMAP_PRIVATE, // there will be distinct copies of the memory region in the child process
34 VMAP_TYPE_SHARED = MMAP_SHARED, // the memory region will be shared between the parent and child processes
35} vmap_type_t;
36
37typedef struct
38{
39 bool is_present, is_write, is_user, is_exec;
40 ptr_t ip; ///< the instruction pointer which caused the fault
41 const platform_regs_t *regs; ///< the registers of the moment that caused the fault
42 phyframe_t *faulting_page; ///< the frame that contains the copy-on-write data (if any)
43 const phyframe_t *backing_page; ///< the frame that contains the data for this page, the on_fault handler should set this
44} pagefault_t;
45
46typedef enum
47{
48 VMFAULT_COMPLETE, ///< no further action is needed, the page is correctly mapped now
49 VMFAULT_MAP_BACKING_PAGE_RO, ///< the caller should map the backing page into the faulting address, and mark it non-writable
50 VMFAULT_MAP_BACKING_PAGE, ///< the caller should map the backing page into the faulting address
51 VMFAULT_COPY_BACKING_PAGE, ///< the caller should copy the backing page into the faulting address
52 VMFAULT_CANNOT_HANDLE = 0xff, ///< the handler cannot handle this fault
53} vmfault_result_t;
54
55struct vmap_t;
56
57typedef vmfault_result_t (*vmfault_handler_t)(vmap_t *vmap, ptr_t fault_addr, pagefault_t *info);
58
59struct vmap_t : mos::NamedType<"VMap">
60{
61 as_linked_list;
62 spinlock_t lock;
63
64 ptr_t vaddr; // virtual addresses
65 size_t npages;
66 VMFlags vmflags; // the expected flags for the region, regardless of the copy-on-write state
67 MMContext *mmctx;
68
69 IO *io; // the io object that (possibly) backs this vmap
70 off_t io_offset; // the offset in the io object, page-aligned
71
72 vmap_content_t content;
73 vmap_type_t type;
74 vmap_stat_t stat;
75 vmfault_handler_t on_fault;
76
77 friend mos::SyslogStreamWriter operator<<(mos::SyslogStreamWriter stream, const vmap_t *vmap)
78 {
79 return stream << fmt("\\{ [{} - {}] }", (void *) vmap->vaddr, (void *) (vmap->vaddr + vmap->npages * MOS_PAGE_SIZE - 1));
80 }
81};
82
83#define pfn_va(pfn) ((ptr_t) (platform_info->direct_map_base + (pfn) * (MOS_PAGE_SIZE)))
84#define va_pfn(va) ((((ptr_t) (va)) - platform_info->direct_map_base) / MOS_PAGE_SIZE)
85#define va_phyframe(va) (&phyframes[va_pfn(va)])
86#define phyframe_va(frame) ((ptr_t) pfn_va(phyframe_pfn(frame)))
87#define pa_va(pa) ((ptr_t) (pa) + platform_info->direct_map_base)
88
89phyframe_t *mm_get_free_page(void);
90phyframe_t *mm_get_free_page_raw(void);
91phyframe_t *mm_get_free_pages(size_t npages);
92
93#define mm_free_page(frame) pmm_free_frames(frame, 1)
94#define mm_free_pages(frame, npages) pmm_free_frames(frame, npages)
95
96/**
97 * @brief Create a user-mode platform-dependent page table.
98 * @return MMContext The created page table.
99 * @note A platform-independent page-map is also created.
100 */
101MMContext *mm_create_context(void);
102
103/**
104 * @brief Destroy a user-mode platform-dependent page table.
105 * @param table The page table to destroy.
106 * @note The platform-independent page-map is also destroyed.
107 */
108void mm_destroy_context(MMContext *table);
109
110/**
111 * @brief Lock and unlock a pair of MMContext objects.
112 *
113 * @param ctx1 The first context
114 * @param ctx2 The second context
115 */
116void mm_lock_context_pair(MMContext *ctx1, MMContext *ctx2 = nullptr);
117void mm_unlock_context_pair(MMContext *ctx1, MMContext *ctx2 = nullptr);
118
119__nodiscard MMContext *mm_switch_context(MMContext *new_ctx);
120
121/**
122 * @brief Create a vmap object and insert it into the address space.
123 *
124 * @param mmctx The address space
125 * @param vaddr Starting virtual address of the region
126 * @param npages Number of pages in the region
127 * @return vmap_t* The created vmap object, with its lock held for further initialization.
128 */
129vmap_t *vmap_create(MMContext *mmctx, ptr_t vaddr, size_t npages);
130
131/**
132 * @brief Destroy a vmap object, and unmmap the region.
133 *
134 * @param vmap The vmap object
135 * @note The vmap object will be freed.
136 */
137void vmap_destroy(vmap_t *vmap);
138
139/**
140 * @brief Get the vmap object for a virtual address.
141 *
142 * @param mmctx The address space to search
143 * @param vaddr The virtual address
144 * @param out_offset An optional pointer to receive the offset of the address in the vmap
145 * @return vmap_t* The vmap object, or NULL if not found, with its lock held.
146 */
147vmap_t *vmap_obtain(MMContext *mmctx, ptr_t vaddr, size_t *out_offset = nullptr);
148
149/**
150 * @brief Split a vmap object into two, at the specified offset.
151 *
152 * @param vmap The vmap object
153 * @param split The number of pages in the first vmap object
154 * @return vmap_t* The second vmap object, with its lock held.
155 */
156vmap_t *vmap_split(vmap_t *vmap, size_t split);
157
158/**
159 * @brief Split a vmap to get a vmap object for a range of pages.
160 *
161 * @param vmap The vmap object to split
162 * @param rstart_pgoff The starting page offset
163 * @param rend_pgoff The ending page offset
164 * @return vmap_t* The vmap object for the range, with its lock held.
165 */
166vmap_t *vmap_split_for_range(vmap_t *vmap, size_t rstart_pgoff, size_t rend_pgoff);
167
168/**
169 * @brief Finalize the initialization of a vmap object.
170 *
171 * @param vmap The vmap object
172 * @param content The content type of the region, \see vmap_content_t
173 * @param type The fork behavior of the region, \see vmap_type_t
174 * @note The vmap object must be locked, and will be unlocked after this function returns.
175 */
176void vmap_finalise_init(vmap_t *vmap, vmap_content_t content, vmap_type_t type);
177
178/**
179 * @brief Helper function to resolve a copy-on-write fault.
180 *
181 * @param vmap The vmap object
182 * @param fault_addr The fault address
183 * @param info The page fault info
184 * @return vmfault_result_t always VMFAULT_COMPLETE
185 */
186[[nodiscard("resolve completed")]] vmfault_result_t mm_resolve_cow_fault(vmap_t *vmap, ptr_t fault_addr, pagefault_t *info);
187
188/**
189 * @brief Handle a page fault
190 *
191 * @param fault_addr The fault address
192 * @param info The page fault info
193 */
194void mm_handle_fault(ptr_t fault_addr, pagefault_t *info);
195
196/** @} */
197