base/dispatch.c, etc.: Replace inline assembler for the `rdrand' fix.
[catacomb] / base / dispatch-x86ish.S
1 /// -*- mode: asm; asm-comment-char: ?/ -*-
2 ///
3 /// CPU dispatch support for x86
4 ///
5 /// (c) 2019 Straylight/Edgeware
6 ///
7
8 ///----- Licensing notice ---------------------------------------------------
9 ///
10 /// This file is part of Catacomb.
11 ///
12 /// Catacomb is free software: you can redistribute it and/or modify it
13 /// under the terms of the GNU Library General Public License as published
14 /// by the Free Software Foundation; either version 2 of the License, or
15 /// (at your option) any later version.
16 ///
17 /// Catacomb is distributed in the hope that it will be useful, but
18 /// WITHOUT ANY WARRANTY; without even the implied warranty of
19 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 /// Library General Public License for more details.
21 ///
22 /// You should have received a copy of the GNU Library General Public
23 /// License along with Catacomb. If not, write to the Free Software
24 /// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 /// USA.
26
27 ///--------------------------------------------------------------------------
28 /// Preliminaries.
29
30 #include "config.h"
31 #include "asm-common.h"
32
33 EFLAGS_ID = 1 << 21
34
35 .text
36
37 ///--------------------------------------------------------------------------
38 /// Probing for CPUID.
39
40 FUNC(dispatch_x86ish_cpuid)
41 // Enter with a pointer to 16 bytes of storage for the output A, B,
42 // C, D values in the first argument, and input A and C values in the
43 // second and third. Fill the output buffer with `cpuid' results and
44 // return zero if we can; otherwise fill with zero and return -1.
45
46 #if CPUFAM_X86
47 pushreg ebx
48 pushreg edi
49 mov edi, [esp + 12]
50 mov eax, [esp + 16]
51 mov ecx, [esp + 20]
52 # define OUT edi
53 #endif
54 #if CPUFAM_AMD64 && ABI_SYSV
55 pushreg rbx
56 mov eax, esi
57 mov ecx, edx
58 # define OUT rdi
59 #endif
60 #if CPUFAM_AMD64 && ABI_WIN
61 pushreg rbx
62 mov r9, rcx
63 mov eax, edx
64 mov ecx, r8d
65 # define OUT r9
66 #endif
67 endprologue
68
69 // First, check that this is even a thing, using the complicated
70 // dance with the flags register.
71 pushf
72 pop R_d(r) // current flags in d
73
74 or R_d(r), EFLAGS_ID // force the id bit on and check it
75 push R_d(r)
76 popf
77 pushf
78 pop R_d(r)
79 test edx, EFLAGS_ID
80 jz 8f
81
82 and R_d(r), ~EFLAGS_ID // force the id bit off and check it
83 push R_d(r)
84 popf
85 pushf
86 pop R_d(r)
87 test edx, EFLAGS_ID
88 jnz 8f
89
90 // OK, that seemed to work.
91 cpuid
92
93 mov [OUT + 0], eax
94 mov [OUT + 4], ebx
95 mov [OUT + 8], ecx
96 mov [OUT + 12], edx
97 xor eax, eax
98
99 // We're done.
100 9:
101 #if CPUFAM_X86
102 popreg edi
103 popreg ebx
104 #endif
105 #if CPUFAM_AMD64
106 popreg rbx
107 #endif
108 ret
109
110 // Failed.
111 8: xor eax, eax
112 mov [OUT + 0], eax
113 mov [OUT + 4], eax
114 mov [OUT + 8], eax
115 mov [OUT + 12], eax
116 mov eax, -1
117 jmp 9b
118 ENDFUNC
119
120 ///--------------------------------------------------------------------------
121 /// Probing for XMM register availability.
122
123 FUNC(dispatch_x86ish_xmmregisters_p)
124 // Enter with no arguments. Return nonzero if the XMM registers are
125 // usable.
126
127 pushreg R_bp(r)
128 setfp
129 stalloc 512
130 and R_sp(r), ~15
131 endprologue
132
133 // Save the floating point and SIMD registers, and try to clobber
134 // xmm0.
135 fxsave [R_sp(r)]
136 mov eax, [R_sp(r) + 160]
137 xor dword ptr [R_sp(r) + 160], 0xaaaa5555
138 fxrstor [R_sp(r)]
139
140 // Save them again, and read back the low word of xmm0. Undo the
141 // clobbering and restore.
142 fxsave [R_sp(r)]
143 mov ecx, [R_sp(r) + 160]
144 mov [R_sp(r) + 160], eax
145 fxrstor [R_sp(r)]
146
147 // The register are live if we read different things.
148 xor eax, ecx
149
150 // Done.
151 dropfp
152 popreg R_bp(r)
153 ret
154 ENDFUNC
155
156 ///--------------------------------------------------------------------------
157 /// Checking `rdrand'.
158
159 FUNC(dispatch_x86ish_rdrand)
160 // Enter with one argument: a pointer X_OUT to a 32-bit word. Try to
161 // generate a random word using `rdrand'. If successful, set *X_OUT
162 // to the generated word, and return zero; otherwise, return -1.
163
164 #if CPUFAM_X86
165 # define X_OUT edx
166 # define COUNT ecx
167 mov X_OUT, [SP + 4]
168 #endif
169 #if CPUFAM_AMD64 && ABI_SYSV
170 # define X_OUT rdi
171 # define COUNT ecx
172 #endif
173 #if CPUFAM_AMD64 && ABI_WIN
174 # define X_OUT rcx
175 # define COUNT edx
176 #endif
177 endprologue
178
179 mov COUNT, 16 // fairly persistent
180 0: rdrand eax
181 jc 9f
182 dec COUNT
183 jnz 0b
184
185 // Failed to come up with a random value.
186 mov eax, -1
187 ret
188
189 // Success.
190 9: mov [X_OUT], eax
191 xor eax, eax
192 ret
193
194 #undef X_OUT
195 #undef COUNT
196
197 ENDFUNC
198
199 ///----- That's all, folks --------------------------------------------------