1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
/* $OpenBSD: uthread_machdep.c,v 1.5 2006/04/06 15:27:08 kurt Exp $ */
/* David Leonard, <d@csee.uq.edu.au>. Public domain. */
/*
* Machine-dependent thread state functions for OpenBSD/i386.
*/
#include <sys/param.h>
#include <sys/sysctl.h>
#include <machine/cpu.h>
#include <pthread.h>
#include "pthread_private.h"
struct frame {
int fr_gs;
int fr_fs;
int fr_es;
int fr_ds;
int fr_edi;
int fr_esi;
int fr_ebp;
int fr_esp;
int fr_ebx;
int fr_edx;
int fr_ecx;
int fr_eax;
int fr_eip;
int fr_cs; /* XXX unreachable? */
};
#define copyreg(reg, lval) \
__asm__("mov %%" #reg ", %0" : "=g"(lval))
static int _thread_machdep_osfxsr(void);
static int
_thread_machdep_osfxsr(void)
{
int mib[] = { CTL_MACHDEP, CPU_OSFXSR };
static int sse = -1;
size_t len;
int val;
if (sse == -1) {
len = sizeof (val);
if (sysctl(mib, 2, &val, &len, NULL, 0) == -1)
return (0);
if (val)
sse = 1;
else
sse = 0;
}
return (sse);
}
/*
* Given a stack and an entry function, initialise a state
* structure that can be later switched to.
*/
void
_thread_machdep_init(struct _machdep_state* statep, void *base, int len,
void (*entry)(void))
{
struct frame *f;
/* Locate the initial frame, aligned at the top of the stack */
f = (struct frame *)(((int)base + len - sizeof *f) & ~15);
/* Set up initial frame */
f->fr_esp = (int)&f->fr_edi;
copyreg(cs, f->fr_cs);
copyreg(ds, f->fr_ds);
copyreg(es, f->fr_es);
copyreg(fs, f->fr_fs);
copyreg(gs, f->fr_gs);
f->fr_ebp = (int)-1;
f->fr_eip = (int)entry;
statep->esp = (int)f;
_thread_machdep_save_float_state(statep);
/*
* The current thread float state is saved into the new thread stack.
* Later pthread_create calls _thread_kern_sched which saves the current
* thread float state again into its own stack. However all float state
* saves must be balanced with a restore on i386 due to the fninit().
* Restore the current thread float state here so that the next save
* gets the correct state.
*/
_thread_machdep_restore_float_state(statep);
}
/*
* Floating point save restore copied from code in npx.c
* (without really understanding what it does).
*/
#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
#define fnsave(addr) __asm("fnsave %0" : "=m" (*addr))
#define fninit() __asm("fninit")
#define frstor(addr) __asm("frstor %0" : : "m" (*addr))
#define fxsave(addr) __asm("fxsave %0" : "=m" (*addr))
#define fxrstor(addr) __asm("fxrstor %0" : : "m" (*addr))
#define fwait() __asm("fwait")
void
_thread_machdep_save_float_state(struct _machdep_state *ms)
{
union savefpu *addr = &ms->fpreg;
if (_thread_machdep_osfxsr()) {
fwait();
fxsave(&addr->sv_xmm);
fninit();
} else
fnsave(&addr->sv_87);
fwait();
}
void
_thread_machdep_restore_float_state(struct _machdep_state *ms)
{
union savefpu *addr = &ms->fpreg;
if (_thread_machdep_osfxsr()) {
fxrstor(&addr->sv_xmm);
fwait();
} else
frstor(&addr->sv_87);
}
|