The Pedigree Project 0.1
vm-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/process/Process.h"
3#include "pedigree/kernel/process/TerminationDeferral.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/VirtualAddressSpace.h"
8#include "pedigree/kernel/syscallError.h"
9#include "pedigree/kernel/utilities/Pointers.h"
10
11#include <limits.h>
12
13#include "PosixSubsystem.h"
15#include "modules/system/vfs/VFS.h"
16#include "vm-syscalls.h"
17
18namespace {
19constexpr int MayMove = 1, Fixed = 2, DontUnmap = 4;
20constexpr int Discard = 4, PageOut = 21;
21
22bool roundLength(size_t length, size_t& rounded) {
23 const size_t mask = PhysicalMemoryManager::getPageSize() - 1;
24 if (length > ~size_t(0) - mask)
25 return false;
26 rounded = (length + mask) & ~mask;
27 return true;
28}
29
30bool userRange(uintptr_t base, size_t length) {
31 auto& space = Processor::information().getVirtualAddressSpace();
32 return base >= space.getUserStart() && base < space.getKernelStart() &&
33 length <= space.getKernelStart() - base &&
34 space.isAddressValid(reinterpret_cast<void*>(base)) &&
35 (!length || space.isAddressValid(reinterpret_cast<void*>(base + length - 1)));
36}
37
38struct ResidencyCredentials {
39 int64_t uid, gid;
40 Vector<int64_t> groups;
41};
42
43bool mayInspectResidency(File* file, void* context) {
44 const auto& credentials = *static_cast<ResidencyCredentials*>(context);
45 if (credentials.uid == 0 || file->getAttributes().uid == static_cast<size_t>(credentials.uid))
46 return true;
47 Thread* thread = Processor::information().getCurrentThread();
48 const size_t savedError = thread->getErrno();
49 const bool permitted = VFS::checkAccess(file, false, true, false, credentials.uid,
50 credentials.gid, credentials.groups);
51 // A masked residency answer succeeds even when write permission is absent.
52 thread->setErrno(savedError);
53 return permitted;
54}
55} // namespace
56
57void* posix_mremap(void* oldAddress, size_t oldLength, size_t newLength, int flags,
58 void* newAddress) {
59 TerminationDeferral lifetime;
60 const uintptr_t source = reinterpret_cast<uintptr_t>(oldAddress);
61 const uintptr_t destination = reinterpret_cast<uintptr_t>(newAddress);
62 const size_t mask = PhysicalMemoryManager::getPageSize() - 1;
63 size_t oldSize, newSize;
64 if ((source & mask) || !newLength || !roundLength(oldLength, oldSize) ||
65 !roundLength(newLength, newSize) || (flags & ~(MayMove | Fixed | DontUnmap)) ||
66 ((flags & (Fixed | DontUnmap)) && !(flags & MayMove)) || (!oldLength && !(flags & MayMove))) {
67 SYSCALL_ERROR(InvalidArgument);
68 return reinterpret_cast<void*>(~uintptr_t(0));
69 }
70 if (!userRange(source, oldSize)) {
71 SYSCALL_ERROR(BadAddress);
72 return reinterpret_cast<void*>(~uintptr_t(0));
73 }
74 if ((flags & Fixed) && ((destination & mask) || !userRange(destination, newSize) ||
75 (source < destination + newSize && destination < source + oldSize))) {
76 SYSCALL_ERROR(InvalidArgument);
77 return reinterpret_cast<void*>(~uintptr_t(0));
78 }
79 if (flags & DontUnmap) {
80 if (oldSize != newSize)
81 SYSCALL_ERROR(InvalidArgument);
82 else
83 SYSCALL_ERROR(OperationNotSupported);
84 return reinterpret_cast<void*>(~uintptr_t(0));
85 }
86
87 MemoryMapManager::RemapRequest request{source, destination, oldSize,
88 newSize, bool(flags & MayMove), bool(flags & Fixed)};
89 uintptr_t result = 0;
90 const auto status = MemoryMapManager::instance().remap(request, result);
91 switch (status) {
92 case MemoryMapManager::VmStatus::Success:
93 Processor::information().getCurrentThread()->setErrno(0);
94 return reinterpret_cast<void*>(result);
95 case MemoryMapManager::VmStatus::InvalidRange:
96 SYSCALL_ERROR(InvalidArgument);
97 break;
98 case MemoryMapManager::VmStatus::Unmapped:
99 SYSCALL_ERROR(BadAddress);
100 break;
101 case MemoryMapManager::VmStatus::Unsupported:
102 SYSCALL_ERROR(OperationNotSupported);
103 break;
104 case MemoryMapManager::VmStatus::NoMemory:
105 SYSCALL_ERROR(OutOfMemory);
106 break;
107 case MemoryMapManager::VmStatus::LockLimit:
108 SYSCALL_ERROR(NoMoreProcesses);
109 break;
110 }
111 return reinterpret_cast<void*>(~uintptr_t(0));
112}
113
114int posix_remap_file_pages(void* address, size_t length, size_t prot, size_t pageOffset,
115 size_t flags) {
116 TerminationDeferral lifetime;
117 const size_t pageSize = PhysicalMemoryManager::getPageSize();
118 const uintptr_t base = reinterpret_cast<uintptr_t>(address) & ~(pageSize - 1);
119 const size_t extent = length & ~(pageSize - 1);
120 if (prot || !extent || extent > ~uintptr_t(0) - base || !userRange(base, extent) ||
121 extent / pageSize > ~size_t(0) - pageOffset) {
122 SYSCALL_ERROR(InvalidArgument);
123 return -1;
124 }
125 if (pageOffset > ~size_t(0) / pageSize ||
126 pageOffset > static_cast<uint64_t>(LLONG_MAX) / pageSize ||
127 extent > static_cast<uint64_t>(LLONG_MAX) - pageOffset * pageSize) {
128 SYSCALL_ERROR(ValueTooLarge);
129 return -1;
130 }
131
132 constexpr size_t Nonblock = 0x10000;
133 using Status = MemoryMapManager::FileRemapStatus;
134 const auto status = MemoryMapManager::instance().remapFilePages(
135 base, extent, pageOffset * pageSize, flags & Nonblock);
136 switch (status) {
137 case Status::Success:
138 Processor::information().getCurrentThread()->setErrno(0);
139 return 0;
140 case Status::InvalidRange:
141 SYSCALL_ERROR(InvalidArgument);
142 break;
143 case Status::Unsupported:
144 SYSCALL_ERROR(OperationNotSupported);
145 break;
146 case Status::NoMemory:
147 SYSCALL_ERROR(OutOfMemory);
148 break;
149 case Status::PolicyDenied:
150 case Status::PermissionDenied:
151 SYSCALL_ERROR(NotEnoughPermissions);
152 break;
153 case Status::LockLimit:
154 SYSCALL_ERROR(NoMoreProcesses);
155 break;
156 }
157 return -1;
158}
159
160int posix_mincore(void* address, size_t length, unsigned char* vector) {
161 TerminationDeferral lifetime;
162 const uintptr_t base = reinterpret_cast<uintptr_t>(address);
163 const size_t pageSize = PhysicalMemoryManager::getPageSize();
164 if (base & (pageSize - 1)) {
165 SYSCALL_ERROR(InvalidArgument);
166 return -1;
167 }
168 size_t extent;
169 if (!roundLength(length, extent)) {
170 SYSCALL_ERROR(OutOfMemory);
171 return -1;
172 }
173 if (!extent)
174 return 0;
175 if (!userRange(base, extent)) {
176 SYSCALL_ERROR(OutOfMemory);
177 return -1;
178 }
179 const size_t pages = extent / pageSize;
180 const size_t capacity = pages < pageSize ? pages : pageSize;
181 auto snapshot = UniqueArray<unsigned char>::allocate(capacity);
182 if (!snapshot) {
183 SYSCALL_ERROR(NoMoreProcesses);
184 return -1;
185 }
186 Thread* thread = Processor::information().getCurrentThread();
187 Process* process = thread->getParent();
188 ResidencyCredentials credentials;
189 credentials.uid = process->getEffectiveUserId();
190 if (credentials.uid < 0)
191 credentials.uid = process->getUserId();
192 credentials.gid = process->getEffectiveGroupId();
193 if (credentials.gid < 0)
194 credentials.gid = process->getGroupId();
195 if (!credentials.groups.tryReserve(NGROUPS_MAX)) {
196 SYSCALL_ERROR(NoMoreProcesses);
197 return -1;
198 }
199 process->getSupplementalGroupIds(credentials.groups);
200 for (size_t completed = 0; completed < pages;) {
201 const size_t count = pages - completed < capacity ? pages - completed : capacity;
202 const auto status =
203 MemoryMapManager::instance().residency(base + completed * pageSize, count * pageSize,
204 snapshot.get(), mayInspectResidency, &credentials);
205 if (status != MemoryMapManager::VmStatus::Success) {
206 if (status == MemoryMapManager::VmStatus::NoMemory)
207 SYSCALL_ERROR(NoMoreProcesses);
208 else
209 SYSCALL_ERROR(OutOfMemory);
210 return -1;
211 }
212 const uintptr_t output = reinterpret_cast<uintptr_t>(vector);
213 if (completed > ~uintptr_t(0) - output ||
214 !PosixSubsystem::copyToUser(reinterpret_cast<void*>(output + completed), snapshot.get(),
215 count)) {
216 SYSCALL_ERROR(BadAddress);
217 return -1;
218 }
219 completed += count;
220 }
221 thread->setErrno(0);
222 return 0;
223}
224
225int posix_madvise(void* address, size_t length, int advice) {
226 TerminationDeferral lifetime;
227 const uintptr_t base = reinterpret_cast<uintptr_t>(address);
228 const size_t mask = PhysicalMemoryManager::getPageSize() - 1;
229 size_t extent;
230 if ((base & mask) || !roundLength(length, extent) || (advice != Discard && advice != PageOut)) {
231 SYSCALL_ERROR(InvalidArgument);
232 return -1;
233 }
234 if (!extent)
235 return 0;
236 if (!userRange(base, extent)) {
237 SYSCALL_ERROR(OutOfMemory);
238 return -1;
239 }
240 if (advice == PageOut) {
241 const auto status = MemoryMapManager::instance().pageOutRange(base, extent);
242 switch (status) {
243 case SwapStatus::Success:
244 syscallError(0);
245 return 0;
246 case SwapStatus::IoError:
247 SYSCALL_ERROR(IoError);
248 break;
249 case SwapStatus::NoMemory:
250 case SwapStatus::Unmapped:
251 SYSCALL_ERROR(OutOfMemory);
252 break;
253 case SwapStatus::Unsupported:
254 case SwapStatus::NotActive:
255 SYSCALL_ERROR(OperationNotSupported);
256 break;
257 default:
258 SYSCALL_ERROR(InvalidArgument);
259 break;
260 }
261 return -1;
262 }
263 const auto status = MemoryMapManager::instance().discard(base, extent);
264 if (status == MemoryMapManager::VmStatus::Success) {
265 Processor::information().getCurrentThread()->setErrno(0);
266 return 0;
267 }
268 if (status == MemoryMapManager::VmStatus::InvalidRange ||
269 status == MemoryMapManager::VmStatus::Unsupported)
270 SYSCALL_ERROR(InvalidArgument);
271 else
272 SYSCALL_ERROR(OutOfMemory);
273 return -1;
274}
Memory-mapped file interface.
Definition File.h:75
static MemoryMapManager & instance()
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
virtual int64_t getUserId() const
Definition Process.cc:2238
static ProcessorInformation & information()
void setErrno(size_t err)
Definition Thread.h:482
size_t getErrno()
Definition Thread.h:477
Process * getParent() const
Definition Thread.h:340
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)
Definition VFS.cc:1502
A vector / dynamic array.
Definition Vector.h:33