The Pedigree Project 0.1
NMFaultHandler.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/Spinlock.h"
22#include "pedigree/kernel/process/Thread.h"
23#include "pedigree/kernel/processor/InterruptManager.h"
24#include "pedigree/kernel/processor/NMFaultHandler.h"
25#include "pedigree/kernel/processor/Processor.h"
26#include "pedigree/kernel/processor/ProcessorInformation.h"
27#include "pedigree/kernel/processor/state.h"
28#include "pedigree/kernel/utilities/lib.h"
29
31
32#define NM_FAULT_EXCEPTION 0x07
34#define CR0_NE (1 << 5)
35#define CR0_TS (1 << 3)
36#define CR0_EM (1 << 2)
37#define CR0_MP (1 << 1)
38#define CR4_OSFXSR (1 << 9)
39#define CR4_OSXMMEXCPT (1 << 10)
40#define CPUID_FEAT_EDX_FXSR (1 << 24)
41#define CPUID_FEAT_EDX_FPU (1 << 0)
42#define CPUID_FEAT_EDX_SSE (1 << 25)
43
44#define MXCSR_PM (1 << 12)
45#define MXCSR_UM (1 << 11)
46#define MXCSR_OM (1 << 10)
47#define MXCSR_ZM (1 << 9)
48#define MXCSR_DM (1 << 8)
49#define MXCSR_IM (1 << 7)
50
51#define MXCSR_RC 13
52
53#define MXCSR_RC_NEAREST 0
54#define MXCSR_RC_DOWN 1
55#define MXCSR_RC_UP 2
56#define MXCSR_RC_TRUNCATE 3
57
58#define MXCSR_MASK 28
59
60static bool FXSR_Support, FPU_Support;
61static X64SchedulerState x87FPU_MMX_XMM_MXCSR_StateBlank;
62static bool g_InitialFxStateValid = false;
63static Spinlock g_InitialFxStateLock(false, true);
64
65static_assert(__builtin_offsetof(X64SchedulerState, x87FPU_MMX_XMM_MXCSR_State) == 112,
66 "x64 Scheduler.s FXSAVE offset is stale");
67
68static void* fpuStateBuffer(X64SchedulerState& state) {
69 uintptr_t address = reinterpret_cast<uintptr_t>(state.x87FPU_MMX_XMM_MXCSR_State);
70 return reinterpret_cast<void*>((address + 15) & ~uintptr_t(15));
71}
72
73static const void* fpuStateBuffer(const X64SchedulerState& state) {
74 uintptr_t address = reinterpret_cast<uintptr_t>(state.x87FPU_MMX_XMM_MXCSR_State);
75 return reinterpret_cast<const void*>((address + 15) & ~uintptr_t(15));
76}
77
78static inline void _SetFPUControlWord(uint16_t cw) {
79 // FLDCW = Load FPU Control Word
80 asm volatile(" fldcw %0; " ::"m"(cw));
81}
82
84 // Register the handler
85 if (!initialiseProcessor()) {
86 return false;
87 }
88 InterruptManager::instance().registerInterruptHandler(NM_FAULT_EXCEPTION, this);
89 return false;
90}
91
93 // Check for FPU and XSAVE
94 uint32_t eax, ebx, ecx, edx, mxcsr = 0;
95 uint64_t cr0, cr4;
96
97 asm volatile("mov %%cr0, %0" : "=r"(cr0));
98 asm volatile("mov %%cr4, %0" : "=r"(cr4));
99 Processor::cpuid(1, 0, eax, ebx, ecx, edx);
100
101 if (edx & CPUID_FEAT_EDX_FPU) {
102 FPU_Support = true;
103
104 cr0 = (cr0 | CR0_NE | CR0_MP) & ~(CR0_EM | CR0_TS);
105 asm volatile("mov %0, %%cr0" ::"r"(cr0));
106
107 // init the FPU
108 asm volatile("finit");
109
110 // set the FPU Control Word
111 _SetFPUControlWord(0x37F);
112
113 asm volatile("mov %0, %%cr0" ::"r"(cr0));
114 } else
115 FPU_Support = false;
116
117 if (edx & CPUID_FEAT_EDX_FXSR) {
118 FXSR_Support = true;
119 cr4 |= CR4_OSFXSR; // set the FXSAVE/FXRSTOR support bit
120 } else
121 FXSR_Support = false;
122
123 if (edx & CPUID_FEAT_EDX_SSE) {
124 cr4 |= CR4_OSXMMEXCPT; // set the SIMD floating-point exception
125 // handling bit
126 asm volatile("mov %0, %%cr4;" ::"r"(cr4));
127
128 // Match the AMD64 userspace reset state: round to nearest and mask
129 // all exceptions.
130 mxcsr = 0x1F80;
131
132 // write the control word
133 asm volatile("ldmxcsr %0;" ::"m"(mxcsr));
134 } else
135 asm volatile("mov %0, %%cr4;" ::"r"(cr4));
136
137 if (FXSR_Support) {
138 while (!g_InitialFxStateLock.acquire(false, false))
139 ;
140 if (!g_InitialFxStateValid) {
141 void* blankState = fpuStateBuffer(x87FPU_MMX_XMM_MXCSR_StateBlank);
142 asm volatile("fxsave64 (%0)" ::"r"(blankState) : "memory");
143 x87FPU_MMX_XMM_MXCSR_StateBlank.flags |= (1 << 1);
144 g_InitialFxStateValid = true;
145 }
146 g_InitialFxStateLock.release();
147 }
148
149 // set the bit that causes a DeviceNotAvailable upon SSE, MMX, or FPU
150 // instruction execution
151 cr0 |= CR0_TS;
152 asm volatile("mov %0, %%cr0" ::"r"(cr0));
153
154 return true;
155}
156
157bool NMFaultHandler::saveCurrentThreadFpuState(void* buffer, bool resetForSignalHandler) {
158 if (!FXSR_Support || !g_InitialFxStateValid || !buffer ||
159 (reinterpret_cast<uintptr_t>(buffer) & 0xF)) {
160 return false;
161 }
162
163 Thread* thread = Processor::information().getCurrentThread();
164 if (!thread) {
165 return false;
166 }
167
168 bool interrupts = Processor::getInterrupts();
170
171 X64SchedulerState& state = thread->state();
172 void* threadState = fpuStateBuffer(state);
173 const void* blankState = fpuStateBuffer(x87FPU_MMX_XMM_MXCSR_StateBlank);
174
175 uint64_t cr0;
176 asm volatile("mov %%cr0, %0" : "=r"(cr0));
177 if (cr0 & CR0_TS) {
178 if (state.flags & (1 << 1)) {
179 MemoryCopy(buffer, threadState, 512);
180 } else {
181 MemoryCopy(buffer, blankState, 512);
182 }
183 } else {
184 asm volatile("fxsave64 (%0)" ::"r"(buffer) : "memory");
185 MemoryCopy(threadState, buffer, 512);
186 state.flags |= (1 << 1);
187 }
188
189 if (resetForSignalHandler) {
190 MemoryCopy(threadState, blankState, 512);
191 state.flags |= (1 << 1);
192 if (!(cr0 & CR0_TS)) {
193 asm volatile("fxrstor64 (%0)" ::"r"(blankState) : "memory");
194 }
195 }
196
197 Processor::setInterrupts(interrupts);
198 return true;
199}
200
202 if (!FXSR_Support || !g_InitialFxStateValid || !buffer ||
203 (reinterpret_cast<uintptr_t>(buffer) & 0xF)) {
204 return false;
205 }
206
207 uint32_t mxcsr = 0;
208 uint32_t mxcsrMask = 0;
209 MemoryCopy(&mxcsr, adjust_pointer(buffer, 24), sizeof(mxcsr));
210 MemoryCopy(&mxcsrMask, adjust_pointer(fpuStateBuffer(x87FPU_MMX_XMM_MXCSR_StateBlank), 28),
211 sizeof(mxcsrMask));
212 if (!mxcsrMask) {
213 mxcsrMask = 0xFFBF;
214 }
215 if (mxcsr & ~mxcsrMask) {
216 return false;
217 }
218
219 Thread* thread = Processor::information().getCurrentThread();
220 if (!thread) {
221 return false;
222 }
223
224 bool interrupts = Processor::getInterrupts();
226
227 X64SchedulerState& state = thread->state();
228 MemoryCopy(fpuStateBuffer(state), buffer, 512);
229 state.flags |= (1 << 1);
230
231 uint64_t cr0 = 0;
232 asm volatile("mov %%cr0, %0" : "=r"(cr0));
233 if (!(cr0 & CR0_TS)) {
234 asm volatile("fxrstor64 (%0)" ::"r"(buffer) : "memory");
235 }
236
237 Processor::setInterrupts(interrupts);
238 return true;
239}
240
242 if (!thread) {
243 return false;
244 }
245
246 alignas(16) uint8_t inherited[512];
247 if (!saveCurrentThreadFpuState(inherited, false)) {
248 return false;
249 }
250
251 X64SchedulerState& state = thread->state();
252 MemoryCopy(fpuStateBuffer(state), inherited, sizeof(inherited));
253 state.flags |= (1 << 1);
254 return true;
255}
256
257void NMFaultHandler::interrupt(size_t interruptNumber, InterruptState& state) {
258 // Check the TS bit
259 uint64_t cr0;
260
261 Thread* pCurrentThread = Processor::information().getCurrentThread();
262 if (!pCurrentThread) {
263 FATAL_NOLOCK("NM: no current thread");
264 }
265 X64SchedulerState* pCurrentState = &pCurrentThread->state();
266
267 asm volatile("mov %%cr0, %0" : "=r"(cr0));
268 if (cr0 & CR0_TS) {
269 cr0 &= ~CR0_TS;
270 asm volatile("mov %0, %%cr0" ::"r"(cr0));
271 } else {
272 FATAL_NOLOCK("NM: TS already disabled");
273 }
274
275 // bochs breakpoint
276 // asm volatile("xchg %bx, %bx;");
277
278 // if this task has never used SSE before, we need to init the state space
279 if (FXSR_Support) {
280 if (!(pCurrentState->flags & (1 << 1))) {
281 MemoryCopy(fpuStateBuffer(*pCurrentState), fpuStateBuffer(x87FPU_MMX_XMM_MXCSR_StateBlank),
282 512);
283 pCurrentState->flags |= (1 << 1);
284 }
285
286 asm volatile("fxrstor64 (%0)" ::"r"(fpuStateBuffer(*pCurrentState)) : "memory");
287 } else if (FPU_Support) {
288 if (!(pCurrentState->flags & (1 << 1))) {
289 MemoryCopy(fpuStateBuffer(*pCurrentState), fpuStateBuffer(x87FPU_MMX_XMM_MXCSR_StateBlank),
290 108);
291
292 pCurrentState->flags |= (1 << 1);
293 }
294
295 asm volatile("frstor (%0)" ::"r"(fpuStateBuffer(*pCurrentState)) : "memory");
296 } else {
297 ERROR("FXSAVE and FSAVE are not supported");
298 }
299}
300
302
303void NMFaultHandler::threadTerminated(Thread* pThread) {
304 // FPU images are saved into scheduler state at every context switch, so
305 // the fault handler keeps no pointers into thread-owned storage.
306 (void)pThread;
307}
static EXPORTED_PUBLIC InterruptManager & instance()
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)=0
virtual void interrupt(size_t interruptNumber, InterruptState &state)
NMFaultHandler() INITIALISATION_ONLY
static EXPORTED_PUBLIC bool saveCurrentThreadFpuState(void *buffer, bool resetForSignalHandler)
static NMFaultHandler m_Instance
static bool inheritCurrentThreadFpuState(class Thread *thread)
static EXPORTED_PUBLIC bool restoreCurrentThreadFpuState(const void *buffer)
bool initialiseProcessor()
static bool getInterrupts()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
void release()
Definition Spinlock.cc:161
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:35
SchedulerState & state()
Definition Thread.cc:875
static void cpuid(uint32_t inEax, uint32_t inEcx, uint32_t &eax, uint32_t &ebx, uint32_t &ecx, uint32_t &edx)