AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
fpu.c
Go to the documentation of this file.
1
11
12#include <core/system.h>
13#include <core/panic.h>
14#include <core/assert.h>
15#include <core/arch.h>
16#include <core/string.h>
17#include <cpu/cpu.h>
18#include <sys/proc.h>
19#include <sys/sched.h>
20#include <sys/signal.h>
21
22MALLOC_DEFINE(M_X86_FPU, "x86-fpu", "x86 FPU context");
23
24#define CPUID_EDX_FXSR _BV(24)
25#define CPUID_EDX_SSE _BV(25)
26
27/* fxsave and fxrstor need a 16-byte aligned area */
28static char fpu_context[512] __aligned(16);
29static int fpu_fxsr = 0;
30
31/* the thread whose state the running CPU's FPU holds */
32#define last_fpu_thread (curcpu()->arch.fpu_thread)
33
34/* The MXCSR bits the CPU accepts (fxsave's MXCSR_MASK, or 0xFFBF if it
35 * says 0). An MXCSR from a signal frame is masked with it, because
36 * fxrstor would fault in the kernel on a reserved bit. */
37static uint32_t mxcsr_mask = 0xFFBF;
38
39/* The state a thread starts with: fninit's, with MXCSR's default value
40 * and the registers zeroed, so that it never sees the last owner's XMM
41 * registers. */
42static char fpu_clean[512] __aligned(16);
43
50void x86_fpu_setup(void)
51{
52 uint32_t eax = 1, ebx, ecx, edx;
53 asm volatile("cpuid":"+a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx));
54
55 write_cr0((read_cr0() & ~CR0_EM) | CR0_MP | CR0_NE | CR0_TS);
56
57 if (edx & CPUID_EDX_FXSR) {
58 fpu_fxsr = 1;
60
61 if (edx & CPUID_EDX_SSE)
62 cr4 |= CR4_OSXMMEXCPT;
63
64 write_cr4(cr4);
65
66 /* record a clean state, and the MXCSR mask, from the FPU */
68 asm volatile("fninit");
69 asm volatile("fxsave (%0)"::"r"(fpu_clean):"memory");
71
72 memset(fpu_clean + 32, 0, sizeof(fpu_clean) - 32); /* the data registers */
73 *(uint32_t *) (fpu_clean + 24) = 0x1F80; /* MXCSR */
74
75 uint32_t mask = *(uint32_t *) (fpu_clean + 28);
76 if (mask)
77 mxcsr_mask = mask;
78 }
79}
80
83{
84 asm volatile("clts");
85}
86
89{
90 write_cr0(read_cr0() | CR0_TS);
91}
92
95void x86_fpu_init(void)
96{
98
99 if (fpu_fxsr) {
101 asm volatile("fxrstor (%0)"::"r"(fpu_context):"memory");
102 } else {
103 asm volatile("fninit");
104 }
105}
106
108static inline void fpu_save(void)
109{
110 if (fpu_fxsr)
111 asm volatile("fxsave (%0)"::"r"(fpu_context):"memory");
112 else
113 asm volatile("fnsave (%0)"::"r"(fpu_context):"memory");
114}
115
117static inline void fpu_restore(void)
118{
119 if (fpu_fxsr)
120 asm volatile("fxrstor (%0)"::"r"(fpu_context):"memory");
121 else
122 asm volatile("frstor (%0)"::"r"(fpu_context):"memory");
123}
124
131void x86_fpu_trap(void)
132{
133 if (!curthread)
134 panic("FPU trap without a current thread");
135
136 struct x86_thread *arch = curthread->arch;
137
138 /* Allocate the thread's save area on its first use of the FPU, so
139 * that saving it when switching away later can never fail. */
140 if (!arch->fpu_context) {
141 arch->fpu_context = kmalloc(512, &M_X86_FPU, 0);
142
143 if (!arch->fpu_context) {
144 /* it can't have the FPU: the process is killed */
145 signal_proc_send(curproc, SIGKILL);
146 return;
147 }
148 }
149
151
152 /* the FPU has this thread's state already */
154 return;
155
156 if (last_fpu_thread) { /* save the previous owner's state */
157 struct x86_thread *_arch = last_fpu_thread->arch;
158 fpu_save();
159 memcpy(_arch->fpu_context, fpu_context, 512);
160 }
161
162 if (arch->fpu_enabled) { /* load this thread's state */
163 memcpy(fpu_context, arch->fpu_context, 512);
164 fpu_restore();
165 } else { /* its first use: a clean state */
166 x86_fpu_init();
167 arch->fpu_enabled = 1;
168 }
169
171}
172
179{
181 return;
182
183 struct x86_thread *arch = curthread->arch;
184
186 fpu_save();
187 memcpy(arch->fpu_context, fpu_context, 512);
189}
190
198int x86_fpu_save_to(void *buf)
199{
200 struct x86_thread *arch = curthread->arch;
201
202 if (!arch->fpu_enabled)
203 return 0;
204
205 if (last_fpu_thread == curthread) {
207 fpu_save();
208 memcpy(buf, fpu_context, 512);
209 } else {
210 memcpy(buf, arch->fpu_context, 512);
211 }
212
213 return 1;
214}
215
219{
220 struct x86_thread *arch = curthread->arch;
221
222 arch->fpu_enabled = 0;
223
226
228}
229
239void x86_fpu_load_from(const void *buf, int valid)
240{
241 struct x86_thread *arch = curthread->arch;
242
244
245 if (!valid)
246 return;
247
248 if (!arch->fpu_context && !(arch->fpu_context = kmalloc(512, &M_X86_FPU, 0)))
249 return;
250
251 memcpy(arch->fpu_context, buf, 512);
252
253 if (fpu_fxsr)
254 *(uint32_t *) ((char *) arch->fpu_context + 24) &= mxcsr_mask;
255
256 arch->fpu_enabled = 1;
257}
#define panic(s)
Definition panic.h:15
#define CR0_NE
Definition cpu.h:160
#define CR0_MP
Definition cpu.h:157
#define CR0_EM
Definition cpu.h:158
#define CR4_OSXMMEXCPT
Definition cpu.h:165
static void write_cr4(uintptr_t val)
Definition cpu.h:259
static uintptr_t read_cr4(void)
Definition cpu.h:212
#define CR4_OSFXSR
Definition cpu.h:164
#define CR0_TS
Definition cpu.h:159
#define assert_alignof(o, al)
Definition assert.h:21
static int fpu_fxsr
Definition fpu.c:29
static char fpu_context[512]
Definition fpu.c:28
static char fpu_clean[512]
Definition fpu.c:42
void x86_fpu_load_from(const void *buf, int valid)
Gives the current thread back the FPU state saved in a signal frame, at sigreturn.
Definition fpu.c:239
void x86_fpu_trap(void)
Handles #NM, the current thread's first FPU or SSE instruction since it was switched in: it saves the...
Definition fpu.c:131
int x86_fpu_save_to(void *buf)
Copies the current thread's FPU state, for a signal handler's frame (sys/signal.c).
Definition fpu.c:198
#define CPUID_EDX_FXSR
Definition fpu.c:24
static void fpu_restore(void)
Loads the FPU's state from fpu_context.
Definition fpu.c:117
void x86_fpu_disable(void)
Makes the next FPU instruction trap (sets CR0.TS).
Definition fpu.c:88
void x86_fpu_init(void)
Loads the clean starting state into the FPU, for a thread's first use of it.
Definition fpu.c:95
#define last_fpu_thread
Definition fpu.c:32
void x86_fpu_setup(void)
Sets up the FPU on the running CPU, at boot: CR0.TS set so that the first FPU or SSE instruction rais...
Definition fpu.c:50
void x86_fpu_enable(void)
Lets the running CPU use the FPU without trapping (clears CR0.TS).
Definition fpu.c:82
void x86_fpu_reset(void)
Makes the current thread start with a clean FPU state at its next use of the FPU, as a signal handler...
Definition fpu.c:218
#define CPUID_EDX_SSE
Definition fpu.c:25
static uint32_t mxcsr_mask
Definition fpu.c:37
void x86_fpu_switch_out(void)
Saves the current thread's FPU state when it is switched out, if the FPU holds it,...
Definition fpu.c:178
static void fpu_save(void)
Saves the FPU's state into fpu_context.
Definition fpu.c:108
#define __aligned(n)
Definition system.h:31
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
void * memcpy(void *dst, const void *src, size_t n)
Copies n bytes; dst and src must not overlap.
Definition libc.c:11
#define curproc
Definition sched.h:13
#define curthread
Definition sched.h:12
int signal_proc_send(struct proc *proc, int signal)
Sends a signal to a process.
Definition signal.c:310
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned int uint32_t
Definition stddef.h:22
A thread's x86 state: its kernel stack, and where it resumes.
Definition arch.h:16
int fpu_enabled
Definition arch.h:37
void * fpu_context
Definition arch.h:35