The Pedigree Project 0.1
SyscallManager.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/LockGuard.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/process/Scheduler.h"
23#include "pedigree/kernel/process/TerminationDeferral.h"
24#include "pedigree/kernel/process/Thread.h"
25#include "pedigree/kernel/processor/Processor.h"
26#include "pedigree/kernel/processor/ProcessorInformation.h"
27#include "pedigree/kernel/processor/SyscallHandler.h"
28#include "pedigree/kernel/processor/SyscallManager.h"
29#include "pedigree/kernel/utilities/assert.h"
30#include "pedigree/kernel/utilities/new"
31
33 HandlerSlot(SyscallHandler* handler, FastEntry entry) : handler(handler), entry(entry) {}
34
35 SyscallHandler* const handler;
36 const FastEntry entry;
37};
38
39SyscallManager::HandlerSlot SyscallManager::m_ClosingSlot(nullptr, nullptr);
40
41SyscallManager::Registration::Registration()
42 : m_pManager(nullptr), m_Service(serviceEnd), m_pSlot(nullptr) {}
43
44SyscallManager::Registration::Registration(Registration&& other)
45 : m_pManager(other.m_pManager), m_Service(other.m_Service), m_pSlot(other.m_pSlot) {
46 other.m_pManager = nullptr;
47 other.m_Service = serviceEnd;
48 other.m_pSlot = nullptr;
49}
50
51SyscallManager::Registration::~Registration() {
52 if (m_pManager && !reset()) {
53 FATAL("Live syscall registration could not be retired.");
54 }
55}
56
57SyscallManager::Registration& SyscallManager::Registration::operator=(Registration&& other) {
58 if (this != &other) {
59 if (m_pManager && !reset()) {
60 FATAL("Syscall registration move could not retire ownership.");
61 }
62 m_pManager = other.m_pManager;
63 m_Service = other.m_Service;
64 m_pSlot = other.m_pSlot;
65 other.m_pManager = nullptr;
66 other.m_Service = serviceEnd;
67 other.m_pSlot = nullptr;
68 }
69 return *this;
70}
71
73 return !m_pManager || m_pManager->closeHandler(*this);
74}
75
76bool SyscallManager::Registration::reset() {
77 if (!m_pManager) {
78 return true;
79 }
80 if (!m_pManager->unregisterHandler(*this)) {
81 return false;
82 }
83
84 m_pManager = nullptr;
85 m_Service = serviceEnd;
86 m_pSlot = nullptr;
87 return true;
88}
89
91 : m_HandlerLock(),
92 m_HandlerSlots(),
93 m_Published()
94#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
95 ,
96 m_HandlerPinHook(nullptr),
97 m_PostSyscallHook(nullptr)
98#endif
99{
100}
101
103
104uintptr_t SyscallManager::dispatchVirtual(SyscallHandler* handler, SyscallState& state) {
105 return handler->syscall(state);
106}
107
108bool SyscallManager::inCallback() {
109 Thread* thread = Processor::information().getCurrentThread();
110 return thread && thread->getSyscallDispatchContext();
111}
112
113bool SyscallManager::registerHandler(Service_t service, SyscallHandler* handler,
114 Registration& registration, FastEntry entry) {
115 if (UNLIKELY(service >= serviceEnd) || !handler || registration) {
116 return false;
117 }
118
119 auto* slot = new HandlerSlot(handler, entry ? entry : dispatchVirtual);
120 if (!slot) {
121 return false;
122 }
123 m_HandlerLock.acquire();
124 if (m_HandlerSlots[service]) {
125 m_HandlerLock.release();
126 delete slot;
127 return false;
128 }
129
130 m_HandlerSlots[service] = slot;
131 registration.m_pManager = this;
132 registration.m_Service = service;
133 registration.m_pSlot = slot;
134 __atomic_store_n(&m_Published[service], slot, __ATOMIC_SEQ_CST);
135 m_HandlerLock.release();
136 return true;
137}
138
139bool SyscallManager::closeHandler(Registration& registration) {
140 if (registration.m_pManager != this || UNLIKELY(registration.m_Service >= serviceEnd) ||
141 !registration.m_pSlot || inCallback()) {
142 return false;
143 }
144
145 LockGuard<Spinlock> guard(m_HandlerLock);
146 if (m_HandlerSlots[registration.m_Service] != registration.m_pSlot) {
147 return false;
148 }
149 __atomic_store_n(&m_Published[registration.m_Service], nullptr, __ATOMIC_SEQ_CST);
150 return true;
151}
152
153#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
154bool SyscallManager::dispatchHandlerForTest(Service_t service, uintptr_t& result) {
155 PostSyscallAction action;
156 HandlerLease handler;
157 if (!acquireHandler(service, handler, action)) {
158 return false;
159 }
160
161 SyscallState state = {};
162 result = dispatchHandler(handler, state);
163 return action.kind == NoPostSyscallAction;
164}
165#endif
166
167bool SyscallManager::unregisterHandler(Registration& registration) {
168 if (registration.m_pManager != this || UNLIKELY(registration.m_Service >= serviceEnd) ||
169 !registration.m_pSlot) {
170 return false;
171 }
172
173 const bool canYield = !inCallback() && Processor::information().getCurrentThread() &&
174 Processor::executionContext() == ExecutionContext::WaitableThread;
175 TerminationDeferral lifetime;
176 HandlerSlot* slot = registration.m_pSlot;
177 const Service_t service = registration.m_Service;
178 Scheduler& scheduler = Scheduler::instance();
179 if (!canYield) {
180 // Take membership protection before publishing Closing: a reader may
181 // already hold this lock, and must never spin while its remover needs it.
183 LockGuard<Spinlock> guard(m_HandlerLock);
184 if (m_HandlerSlots[service] != slot) {
185 return false;
186 }
187 HandlerSlot* published =
188 __atomic_exchange_n(&m_Published[service], &m_ClosingSlot, __ATOMIC_SEQ_CST);
189 if (scheduler.hasActiveSyscallLocked(service)) {
190 __atomic_store_n(&m_Published[service], published, __ATOMIC_SEQ_CST);
191 return false;
192 }
193 __atomic_store_n(&m_Published[service], nullptr, __ATOMIC_SEQ_CST);
194 m_HandlerSlots[service] = nullptr;
195 } else {
196 {
197 LockGuard<Spinlock> guard(m_HandlerLock);
198 if (m_HandlerSlots[service] != slot) {
199 return false;
200 }
201 __atomic_store_n(&m_Published[service], nullptr, __ATOMIC_SEQ_CST);
202 }
203
204 // Closed publication prevents new callbacks. Scalar inspection under the
205 // scheduler lock covers running and blocked Threads without borrowing stacks.
206 while (true) {
207 bool active;
208 {
210 active = scheduler.hasActiveSyscallLocked(service);
211 }
212 if (!active) {
213 break;
214 }
215 scheduler.yield();
216 }
217 LockGuard<Spinlock> guard(m_HandlerLock);
218 m_HandlerSlots[service] = nullptr;
219 }
220 delete slot;
221 return true;
222}
223
224bool SyscallManager::acquireHandler(Service_t service, HandlerLease& lease,
225 PostSyscallAction& action) {
226 Thread* thread = Processor::information().getCurrentThread();
227 if (UNLIKELY(service >= serviceEnd) || lease.m_pThread || !thread) {
228 return false;
229 }
230
231 const bool interrupts = Processor::getInterrupts();
233 size_t& admission = thread->m_ActiveSyscalls[service];
234 const size_t count = __atomic_load_n(&admission, __ATOMIC_RELAXED);
235 if (count == ~size_t(0)) {
236 FATAL("Syscall admission nesting overflow.");
237 }
238 // Publish admission before reading the handler. The SC order with
239 // unpublication and the writer's scan either includes us in the drain or
240 // makes us observe the closed slot, without first borrowing its pointer.
241 __atomic_store_n(&admission, count + 1, __ATOMIC_SEQ_CST);
242 HandlerSlot* slot;
243 do {
244 slot = __atomic_load_n(&m_Published[service], __ATOMIC_SEQ_CST);
245 if (slot == &m_ClosingSlot) {
247 }
248 } while (slot == &m_ClosingSlot);
249 if (!slot) {
250 __atomic_store_n(&admission, count, __ATOMIC_RELEASE);
251 Processor::setInterrupts(interrupts);
252 return false;
253 }
254
255 lease.m_Service = service;
256 lease.m_pHandler = slot->handler;
257 lease.m_Entry = slot->entry;
258 lease.m_pThread = thread;
259 lease.m_Previous = static_cast<HandlerLease*>(thread->getSyscallDispatchContext());
260 lease.m_Action = &action;
261 thread->registerFreshTerminationDeferral(lease.m_Cleanup, abandonedHandlerCleanup, &lease);
262 thread->setSyscallDispatchContext(&lease);
263 Processor::setInterrupts(interrupts);
264
265#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
266 HandlerPinHook hook = __atomic_load_n(&m_HandlerPinHook, __ATOMIC_ACQUIRE);
267 if (hook) {
268 hook(service, lease.m_pHandler);
269 }
270#endif
271 return true;
272}
273
274void SyscallManager::releaseHandler(HandlerLease& lease, bool normalReturn) {
275 const bool interrupts = Processor::getInterrupts();
277 Thread* thread = lease.m_pThread;
278 if (normalReturn) {
279 if (thread != Processor::information().getCurrentThread()) {
280 FATAL("Syscall lease released by a different Thread.");
281 }
282 thread->unregisterDeferredScope(lease.m_Cleanup);
283 }
284 auto* current = static_cast<HandlerLease*>(thread->getSyscallDispatchContext());
285 if (current == &lease) {
286 thread->setSyscallDispatchContext(lease.m_Previous);
287 } else {
288 // Nested exec can retire a lower state's lease while its own dispatch
289 // remains active. Remove that ancestor before its action storage vanishes.
290 while (current && current->m_Previous != &lease) {
291 current = current->m_Previous;
292 }
293 if (!current) {
294 FATAL("Syscall lease was absent from its Thread's dispatch chain.");
295 }
296 current->m_Previous = lease.m_Previous;
297 }
298 size_t& admission = thread->m_ActiveSyscalls[lease.m_Service];
299 const size_t count = __atomic_load_n(&admission, __ATOMIC_RELAXED);
300 if (!count) {
301 FATAL("Syscall admission nesting underflow.");
302 }
303 lease.m_pThread = nullptr;
304 __atomic_store_n(&admission, count - 1, __ATOMIC_RELEASE);
305 Processor::setInterrupts(interrupts);
306}
307
308void SyscallManager::abandonedHandlerCleanup(void* context) {
309 HandlerLease* lease = reinterpret_cast<HandlerLease*>(context);
310 if (lease && lease->m_pThread) {
311 releaseHandler(*lease, false);
312 }
313}
314
315bool SyscallManager::requestPostSyscallAction(PostSyscallActionKind kind, intptr_t value,
316 const ProcessorState* state) {
317 Thread* thread = Processor::information().getCurrentThread();
318 auto* lease = thread ? static_cast<HandlerLease*>(thread->getSyscallDispatchContext()) : nullptr;
319 PostSyscallAction* action = lease ? lease->m_Action : nullptr;
320 if (!action || action->kind != NoPostSyscallAction) {
321 return false;
322 }
323 if ((kind == ReturnFromEvent || kind == PopEventState) && !thread->getStateLevel()) {
324 return false;
325 }
326
327 if (state) {
328 new (&action->state) ProcessorState(*state);
329 }
330 action->value = value;
331 action->kind = kind;
332 return true;
333}
334
336 return requestPostSyscallAction(TerminateCurrentThread, 0);
337}
338
339bool SyscallManager::requestProcessExit(int status) {
340 return requestPostSyscallAction(ExitCurrentProcess, static_cast<intptr_t>(status));
341}
342
343bool SyscallManager::requestEventReturn() {
344 return requestPostSyscallAction(ReturnFromEvent, 0);
345}
346
347bool SyscallManager::requestEventStatePop() {
348 return requestPostSyscallAction(PopEventState, 0);
349}
350
351bool SyscallManager::requestStateRestore(const ProcessorState& state) {
352 return requestPostSyscallAction(RestoreProcessorState, 0, &state);
353}
354
355bool SyscallManager::requestUserJump(uintptr_t instructionPointer, uintptr_t stackPointer) {
356 ProcessorState state;
357 state.setInstructionPointer(instructionPointer);
358 state.setStackPointer(stackPointer);
359 return requestPostSyscallAction(JumpToUserspace, 0, &state);
360}
361
362bool SyscallManager::requestReboot(Machine::ShutdownType type) {
363 return requestPostSyscallAction(RebootSystem, static_cast<intptr_t>(type));
364}
365
366#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
367void SyscallManager::setHandlerPinHook(HandlerPinHook hook) {
368 __atomic_store_n(&m_HandlerPinHook, hook, __ATOMIC_RELEASE);
369}
370
371void SyscallManager::setPostSyscallHook(PostSyscallHook hook) {
372 __atomic_store_n(&m_PostSyscallHook, hook, __ATOMIC_RELEASE);
373}
374
375bool SyscallManager::postSyscallHookHandled(const PostSyscallAction& action) {
376 PostSyscallHook hook = __atomic_load_n(&m_PostSyscallHook, __ATOMIC_ACQUIRE);
377 const ProcessorState* state =
378 action.kind == RestoreProcessorState || action.kind == JumpToUserspace ? &action.state
379 : nullptr;
380 return hook && hook(action.kind, action.value, state);
381}
382#endif
static bool getInterrupts()
static ProcessorInformation & information()
static void pause()
static ExecutionContext executionContext()
Definition Processor.cc:109
static void setInterrupts(bool bEnable)
This class manages how processes and threads are scheduled across processors.
Definition Scheduler.h:50
bool hasActiveSyscallLocked(Service_t service) const
Definition Scheduler.cc:169
Spinlock m_SchedulerLock
Definition Scheduler.h:268
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:236
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
virtual uintptr_t syscall(SyscallState &State)=0
virtual ~SyscallManager()
EXPORTED_PUBLIC bool requestThreadExit()
size_t m_ActiveSyscalls[serviceEnd]
Definition Thread.h:1307
size_t getStateLevel() const
Definition Thread.h:316