The Pedigree Project  0.1
x64/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/processor/NMFaultHandler.h"
21 #include "pedigree/kernel/Log.h"
22 #include "pedigree/kernel/process/Thread.h"
23 #include "pedigree/kernel/processor/InterruptManager.h"
24 #include "pedigree/kernel/processor/Processor.h"
25 #include "pedigree/kernel/processor/ProcessorInformation.h"
26 #include "pedigree/kernel/processor/state.h"
27 
29 
30 #define NM_FAULT_EXCEPTION 0x07
31 #define CR0_NE (1 << 5)
33 #define CR0_TS (1 << 3)
34 #define CR0_EM (1 << 2)
35 #define CR0_MP (1 << 1)
36 #define CR4_OSFXSR (1 << 9)
37 #define CR4_OSXMMEXCPT (1 << 10)
38 #define CPUID_FEAT_EDX_FXSR (1 << 24)
39 #define CPUID_FEAT_EDX_FPU (1 << 0)
40 #define CPUID_FEAT_EDX_SSE (1 << 25)
41 
42 #define MXCSR_PM (1 << 12)
43 #define MXCSR_UM (1 << 11)
44 #define MXCSR_OM (1 << 10)
45 #define MXCSR_ZM (1 << 9)
46 #define MXCSR_DM (1 << 8)
47 #define MXCSR_IM (1 << 7)
48 
49 #define MXCSR_RC 13
50 
51 #define MXCSR_RC_NEAREST 0
52 #define MXCSR_RC_DOWN 1
53 #define MXCSR_RC_UP 2
54 #define MXCSR_RC_TRUNCATE 3
55 
56 #define MXCSR_MASK 28
57 
58 static bool FXSR_Support, FPU_Support;
59 
60 static inline void _SetFPUControlWord(uint16_t cw)
61 {
62  // FLDCW = Load FPU Control Word
63  asm volatile(" fldcw %0; " ::"m"(cw));
64 }
65 
67 {
68  // Register the handler
69  if (!initialiseProcessor())
70  {
71  return false;
72  }
74  NM_FAULT_EXCEPTION, this);
75  return false;
76 }
77 
79 {
80  // Check for FPU and XSAVE
81  uint32_t eax, ebx, ecx, edx, mxcsr = 0;
82  uint64_t cr0, cr4;
83 
84  asm volatile("mov %%cr0, %0" : "=r"(cr0));
85  asm volatile("mov %%cr4, %0" : "=r"(cr4));
86  Processor::cpuid(1, 0, eax, ebx, ecx, edx);
87 
88  if (edx & CPUID_FEAT_EDX_FPU)
89  {
90  FPU_Support = true;
91 
92  cr0 = (cr0 | CR0_NE | CR0_MP) & ~(CR0_EM | CR0_TS);
93  asm volatile("mov %0, %%cr0" ::"r"(cr0));
94 
95  // init the FPU
96  asm volatile("finit");
97 
98  // set the FPU Control Word
99  _SetFPUControlWord(0x33F);
100 
101  asm volatile("mov %0, %%cr0" ::"r"(cr0));
102  }
103  else
104  FPU_Support = false;
105 
106  if (edx & CPUID_FEAT_EDX_FXSR)
107  {
108  FXSR_Support = true;
109  cr4 |= CR4_OSFXSR; // set the FXSAVE/FXRSTOR support bit
110  }
111  else
112  FXSR_Support = false;
113 
114  if (edx & CPUID_FEAT_EDX_SSE)
115  {
116  cr4 |= CR4_OSXMMEXCPT; // set the SIMD floating-point exception
117  // handling bit
118  asm volatile("mov %0, %%cr4;" ::"r"(cr4));
119 
120  // mask all exceptions
121  mxcsr |=
122  (MXCSR_PM | MXCSR_UM | MXCSR_OM | MXCSR_ZM | MXCSR_DM | MXCSR_IM);
123 
124  // set the rounding method
125  mxcsr |= (MXCSR_RC_TRUNCATE << MXCSR_RC);
126 
127  // write the control word
128  asm volatile("ldmxcsr %0;" ::"m"(mxcsr));
129  }
130  else
131  asm volatile("mov %0, %%cr4;" ::"r"(cr4));
132 
133  // set the bit that causes a DeviceNotAvailable upon SSE, MMX, or FPU
134  // instruction execution
135  cr0 |= CR0_TS;
136  asm volatile("mov %0, %%cr0" ::"r"(cr0));
137 
138  return true;
139 }
140 
141 // old/current owner
142 static X64SchedulerState *x87FPU_MMX_XMM_MXCSR_StateOwner = 0;
143 static X64SchedulerState x87FPU_MMX_XMM_MXCSR_StateBlank;
144 void NMFaultHandler::interrupt(size_t interruptNumber, InterruptState &state)
145 {
146  // Check the TS bit
147  uint64_t cr0;
148 
149  // new owner
150  Thread *pCurrentThread = Processor::information().getCurrentThread();
151  X64SchedulerState *pCurrentState = &pCurrentThread->state();
152 
153  asm volatile("mov %%cr0, %0" : "=r"(cr0));
154  if (cr0 & CR0_TS)
155  {
156  cr0 &= ~CR0_TS;
157  asm volatile("mov %0, %%cr0" ::"r"(cr0));
158  }
159  else
160  {
161  FATAL_NOLOCK("NM: TS already disabled");
162  }
163 
164  // bochs breakpoint
165  // asm volatile("xchg %bx, %bx;");
166 
167  // if this task has never used SSE before, we need to init the state space
168  if (FXSR_Support)
169  {
170  if (!(pCurrentState->flags & (1 << 1)))
171  {
172  if (x87FPU_MMX_XMM_MXCSR_StateOwner)
173  asm volatile("fxrstor (%0);" ::"r"(
174  (((reinterpret_cast<uintptr_t>(
175  x87FPU_MMX_XMM_MXCSR_StateBlank
176  .x87FPU_MMX_XMM_MXCSR_State) +
177  32) &
178  ~15) +
179  16)));
180 
181  asm volatile("fxsave (%0);" ::"r"(
182  (((reinterpret_cast<uintptr_t>(
183  pCurrentState->x87FPU_MMX_XMM_MXCSR_State) +
184  32) &
185  ~15) +
186  16)));
187 
188  pCurrentState->flags |= (1 << 1);
189  }
190 
191  // we don't need to save/restore if this is true!
192  if (x87FPU_MMX_XMM_MXCSR_StateOwner == pCurrentState)
193  return;
194 
195  // if this is NULL, then no thread has ever been here before! :)
196  if (x87FPU_MMX_XMM_MXCSR_StateOwner)
197  {
198  // save the old owner's state
199  asm volatile("fxsave (%0);" ::"r"(
200  (((reinterpret_cast<uintptr_t>(
201  x87FPU_MMX_XMM_MXCSR_StateOwner
202  ->x87FPU_MMX_XMM_MXCSR_State) +
203  32) &
204  ~15) +
205  16)));
206 
207  if (pCurrentState)
208  {
209  // restore the new owner's state
210  asm volatile("fxrstor (%0);" ::"r"(
211  (((reinterpret_cast<uintptr_t>(
212  pCurrentState->x87FPU_MMX_XMM_MXCSR_State) +
213  32) &
214  ~15) +
215  16)));
216  }
217  }
218  else
219  {
220  // if no owner is defined, skip the restoration process as there's
221  // no need
222  asm volatile("fxsave (%0);" ::"r"(
223  (((reinterpret_cast<uintptr_t>(
224  pCurrentState->x87FPU_MMX_XMM_MXCSR_State) +
225  32) &
226  ~15) +
227  16)));
228  asm volatile("fxsave (%0);" ::"r"((
229  ((reinterpret_cast<uintptr_t>(x87FPU_MMX_XMM_MXCSR_StateBlank
230  .x87FPU_MMX_XMM_MXCSR_State) +
231  32) &
232  ~15) +
233  16)));
234  }
235  }
236  else if (FPU_Support)
237  {
238  if (!(pCurrentState->flags & (1 << 1)))
239  {
240  if (x87FPU_MMX_XMM_MXCSR_StateOwner)
241  asm volatile("frstor (%0);" ::"r"(
242  (((reinterpret_cast<uintptr_t>(
243  x87FPU_MMX_XMM_MXCSR_StateBlank
244  .x87FPU_MMX_XMM_MXCSR_State) +
245  32) &
246  ~15) +
247  16)));
248 
249  asm volatile("fsave (%0);" ::"r"(
250  (((reinterpret_cast<uintptr_t>(
251  pCurrentState->x87FPU_MMX_XMM_MXCSR_State) +
252  32) &
253  ~15) +
254  16)));
255 
256  pCurrentState->flags |= (1 << 1);
257  }
258 
259  // we don't need to save/restore if this is true!
260  if (x87FPU_MMX_XMM_MXCSR_StateOwner == pCurrentState)
261  return;
262 
263  // if this is NULL, then no thread has ever been here before! :)
264  if (x87FPU_MMX_XMM_MXCSR_StateOwner)
265  {
266  // save the old owner's state
267  asm volatile("fsave (%0);" ::"r"(
268  (((reinterpret_cast<uintptr_t>(
269  x87FPU_MMX_XMM_MXCSR_StateOwner
270  ->x87FPU_MMX_XMM_MXCSR_State) +
271  32) &
272  ~15) +
273  16)));
274 
275  if (pCurrentState)
276  {
277  // restore the new owner's state
278  asm volatile("frstor (%0);" ::"r"(
279  (((reinterpret_cast<uintptr_t>(
280  pCurrentState->x87FPU_MMX_XMM_MXCSR_State) +
281  32) &
282  ~15) +
283  16)));
284  }
285  }
286  else
287  {
288  // if no owner is defined, skip the restoration process as there's
289  // no need
290  asm volatile("fsave (%0);" ::"r"(
291  (((reinterpret_cast<uintptr_t>(
292  pCurrentState->x87FPU_MMX_XMM_MXCSR_State) +
293  32) &
294  ~15) +
295  16)));
296  asm volatile("fsave (%0);" ::"r"((
297  ((reinterpret_cast<uintptr_t>(x87FPU_MMX_XMM_MXCSR_StateBlank
298  .x87FPU_MMX_XMM_MXCSR_State) +
299  32) &
300  ~15) +
301  16)));
302  }
303  }
304  else
305  {
306  ERROR("FXSAVE and FSAVE are not supported");
307  }
308 
309  // old/current = new
310  x87FPU_MMX_XMM_MXCSR_StateOwner = pCurrentState;
311 }
312 
314 {
315 }
316 
317 void NMFaultHandler::threadTerminated(Thread *pThread)
318 {
319  // Remove owner if the terminated thread is the current owner
320  // There's no way to pick a new owner so we'll do that on the next fault
321  X64SchedulerState *state = &pThread->state();
322  if (x87FPU_MMX_XMM_MXCSR_StateOwner == state)
323  {
324  x87FPU_MMX_XMM_MXCSR_StateOwner = nullptr;
325  }
326 }
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)=0
static void cpuid(uint32_t inEax, uint32_t inEcx, uint32_t &eax, uint32_t &ebx, uint32_t &ecx, uint32_t &edx)
virtual void interrupt(size_t interruptNumber, InterruptState &state)
static ProcessorInformation & information()
Definition: Processor.cc:45
SchedulerState & state()
Definition: Thread.cc:420
static NMFaultHandler m_Instance
Definition: Thread.h:54
void * state
Definition: netif.h:298
#define ERROR(text)
Definition: Log.h:82
static InterruptManager & instance()
NMFaultHandler() INITIALISATION_ONLY