The Pedigree Project 0.1
hosted/PageFaultHandler.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/Log.h"
21#include "pedigree/kernel/Metrics.h"
22#include "pedigree/kernel/Subsystem.h"
23#include "pedigree/kernel/debugger/Debugger.h"
24#include "pedigree/kernel/panic.h"
25#include "pedigree/kernel/process/Process.h"
26#include "pedigree/kernel/process/Scheduler.h"
27#include "pedigree/kernel/process/Thread.h"
28#include "pedigree/kernel/processor/InterruptManager.h"
29#include "pedigree/kernel/processor/PageFaultHandler.h"
30#include "pedigree/kernel/processor/Processor.h"
31#include "pedigree/kernel/processor/state.h"
32
33#include "HostedPlatform.h"
34#include "InterruptManager.h"
35#include "VirtualAddressSpace.h"
36
37namespace __pedigree_hosted {};
38using namespace __pedigree_hosted;
39
40#if HAS_ADDRESS_SANITIZER
41extern "C" void __asan_report_error(void* pc, void* bp, void* sp, void* addr, int is_write,
42 size_t access_size);
43#endif
44
45#include <signal.h>
46#include <ucontext.h>
47
49
52 bool r1 = IntManager.registerInterruptHandler(SIGSEGV, this);
53 bool r2 = IntManager.registerInterruptHandler(SIGBUS, this);
54
55 return r1 && r2;
56}
57
58void PageFaultHandler::interrupt(size_t interruptNumber, InterruptState& state) {
59 const bool collectMetrics = Processor::m_Initialised == 2;
60 if (collectMetrics) {
61 Metrics::increment(Metrics::PageFault);
62 }
63
64 siginfo_t* info = reinterpret_cast<siginfo_t*>(state.getRegister(1));
65
66 uintptr_t page = reinterpret_cast<uintptr_t>(page_align(info->si_addr));
67 uintptr_t unaligned_page = reinterpret_cast<uintptr_t>(info->si_addr);
68 uintptr_t code = info->si_code;
69 uintptr_t ucontext_loc = state.getRegister(2);
70 ucontext_t* ctx = reinterpret_cast<ucontext_t*>(ucontext_loc);
71
72 state.setInstructionPointer(HostedPlatform::instructionPointer(ctx));
73 state.setStackPointer(HostedPlatform::stackPointer(ctx));
74 state.setBasePointer(HostedPlatform::basePointer(ctx));
75
76 bool isWrite = false;
77 uintptr_t errorCode = code;
78 bool wasPresent = code == SEGV_ACCERR;
79#if defined(__APPLE__) && defined(__aarch64__)
80 isWrite = (ctx->uc_mcontext->__es.__esr & (1U << 6U)) != 0;
81 errorCode = (wasPresent ? 0x1 : 0) | (isWrite ? 0x2 : 0);
82#elif defined(__APPLE__) && defined(__x86_64__)
83 errorCode = ctx->uc_mcontext->__es.__err;
84 isWrite = (errorCode & 0x2) != 0;
85 wasPresent = (errorCode & 0x1) != 0;
86#elif defined(REG_ERR)
87 // SIGSEGV's si_code only distinguishes missing pages from protection
88 // faults. The processor error code retains the read/write distinction.
89 isWrite = (ctx->uc_mcontext.gregs[REG_ERR] & 0x2) != 0;
90 errorCode = ctx->uc_mcontext.gregs[REG_ERR];
91 wasPresent = (errorCode & 0x1) != 0;
92#endif
93
94 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
95 if (wasPresent && isWrite &&
96 va.handleCopyOnWriteFault(reinterpret_cast<void*>(page), !state.kernelMode())) {
97 if (collectMetrics) {
98 Metrics::increment(Metrics::PageFaultCopyOnWrite);
99 }
100 return;
101 }
102
103 if (page < reinterpret_cast<uintptr_t>(KERNEL_SPACE_START)) {
104 if (dispatchHandlers(state, page, isWrite, wasPresent)) {
105 if (collectMetrics) {
106 Metrics::increment(Metrics::PageFaultHandled);
107 }
108 return;
109 }
110 }
111
112 Thread* pThread = Processor::information().getCurrentThread();
113 if (pThread && !state.kernelMode()) {
114 Process* process = pThread->getParent();
115 if (process && process->getSubsystem()) {
116 pThread->deferSubsystemException(static_cast<size_t>(Subsystem::PageFault), unaligned_page,
117 errorCode);
118 if (collectMetrics) {
119 Metrics::increment(Metrics::PageFaultDeferred);
120 }
121 return;
122 }
123 }
124
125#if HAS_ADDRESS_SANITIZER
126 // Escalate to the original signal handler - this is a real error, and in
127 // asan we get asan-based analysis in the asan segv handler.
128 struct sigaction oact = static_cast<HostedInterruptManager&>(InterruptManager::instance())
129 .getOriginalSigaction(info->si_signo);
130 if (oact.sa_handler == SIG_IGN) {
131 return;
132 } else if (oact.sa_handler == SIG_DFL) {
133 sigaction(info->si_signo, &oact, nullptr);
134 raise(info->si_signo);
135 } else if (oact.sa_flags & SA_SIGINFO) {
136 oact.sa_sigaction(info->si_signo, info, ctx);
137 } else {
138 oact.sa_handler(info->si_signo);
139 }
140
141 return;
142#endif
143
144 // Get PFE location and error code
145 static LargeStaticString sError;
146 sError.clear();
147 sError.append("Page Fault Exception at 0x");
148 sError.append(unaligned_page, 16, 8, '0');
149 sError.append(", error code 0x");
150 sError.append(code, 16, 8, '0');
151 sError.append(", EIP 0x");
152 sError.append(state.getInstructionPointer(), 16, 8, '0');
153
154 // Extract error code information
155 static LargeStaticString sCode;
156 sCode.clear();
157 sCode.append("Details: PID=");
158 sCode.append(Processor::information().getCurrentThread()->getParent()->getId());
159 sCode.append(" ");
160
161 if (code == SEGV_MAPERR)
162 sCode.append("NOT ");
163 sCode.append("PRESENT | ");
164 if (code == SEGV_ACCERR)
165 sCode.append("ACCESS ERROR");
166
167 ERROR(static_cast<const char*>(sError));
168 ERROR(static_cast<const char*>(sCode));
169
170#if DEBUGGER
171 if (state.kernelMode())
172 Debugger::instance().start(state, sError);
173#endif
174
175 Scheduler& scheduler = Scheduler::instance();
176 if (UNLIKELY(scheduler.getNumProcesses() == 0)) {
177 // We are in the early stages of the boot process (no processes
178 // started)
179 panic(sError);
180 } else {
181 // Unrecoverable PFE in a process - Kill the process and yield
182 // Processor::information().getCurrentThread()->getParent()->kill();
183 pThread = Processor::information().getCurrentThread();
184 Process* pProcess = pThread->getParent();
185 Subsystem* pSubsystem = pProcess->getSubsystem();
186 if (!pSubsystem || state.kernelMode()) {
187 pProcess->kill();
188
189 // kill member function also calls yield(), so shouldn't get here.
190 for (;;)
191 ;
192 }
193 }
194}
void start(InterruptState &state, LargeStaticString &description)
Definition Debugger.cc:118
static Debugger & instance()
Definition Debugger.h:47
Handles interrupts and interrupt registrations from kernel components.
static EXPORTED_PUBLIC InterruptManager & instance()
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)=0
static EXPORTED_PUBLIC PageFaultHandler m_Instance
virtual void interrupt(size_t interruptNumber, InterruptState &state)
void kill() NORETURN
Definition Process.cc:2019
static ProcessorInformation & information()
static size_t m_Initialised
Definition Processor.h:483
This class manages how processes and threads are scheduled across processors.
Definition Scheduler.h:50
static Scheduler & instance()
Definition Scheduler.h:96
size_t getNumProcesses()
Definition Scheduler.cc:268
bool deferSubsystemException(size_t type, uintptr_t faultAddress, uintptr_t errorCode)
Definition Thread.cc:3627
Process * getParent() const
Definition Thread.h:340
virtual bool handleCopyOnWriteFault(void *virtualAddress, bool userMode)=0
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
EXPORTED_PUBLIC void * page_align(void *p) PURE
Definition utility.cc:29