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