blob: f3b369a51664c665fb7bbd5a860ed6122137e1fb [file] [log] [blame]
Jun Nakajima86797932011-01-29 14:24:24 -08001/*
2 * i386 helpers
3 *
4 * Copyright (c) 2003 Fabrice Bellard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
19 */
David 'Digit' Turnere2288402014-01-09 18:35:14 +010020#include <math.h>
21
Jun Nakajima86797932011-01-29 14:24:24 -080022#define CPU_NO_GLOBAL_REGS
23#include "exec.h"
David 'Digit' Turner852088c2013-12-14 23:04:12 +010024#include "exec/exec-all.h"
David 'Digit' Turnere90d6652013-12-14 14:55:12 +010025#include "qemu/host-utils.h"
Jun Nakajima86797932011-01-29 14:24:24 -080026
27//#define DEBUG_PCALL
28
29
30#ifdef DEBUG_PCALL
31# define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__)
32# define LOG_PCALL_STATE(env) \
33 log_cpu_state_mask(CPU_LOG_PCALL, (env), X86_DUMP_CCOP)
34#else
35# define LOG_PCALL(...) do { } while (0)
36# define LOG_PCALL_STATE(env) do { } while (0)
37#endif
38
39
40#if 0
41#define raise_exception_err(a, b)\
42do {\
43 qemu_log("raise_exception line=%d\n", __LINE__);\
44 (raise_exception_err)(a, b);\
45} while (0)
46#endif
47
48static const uint8_t parity_table[256] = {
49 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
50 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
51 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
52 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
53 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
54 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
55 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
56 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
57 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
58 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
59 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
60 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
61 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
62 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
63 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
64 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
65 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
66 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
67 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
68 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
69 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
70 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
71 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
72 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
73 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
74 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
75 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
76 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
77 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
78 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
79 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
80 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
81};
82
83/* modulo 17 table */
84static const uint8_t rclw_table[32] = {
85 0, 1, 2, 3, 4, 5, 6, 7,
86 8, 9,10,11,12,13,14,15,
87 16, 0, 1, 2, 3, 4, 5, 6,
88 7, 8, 9,10,11,12,13,14,
89};
90
91/* modulo 9 table */
92static const uint8_t rclb_table[32] = {
93 0, 1, 2, 3, 4, 5, 6, 7,
94 8, 0, 1, 2, 3, 4, 5, 6,
95 7, 8, 0, 1, 2, 3, 4, 5,
96 6, 7, 8, 0, 1, 2, 3, 4,
97};
98
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +010099#define floatx80_lg2 make_floatx80( 0x3ffd, 0x9a209a84fbcff799LL )
100#define floatx80_l2e make_floatx80( 0x3fff, 0xb8aa3b295c17f0bcLL )
101#define floatx80_l2t make_floatx80( 0x4000, 0xd49a784bcd1b8afeLL )
102
103static const floatx80 f15rk[7] =
Jun Nakajima86797932011-01-29 14:24:24 -0800104{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +0100105 floatx80_zero,
106 floatx80_one,
107 floatx80_pi,
108 floatx80_lg2,
109 floatx80_ln2,
110 floatx80_l2e,
111 floatx80_l2t,
Jun Nakajima86797932011-01-29 14:24:24 -0800112};
113
114/* broken thread support */
115
116static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
117
118void helper_lock(void)
119{
120 spin_lock(&global_cpu_lock);
121}
122
123void helper_unlock(void)
124{
125 spin_unlock(&global_cpu_lock);
126}
127
128void helper_write_eflags(target_ulong t0, uint32_t update_mask)
129{
130 load_eflags(t0, update_mask);
131}
132
133target_ulong helper_read_eflags(void)
134{
135 uint32_t eflags;
136 eflags = helper_cc_compute_all(CC_OP);
137 eflags |= (DF & DF_MASK);
138 eflags |= env->eflags & ~(VM_MASK | RF_MASK);
139 return eflags;
140}
141
142/* return non zero if error */
143static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr,
144 int selector)
145{
146 SegmentCache *dt;
147 int index;
148 target_ulong ptr;
149
150 if (selector & 0x4)
151 dt = &env->ldt;
152 else
153 dt = &env->gdt;
154 index = selector & ~7;
155 if ((index + 7) > dt->limit)
156 return -1;
157 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100158 *e1_ptr = cpu_ldl_kernel(env, ptr);
159 *e2_ptr = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800160 return 0;
161}
162
163static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
164{
165 unsigned int limit;
166 limit = (e1 & 0xffff) | (e2 & 0x000f0000);
167 if (e2 & DESC_G_MASK)
168 limit = (limit << 12) | 0xfff;
169 return limit;
170}
171
172static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
173{
174 return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
175}
176
177static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2)
178{
179 sc->base = get_seg_base(e1, e2);
180 sc->limit = get_seg_limit(e1, e2);
181 sc->flags = e2;
182}
183
184/* init the segment cache in vm86 mode. */
185static inline void load_seg_vm(int seg, int selector)
186{
187 selector &= 0xffff;
188 cpu_x86_load_seg_cache(env, seg, selector,
189 (selector << 4), 0xffff, 0);
190}
191
192static inline void get_ss_esp_from_tss(uint32_t *ss_ptr,
193 uint32_t *esp_ptr, int dpl)
194{
195 int type, index, shift;
196
197#if 0
198 {
199 int i;
200 printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
201 for(i=0;i<env->tr.limit;i++) {
202 printf("%02x ", env->tr.base[i]);
203 if ((i & 7) == 7) printf("\n");
204 }
205 printf("\n");
206 }
207#endif
208
209 if (!(env->tr.flags & DESC_P_MASK))
210 cpu_abort(env, "invalid tss");
211 type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
212 if ((type & 7) != 1)
213 cpu_abort(env, "invalid tss type");
214 shift = type >> 3;
215 index = (dpl * 4 + 2) << shift;
216 if (index + (4 << shift) - 1 > env->tr.limit)
217 raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
218 if (shift == 0) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100219 *esp_ptr = cpu_lduw_kernel(env, env->tr.base + index);
220 *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 2);
Jun Nakajima86797932011-01-29 14:24:24 -0800221 } else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100222 *esp_ptr = cpu_ldl_kernel(env, env->tr.base + index);
223 *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800224 }
225}
226
227/* XXX: merge with load_seg() */
228static void tss_load_seg(int seg_reg, int selector)
229{
230 uint32_t e1, e2;
231 int rpl, dpl, cpl;
232
233 if ((selector & 0xfffc) != 0) {
234 if (load_segment(&e1, &e2, selector) != 0)
235 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
236 if (!(e2 & DESC_S_MASK))
237 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
238 rpl = selector & 3;
239 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
240 cpl = env->hflags & HF_CPL_MASK;
241 if (seg_reg == R_CS) {
242 if (!(e2 & DESC_CS_MASK))
243 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
244 /* XXX: is it correct ? */
245 if (dpl != rpl)
246 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
247 if ((e2 & DESC_C_MASK) && dpl > rpl)
248 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
249 } else if (seg_reg == R_SS) {
250 /* SS must be writable data */
251 if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
252 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
253 if (dpl != cpl || dpl != rpl)
254 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
255 } else {
256 /* not readable code */
257 if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK))
258 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
259 /* if data or non conforming code, checks the rights */
260 if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) {
261 if (dpl < cpl || dpl < rpl)
262 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
263 }
264 }
265 if (!(e2 & DESC_P_MASK))
266 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
267 cpu_x86_load_seg_cache(env, seg_reg, selector,
268 get_seg_base(e1, e2),
269 get_seg_limit(e1, e2),
270 e2);
271 } else {
272 if (seg_reg == R_SS || seg_reg == R_CS)
273 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
274 }
275}
276
277#define SWITCH_TSS_JMP 0
278#define SWITCH_TSS_IRET 1
279#define SWITCH_TSS_CALL 2
280
281/* XXX: restore CPU state in registers (PowerPC case) */
282static void switch_tss(int tss_selector,
283 uint32_t e1, uint32_t e2, int source,
284 uint32_t next_eip)
285{
286 int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
287 target_ulong tss_base;
288 uint32_t new_regs[8], new_segs[6];
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +0100289 uint32_t new_eflags, new_eip, new_cr3, new_ldt;
Jun Nakajima86797932011-01-29 14:24:24 -0800290 uint32_t old_eflags, eflags_mask;
291 SegmentCache *dt;
292 int index;
293 target_ulong ptr;
294
295 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
296 LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source);
297
298 /* if task gate, we read the TSS segment and we load it */
299 if (type == 5) {
300 if (!(e2 & DESC_P_MASK))
301 raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
302 tss_selector = e1 >> 16;
303 if (tss_selector & 4)
304 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
305 if (load_segment(&e1, &e2, tss_selector) != 0)
306 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
307 if (e2 & DESC_S_MASK)
308 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
309 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
310 if ((type & 7) != 1)
311 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
312 }
313
314 if (!(e2 & DESC_P_MASK))
315 raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
316
317 if (type & 8)
318 tss_limit_max = 103;
319 else
320 tss_limit_max = 43;
321 tss_limit = get_seg_limit(e1, e2);
322 tss_base = get_seg_base(e1, e2);
323 if ((tss_selector & 4) != 0 ||
324 tss_limit < tss_limit_max)
325 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
326 old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
327 if (old_type & 8)
328 old_tss_limit_max = 103;
329 else
330 old_tss_limit_max = 43;
331
332 /* read all the registers from the new TSS */
333 if (type & 8) {
334 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100335 new_cr3 = cpu_ldl_kernel(env, tss_base + 0x1c);
336 new_eip = cpu_ldl_kernel(env, tss_base + 0x20);
337 new_eflags = cpu_ldl_kernel(env, tss_base + 0x24);
Jun Nakajima86797932011-01-29 14:24:24 -0800338 for(i = 0; i < 8; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100339 new_regs[i] = cpu_ldl_kernel(env, tss_base + (0x28 + i * 4));
Jun Nakajima86797932011-01-29 14:24:24 -0800340 for(i = 0; i < 6; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100341 new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x48 + i * 4));
342 new_ldt = cpu_lduw_kernel(env, tss_base + 0x60);
343 cpu_ldl_kernel(env, tss_base + 0x64);
Jun Nakajima86797932011-01-29 14:24:24 -0800344 } else {
345 /* 16 bit */
346 new_cr3 = 0;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100347 new_eip = cpu_lduw_kernel(env, tss_base + 0x0e);
348 new_eflags = cpu_lduw_kernel(env, tss_base + 0x10);
Jun Nakajima86797932011-01-29 14:24:24 -0800349 for(i = 0; i < 8; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100350 new_regs[i] = cpu_lduw_kernel(env, tss_base + (0x12 + i * 2)) | 0xffff0000;
Jun Nakajima86797932011-01-29 14:24:24 -0800351 for(i = 0; i < 4; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100352 new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x22 + i * 4));
353 new_ldt = cpu_lduw_kernel(env, tss_base + 0x2a);
Jun Nakajima86797932011-01-29 14:24:24 -0800354 new_segs[R_FS] = 0;
355 new_segs[R_GS] = 0;
Jun Nakajima86797932011-01-29 14:24:24 -0800356 }
357
358 /* NOTE: we must avoid memory exceptions during the task switch,
359 so we make dummy accesses before */
360 /* XXX: it can still fail in some cases, so a bigger hack is
361 necessary to valid the TLB after having done the accesses */
362
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100363 v1 = cpu_ldub_kernel(env, env->tr.base);
364 v2 = cpu_ldub_kernel(env, env->tr.base + old_tss_limit_max);
365 cpu_stb_kernel(env, env->tr.base, v1);
366 cpu_stb_kernel(env, env->tr.base + old_tss_limit_max, v2);
Jun Nakajima86797932011-01-29 14:24:24 -0800367
368 /* clear busy bit (it is restartable) */
369 if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
370 target_ulong ptr;
371 uint32_t e2;
372 ptr = env->gdt.base + (env->tr.selector & ~7);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100373 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800374 e2 &= ~DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100375 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -0800376 }
377 old_eflags = compute_eflags();
378 if (source == SWITCH_TSS_IRET)
379 old_eflags &= ~NT_MASK;
380
381 /* save the current state in the old TSS */
382 if (type & 8) {
383 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100384 cpu_stl_kernel(env, env->tr.base + 0x20, next_eip);
385 cpu_stl_kernel(env, env->tr.base + 0x24, old_eflags);
386 cpu_stl_kernel(env, env->tr.base + (0x28 + 0 * 4), EAX);
387 cpu_stl_kernel(env, env->tr.base + (0x28 + 1 * 4), ECX);
388 cpu_stl_kernel(env, env->tr.base + (0x28 + 2 * 4), EDX);
389 cpu_stl_kernel(env, env->tr.base + (0x28 + 3 * 4), EBX);
390 cpu_stl_kernel(env, env->tr.base + (0x28 + 4 * 4), ESP);
391 cpu_stl_kernel(env, env->tr.base + (0x28 + 5 * 4), EBP);
392 cpu_stl_kernel(env, env->tr.base + (0x28 + 6 * 4), ESI);
393 cpu_stl_kernel(env, env->tr.base + (0x28 + 7 * 4), EDI);
Jun Nakajima86797932011-01-29 14:24:24 -0800394 for(i = 0; i < 6; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100395 cpu_stw_kernel(env, env->tr.base + (0x48 + i * 4), env->segs[i].selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800396 } else {
397 /* 16 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100398 cpu_stw_kernel(env, env->tr.base + 0x0e, next_eip);
399 cpu_stw_kernel(env, env->tr.base + 0x10, old_eflags);
400 cpu_stw_kernel(env, env->tr.base + (0x12 + 0 * 2), EAX);
401 cpu_stw_kernel(env, env->tr.base + (0x12 + 1 * 2), ECX);
402 cpu_stw_kernel(env, env->tr.base + (0x12 + 2 * 2), EDX);
403 cpu_stw_kernel(env, env->tr.base + (0x12 + 3 * 2), EBX);
404 cpu_stw_kernel(env, env->tr.base + (0x12 + 4 * 2), ESP);
405 cpu_stw_kernel(env, env->tr.base + (0x12 + 5 * 2), EBP);
406 cpu_stw_kernel(env, env->tr.base + (0x12 + 6 * 2), ESI);
407 cpu_stw_kernel(env, env->tr.base + (0x12 + 7 * 2), EDI);
Jun Nakajima86797932011-01-29 14:24:24 -0800408 for(i = 0; i < 4; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100409 cpu_stw_kernel(env, env->tr.base + (0x22 + i * 4), env->segs[i].selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800410 }
411
412 /* now if an exception occurs, it will occurs in the next task
413 context */
414
415 if (source == SWITCH_TSS_CALL) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100416 cpu_stw_kernel(env, tss_base, env->tr.selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800417 new_eflags |= NT_MASK;
418 }
419
420 /* set busy bit */
421 if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
422 target_ulong ptr;
423 uint32_t e2;
424 ptr = env->gdt.base + (tss_selector & ~7);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100425 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800426 e2 |= DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100427 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -0800428 }
429
430 /* set the new CPU state */
431 /* from this point, any exception which occurs can give problems */
432 env->cr[0] |= CR0_TS_MASK;
433 env->hflags |= HF_TS_MASK;
434 env->tr.selector = tss_selector;
435 env->tr.base = tss_base;
436 env->tr.limit = tss_limit;
437 env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
438
439 if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
440 cpu_x86_update_cr3(env, new_cr3);
441 }
442
443 /* load all registers without an exception, then reload them with
444 possible exception */
445 env->eip = new_eip;
446 eflags_mask = TF_MASK | AC_MASK | ID_MASK |
447 IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
448 if (!(type & 8))
449 eflags_mask &= 0xffff;
450 load_eflags(new_eflags, eflags_mask);
451 /* XXX: what to do in 16 bit case ? */
452 EAX = new_regs[0];
453 ECX = new_regs[1];
454 EDX = new_regs[2];
455 EBX = new_regs[3];
456 ESP = new_regs[4];
457 EBP = new_regs[5];
458 ESI = new_regs[6];
459 EDI = new_regs[7];
460 if (new_eflags & VM_MASK) {
461 for(i = 0; i < 6; i++)
462 load_seg_vm(i, new_segs[i]);
463 /* in vm86, CPL is always 3 */
464 cpu_x86_set_cpl(env, 3);
465 } else {
466 /* CPL is set the RPL of CS */
467 cpu_x86_set_cpl(env, new_segs[R_CS] & 3);
468 /* first just selectors as the rest may trigger exceptions */
469 for(i = 0; i < 6; i++)
470 cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
471 }
472
473 env->ldt.selector = new_ldt & ~4;
474 env->ldt.base = 0;
475 env->ldt.limit = 0;
476 env->ldt.flags = 0;
477
478 /* load the LDT */
479 if (new_ldt & 4)
480 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
481
482 if ((new_ldt & 0xfffc) != 0) {
483 dt = &env->gdt;
484 index = new_ldt & ~7;
485 if ((index + 7) > dt->limit)
486 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
487 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100488 e1 = cpu_ldl_kernel(env, ptr);
489 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800490 if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
491 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
492 if (!(e2 & DESC_P_MASK))
493 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
494 load_seg_cache_raw_dt(&env->ldt, e1, e2);
495 }
496
497 /* load the segments */
498 if (!(new_eflags & VM_MASK)) {
499 tss_load_seg(R_CS, new_segs[R_CS]);
500 tss_load_seg(R_SS, new_segs[R_SS]);
501 tss_load_seg(R_ES, new_segs[R_ES]);
502 tss_load_seg(R_DS, new_segs[R_DS]);
503 tss_load_seg(R_FS, new_segs[R_FS]);
504 tss_load_seg(R_GS, new_segs[R_GS]);
505 }
506
507 /* check that EIP is in the CS segment limits */
508 if (new_eip > env->segs[R_CS].limit) {
509 /* XXX: different exception if CALL ? */
510 raise_exception_err(EXCP0D_GPF, 0);
511 }
512
513#ifndef CONFIG_USER_ONLY
514 /* reset local breakpoints */
515 if (env->dr[7] & 0x55) {
516 for (i = 0; i < 4; i++) {
517 if (hw_breakpoint_enabled(env->dr[7], i) == 0x1)
518 hw_breakpoint_remove(env, i);
519 }
520 env->dr[7] &= ~0x55;
521 }
522#endif
523}
524
525/* check if Port I/O is allowed in TSS */
526static inline void check_io(int addr, int size)
527{
528 int io_offset, val, mask;
529
530 /* TSS must be a valid 32 bit one */
531 if (!(env->tr.flags & DESC_P_MASK) ||
532 ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
533 env->tr.limit < 103)
534 goto fail;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100535 io_offset = cpu_lduw_kernel(env, env->tr.base + 0x66);
Jun Nakajima86797932011-01-29 14:24:24 -0800536 io_offset += (addr >> 3);
537 /* Note: the check needs two bytes */
538 if ((io_offset + 1) > env->tr.limit)
539 goto fail;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100540 val = cpu_lduw_kernel(env, env->tr.base + io_offset);
Jun Nakajima86797932011-01-29 14:24:24 -0800541 val >>= (addr & 7);
542 mask = (1 << size) - 1;
543 /* all bits must be zero to allow the I/O */
544 if ((val & mask) != 0) {
545 fail:
546 raise_exception_err(EXCP0D_GPF, 0);
547 }
548}
549
550void helper_check_iob(uint32_t t0)
551{
552 check_io(t0, 1);
553}
554
555void helper_check_iow(uint32_t t0)
556{
557 check_io(t0, 2);
558}
559
560void helper_check_iol(uint32_t t0)
561{
562 check_io(t0, 4);
563}
564
565void helper_outb(uint32_t port, uint32_t data)
566{
567 cpu_outb(port, data & 0xff);
568}
569
570target_ulong helper_inb(uint32_t port)
571{
572 return cpu_inb(port);
573}
574
575void helper_outw(uint32_t port, uint32_t data)
576{
577 cpu_outw(port, data & 0xffff);
578}
579
580target_ulong helper_inw(uint32_t port)
581{
582 return cpu_inw(port);
583}
584
585void helper_outl(uint32_t port, uint32_t data)
586{
587 cpu_outl(port, data);
588}
589
590target_ulong helper_inl(uint32_t port)
591{
592 return cpu_inl(port);
593}
594
595static inline unsigned int get_sp_mask(unsigned int e2)
596{
597 if (e2 & DESC_B_MASK)
598 return 0xffffffff;
599 else
600 return 0xffff;
601}
602
603static int exeption_has_error_code(int intno)
604{
605 switch(intno) {
606 case 8:
607 case 10:
608 case 11:
609 case 12:
610 case 13:
611 case 14:
612 case 17:
613 return 1;
614 }
615 return 0;
616}
617
618#ifdef TARGET_X86_64
619#define SET_ESP(val, sp_mask)\
620do {\
621 if ((sp_mask) == 0xffff)\
622 ESP = (ESP & ~0xffff) | ((val) & 0xffff);\
623 else if ((sp_mask) == 0xffffffffLL)\
624 ESP = (uint32_t)(val);\
625 else\
626 ESP = (val);\
627} while (0)
628#else
629#define SET_ESP(val, sp_mask) ESP = (ESP & ~(sp_mask)) | ((val) & (sp_mask))
630#endif
631
632/* in 64-bit machines, this can overflow. So this segment addition macro
633 * can be used to trim the value to 32-bit whenever needed */
634#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask))))
635
636/* XXX: add a is_user flag to have proper security support */
637#define PUSHW(ssp, sp, sp_mask, val)\
638{\
639 sp -= 2;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100640 cpu_stw_kernel(env, (ssp) + (sp & (sp_mask)), (val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800641}
642
643#define PUSHL(ssp, sp, sp_mask, val)\
644{\
645 sp -= 4;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100646 cpu_stl_kernel(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800647}
648
649#define POPW(ssp, sp, sp_mask, val)\
650{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100651 val = cpu_lduw_kernel(env, (ssp) + (sp & (sp_mask)));\
Jun Nakajima86797932011-01-29 14:24:24 -0800652 sp += 2;\
653}
654
655#define POPL(ssp, sp, sp_mask, val)\
656{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100657 val = (uint32_t)cpu_ldl_kernel(env, SEG_ADDL(ssp, sp, sp_mask));\
Jun Nakajima86797932011-01-29 14:24:24 -0800658 sp += 4;\
659}
660
661/* protected mode interrupt */
662static void do_interrupt_protected(int intno, int is_int, int error_code,
663 unsigned int next_eip, int is_hw)
664{
665 SegmentCache *dt;
666 target_ulong ptr, ssp;
667 int type, dpl, selector, ss_dpl, cpl;
668 int has_error_code, new_stack, shift;
669 uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
670 uint32_t old_eip, sp_mask;
671
672 has_error_code = 0;
673 if (!is_int && !is_hw)
674 has_error_code = exeption_has_error_code(intno);
675 if (is_int)
676 old_eip = next_eip;
677 else
678 old_eip = env->eip;
679
680 dt = &env->idt;
681 if (intno * 8 + 7 > dt->limit)
682 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
683 ptr = dt->base + intno * 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100684 e1 = cpu_ldl_kernel(env, ptr);
685 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800686 /* check gate type */
687 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
688 switch(type) {
689 case 5: /* task gate */
690 /* must do that check here to return the correct error code */
691 if (!(e2 & DESC_P_MASK))
692 raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
693 switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
694 if (has_error_code) {
695 int type;
696 uint32_t mask;
697 /* push the error code */
698 type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
699 shift = type >> 3;
700 if (env->segs[R_SS].flags & DESC_B_MASK)
701 mask = 0xffffffff;
702 else
703 mask = 0xffff;
704 esp = (ESP - (2 << shift)) & mask;
705 ssp = env->segs[R_SS].base + esp;
706 if (shift)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100707 cpu_stl_kernel(env, ssp, error_code);
Jun Nakajima86797932011-01-29 14:24:24 -0800708 else
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100709 cpu_stw_kernel(env, ssp, error_code);
Jun Nakajima86797932011-01-29 14:24:24 -0800710 SET_ESP(esp, mask);
711 }
712 return;
713 case 6: /* 286 interrupt gate */
714 case 7: /* 286 trap gate */
715 case 14: /* 386 interrupt gate */
716 case 15: /* 386 trap gate */
717 break;
718 default:
719 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
720 break;
721 }
722 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
723 cpl = env->hflags & HF_CPL_MASK;
724 /* check privilege if software int */
725 if (is_int && dpl < cpl)
726 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
727 /* check valid bit */
728 if (!(e2 & DESC_P_MASK))
729 raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
730 selector = e1 >> 16;
731 offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
732 if ((selector & 0xfffc) == 0)
733 raise_exception_err(EXCP0D_GPF, 0);
734
735 if (load_segment(&e1, &e2, selector) != 0)
736 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
737 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
738 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
739 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
740 if (dpl > cpl)
741 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
742 if (!(e2 & DESC_P_MASK))
743 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
744 if (!(e2 & DESC_C_MASK) && dpl < cpl) {
745 /* to inner privilege */
746 get_ss_esp_from_tss(&ss, &esp, dpl);
747 if ((ss & 0xfffc) == 0)
748 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
749 if ((ss & 3) != dpl)
750 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
751 if (load_segment(&ss_e1, &ss_e2, ss) != 0)
752 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
753 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
754 if (ss_dpl != dpl)
755 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
756 if (!(ss_e2 & DESC_S_MASK) ||
757 (ss_e2 & DESC_CS_MASK) ||
758 !(ss_e2 & DESC_W_MASK))
759 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
760 if (!(ss_e2 & DESC_P_MASK))
761 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
762 new_stack = 1;
763 sp_mask = get_sp_mask(ss_e2);
764 ssp = get_seg_base(ss_e1, ss_e2);
765 } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
766 /* to same privilege */
767 if (env->eflags & VM_MASK)
768 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
769 new_stack = 0;
770 sp_mask = get_sp_mask(env->segs[R_SS].flags);
771 ssp = env->segs[R_SS].base;
772 esp = ESP;
773 dpl = cpl;
774 } else {
775 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
776 new_stack = 0; /* avoid warning */
777 sp_mask = 0; /* avoid warning */
778 ssp = 0; /* avoid warning */
779 esp = 0; /* avoid warning */
780 }
781
782 shift = type >> 3;
783
784#if 0
785 /* XXX: check that enough room is available */
786 push_size = 6 + (new_stack << 2) + (has_error_code << 1);
787 if (env->eflags & VM_MASK)
788 push_size += 8;
789 push_size <<= shift;
790#endif
791 if (shift == 1) {
792 if (new_stack) {
793 if (env->eflags & VM_MASK) {
794 PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
795 PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
796 PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
797 PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
798 }
799 PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
800 PUSHL(ssp, esp, sp_mask, ESP);
801 }
802 PUSHL(ssp, esp, sp_mask, compute_eflags());
803 PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
804 PUSHL(ssp, esp, sp_mask, old_eip);
805 if (has_error_code) {
806 PUSHL(ssp, esp, sp_mask, error_code);
807 }
808 } else {
809 if (new_stack) {
810 if (env->eflags & VM_MASK) {
811 PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
812 PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
813 PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
814 PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector);
815 }
816 PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
817 PUSHW(ssp, esp, sp_mask, ESP);
818 }
819 PUSHW(ssp, esp, sp_mask, compute_eflags());
820 PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
821 PUSHW(ssp, esp, sp_mask, old_eip);
822 if (has_error_code) {
823 PUSHW(ssp, esp, sp_mask, error_code);
824 }
825 }
826
827 if (new_stack) {
828 if (env->eflags & VM_MASK) {
829 cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
830 cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
831 cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
832 cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
833 }
834 ss = (ss & ~3) | dpl;
835 cpu_x86_load_seg_cache(env, R_SS, ss,
836 ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
837 }
838 SET_ESP(esp, sp_mask);
839
840 selector = (selector & ~3) | dpl;
841 cpu_x86_load_seg_cache(env, R_CS, selector,
842 get_seg_base(e1, e2),
843 get_seg_limit(e1, e2),
844 e2);
845 cpu_x86_set_cpl(env, dpl);
846 env->eip = offset;
847
848 /* interrupt gate clear IF mask */
849 if ((type & 1) == 0) {
850 env->eflags &= ~IF_MASK;
851 }
852 env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
853}
854
855#ifdef TARGET_X86_64
856
857#define PUSHQ(sp, val)\
858{\
859 sp -= 8;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100860 cpu_stq_kernel(env, sp, (val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800861}
862
863#define POPQ(sp, val)\
864{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100865 val = cpu_ldq_kernel(env, sp);\
Jun Nakajima86797932011-01-29 14:24:24 -0800866 sp += 8;\
867}
868
869static inline target_ulong get_rsp_from_tss(int level)
870{
871 int index;
872
873#if 0
874 printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
875 env->tr.base, env->tr.limit);
876#endif
877
878 if (!(env->tr.flags & DESC_P_MASK))
879 cpu_abort(env, "invalid tss");
880 index = 8 * level + 4;
881 if ((index + 7) > env->tr.limit)
882 raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100883 return cpu_ldq_kernel(env, env->tr.base + index);
Jun Nakajima86797932011-01-29 14:24:24 -0800884}
885
886/* 64 bit interrupt */
887static void do_interrupt64(int intno, int is_int, int error_code,
888 target_ulong next_eip, int is_hw)
889{
890 SegmentCache *dt;
891 target_ulong ptr;
892 int type, dpl, selector, cpl, ist;
893 int has_error_code, new_stack;
894 uint32_t e1, e2, e3, ss;
895 target_ulong old_eip, esp, offset;
896
897 has_error_code = 0;
898 if (!is_int && !is_hw)
899 has_error_code = exeption_has_error_code(intno);
900 if (is_int)
901 old_eip = next_eip;
902 else
903 old_eip = env->eip;
904
905 dt = &env->idt;
906 if (intno * 16 + 15 > dt->limit)
907 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
908 ptr = dt->base + intno * 16;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100909 e1 = cpu_ldl_kernel(env, ptr);
910 e2 = cpu_ldl_kernel(env, ptr + 4);
911 e3 = cpu_ldl_kernel(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -0800912 /* check gate type */
913 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
914 switch(type) {
915 case 14: /* 386 interrupt gate */
916 case 15: /* 386 trap gate */
917 break;
918 default:
919 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
920 break;
921 }
922 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
923 cpl = env->hflags & HF_CPL_MASK;
924 /* check privilege if software int */
925 if (is_int && dpl < cpl)
926 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
927 /* check valid bit */
928 if (!(e2 & DESC_P_MASK))
929 raise_exception_err(EXCP0B_NOSEG, intno * 16 + 2);
930 selector = e1 >> 16;
931 offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
932 ist = e2 & 7;
933 if ((selector & 0xfffc) == 0)
934 raise_exception_err(EXCP0D_GPF, 0);
935
936 if (load_segment(&e1, &e2, selector) != 0)
937 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
938 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
939 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
940 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
941 if (dpl > cpl)
942 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
943 if (!(e2 & DESC_P_MASK))
944 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
945 if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK))
946 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
947 if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {
948 /* to inner privilege */
949 if (ist != 0)
950 esp = get_rsp_from_tss(ist + 3);
951 else
952 esp = get_rsp_from_tss(dpl);
953 esp &= ~0xfLL; /* align stack */
954 ss = 0;
955 new_stack = 1;
956 } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
957 /* to same privilege */
958 if (env->eflags & VM_MASK)
959 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
960 new_stack = 0;
961 if (ist != 0)
962 esp = get_rsp_from_tss(ist + 3);
963 else
964 esp = ESP;
965 esp &= ~0xfLL; /* align stack */
966 dpl = cpl;
967 } else {
968 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
969 new_stack = 0; /* avoid warning */
970 esp = 0; /* avoid warning */
971 }
972
973 PUSHQ(esp, env->segs[R_SS].selector);
974 PUSHQ(esp, ESP);
975 PUSHQ(esp, compute_eflags());
976 PUSHQ(esp, env->segs[R_CS].selector);
977 PUSHQ(esp, old_eip);
978 if (has_error_code) {
979 PUSHQ(esp, error_code);
980 }
981
982 if (new_stack) {
983 ss = 0 | dpl;
984 cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
985 }
986 ESP = esp;
987
988 selector = (selector & ~3) | dpl;
989 cpu_x86_load_seg_cache(env, R_CS, selector,
990 get_seg_base(e1, e2),
991 get_seg_limit(e1, e2),
992 e2);
993 cpu_x86_set_cpl(env, dpl);
994 env->eip = offset;
995
996 /* interrupt gate clear IF mask */
997 if ((type & 1) == 0) {
998 env->eflags &= ~IF_MASK;
999 }
1000 env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
1001}
1002#endif
1003
1004#ifdef TARGET_X86_64
1005#if defined(CONFIG_USER_ONLY)
1006void helper_syscall(int next_eip_addend)
1007{
1008 env->exception_index = EXCP_SYSCALL;
1009 env->exception_next_eip = env->eip + next_eip_addend;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01001010 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08001011}
1012#else
1013void helper_syscall(int next_eip_addend)
1014{
1015 int selector;
1016
1017 if (!(env->efer & MSR_EFER_SCE)) {
1018 raise_exception_err(EXCP06_ILLOP, 0);
1019 }
1020 selector = (env->star >> 32) & 0xffff;
1021 if (env->hflags & HF_LMA_MASK) {
1022 int code64;
1023
1024 ECX = env->eip + next_eip_addend;
1025 env->regs[11] = compute_eflags();
1026
1027 code64 = env->hflags & HF_CS64_MASK;
1028
1029 cpu_x86_set_cpl(env, 0);
1030 cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1031 0, 0xffffffff,
1032 DESC_G_MASK | DESC_P_MASK |
1033 DESC_S_MASK |
1034 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
1035 cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1036 0, 0xffffffff,
1037 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1038 DESC_S_MASK |
1039 DESC_W_MASK | DESC_A_MASK);
1040 env->eflags &= ~env->fmask;
1041 load_eflags(env->eflags, 0);
1042 if (code64)
1043 env->eip = env->lstar;
1044 else
1045 env->eip = env->cstar;
1046 } else {
1047 ECX = (uint32_t)(env->eip + next_eip_addend);
1048
1049 cpu_x86_set_cpl(env, 0);
1050 cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1051 0, 0xffffffff,
1052 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1053 DESC_S_MASK |
1054 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1055 cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1056 0, 0xffffffff,
1057 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1058 DESC_S_MASK |
1059 DESC_W_MASK | DESC_A_MASK);
1060 env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
1061 env->eip = (uint32_t)env->star;
1062 }
1063}
1064#endif
1065#endif
1066
1067#ifdef TARGET_X86_64
1068void helper_sysret(int dflag)
1069{
1070 int cpl, selector;
1071
1072 if (!(env->efer & MSR_EFER_SCE)) {
1073 raise_exception_err(EXCP06_ILLOP, 0);
1074 }
1075 cpl = env->hflags & HF_CPL_MASK;
1076 if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
1077 raise_exception_err(EXCP0D_GPF, 0);
1078 }
1079 selector = (env->star >> 48) & 0xffff;
1080 if (env->hflags & HF_LMA_MASK) {
1081 if (dflag == 2) {
1082 cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
1083 0, 0xffffffff,
1084 DESC_G_MASK | DESC_P_MASK |
1085 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1086 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
1087 DESC_L_MASK);
1088 env->eip = ECX;
1089 } else {
1090 cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1091 0, 0xffffffff,
1092 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1093 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1094 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1095 env->eip = (uint32_t)ECX;
1096 }
1097 cpu_x86_load_seg_cache(env, R_SS, selector + 8,
1098 0, 0xffffffff,
1099 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1100 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1101 DESC_W_MASK | DESC_A_MASK);
1102 load_eflags((uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK |
1103 IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK);
1104 cpu_x86_set_cpl(env, 3);
1105 } else {
1106 cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1107 0, 0xffffffff,
1108 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1109 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1110 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1111 env->eip = (uint32_t)ECX;
1112 cpu_x86_load_seg_cache(env, R_SS, selector + 8,
1113 0, 0xffffffff,
1114 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1115 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1116 DESC_W_MASK | DESC_A_MASK);
1117 env->eflags |= IF_MASK;
1118 cpu_x86_set_cpl(env, 3);
1119 }
Jun Nakajima86797932011-01-29 14:24:24 -08001120}
1121#endif
1122
1123/* real mode interrupt */
1124static void do_interrupt_real(int intno, int is_int, int error_code,
1125 unsigned int next_eip)
1126{
1127 SegmentCache *dt;
1128 target_ulong ptr, ssp;
1129 int selector;
1130 uint32_t offset, esp;
1131 uint32_t old_cs, old_eip;
1132
1133 /* real mode (simpler !) */
1134 dt = &env->idt;
1135 if (intno * 4 + 3 > dt->limit)
1136 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
1137 ptr = dt->base + intno * 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001138 offset = cpu_lduw_kernel(env, ptr);
1139 selector = cpu_lduw_kernel(env, ptr + 2);
Jun Nakajima86797932011-01-29 14:24:24 -08001140 esp = ESP;
1141 ssp = env->segs[R_SS].base;
1142 if (is_int)
1143 old_eip = next_eip;
1144 else
1145 old_eip = env->eip;
1146 old_cs = env->segs[R_CS].selector;
1147 /* XXX: use SS segment size ? */
1148 PUSHW(ssp, esp, 0xffff, compute_eflags());
1149 PUSHW(ssp, esp, 0xffff, old_cs);
1150 PUSHW(ssp, esp, 0xffff, old_eip);
1151
1152 /* update processor state */
1153 ESP = (ESP & ~0xffff) | (esp & 0xffff);
1154 env->eip = offset;
1155 env->segs[R_CS].selector = selector;
1156 env->segs[R_CS].base = (selector << 4);
1157 env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
1158}
1159
1160/* fake user mode interrupt */
1161void do_interrupt_user(int intno, int is_int, int error_code,
1162 target_ulong next_eip)
1163{
1164 SegmentCache *dt;
1165 target_ulong ptr;
1166 int dpl, cpl, shift;
1167 uint32_t e2;
1168
1169 dt = &env->idt;
1170 if (env->hflags & HF_LMA_MASK) {
1171 shift = 4;
1172 } else {
1173 shift = 3;
1174 }
1175 ptr = dt->base + (intno << shift);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001176 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08001177
1178 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1179 cpl = env->hflags & HF_CPL_MASK;
1180 /* check privilege if software int */
1181 if (is_int && dpl < cpl)
1182 raise_exception_err(EXCP0D_GPF, (intno << shift) + 2);
1183
1184 /* Since we emulate only user space, we cannot do more than
1185 exiting the emulation with the suitable exception and error
1186 code */
1187 if (is_int)
1188 EIP = next_eip;
1189}
1190
1191#if !defined(CONFIG_USER_ONLY)
1192static void handle_even_inj(int intno, int is_int, int error_code,
1193 int is_hw, int rm)
1194{
1195 uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
1196 if (!(event_inj & SVM_EVTINJ_VALID)) {
1197 int type;
1198 if (is_int)
1199 type = SVM_EVTINJ_TYPE_SOFT;
1200 else
1201 type = SVM_EVTINJ_TYPE_EXEPT;
1202 event_inj = intno | type | SVM_EVTINJ_VALID;
1203 if (!rm && exeption_has_error_code(intno)) {
1204 event_inj |= SVM_EVTINJ_VALID_ERR;
1205 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err), error_code);
1206 }
1207 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj);
1208 }
1209}
1210#endif
1211
1212/*
1213 * Begin execution of an interruption. is_int is TRUE if coming from
1214 * the int instruction. next_eip is the EIP value AFTER the interrupt
1215 * instruction. It is only relevant if is_int is TRUE.
1216 */
1217void do_interrupt(int intno, int is_int, int error_code,
1218 target_ulong next_eip, int is_hw)
1219{
1220 if (qemu_loglevel_mask(CPU_LOG_INT)) {
1221 if ((env->cr[0] & CR0_PE_MASK)) {
1222 static int count;
1223 qemu_log("%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx,
1224 count, intno, error_code, is_int,
1225 env->hflags & HF_CPL_MASK,
1226 env->segs[R_CS].selector, EIP,
1227 (int)env->segs[R_CS].base + EIP,
1228 env->segs[R_SS].selector, ESP);
1229 if (intno == 0x0e) {
1230 qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]);
1231 } else {
1232 qemu_log(" EAX=" TARGET_FMT_lx, EAX);
1233 }
1234 qemu_log("\n");
1235 log_cpu_state(env, X86_DUMP_CCOP);
1236#if 0
1237 {
1238 int i;
1239 uint8_t *ptr;
1240 qemu_log(" code=");
1241 ptr = env->segs[R_CS].base + env->eip;
1242 for(i = 0; i < 16; i++) {
1243 qemu_log(" %02x", ldub(ptr + i));
1244 }
1245 qemu_log("\n");
1246 }
1247#endif
1248 count++;
1249 }
1250 }
1251 if (env->cr[0] & CR0_PE_MASK) {
1252#if !defined(CONFIG_USER_ONLY)
1253 if (env->hflags & HF_SVMI_MASK)
1254 handle_even_inj(intno, is_int, error_code, is_hw, 0);
1255#endif
1256#ifdef TARGET_X86_64
1257 if (env->hflags & HF_LMA_MASK) {
1258 do_interrupt64(intno, is_int, error_code, next_eip, is_hw);
1259 } else
1260#endif
1261 {
1262 do_interrupt_protected(intno, is_int, error_code, next_eip, is_hw);
1263 }
1264 } else {
1265#if !defined(CONFIG_USER_ONLY)
1266 if (env->hflags & HF_SVMI_MASK)
1267 handle_even_inj(intno, is_int, error_code, is_hw, 1);
1268#endif
1269 do_interrupt_real(intno, is_int, error_code, next_eip);
1270 }
1271
1272#if !defined(CONFIG_USER_ONLY)
1273 if (env->hflags & HF_SVMI_MASK) {
1274 uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
1275 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj & ~SVM_EVTINJ_VALID);
1276 }
1277#endif
1278}
1279
1280/* This should come from sysemu.h - if we could include it here... */
1281void qemu_system_reset_request(void);
1282
1283/*
1284 * Check nested exceptions and change to double or triple fault if
1285 * needed. It should only be called, if this is not an interrupt.
1286 * Returns the new exception number.
1287 */
1288static int check_exception(int intno, int *error_code)
1289{
1290 int first_contributory = env->old_exception == 0 ||
1291 (env->old_exception >= 10 &&
1292 env->old_exception <= 13);
1293 int second_contributory = intno == 0 ||
1294 (intno >= 10 && intno <= 13);
1295
1296 qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n",
1297 env->old_exception, intno);
1298
1299#if !defined(CONFIG_USER_ONLY)
1300 if (env->old_exception == EXCP08_DBLE) {
1301 if (env->hflags & HF_SVMI_MASK)
1302 helper_vmexit(SVM_EXIT_SHUTDOWN, 0); /* does not return */
1303
1304 qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
1305
1306 qemu_system_reset_request();
1307 return EXCP_HLT;
1308 }
1309#endif
1310
1311 if ((first_contributory && second_contributory)
1312 || (env->old_exception == EXCP0E_PAGE &&
1313 (second_contributory || (intno == EXCP0E_PAGE)))) {
1314 intno = EXCP08_DBLE;
1315 *error_code = 0;
1316 }
1317
1318 if (second_contributory || (intno == EXCP0E_PAGE) ||
1319 (intno == EXCP08_DBLE))
1320 env->old_exception = intno;
1321
1322 return intno;
1323}
1324
1325/*
1326 * Signal an interruption. It is executed in the main CPU loop.
1327 * is_int is TRUE if coming from the int instruction. next_eip is the
1328 * EIP value AFTER the interrupt instruction. It is only relevant if
1329 * is_int is TRUE.
1330 */
1331static void QEMU_NORETURN raise_interrupt(int intno, int is_int, int error_code,
1332 int next_eip_addend)
1333{
1334 if (!is_int) {
1335 helper_svm_check_intercept_param(SVM_EXIT_EXCP_BASE + intno, error_code);
1336 intno = check_exception(intno, &error_code);
1337 } else {
1338 helper_svm_check_intercept_param(SVM_EXIT_SWINT, 0);
1339 }
1340
1341 env->exception_index = intno;
1342 env->error_code = error_code;
1343 env->exception_is_int = is_int;
1344 env->exception_next_eip = env->eip + next_eip_addend;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01001345 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08001346}
1347
1348/* shortcuts to generate exceptions */
1349
1350void raise_exception_err(int exception_index, int error_code)
1351{
1352 raise_interrupt(exception_index, 0, error_code, 0);
1353}
1354
1355void raise_exception(int exception_index)
1356{
1357 raise_interrupt(exception_index, 0, 0, 0);
1358}
1359
1360/* SMM support */
1361
1362#if defined(CONFIG_USER_ONLY)
1363
1364void do_smm_enter(void)
1365{
1366}
1367
1368void helper_rsm(void)
1369{
1370}
1371
1372#else
1373
1374#ifdef TARGET_X86_64
1375#define SMM_REVISION_ID 0x00020064
1376#else
1377#define SMM_REVISION_ID 0x00020000
1378#endif
1379
1380void do_smm_enter(void)
1381{
1382 target_ulong sm_state;
1383 SegmentCache *dt;
1384 int i, offset;
1385
1386 qemu_log_mask(CPU_LOG_INT, "SMM: enter\n");
1387 log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
1388
1389 env->hflags |= HF_SMM_MASK;
1390 cpu_smm_update(env);
1391
1392 sm_state = env->smbase + 0x8000;
1393
1394#ifdef TARGET_X86_64
1395 for(i = 0; i < 6; i++) {
1396 dt = &env->segs[i];
1397 offset = 0x7e00 + i * 16;
1398 stw_phys(sm_state + offset, dt->selector);
1399 stw_phys(sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff);
1400 stl_phys(sm_state + offset + 4, dt->limit);
1401 stq_phys(sm_state + offset + 8, dt->base);
1402 }
1403
1404 stq_phys(sm_state + 0x7e68, env->gdt.base);
1405 stl_phys(sm_state + 0x7e64, env->gdt.limit);
1406
1407 stw_phys(sm_state + 0x7e70, env->ldt.selector);
1408 stq_phys(sm_state + 0x7e78, env->ldt.base);
1409 stl_phys(sm_state + 0x7e74, env->ldt.limit);
1410 stw_phys(sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff);
1411
1412 stq_phys(sm_state + 0x7e88, env->idt.base);
1413 stl_phys(sm_state + 0x7e84, env->idt.limit);
1414
1415 stw_phys(sm_state + 0x7e90, env->tr.selector);
1416 stq_phys(sm_state + 0x7e98, env->tr.base);
1417 stl_phys(sm_state + 0x7e94, env->tr.limit);
1418 stw_phys(sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff);
1419
1420 stq_phys(sm_state + 0x7ed0, env->efer);
1421
1422 stq_phys(sm_state + 0x7ff8, EAX);
1423 stq_phys(sm_state + 0x7ff0, ECX);
1424 stq_phys(sm_state + 0x7fe8, EDX);
1425 stq_phys(sm_state + 0x7fe0, EBX);
1426 stq_phys(sm_state + 0x7fd8, ESP);
1427 stq_phys(sm_state + 0x7fd0, EBP);
1428 stq_phys(sm_state + 0x7fc8, ESI);
1429 stq_phys(sm_state + 0x7fc0, EDI);
1430 for(i = 8; i < 16; i++)
1431 stq_phys(sm_state + 0x7ff8 - i * 8, env->regs[i]);
1432 stq_phys(sm_state + 0x7f78, env->eip);
1433 stl_phys(sm_state + 0x7f70, compute_eflags());
1434 stl_phys(sm_state + 0x7f68, env->dr[6]);
1435 stl_phys(sm_state + 0x7f60, env->dr[7]);
1436
1437 stl_phys(sm_state + 0x7f48, env->cr[4]);
1438 stl_phys(sm_state + 0x7f50, env->cr[3]);
1439 stl_phys(sm_state + 0x7f58, env->cr[0]);
1440
1441 stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
1442 stl_phys(sm_state + 0x7f00, env->smbase);
1443#else
1444 stl_phys(sm_state + 0x7ffc, env->cr[0]);
1445 stl_phys(sm_state + 0x7ff8, env->cr[3]);
1446 stl_phys(sm_state + 0x7ff4, compute_eflags());
1447 stl_phys(sm_state + 0x7ff0, env->eip);
1448 stl_phys(sm_state + 0x7fec, EDI);
1449 stl_phys(sm_state + 0x7fe8, ESI);
1450 stl_phys(sm_state + 0x7fe4, EBP);
1451 stl_phys(sm_state + 0x7fe0, ESP);
1452 stl_phys(sm_state + 0x7fdc, EBX);
1453 stl_phys(sm_state + 0x7fd8, EDX);
1454 stl_phys(sm_state + 0x7fd4, ECX);
1455 stl_phys(sm_state + 0x7fd0, EAX);
1456 stl_phys(sm_state + 0x7fcc, env->dr[6]);
1457 stl_phys(sm_state + 0x7fc8, env->dr[7]);
1458
1459 stl_phys(sm_state + 0x7fc4, env->tr.selector);
1460 stl_phys(sm_state + 0x7f64, env->tr.base);
1461 stl_phys(sm_state + 0x7f60, env->tr.limit);
1462 stl_phys(sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff);
1463
1464 stl_phys(sm_state + 0x7fc0, env->ldt.selector);
1465 stl_phys(sm_state + 0x7f80, env->ldt.base);
1466 stl_phys(sm_state + 0x7f7c, env->ldt.limit);
1467 stl_phys(sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff);
1468
1469 stl_phys(sm_state + 0x7f74, env->gdt.base);
1470 stl_phys(sm_state + 0x7f70, env->gdt.limit);
1471
1472 stl_phys(sm_state + 0x7f58, env->idt.base);
1473 stl_phys(sm_state + 0x7f54, env->idt.limit);
1474
1475 for(i = 0; i < 6; i++) {
1476 dt = &env->segs[i];
1477 if (i < 3)
1478 offset = 0x7f84 + i * 12;
1479 else
1480 offset = 0x7f2c + (i - 3) * 12;
1481 stl_phys(sm_state + 0x7fa8 + i * 4, dt->selector);
1482 stl_phys(sm_state + offset + 8, dt->base);
1483 stl_phys(sm_state + offset + 4, dt->limit);
1484 stl_phys(sm_state + offset, (dt->flags >> 8) & 0xf0ff);
1485 }
1486 stl_phys(sm_state + 0x7f14, env->cr[4]);
1487
1488 stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
1489 stl_phys(sm_state + 0x7ef8, env->smbase);
1490#endif
1491 /* init SMM cpu state */
1492
1493#ifdef TARGET_X86_64
1494 cpu_load_efer(env, 0);
1495#endif
1496 load_eflags(0, ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1497 env->eip = 0x00008000;
1498 cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase,
1499 0xffffffff, 0);
1500 cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, 0);
1501 cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, 0);
1502 cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, 0);
1503 cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, 0);
1504 cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, 0);
1505
1506 cpu_x86_update_cr0(env,
1507 env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK | CR0_PG_MASK));
1508 cpu_x86_update_cr4(env, 0);
1509 env->dr[7] = 0x00000400;
1510 CC_OP = CC_OP_EFLAGS;
1511}
1512
1513void helper_rsm(void)
1514{
1515 target_ulong sm_state;
1516 int i, offset;
1517 uint32_t val;
1518
1519 sm_state = env->smbase + 0x8000;
1520#ifdef TARGET_X86_64
1521 cpu_load_efer(env, ldq_phys(sm_state + 0x7ed0));
1522
1523 for(i = 0; i < 6; i++) {
1524 offset = 0x7e00 + i * 16;
1525 cpu_x86_load_seg_cache(env, i,
1526 lduw_phys(sm_state + offset),
1527 ldq_phys(sm_state + offset + 8),
1528 ldl_phys(sm_state + offset + 4),
1529 (lduw_phys(sm_state + offset + 2) & 0xf0ff) << 8);
1530 }
1531
1532 env->gdt.base = ldq_phys(sm_state + 0x7e68);
1533 env->gdt.limit = ldl_phys(sm_state + 0x7e64);
1534
1535 env->ldt.selector = lduw_phys(sm_state + 0x7e70);
1536 env->ldt.base = ldq_phys(sm_state + 0x7e78);
1537 env->ldt.limit = ldl_phys(sm_state + 0x7e74);
1538 env->ldt.flags = (lduw_phys(sm_state + 0x7e72) & 0xf0ff) << 8;
1539
1540 env->idt.base = ldq_phys(sm_state + 0x7e88);
1541 env->idt.limit = ldl_phys(sm_state + 0x7e84);
1542
1543 env->tr.selector = lduw_phys(sm_state + 0x7e90);
1544 env->tr.base = ldq_phys(sm_state + 0x7e98);
1545 env->tr.limit = ldl_phys(sm_state + 0x7e94);
1546 env->tr.flags = (lduw_phys(sm_state + 0x7e92) & 0xf0ff) << 8;
1547
1548 EAX = ldq_phys(sm_state + 0x7ff8);
1549 ECX = ldq_phys(sm_state + 0x7ff0);
1550 EDX = ldq_phys(sm_state + 0x7fe8);
1551 EBX = ldq_phys(sm_state + 0x7fe0);
1552 ESP = ldq_phys(sm_state + 0x7fd8);
1553 EBP = ldq_phys(sm_state + 0x7fd0);
1554 ESI = ldq_phys(sm_state + 0x7fc8);
1555 EDI = ldq_phys(sm_state + 0x7fc0);
1556 for(i = 8; i < 16; i++)
1557 env->regs[i] = ldq_phys(sm_state + 0x7ff8 - i * 8);
1558 env->eip = ldq_phys(sm_state + 0x7f78);
1559 load_eflags(ldl_phys(sm_state + 0x7f70),
1560 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1561 env->dr[6] = ldl_phys(sm_state + 0x7f68);
1562 env->dr[7] = ldl_phys(sm_state + 0x7f60);
1563
1564 cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f48));
1565 cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7f50));
1566 cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7f58));
1567
1568 val = ldl_phys(sm_state + 0x7efc); /* revision ID */
1569 if (val & 0x20000) {
1570 env->smbase = ldl_phys(sm_state + 0x7f00) & ~0x7fff;
1571 }
1572#else
1573 cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7ffc));
1574 cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7ff8));
1575 load_eflags(ldl_phys(sm_state + 0x7ff4),
1576 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1577 env->eip = ldl_phys(sm_state + 0x7ff0);
1578 EDI = ldl_phys(sm_state + 0x7fec);
1579 ESI = ldl_phys(sm_state + 0x7fe8);
1580 EBP = ldl_phys(sm_state + 0x7fe4);
1581 ESP = ldl_phys(sm_state + 0x7fe0);
1582 EBX = ldl_phys(sm_state + 0x7fdc);
1583 EDX = ldl_phys(sm_state + 0x7fd8);
1584 ECX = ldl_phys(sm_state + 0x7fd4);
1585 EAX = ldl_phys(sm_state + 0x7fd0);
1586 env->dr[6] = ldl_phys(sm_state + 0x7fcc);
1587 env->dr[7] = ldl_phys(sm_state + 0x7fc8);
1588
1589 env->tr.selector = ldl_phys(sm_state + 0x7fc4) & 0xffff;
1590 env->tr.base = ldl_phys(sm_state + 0x7f64);
1591 env->tr.limit = ldl_phys(sm_state + 0x7f60);
1592 env->tr.flags = (ldl_phys(sm_state + 0x7f5c) & 0xf0ff) << 8;
1593
1594 env->ldt.selector = ldl_phys(sm_state + 0x7fc0) & 0xffff;
1595 env->ldt.base = ldl_phys(sm_state + 0x7f80);
1596 env->ldt.limit = ldl_phys(sm_state + 0x7f7c);
1597 env->ldt.flags = (ldl_phys(sm_state + 0x7f78) & 0xf0ff) << 8;
1598
1599 env->gdt.base = ldl_phys(sm_state + 0x7f74);
1600 env->gdt.limit = ldl_phys(sm_state + 0x7f70);
1601
1602 env->idt.base = ldl_phys(sm_state + 0x7f58);
1603 env->idt.limit = ldl_phys(sm_state + 0x7f54);
1604
1605 for(i = 0; i < 6; i++) {
1606 if (i < 3)
1607 offset = 0x7f84 + i * 12;
1608 else
1609 offset = 0x7f2c + (i - 3) * 12;
1610 cpu_x86_load_seg_cache(env, i,
1611 ldl_phys(sm_state + 0x7fa8 + i * 4) & 0xffff,
1612 ldl_phys(sm_state + offset + 8),
1613 ldl_phys(sm_state + offset + 4),
1614 (ldl_phys(sm_state + offset) & 0xf0ff) << 8);
1615 }
1616 cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f14));
1617
1618 val = ldl_phys(sm_state + 0x7efc); /* revision ID */
1619 if (val & 0x20000) {
1620 env->smbase = ldl_phys(sm_state + 0x7ef8) & ~0x7fff;
1621 }
1622#endif
1623 CC_OP = CC_OP_EFLAGS;
1624 env->hflags &= ~HF_SMM_MASK;
1625 cpu_smm_update(env);
1626
1627 qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
1628 log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
1629}
1630
1631#endif /* !CONFIG_USER_ONLY */
1632
1633
1634/* division, flags are undefined */
1635
1636void helper_divb_AL(target_ulong t0)
1637{
1638 unsigned int num, den, q, r;
1639
1640 num = (EAX & 0xffff);
1641 den = (t0 & 0xff);
1642 if (den == 0) {
1643 raise_exception(EXCP00_DIVZ);
1644 }
1645 q = (num / den);
1646 if (q > 0xff)
1647 raise_exception(EXCP00_DIVZ);
1648 q &= 0xff;
1649 r = (num % den) & 0xff;
1650 EAX = (EAX & ~0xffff) | (r << 8) | q;
1651}
1652
1653void helper_idivb_AL(target_ulong t0)
1654{
1655 int num, den, q, r;
1656
1657 num = (int16_t)EAX;
1658 den = (int8_t)t0;
1659 if (den == 0) {
1660 raise_exception(EXCP00_DIVZ);
1661 }
1662 q = (num / den);
1663 if (q != (int8_t)q)
1664 raise_exception(EXCP00_DIVZ);
1665 q &= 0xff;
1666 r = (num % den) & 0xff;
1667 EAX = (EAX & ~0xffff) | (r << 8) | q;
1668}
1669
1670void helper_divw_AX(target_ulong t0)
1671{
1672 unsigned int num, den, q, r;
1673
1674 num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
1675 den = (t0 & 0xffff);
1676 if (den == 0) {
1677 raise_exception(EXCP00_DIVZ);
1678 }
1679 q = (num / den);
1680 if (q > 0xffff)
1681 raise_exception(EXCP00_DIVZ);
1682 q &= 0xffff;
1683 r = (num % den) & 0xffff;
1684 EAX = (EAX & ~0xffff) | q;
1685 EDX = (EDX & ~0xffff) | r;
1686}
1687
1688void helper_idivw_AX(target_ulong t0)
1689{
1690 int num, den, q, r;
1691
1692 num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
1693 den = (int16_t)t0;
1694 if (den == 0) {
1695 raise_exception(EXCP00_DIVZ);
1696 }
1697 q = (num / den);
1698 if (q != (int16_t)q)
1699 raise_exception(EXCP00_DIVZ);
1700 q &= 0xffff;
1701 r = (num % den) & 0xffff;
1702 EAX = (EAX & ~0xffff) | q;
1703 EDX = (EDX & ~0xffff) | r;
1704}
1705
1706void helper_divl_EAX(target_ulong t0)
1707{
1708 unsigned int den, r;
1709 uint64_t num, q;
1710
1711 num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
1712 den = t0;
1713 if (den == 0) {
1714 raise_exception(EXCP00_DIVZ);
1715 }
1716 q = (num / den);
1717 r = (num % den);
1718 if (q > 0xffffffff)
1719 raise_exception(EXCP00_DIVZ);
1720 EAX = (uint32_t)q;
1721 EDX = (uint32_t)r;
1722}
1723
1724void helper_idivl_EAX(target_ulong t0)
1725{
1726 int den, r;
1727 int64_t num, q;
1728
1729 num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
1730 den = t0;
1731 if (den == 0) {
1732 raise_exception(EXCP00_DIVZ);
1733 }
1734 q = (num / den);
1735 r = (num % den);
1736 if (q != (int32_t)q)
1737 raise_exception(EXCP00_DIVZ);
1738 EAX = (uint32_t)q;
1739 EDX = (uint32_t)r;
1740}
1741
1742/* bcd */
1743
1744/* XXX: exception */
1745void helper_aam(int base)
1746{
1747 int al, ah;
1748 al = EAX & 0xff;
1749 ah = al / base;
1750 al = al % base;
1751 EAX = (EAX & ~0xffff) | al | (ah << 8);
1752 CC_DST = al;
1753}
1754
1755void helper_aad(int base)
1756{
1757 int al, ah;
1758 al = EAX & 0xff;
1759 ah = (EAX >> 8) & 0xff;
1760 al = ((ah * base) + al) & 0xff;
1761 EAX = (EAX & ~0xffff) | al;
1762 CC_DST = al;
1763}
1764
1765void helper_aaa(void)
1766{
1767 int icarry;
1768 int al, ah, af;
1769 int eflags;
1770
1771 eflags = helper_cc_compute_all(CC_OP);
1772 af = eflags & CC_A;
1773 al = EAX & 0xff;
1774 ah = (EAX >> 8) & 0xff;
1775
1776 icarry = (al > 0xf9);
1777 if (((al & 0x0f) > 9 ) || af) {
1778 al = (al + 6) & 0x0f;
1779 ah = (ah + 1 + icarry) & 0xff;
1780 eflags |= CC_C | CC_A;
1781 } else {
1782 eflags &= ~(CC_C | CC_A);
1783 al &= 0x0f;
1784 }
1785 EAX = (EAX & ~0xffff) | al | (ah << 8);
1786 CC_SRC = eflags;
1787}
1788
1789void helper_aas(void)
1790{
1791 int icarry;
1792 int al, ah, af;
1793 int eflags;
1794
1795 eflags = helper_cc_compute_all(CC_OP);
1796 af = eflags & CC_A;
1797 al = EAX & 0xff;
1798 ah = (EAX >> 8) & 0xff;
1799
1800 icarry = (al < 6);
1801 if (((al & 0x0f) > 9 ) || af) {
1802 al = (al - 6) & 0x0f;
1803 ah = (ah - 1 - icarry) & 0xff;
1804 eflags |= CC_C | CC_A;
1805 } else {
1806 eflags &= ~(CC_C | CC_A);
1807 al &= 0x0f;
1808 }
1809 EAX = (EAX & ~0xffff) | al | (ah << 8);
1810 CC_SRC = eflags;
1811}
1812
1813void helper_daa(void)
1814{
1815 int al, af, cf;
1816 int eflags;
1817
1818 eflags = helper_cc_compute_all(CC_OP);
1819 cf = eflags & CC_C;
1820 af = eflags & CC_A;
1821 al = EAX & 0xff;
1822
1823 eflags = 0;
1824 if (((al & 0x0f) > 9 ) || af) {
1825 al = (al + 6) & 0xff;
1826 eflags |= CC_A;
1827 }
1828 if ((al > 0x9f) || cf) {
1829 al = (al + 0x60) & 0xff;
1830 eflags |= CC_C;
1831 }
1832 EAX = (EAX & ~0xff) | al;
1833 /* well, speed is not an issue here, so we compute the flags by hand */
1834 eflags |= (al == 0) << 6; /* zf */
1835 eflags |= parity_table[al]; /* pf */
1836 eflags |= (al & 0x80); /* sf */
1837 CC_SRC = eflags;
1838}
1839
1840void helper_das(void)
1841{
1842 int al, al1, af, cf;
1843 int eflags;
1844
1845 eflags = helper_cc_compute_all(CC_OP);
1846 cf = eflags & CC_C;
1847 af = eflags & CC_A;
1848 al = EAX & 0xff;
1849
1850 eflags = 0;
1851 al1 = al;
1852 if (((al & 0x0f) > 9 ) || af) {
1853 eflags |= CC_A;
1854 if (al < 6 || cf)
1855 eflags |= CC_C;
1856 al = (al - 6) & 0xff;
1857 }
1858 if ((al1 > 0x99) || cf) {
1859 al = (al - 0x60) & 0xff;
1860 eflags |= CC_C;
1861 }
1862 EAX = (EAX & ~0xff) | al;
1863 /* well, speed is not an issue here, so we compute the flags by hand */
1864 eflags |= (al == 0) << 6; /* zf */
1865 eflags |= parity_table[al]; /* pf */
1866 eflags |= (al & 0x80); /* sf */
1867 CC_SRC = eflags;
1868}
1869
1870void helper_into(int next_eip_addend)
1871{
1872 int eflags;
1873 eflags = helper_cc_compute_all(CC_OP);
1874 if (eflags & CC_O) {
1875 raise_interrupt(EXCP04_INTO, 1, 0, next_eip_addend);
1876 }
1877}
1878
1879void helper_cmpxchg8b(target_ulong a0)
1880{
1881 uint64_t d;
1882 int eflags;
1883
1884 eflags = helper_cc_compute_all(CC_OP);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001885 d = cpu_ldq_data(env, a0);
Jun Nakajima86797932011-01-29 14:24:24 -08001886 if (d == (((uint64_t)EDX << 32) | (uint32_t)EAX)) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001887 cpu_stq_data(env, a0, ((uint64_t)ECX << 32) | (uint32_t)EBX);
Jun Nakajima86797932011-01-29 14:24:24 -08001888 eflags |= CC_Z;
1889 } else {
1890 /* always do the store */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001891 cpu_stq_data(env, a0, d);
Jun Nakajima86797932011-01-29 14:24:24 -08001892 EDX = (uint32_t)(d >> 32);
1893 EAX = (uint32_t)d;
1894 eflags &= ~CC_Z;
1895 }
1896 CC_SRC = eflags;
1897}
1898
1899#ifdef TARGET_X86_64
1900void helper_cmpxchg16b(target_ulong a0)
1901{
1902 uint64_t d0, d1;
1903 int eflags;
1904
1905 if ((a0 & 0xf) != 0)
1906 raise_exception(EXCP0D_GPF);
1907 eflags = helper_cc_compute_all(CC_OP);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001908 d0 = cpu_ldq_data(env, a0);
1909 d1 = cpu_ldq_data(env, a0 + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08001910 if (d0 == EAX && d1 == EDX) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001911 cpu_stq_data(env, a0, EBX);
1912 cpu_stq_data(env, a0 + 8, ECX);
Jun Nakajima86797932011-01-29 14:24:24 -08001913 eflags |= CC_Z;
1914 } else {
1915 /* always do the store */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001916 cpu_stq_data(env, a0, d0);
1917 cpu_stq_data(env, a0 + 8, d1);
Jun Nakajima86797932011-01-29 14:24:24 -08001918 EDX = d1;
1919 EAX = d0;
1920 eflags &= ~CC_Z;
1921 }
1922 CC_SRC = eflags;
1923}
1924#endif
1925
1926void helper_single_step(void)
1927{
1928#ifndef CONFIG_USER_ONLY
1929 check_hw_breakpoints(env, 1);
1930 env->dr[6] |= DR6_BS;
1931#endif
1932 raise_exception(EXCP01_DB);
1933}
1934
1935void helper_cpuid(void)
1936{
1937 uint32_t eax, ebx, ecx, edx;
1938
1939 helper_svm_check_intercept_param(SVM_EXIT_CPUID, 0);
1940
1941 cpu_x86_cpuid(env, (uint32_t)EAX, (uint32_t)ECX, &eax, &ebx, &ecx, &edx);
1942 EAX = eax;
1943 EBX = ebx;
1944 ECX = ecx;
1945 EDX = edx;
1946}
1947
1948void helper_enter_level(int level, int data32, target_ulong t1)
1949{
1950 target_ulong ssp;
1951 uint32_t esp_mask, esp, ebp;
1952
1953 esp_mask = get_sp_mask(env->segs[R_SS].flags);
1954 ssp = env->segs[R_SS].base;
1955 ebp = EBP;
1956 esp = ESP;
1957 if (data32) {
1958 /* 32 bit */
1959 esp -= 4;
1960 while (--level) {
1961 esp -= 4;
1962 ebp -= 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001963 cpu_stl_data(env, ssp + (esp & esp_mask),
1964 cpu_ldl_data(env, ssp + (ebp & esp_mask)));
Jun Nakajima86797932011-01-29 14:24:24 -08001965 }
1966 esp -= 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001967 cpu_stl_data(env, ssp + (esp & esp_mask), t1);
Jun Nakajima86797932011-01-29 14:24:24 -08001968 } else {
1969 /* 16 bit */
1970 esp -= 2;
1971 while (--level) {
1972 esp -= 2;
1973 ebp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001974 cpu_stw_data(env, ssp + (esp & esp_mask),
1975 cpu_lduw_data(env, ssp + (ebp & esp_mask)));
Jun Nakajima86797932011-01-29 14:24:24 -08001976 }
1977 esp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001978 cpu_stw_data(env, ssp + (esp & esp_mask), t1);
Jun Nakajima86797932011-01-29 14:24:24 -08001979 }
1980}
1981
1982#ifdef TARGET_X86_64
1983void helper_enter64_level(int level, int data64, target_ulong t1)
1984{
1985 target_ulong esp, ebp;
1986 ebp = EBP;
1987 esp = ESP;
1988
1989 if (data64) {
1990 /* 64 bit */
1991 esp -= 8;
1992 while (--level) {
1993 esp -= 8;
1994 ebp -= 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001995 cpu_stq_data(env, esp, cpu_ldq_data(env, ebp));
Jun Nakajima86797932011-01-29 14:24:24 -08001996 }
1997 esp -= 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001998 cpu_stq_data(env, esp, t1);
Jun Nakajima86797932011-01-29 14:24:24 -08001999 } else {
2000 /* 16 bit */
2001 esp -= 2;
2002 while (--level) {
2003 esp -= 2;
2004 ebp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002005 cpu_stw_data(env, esp, cpu_lduw_data(env, ebp));
Jun Nakajima86797932011-01-29 14:24:24 -08002006 }
2007 esp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002008 cpu_stw_data(env, esp, t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002009 }
2010}
2011#endif
2012
2013void helper_lldt(int selector)
2014{
2015 SegmentCache *dt;
2016 uint32_t e1, e2;
2017 int index, entry_limit;
2018 target_ulong ptr;
2019
2020 selector &= 0xffff;
2021 if ((selector & 0xfffc) == 0) {
2022 /* XXX: NULL selector case: invalid LDT */
2023 env->ldt.base = 0;
2024 env->ldt.limit = 0;
2025 } else {
2026 if (selector & 0x4)
2027 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2028 dt = &env->gdt;
2029 index = selector & ~7;
2030#ifdef TARGET_X86_64
2031 if (env->hflags & HF_LMA_MASK)
2032 entry_limit = 15;
2033 else
2034#endif
2035 entry_limit = 7;
2036 if ((index + entry_limit) > dt->limit)
2037 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2038 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002039 e1 = cpu_ldl_kernel(env, ptr);
2040 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002041 if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
2042 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2043 if (!(e2 & DESC_P_MASK))
2044 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2045#ifdef TARGET_X86_64
2046 if (env->hflags & HF_LMA_MASK) {
2047 uint32_t e3;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002048 e3 = cpu_ldl_kernel(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08002049 load_seg_cache_raw_dt(&env->ldt, e1, e2);
2050 env->ldt.base |= (target_ulong)e3 << 32;
2051 } else
2052#endif
2053 {
2054 load_seg_cache_raw_dt(&env->ldt, e1, e2);
2055 }
2056 }
2057 env->ldt.selector = selector;
2058}
2059
2060void helper_ltr(int selector)
2061{
2062 SegmentCache *dt;
2063 uint32_t e1, e2;
2064 int index, type, entry_limit;
2065 target_ulong ptr;
2066
2067 selector &= 0xffff;
2068 if ((selector & 0xfffc) == 0) {
2069 /* NULL selector case: invalid TR */
2070 env->tr.base = 0;
2071 env->tr.limit = 0;
2072 env->tr.flags = 0;
2073 } else {
2074 if (selector & 0x4)
2075 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2076 dt = &env->gdt;
2077 index = selector & ~7;
2078#ifdef TARGET_X86_64
2079 if (env->hflags & HF_LMA_MASK)
2080 entry_limit = 15;
2081 else
2082#endif
2083 entry_limit = 7;
2084 if ((index + entry_limit) > dt->limit)
2085 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2086 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002087 e1 = cpu_ldl_kernel(env, ptr);
2088 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002089 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2090 if ((e2 & DESC_S_MASK) ||
2091 (type != 1 && type != 9))
2092 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2093 if (!(e2 & DESC_P_MASK))
2094 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2095#ifdef TARGET_X86_64
2096 if (env->hflags & HF_LMA_MASK) {
2097 uint32_t e3, e4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002098 e3 = cpu_ldl_kernel(env, ptr + 8);
2099 e4 = cpu_ldl_kernel(env, ptr + 12);
Jun Nakajima86797932011-01-29 14:24:24 -08002100 if ((e4 >> DESC_TYPE_SHIFT) & 0xf)
2101 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2102 load_seg_cache_raw_dt(&env->tr, e1, e2);
2103 env->tr.base |= (target_ulong)e3 << 32;
2104 } else
2105#endif
2106 {
2107 load_seg_cache_raw_dt(&env->tr, e1, e2);
2108 }
2109 e2 |= DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002110 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -08002111 }
2112 env->tr.selector = selector;
2113}
2114
2115/* only works if protected mode and not VM86. seg_reg must be != R_CS */
2116void helper_load_seg(int seg_reg, int selector)
2117{
2118 uint32_t e1, e2;
2119 int cpl, dpl, rpl;
2120 SegmentCache *dt;
2121 int index;
2122 target_ulong ptr;
2123
2124 selector &= 0xffff;
2125 cpl = env->hflags & HF_CPL_MASK;
2126 if ((selector & 0xfffc) == 0) {
2127 /* null selector case */
2128 if (seg_reg == R_SS
2129#ifdef TARGET_X86_64
2130 && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
2131#endif
2132 )
2133 raise_exception_err(EXCP0D_GPF, 0);
2134 cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
2135 } else {
2136
2137 if (selector & 0x4)
2138 dt = &env->ldt;
2139 else
2140 dt = &env->gdt;
2141 index = selector & ~7;
2142 if ((index + 7) > dt->limit)
2143 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2144 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002145 e1 = cpu_ldl_kernel(env, ptr);
2146 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002147
2148 if (!(e2 & DESC_S_MASK))
2149 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2150 rpl = selector & 3;
2151 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2152 if (seg_reg == R_SS) {
2153 /* must be writable segment */
2154 if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
2155 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2156 if (rpl != cpl || dpl != cpl)
2157 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2158 } else {
2159 /* must be readable segment */
2160 if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK)
2161 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2162
2163 if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2164 /* if not conforming code, test rights */
2165 if (dpl < cpl || dpl < rpl)
2166 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2167 }
2168 }
2169
2170 if (!(e2 & DESC_P_MASK)) {
2171 if (seg_reg == R_SS)
2172 raise_exception_err(EXCP0C_STACK, selector & 0xfffc);
2173 else
2174 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2175 }
2176
2177 /* set the access bit if not already set */
2178 if (!(e2 & DESC_A_MASK)) {
2179 e2 |= DESC_A_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002180 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -08002181 }
2182
2183 cpu_x86_load_seg_cache(env, seg_reg, selector,
2184 get_seg_base(e1, e2),
2185 get_seg_limit(e1, e2),
2186 e2);
2187#if 0
2188 qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
2189 selector, (unsigned long)sc->base, sc->limit, sc->flags);
2190#endif
2191 }
2192}
2193
2194/* protected mode jump */
2195void helper_ljmp_protected(int new_cs, target_ulong new_eip,
2196 int next_eip_addend)
2197{
2198 int gate_cs, type;
2199 uint32_t e1, e2, cpl, dpl, rpl, limit;
2200 target_ulong next_eip;
2201
2202 if ((new_cs & 0xfffc) == 0)
2203 raise_exception_err(EXCP0D_GPF, 0);
2204 if (load_segment(&e1, &e2, new_cs) != 0)
2205 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2206 cpl = env->hflags & HF_CPL_MASK;
2207 if (e2 & DESC_S_MASK) {
2208 if (!(e2 & DESC_CS_MASK))
2209 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2210 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2211 if (e2 & DESC_C_MASK) {
2212 /* conforming code segment */
2213 if (dpl > cpl)
2214 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2215 } else {
2216 /* non conforming code segment */
2217 rpl = new_cs & 3;
2218 if (rpl > cpl)
2219 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2220 if (dpl != cpl)
2221 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2222 }
2223 if (!(e2 & DESC_P_MASK))
2224 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2225 limit = get_seg_limit(e1, e2);
2226 if (new_eip > limit &&
2227 !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK))
2228 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2229 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2230 get_seg_base(e1, e2), limit, e2);
2231 EIP = new_eip;
2232 } else {
2233 /* jump to call or task gate */
2234 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2235 rpl = new_cs & 3;
2236 cpl = env->hflags & HF_CPL_MASK;
2237 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2238 switch(type) {
2239 case 1: /* 286 TSS */
2240 case 9: /* 386 TSS */
2241 case 5: /* task gate */
2242 if (dpl < cpl || dpl < rpl)
2243 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2244 next_eip = env->eip + next_eip_addend;
2245 switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
2246 CC_OP = CC_OP_EFLAGS;
2247 break;
2248 case 4: /* 286 call gate */
2249 case 12: /* 386 call gate */
2250 if ((dpl < cpl) || (dpl < rpl))
2251 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2252 if (!(e2 & DESC_P_MASK))
2253 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2254 gate_cs = e1 >> 16;
2255 new_eip = (e1 & 0xffff);
2256 if (type == 12)
2257 new_eip |= (e2 & 0xffff0000);
2258 if (load_segment(&e1, &e2, gate_cs) != 0)
2259 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2260 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2261 /* must be code segment */
2262 if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
2263 (DESC_S_MASK | DESC_CS_MASK)))
2264 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2265 if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
2266 (!(e2 & DESC_C_MASK) && (dpl != cpl)))
2267 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2268 if (!(e2 & DESC_P_MASK))
2269 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2270 limit = get_seg_limit(e1, e2);
2271 if (new_eip > limit)
2272 raise_exception_err(EXCP0D_GPF, 0);
2273 cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl,
2274 get_seg_base(e1, e2), limit, e2);
2275 EIP = new_eip;
2276 break;
2277 default:
2278 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2279 break;
2280 }
2281 }
2282}
2283
2284/* real mode call */
2285void helper_lcall_real(int new_cs, target_ulong new_eip1,
2286 int shift, int next_eip)
2287{
2288 int new_eip;
2289 uint32_t esp, esp_mask;
2290 target_ulong ssp;
2291
2292 new_eip = new_eip1;
2293 esp = ESP;
2294 esp_mask = get_sp_mask(env->segs[R_SS].flags);
2295 ssp = env->segs[R_SS].base;
2296 if (shift) {
2297 PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector);
2298 PUSHL(ssp, esp, esp_mask, next_eip);
2299 } else {
2300 PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector);
2301 PUSHW(ssp, esp, esp_mask, next_eip);
2302 }
2303
2304 SET_ESP(esp, esp_mask);
2305 env->eip = new_eip;
2306 env->segs[R_CS].selector = new_cs;
2307 env->segs[R_CS].base = (new_cs << 4);
2308}
2309
2310/* protected mode call */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02002311void helper_lcall_protected(int new_cs, target_ulong new_eip,
Jun Nakajima86797932011-01-29 14:24:24 -08002312 int shift, int next_eip_addend)
2313{
2314 int new_stack, i;
2315 uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
2316 uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask;
2317 uint32_t val, limit, old_sp_mask;
2318 target_ulong ssp, old_ssp, next_eip;
2319
2320 next_eip = env->eip + next_eip_addend;
2321 LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift);
2322 LOG_PCALL_STATE(env);
2323 if ((new_cs & 0xfffc) == 0)
2324 raise_exception_err(EXCP0D_GPF, 0);
2325 if (load_segment(&e1, &e2, new_cs) != 0)
2326 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2327 cpl = env->hflags & HF_CPL_MASK;
2328 LOG_PCALL("desc=%08x:%08x\n", e1, e2);
2329 if (e2 & DESC_S_MASK) {
2330 if (!(e2 & DESC_CS_MASK))
2331 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2332 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2333 if (e2 & DESC_C_MASK) {
2334 /* conforming code segment */
2335 if (dpl > cpl)
2336 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2337 } else {
2338 /* non conforming code segment */
2339 rpl = new_cs & 3;
2340 if (rpl > cpl)
2341 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2342 if (dpl != cpl)
2343 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2344 }
2345 if (!(e2 & DESC_P_MASK))
2346 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2347
2348#ifdef TARGET_X86_64
2349 /* XXX: check 16/32 bit cases in long mode */
2350 if (shift == 2) {
2351 target_ulong rsp;
2352 /* 64 bit case */
2353 rsp = ESP;
2354 PUSHQ(rsp, env->segs[R_CS].selector);
2355 PUSHQ(rsp, next_eip);
2356 /* from this point, not restartable */
2357 ESP = rsp;
2358 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2359 get_seg_base(e1, e2),
2360 get_seg_limit(e1, e2), e2);
2361 EIP = new_eip;
2362 } else
2363#endif
2364 {
2365 sp = ESP;
2366 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2367 ssp = env->segs[R_SS].base;
2368 if (shift) {
2369 PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
2370 PUSHL(ssp, sp, sp_mask, next_eip);
2371 } else {
2372 PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
2373 PUSHW(ssp, sp, sp_mask, next_eip);
2374 }
2375
2376 limit = get_seg_limit(e1, e2);
2377 if (new_eip > limit)
2378 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2379 /* from this point, not restartable */
2380 SET_ESP(sp, sp_mask);
2381 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2382 get_seg_base(e1, e2), limit, e2);
2383 EIP = new_eip;
2384 }
2385 } else {
2386 /* check gate type */
2387 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
2388 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2389 rpl = new_cs & 3;
2390 switch(type) {
2391 case 1: /* available 286 TSS */
2392 case 9: /* available 386 TSS */
2393 case 5: /* task gate */
2394 if (dpl < cpl || dpl < rpl)
2395 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2396 switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
2397 CC_OP = CC_OP_EFLAGS;
2398 return;
2399 case 4: /* 286 call gate */
2400 case 12: /* 386 call gate */
2401 break;
2402 default:
2403 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2404 break;
2405 }
2406 shift = type >> 3;
2407
2408 if (dpl < cpl || dpl < rpl)
2409 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2410 /* check valid bit */
2411 if (!(e2 & DESC_P_MASK))
2412 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2413 selector = e1 >> 16;
2414 offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
2415 param_count = e2 & 0x1f;
2416 if ((selector & 0xfffc) == 0)
2417 raise_exception_err(EXCP0D_GPF, 0);
2418
2419 if (load_segment(&e1, &e2, selector) != 0)
2420 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2421 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
2422 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2423 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2424 if (dpl > cpl)
2425 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2426 if (!(e2 & DESC_P_MASK))
2427 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2428
2429 if (!(e2 & DESC_C_MASK) && dpl < cpl) {
2430 /* to inner privilege */
2431 get_ss_esp_from_tss(&ss, &sp, dpl);
2432 LOG_PCALL("new ss:esp=%04x:%08x param_count=%d ESP=" TARGET_FMT_lx "\n",
2433 ss, sp, param_count, ESP);
2434 if ((ss & 0xfffc) == 0)
2435 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2436 if ((ss & 3) != dpl)
2437 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2438 if (load_segment(&ss_e1, &ss_e2, ss) != 0)
2439 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2440 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
2441 if (ss_dpl != dpl)
2442 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2443 if (!(ss_e2 & DESC_S_MASK) ||
2444 (ss_e2 & DESC_CS_MASK) ||
2445 !(ss_e2 & DESC_W_MASK))
2446 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2447 if (!(ss_e2 & DESC_P_MASK))
2448 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2449
2450 // push_size = ((param_count * 2) + 8) << shift;
2451
2452 old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
2453 old_ssp = env->segs[R_SS].base;
2454
2455 sp_mask = get_sp_mask(ss_e2);
2456 ssp = get_seg_base(ss_e1, ss_e2);
2457 if (shift) {
2458 PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector);
2459 PUSHL(ssp, sp, sp_mask, ESP);
2460 for(i = param_count - 1; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002461 val = cpu_ldl_kernel(env, old_ssp + ((ESP + i * 4) & old_sp_mask));
Jun Nakajima86797932011-01-29 14:24:24 -08002462 PUSHL(ssp, sp, sp_mask, val);
2463 }
2464 } else {
2465 PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector);
2466 PUSHW(ssp, sp, sp_mask, ESP);
2467 for(i = param_count - 1; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002468 val = cpu_lduw_kernel(env, old_ssp + ((ESP + i * 2) & old_sp_mask));
Jun Nakajima86797932011-01-29 14:24:24 -08002469 PUSHW(ssp, sp, sp_mask, val);
2470 }
2471 }
2472 new_stack = 1;
2473 } else {
2474 /* to same privilege */
2475 sp = ESP;
2476 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2477 ssp = env->segs[R_SS].base;
2478 // push_size = (4 << shift);
2479 new_stack = 0;
2480 }
2481
2482 if (shift) {
2483 PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
2484 PUSHL(ssp, sp, sp_mask, next_eip);
2485 } else {
2486 PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
2487 PUSHW(ssp, sp, sp_mask, next_eip);
2488 }
2489
2490 /* from this point, not restartable */
2491
2492 if (new_stack) {
2493 ss = (ss & ~3) | dpl;
2494 cpu_x86_load_seg_cache(env, R_SS, ss,
2495 ssp,
2496 get_seg_limit(ss_e1, ss_e2),
2497 ss_e2);
2498 }
2499
2500 selector = (selector & ~3) | dpl;
2501 cpu_x86_load_seg_cache(env, R_CS, selector,
2502 get_seg_base(e1, e2),
2503 get_seg_limit(e1, e2),
2504 e2);
2505 cpu_x86_set_cpl(env, dpl);
2506 SET_ESP(sp, sp_mask);
2507 EIP = offset;
2508 }
Jun Nakajima86797932011-01-29 14:24:24 -08002509}
2510
2511/* real and vm86 mode iret */
2512void helper_iret_real(int shift)
2513{
2514 uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
2515 target_ulong ssp;
2516 int eflags_mask;
2517
2518 sp_mask = 0xffff; /* XXXX: use SS segment size ? */
2519 sp = ESP;
2520 ssp = env->segs[R_SS].base;
2521 if (shift == 1) {
2522 /* 32 bits */
2523 POPL(ssp, sp, sp_mask, new_eip);
2524 POPL(ssp, sp, sp_mask, new_cs);
2525 new_cs &= 0xffff;
2526 POPL(ssp, sp, sp_mask, new_eflags);
2527 } else {
2528 /* 16 bits */
2529 POPW(ssp, sp, sp_mask, new_eip);
2530 POPW(ssp, sp, sp_mask, new_cs);
2531 POPW(ssp, sp, sp_mask, new_eflags);
2532 }
2533 ESP = (ESP & ~sp_mask) | (sp & sp_mask);
2534 env->segs[R_CS].selector = new_cs;
2535 env->segs[R_CS].base = (new_cs << 4);
2536 env->eip = new_eip;
2537 if (env->eflags & VM_MASK)
2538 eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK;
2539 else
2540 eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK | NT_MASK;
2541 if (shift == 0)
2542 eflags_mask &= 0xffff;
2543 load_eflags(new_eflags, eflags_mask);
2544 env->hflags2 &= ~HF2_NMI_MASK;
2545}
2546
2547static inline void validate_seg(int seg_reg, int cpl)
2548{
2549 int dpl;
2550 uint32_t e2;
2551
2552 /* XXX: on x86_64, we do not want to nullify FS and GS because
2553 they may still contain a valid base. I would be interested to
2554 know how a real x86_64 CPU behaves */
2555 if ((seg_reg == R_FS || seg_reg == R_GS) &&
2556 (env->segs[seg_reg].selector & 0xfffc) == 0)
2557 return;
2558
2559 e2 = env->segs[seg_reg].flags;
2560 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2561 if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2562 /* data or non conforming code segment */
2563 if (dpl < cpl) {
2564 cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0);
2565 }
2566 }
2567}
2568
2569/* protected mode iret */
2570static inline void helper_ret_protected(int shift, int is_iret, int addend)
2571{
2572 uint32_t new_cs, new_eflags, new_ss;
2573 uint32_t new_es, new_ds, new_fs, new_gs;
2574 uint32_t e1, e2, ss_e1, ss_e2;
2575 int cpl, dpl, rpl, eflags_mask, iopl;
2576 target_ulong ssp, sp, new_eip, new_esp, sp_mask;
2577
2578#ifdef TARGET_X86_64
2579 if (shift == 2)
2580 sp_mask = -1;
2581 else
2582#endif
2583 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2584 sp = ESP;
2585 ssp = env->segs[R_SS].base;
2586 new_eflags = 0; /* avoid warning */
2587#ifdef TARGET_X86_64
2588 if (shift == 2) {
2589 POPQ(sp, new_eip);
2590 POPQ(sp, new_cs);
2591 new_cs &= 0xffff;
2592 if (is_iret) {
2593 POPQ(sp, new_eflags);
2594 }
2595 } else
2596#endif
2597 if (shift == 1) {
2598 /* 32 bits */
2599 POPL(ssp, sp, sp_mask, new_eip);
2600 POPL(ssp, sp, sp_mask, new_cs);
2601 new_cs &= 0xffff;
2602 if (is_iret) {
2603 POPL(ssp, sp, sp_mask, new_eflags);
2604 if (new_eflags & VM_MASK)
2605 goto return_to_vm86;
2606 }
2607 } else {
2608 /* 16 bits */
2609 POPW(ssp, sp, sp_mask, new_eip);
2610 POPW(ssp, sp, sp_mask, new_cs);
2611 if (is_iret)
2612 POPW(ssp, sp, sp_mask, new_eflags);
2613 }
2614 LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
2615 new_cs, new_eip, shift, addend);
2616 LOG_PCALL_STATE(env);
2617 if ((new_cs & 0xfffc) == 0)
2618 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2619 if (load_segment(&e1, &e2, new_cs) != 0)
2620 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2621 if (!(e2 & DESC_S_MASK) ||
2622 !(e2 & DESC_CS_MASK))
2623 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2624 cpl = env->hflags & HF_CPL_MASK;
2625 rpl = new_cs & 3;
2626 if (rpl < cpl)
2627 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2628 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2629 if (e2 & DESC_C_MASK) {
2630 if (dpl > rpl)
2631 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2632 } else {
2633 if (dpl != rpl)
2634 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2635 }
2636 if (!(e2 & DESC_P_MASK))
2637 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2638
2639 sp += addend;
2640 if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
2641 ((env->hflags & HF_CS64_MASK) && !is_iret))) {
2642 /* return to same privilege level */
2643 cpu_x86_load_seg_cache(env, R_CS, new_cs,
2644 get_seg_base(e1, e2),
2645 get_seg_limit(e1, e2),
2646 e2);
2647 } else {
2648 /* return to different privilege level */
2649#ifdef TARGET_X86_64
2650 if (shift == 2) {
2651 POPQ(sp, new_esp);
2652 POPQ(sp, new_ss);
2653 new_ss &= 0xffff;
2654 } else
2655#endif
2656 if (shift == 1) {
2657 /* 32 bits */
2658 POPL(ssp, sp, sp_mask, new_esp);
2659 POPL(ssp, sp, sp_mask, new_ss);
2660 new_ss &= 0xffff;
2661 } else {
2662 /* 16 bits */
2663 POPW(ssp, sp, sp_mask, new_esp);
2664 POPW(ssp, sp, sp_mask, new_ss);
2665 }
2666 LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n",
2667 new_ss, new_esp);
2668 if ((new_ss & 0xfffc) == 0) {
2669#ifdef TARGET_X86_64
2670 /* NULL ss is allowed in long mode if cpl != 3*/
2671 /* XXX: test CS64 ? */
2672 if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
2673 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2674 0, 0xffffffff,
2675 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2676 DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
2677 DESC_W_MASK | DESC_A_MASK);
2678 ss_e2 = DESC_B_MASK; /* XXX: should not be needed ? */
2679 } else
2680#endif
2681 {
2682 raise_exception_err(EXCP0D_GPF, 0);
2683 }
2684 } else {
2685 if ((new_ss & 3) != rpl)
2686 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2687 if (load_segment(&ss_e1, &ss_e2, new_ss) != 0)
2688 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2689 if (!(ss_e2 & DESC_S_MASK) ||
2690 (ss_e2 & DESC_CS_MASK) ||
2691 !(ss_e2 & DESC_W_MASK))
2692 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2693 dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
2694 if (dpl != rpl)
2695 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2696 if (!(ss_e2 & DESC_P_MASK))
2697 raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
2698 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2699 get_seg_base(ss_e1, ss_e2),
2700 get_seg_limit(ss_e1, ss_e2),
2701 ss_e2);
2702 }
2703
2704 cpu_x86_load_seg_cache(env, R_CS, new_cs,
2705 get_seg_base(e1, e2),
2706 get_seg_limit(e1, e2),
2707 e2);
2708 cpu_x86_set_cpl(env, rpl);
2709 sp = new_esp;
2710#ifdef TARGET_X86_64
2711 if (env->hflags & HF_CS64_MASK)
2712 sp_mask = -1;
2713 else
2714#endif
2715 sp_mask = get_sp_mask(ss_e2);
2716
2717 /* validate data segments */
2718 validate_seg(R_ES, rpl);
2719 validate_seg(R_DS, rpl);
2720 validate_seg(R_FS, rpl);
2721 validate_seg(R_GS, rpl);
2722
2723 sp += addend;
2724 }
2725 SET_ESP(sp, sp_mask);
2726 env->eip = new_eip;
2727 if (is_iret) {
2728 /* NOTE: 'cpl' is the _old_ CPL */
2729 eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
2730 if (cpl == 0)
2731 eflags_mask |= IOPL_MASK;
2732 iopl = (env->eflags >> IOPL_SHIFT) & 3;
2733 if (cpl <= iopl)
2734 eflags_mask |= IF_MASK;
2735 if (shift == 0)
2736 eflags_mask &= 0xffff;
2737 load_eflags(new_eflags, eflags_mask);
2738 }
2739 return;
2740
2741 return_to_vm86:
2742 POPL(ssp, sp, sp_mask, new_esp);
2743 POPL(ssp, sp, sp_mask, new_ss);
2744 POPL(ssp, sp, sp_mask, new_es);
2745 POPL(ssp, sp, sp_mask, new_ds);
2746 POPL(ssp, sp, sp_mask, new_fs);
2747 POPL(ssp, sp, sp_mask, new_gs);
2748
2749 /* modify processor state */
2750 load_eflags(new_eflags, TF_MASK | AC_MASK | ID_MASK |
2751 IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | VIP_MASK);
2752 load_seg_vm(R_CS, new_cs & 0xffff);
2753 cpu_x86_set_cpl(env, 3);
2754 load_seg_vm(R_SS, new_ss & 0xffff);
2755 load_seg_vm(R_ES, new_es & 0xffff);
2756 load_seg_vm(R_DS, new_ds & 0xffff);
2757 load_seg_vm(R_FS, new_fs & 0xffff);
2758 load_seg_vm(R_GS, new_gs & 0xffff);
2759
2760 env->eip = new_eip & 0xffff;
2761 ESP = new_esp;
2762}
2763
2764void helper_iret_protected(int shift, int next_eip)
2765{
2766 int tss_selector, type;
2767 uint32_t e1, e2;
2768
2769 /* specific case for TSS */
2770 if (env->eflags & NT_MASK) {
2771#ifdef TARGET_X86_64
2772 if (env->hflags & HF_LMA_MASK)
2773 raise_exception_err(EXCP0D_GPF, 0);
2774#endif
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002775 tss_selector = cpu_lduw_kernel(env, env->tr.base + 0);
Jun Nakajima86797932011-01-29 14:24:24 -08002776 if (tss_selector & 4)
2777 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2778 if (load_segment(&e1, &e2, tss_selector) != 0)
2779 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2780 type = (e2 >> DESC_TYPE_SHIFT) & 0x17;
2781 /* NOTE: we check both segment and busy TSS */
2782 if (type != 3)
2783 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2784 switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip);
2785 } else {
2786 helper_ret_protected(shift, 1, 0);
2787 }
2788 env->hflags2 &= ~HF2_NMI_MASK;
Jun Nakajima86797932011-01-29 14:24:24 -08002789}
2790
2791void helper_lret_protected(int shift, int addend)
2792{
2793 helper_ret_protected(shift, 0, addend);
Jun Nakajima86797932011-01-29 14:24:24 -08002794}
2795
2796void helper_sysenter(void)
2797{
2798 if (env->sysenter_cs == 0) {
2799 raise_exception_err(EXCP0D_GPF, 0);
2800 }
2801 env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
2802 cpu_x86_set_cpl(env, 0);
2803
2804#ifdef TARGET_X86_64
2805 if (env->hflags & HF_LMA_MASK) {
2806 cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2807 0, 0xffffffff,
2808 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2809 DESC_S_MASK |
2810 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
2811 } else
2812#endif
2813 {
2814 cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2815 0, 0xffffffff,
2816 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2817 DESC_S_MASK |
2818 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
2819 }
2820 cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
2821 0, 0xffffffff,
2822 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2823 DESC_S_MASK |
2824 DESC_W_MASK | DESC_A_MASK);
2825 ESP = env->sysenter_esp;
2826 EIP = env->sysenter_eip;
2827}
2828
2829void helper_sysexit(int dflag)
2830{
2831 int cpl;
2832
2833 cpl = env->hflags & HF_CPL_MASK;
2834 if (env->sysenter_cs == 0 || cpl != 0) {
2835 raise_exception_err(EXCP0D_GPF, 0);
2836 }
2837 cpu_x86_set_cpl(env, 3);
2838#ifdef TARGET_X86_64
2839 if (dflag == 2) {
2840 cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | 3,
2841 0, 0xffffffff,
2842 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2843 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2844 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
2845 cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | 3,
2846 0, 0xffffffff,
2847 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2848 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2849 DESC_W_MASK | DESC_A_MASK);
2850 } else
2851#endif
2852 {
2853 cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3,
2854 0, 0xffffffff,
2855 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2856 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2857 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
2858 cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3,
2859 0, 0xffffffff,
2860 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2861 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2862 DESC_W_MASK | DESC_A_MASK);
2863 }
2864 ESP = ECX;
2865 EIP = EDX;
Jun Nakajima86797932011-01-29 14:24:24 -08002866}
2867
2868#if defined(CONFIG_USER_ONLY)
2869target_ulong helper_read_crN(int reg)
2870{
2871 return 0;
2872}
2873
2874void helper_write_crN(int reg, target_ulong t0)
2875{
2876}
2877
2878void helper_movl_drN_T0(int reg, target_ulong t0)
2879{
2880}
2881#else
2882target_ulong helper_read_crN(int reg)
2883{
2884 target_ulong val;
2885
2886 helper_svm_check_intercept_param(SVM_EXIT_READ_CR0 + reg, 0);
2887 switch(reg) {
2888 default:
2889 val = env->cr[reg];
2890 break;
2891 case 8:
2892 if (!(env->hflags2 & HF2_VINTR_MASK)) {
2893 val = cpu_get_apic_tpr(env);
2894 } else {
2895 val = env->v_tpr;
2896 }
2897 break;
2898 }
2899 return val;
2900}
2901
2902void helper_write_crN(int reg, target_ulong t0)
2903{
2904 helper_svm_check_intercept_param(SVM_EXIT_WRITE_CR0 + reg, 0);
2905 switch(reg) {
2906 case 0:
2907 cpu_x86_update_cr0(env, t0);
2908 break;
2909 case 3:
2910 cpu_x86_update_cr3(env, t0);
2911 break;
2912 case 4:
2913 cpu_x86_update_cr4(env, t0);
2914 break;
2915 case 8:
2916 if (!(env->hflags2 & HF2_VINTR_MASK)) {
2917 cpu_set_apic_tpr(env, t0);
2918 }
2919 env->v_tpr = t0 & 0x0f;
2920 break;
2921 default:
2922 env->cr[reg] = t0;
2923 break;
2924 }
2925}
2926
2927void helper_movl_drN_T0(int reg, target_ulong t0)
2928{
2929 int i;
2930
2931 if (reg < 4) {
2932 hw_breakpoint_remove(env, reg);
2933 env->dr[reg] = t0;
2934 hw_breakpoint_insert(env, reg);
2935 } else if (reg == 7) {
2936 for (i = 0; i < 4; i++)
2937 hw_breakpoint_remove(env, i);
2938 env->dr[7] = t0;
2939 for (i = 0; i < 4; i++)
2940 hw_breakpoint_insert(env, i);
2941 } else
2942 env->dr[reg] = t0;
2943}
2944#endif
2945
2946void helper_lmsw(target_ulong t0)
2947{
2948 /* only 4 lower bits of CR0 are modified. PE cannot be set to zero
2949 if already set to one. */
2950 t0 = (env->cr[0] & ~0xe) | (t0 & 0xf);
2951 helper_write_crN(0, t0);
2952}
2953
2954void helper_clts(void)
2955{
2956 env->cr[0] &= ~CR0_TS_MASK;
2957 env->hflags &= ~HF_TS_MASK;
2958}
2959
2960void helper_invlpg(target_ulong addr)
2961{
2962 helper_svm_check_intercept_param(SVM_EXIT_INVLPG, 0);
2963 tlb_flush_page(env, addr);
2964}
2965
2966void helper_rdtsc(void)
2967{
2968 uint64_t val;
2969
2970 if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
2971 raise_exception(EXCP0D_GPF);
2972 }
2973 helper_svm_check_intercept_param(SVM_EXIT_RDTSC, 0);
2974
2975 val = cpu_get_tsc(env) + env->tsc_offset;
2976 EAX = (uint32_t)(val);
2977 EDX = (uint32_t)(val >> 32);
2978}
2979
2980void helper_rdpmc(void)
2981{
2982 if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
2983 raise_exception(EXCP0D_GPF);
2984 }
2985 helper_svm_check_intercept_param(SVM_EXIT_RDPMC, 0);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02002986
Jun Nakajima86797932011-01-29 14:24:24 -08002987 /* currently unimplemented */
2988 raise_exception_err(EXCP06_ILLOP, 0);
2989}
2990
2991#if defined(CONFIG_USER_ONLY)
2992void helper_wrmsr(void)
2993{
2994}
2995
2996void helper_rdmsr(void)
2997{
2998}
2999#else
3000void helper_wrmsr(void)
3001{
3002 uint64_t val;
3003
3004 helper_svm_check_intercept_param(SVM_EXIT_MSR, 1);
3005
3006 val = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
3007
3008 switch((uint32_t)ECX) {
3009 case MSR_IA32_SYSENTER_CS:
3010 env->sysenter_cs = val & 0xffff;
3011 break;
3012 case MSR_IA32_SYSENTER_ESP:
3013 env->sysenter_esp = val;
3014 break;
3015 case MSR_IA32_SYSENTER_EIP:
3016 env->sysenter_eip = val;
3017 break;
3018 case MSR_IA32_APICBASE:
3019 cpu_set_apic_base(env, val);
3020 break;
3021 case MSR_EFER:
3022 {
3023 uint64_t update_mask;
3024 update_mask = 0;
3025 if (env->cpuid_ext2_features & CPUID_EXT2_SYSCALL)
3026 update_mask |= MSR_EFER_SCE;
3027 if (env->cpuid_ext2_features & CPUID_EXT2_LM)
3028 update_mask |= MSR_EFER_LME;
3029 if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
3030 update_mask |= MSR_EFER_FFXSR;
3031 if (env->cpuid_ext2_features & CPUID_EXT2_NX)
3032 update_mask |= MSR_EFER_NXE;
3033 if (env->cpuid_ext3_features & CPUID_EXT3_SVM)
3034 update_mask |= MSR_EFER_SVME;
3035 if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
3036 update_mask |= MSR_EFER_FFXSR;
3037 cpu_load_efer(env, (env->efer & ~update_mask) |
3038 (val & update_mask));
3039 }
3040 break;
3041 case MSR_STAR:
3042 env->star = val;
3043 break;
3044 case MSR_PAT:
3045 env->pat = val;
3046 break;
3047 case MSR_VM_HSAVE_PA:
3048 env->vm_hsave = val;
3049 break;
3050#ifdef TARGET_X86_64
3051 case MSR_LSTAR:
3052 env->lstar = val;
3053 break;
3054 case MSR_CSTAR:
3055 env->cstar = val;
3056 break;
3057 case MSR_FMASK:
3058 env->fmask = val;
3059 break;
3060 case MSR_FSBASE:
3061 env->segs[R_FS].base = val;
3062 break;
3063 case MSR_GSBASE:
3064 env->segs[R_GS].base = val;
3065 break;
3066 case MSR_KERNELGSBASE:
3067 env->kernelgsbase = val;
3068 break;
3069#endif
3070 case MSR_MTRRphysBase(0):
3071 case MSR_MTRRphysBase(1):
3072 case MSR_MTRRphysBase(2):
3073 case MSR_MTRRphysBase(3):
3074 case MSR_MTRRphysBase(4):
3075 case MSR_MTRRphysBase(5):
3076 case MSR_MTRRphysBase(6):
3077 case MSR_MTRRphysBase(7):
3078 env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base = val;
3079 break;
3080 case MSR_MTRRphysMask(0):
3081 case MSR_MTRRphysMask(1):
3082 case MSR_MTRRphysMask(2):
3083 case MSR_MTRRphysMask(3):
3084 case MSR_MTRRphysMask(4):
3085 case MSR_MTRRphysMask(5):
3086 case MSR_MTRRphysMask(6):
3087 case MSR_MTRRphysMask(7):
3088 env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask = val;
3089 break;
3090 case MSR_MTRRfix64K_00000:
3091 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix64K_00000] = val;
3092 break;
3093 case MSR_MTRRfix16K_80000:
3094 case MSR_MTRRfix16K_A0000:
3095 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1] = val;
3096 break;
3097 case MSR_MTRRfix4K_C0000:
3098 case MSR_MTRRfix4K_C8000:
3099 case MSR_MTRRfix4K_D0000:
3100 case MSR_MTRRfix4K_D8000:
3101 case MSR_MTRRfix4K_E0000:
3102 case MSR_MTRRfix4K_E8000:
3103 case MSR_MTRRfix4K_F0000:
3104 case MSR_MTRRfix4K_F8000:
3105 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3] = val;
3106 break;
3107 case MSR_MTRRdefType:
3108 env->mtrr_deftype = val;
3109 break;
3110 case MSR_MCG_STATUS:
3111 env->mcg_status = val;
3112 break;
3113 case MSR_MCG_CTL:
3114 if ((env->mcg_cap & MCG_CTL_P)
3115 && (val == 0 || val == ~(uint64_t)0))
3116 env->mcg_ctl = val;
3117 break;
3118 default:
3119 if ((uint32_t)ECX >= MSR_MC0_CTL
3120 && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
3121 uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
3122 if ((offset & 0x3) != 0
3123 || (val == 0 || val == ~(uint64_t)0))
3124 env->mce_banks[offset] = val;
3125 break;
3126 }
3127 /* XXX: exception ? */
3128 break;
3129 }
3130}
3131
3132void helper_rdmsr(void)
3133{
3134 uint64_t val;
3135
3136 helper_svm_check_intercept_param(SVM_EXIT_MSR, 0);
3137
3138 switch((uint32_t)ECX) {
3139 case MSR_IA32_SYSENTER_CS:
3140 val = env->sysenter_cs;
3141 break;
3142 case MSR_IA32_SYSENTER_ESP:
3143 val = env->sysenter_esp;
3144 break;
3145 case MSR_IA32_SYSENTER_EIP:
3146 val = env->sysenter_eip;
3147 break;
3148 case MSR_IA32_APICBASE:
3149 val = cpu_get_apic_base(env);
3150 break;
3151 case MSR_EFER:
3152 val = env->efer;
3153 break;
3154 case MSR_STAR:
3155 val = env->star;
3156 break;
3157 case MSR_PAT:
3158 val = env->pat;
3159 break;
3160 case MSR_VM_HSAVE_PA:
3161 val = env->vm_hsave;
3162 break;
3163 case MSR_IA32_PERF_STATUS:
3164 /* tsc_increment_by_tick */
3165 val = 1000ULL;
3166 /* CPU multiplier */
3167 val |= (((uint64_t)4ULL) << 40);
3168 break;
3169#ifdef TARGET_X86_64
3170 case MSR_LSTAR:
3171 val = env->lstar;
3172 break;
3173 case MSR_CSTAR:
3174 val = env->cstar;
3175 break;
3176 case MSR_FMASK:
3177 val = env->fmask;
3178 break;
3179 case MSR_FSBASE:
3180 val = env->segs[R_FS].base;
3181 break;
3182 case MSR_GSBASE:
3183 val = env->segs[R_GS].base;
3184 break;
3185 case MSR_KERNELGSBASE:
3186 val = env->kernelgsbase;
3187 break;
3188#endif
Jun Nakajima86797932011-01-29 14:24:24 -08003189 case MSR_MTRRphysBase(0):
3190 case MSR_MTRRphysBase(1):
3191 case MSR_MTRRphysBase(2):
3192 case MSR_MTRRphysBase(3):
3193 case MSR_MTRRphysBase(4):
3194 case MSR_MTRRphysBase(5):
3195 case MSR_MTRRphysBase(6):
3196 case MSR_MTRRphysBase(7):
3197 val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base;
3198 break;
3199 case MSR_MTRRphysMask(0):
3200 case MSR_MTRRphysMask(1):
3201 case MSR_MTRRphysMask(2):
3202 case MSR_MTRRphysMask(3):
3203 case MSR_MTRRphysMask(4):
3204 case MSR_MTRRphysMask(5):
3205 case MSR_MTRRphysMask(6):
3206 case MSR_MTRRphysMask(7):
3207 val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask;
3208 break;
3209 case MSR_MTRRfix64K_00000:
3210 val = env->mtrr_fixed[0];
3211 break;
3212 case MSR_MTRRfix16K_80000:
3213 case MSR_MTRRfix16K_A0000:
3214 val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1];
3215 break;
3216 case MSR_MTRRfix4K_C0000:
3217 case MSR_MTRRfix4K_C8000:
3218 case MSR_MTRRfix4K_D0000:
3219 case MSR_MTRRfix4K_D8000:
3220 case MSR_MTRRfix4K_E0000:
3221 case MSR_MTRRfix4K_E8000:
3222 case MSR_MTRRfix4K_F0000:
3223 case MSR_MTRRfix4K_F8000:
3224 val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3];
3225 break;
3226 case MSR_MTRRdefType:
3227 val = env->mtrr_deftype;
3228 break;
3229 case MSR_MTRRcap:
3230 if (env->cpuid_features & CPUID_MTRR)
3231 val = MSR_MTRRcap_VCNT | MSR_MTRRcap_FIXRANGE_SUPPORT | MSR_MTRRcap_WC_SUPPORTED;
3232 else
3233 /* XXX: exception ? */
3234 val = 0;
3235 break;
3236 case MSR_MCG_CAP:
3237 val = env->mcg_cap;
3238 break;
3239 case MSR_MCG_CTL:
3240 if (env->mcg_cap & MCG_CTL_P)
3241 val = env->mcg_ctl;
3242 else
3243 val = 0;
3244 break;
3245 case MSR_MCG_STATUS:
3246 val = env->mcg_status;
3247 break;
3248 default:
3249 if ((uint32_t)ECX >= MSR_MC0_CTL
3250 && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
3251 uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
3252 val = env->mce_banks[offset];
3253 break;
3254 }
3255 /* XXX: exception ? */
3256 val = 0;
3257 break;
3258 }
3259 EAX = (uint32_t)(val);
3260 EDX = (uint32_t)(val >> 32);
3261}
3262#endif
3263
3264target_ulong helper_lsl(target_ulong selector1)
3265{
3266 unsigned int limit;
3267 uint32_t e1, e2, eflags, selector;
3268 int rpl, dpl, cpl, type;
3269
3270 selector = selector1 & 0xffff;
3271 eflags = helper_cc_compute_all(CC_OP);
3272 if ((selector & 0xfffc) == 0)
3273 goto fail;
3274 if (load_segment(&e1, &e2, selector) != 0)
3275 goto fail;
3276 rpl = selector & 3;
3277 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3278 cpl = env->hflags & HF_CPL_MASK;
3279 if (e2 & DESC_S_MASK) {
3280 if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
3281 /* conforming */
3282 } else {
3283 if (dpl < cpl || dpl < rpl)
3284 goto fail;
3285 }
3286 } else {
3287 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
3288 switch(type) {
3289 case 1:
3290 case 2:
3291 case 3:
3292 case 9:
3293 case 11:
3294 break;
3295 default:
3296 goto fail;
3297 }
3298 if (dpl < cpl || dpl < rpl) {
3299 fail:
3300 CC_SRC = eflags & ~CC_Z;
3301 return 0;
3302 }
3303 }
3304 limit = get_seg_limit(e1, e2);
3305 CC_SRC = eflags | CC_Z;
3306 return limit;
3307}
3308
3309target_ulong helper_lar(target_ulong selector1)
3310{
3311 uint32_t e1, e2, eflags, selector;
3312 int rpl, dpl, cpl, type;
3313
3314 selector = selector1 & 0xffff;
3315 eflags = helper_cc_compute_all(CC_OP);
3316 if ((selector & 0xfffc) == 0)
3317 goto fail;
3318 if (load_segment(&e1, &e2, selector) != 0)
3319 goto fail;
3320 rpl = selector & 3;
3321 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3322 cpl = env->hflags & HF_CPL_MASK;
3323 if (e2 & DESC_S_MASK) {
3324 if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
3325 /* conforming */
3326 } else {
3327 if (dpl < cpl || dpl < rpl)
3328 goto fail;
3329 }
3330 } else {
3331 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
3332 switch(type) {
3333 case 1:
3334 case 2:
3335 case 3:
3336 case 4:
3337 case 5:
3338 case 9:
3339 case 11:
3340 case 12:
3341 break;
3342 default:
3343 goto fail;
3344 }
3345 if (dpl < cpl || dpl < rpl) {
3346 fail:
3347 CC_SRC = eflags & ~CC_Z;
3348 return 0;
3349 }
3350 }
3351 CC_SRC = eflags | CC_Z;
3352 return e2 & 0x00f0ff00;
3353}
3354
3355void helper_verr(target_ulong selector1)
3356{
3357 uint32_t e1, e2, eflags, selector;
3358 int rpl, dpl, cpl;
3359
3360 selector = selector1 & 0xffff;
3361 eflags = helper_cc_compute_all(CC_OP);
3362 if ((selector & 0xfffc) == 0)
3363 goto fail;
3364 if (load_segment(&e1, &e2, selector) != 0)
3365 goto fail;
3366 if (!(e2 & DESC_S_MASK))
3367 goto fail;
3368 rpl = selector & 3;
3369 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3370 cpl = env->hflags & HF_CPL_MASK;
3371 if (e2 & DESC_CS_MASK) {
3372 if (!(e2 & DESC_R_MASK))
3373 goto fail;
3374 if (!(e2 & DESC_C_MASK)) {
3375 if (dpl < cpl || dpl < rpl)
3376 goto fail;
3377 }
3378 } else {
3379 if (dpl < cpl || dpl < rpl) {
3380 fail:
3381 CC_SRC = eflags & ~CC_Z;
3382 return;
3383 }
3384 }
3385 CC_SRC = eflags | CC_Z;
3386}
3387
3388void helper_verw(target_ulong selector1)
3389{
3390 uint32_t e1, e2, eflags, selector;
3391 int rpl, dpl, cpl;
3392
3393 selector = selector1 & 0xffff;
3394 eflags = helper_cc_compute_all(CC_OP);
3395 if ((selector & 0xfffc) == 0)
3396 goto fail;
3397 if (load_segment(&e1, &e2, selector) != 0)
3398 goto fail;
3399 if (!(e2 & DESC_S_MASK))
3400 goto fail;
3401 rpl = selector & 3;
3402 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3403 cpl = env->hflags & HF_CPL_MASK;
3404 if (e2 & DESC_CS_MASK) {
3405 goto fail;
3406 } else {
3407 if (dpl < cpl || dpl < rpl)
3408 goto fail;
3409 if (!(e2 & DESC_W_MASK)) {
3410 fail:
3411 CC_SRC = eflags & ~CC_Z;
3412 return;
3413 }
3414 }
3415 CC_SRC = eflags | CC_Z;
3416}
3417
3418/* x87 FPU helpers */
3419
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003420static inline double floatx80_to_double(CPUX86State *env, floatx80 a)
3421{
3422 union {
3423 float64 f64;
3424 double d;
3425 } u;
3426
3427 u.f64 = floatx80_to_float64(a, &env->fp_status);
3428 return u.d;
3429}
3430
3431static inline floatx80 double_to_floatx80(CPUX86State *env, double a)
3432{
3433 union {
3434 float64 f64;
3435 double d;
3436 } u;
3437
3438 u.d = a;
3439 return float64_to_floatx80(u.f64, &env->fp_status);
3440}
3441
Jun Nakajima86797932011-01-29 14:24:24 -08003442static void fpu_set_exception(int mask)
3443{
3444 env->fpus |= mask;
3445 if (env->fpus & (~env->fpuc & FPUC_EM))
3446 env->fpus |= FPUS_SE | FPUS_B;
3447}
3448
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003449static inline floatx80 helper_fdiv(floatx80 a, floatx80 b)
Jun Nakajima86797932011-01-29 14:24:24 -08003450{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003451 if (floatx80_is_zero(b)) {
Jun Nakajima86797932011-01-29 14:24:24 -08003452 fpu_set_exception(FPUS_ZE);
David 'Digit' Turner763b5972014-03-26 17:10:52 +01003453 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003454 return floatx80_div(a, b, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003455}
3456
3457static void fpu_raise_exception(void)
3458{
3459 if (env->cr[0] & CR0_NE_MASK) {
3460 raise_exception(EXCP10_COPR);
3461 }
3462#if !defined(CONFIG_USER_ONLY)
3463 else {
3464 cpu_set_ferr(env);
3465 }
3466#endif
3467}
3468
3469void helper_flds_FT0(uint32_t val)
3470{
3471 union {
3472 float32 f;
3473 uint32_t i;
3474 } u;
3475 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003476 FT0 = float32_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003477}
3478
3479void helper_fldl_FT0(uint64_t val)
3480{
3481 union {
3482 float64 f;
3483 uint64_t i;
3484 } u;
3485 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003486 FT0 = float64_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003487}
3488
3489void helper_fildl_FT0(int32_t val)
3490{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003491 FT0 = int32_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003492}
3493
3494void helper_flds_ST0(uint32_t val)
3495{
3496 int new_fpstt;
3497 union {
3498 float32 f;
3499 uint32_t i;
3500 } u;
3501 new_fpstt = (env->fpstt - 1) & 7;
3502 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003503 env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003504 env->fpstt = new_fpstt;
3505 env->fptags[new_fpstt] = 0; /* validate stack entry */
3506}
3507
3508void helper_fldl_ST0(uint64_t val)
3509{
3510 int new_fpstt;
3511 union {
3512 float64 f;
3513 uint64_t i;
3514 } u;
3515 new_fpstt = (env->fpstt - 1) & 7;
3516 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003517 env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003518 env->fpstt = new_fpstt;
3519 env->fptags[new_fpstt] = 0; /* validate stack entry */
3520}
3521
3522void helper_fildl_ST0(int32_t val)
3523{
3524 int new_fpstt;
3525 new_fpstt = (env->fpstt - 1) & 7;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003526 env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003527 env->fpstt = new_fpstt;
3528 env->fptags[new_fpstt] = 0; /* validate stack entry */
3529}
3530
3531void helper_fildll_ST0(int64_t val)
3532{
3533 int new_fpstt;
3534 new_fpstt = (env->fpstt - 1) & 7;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003535 env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003536 env->fpstt = new_fpstt;
3537 env->fptags[new_fpstt] = 0; /* validate stack entry */
3538}
3539
3540uint32_t helper_fsts_ST0(void)
3541{
3542 union {
3543 float32 f;
3544 uint32_t i;
3545 } u;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003546 u.f = floatx80_to_float32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003547 return u.i;
3548}
3549
3550uint64_t helper_fstl_ST0(void)
3551{
3552 union {
3553 float64 f;
3554 uint64_t i;
3555 } u;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003556 u.f = floatx80_to_float64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003557 return u.i;
3558}
3559
3560int32_t helper_fist_ST0(void)
3561{
3562 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003563 val = floatx80_to_int32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003564 if (val != (int16_t)val)
3565 val = -32768;
3566 return val;
3567}
3568
3569int32_t helper_fistl_ST0(void)
3570{
3571 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003572 val = floatx80_to_int32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003573 return val;
3574}
3575
3576int64_t helper_fistll_ST0(void)
3577{
3578 int64_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003579 val = floatx80_to_int64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003580 return val;
3581}
3582
3583int32_t helper_fistt_ST0(void)
3584{
3585 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003586 val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003587 if (val != (int16_t)val)
3588 val = -32768;
3589 return val;
3590}
3591
3592int32_t helper_fisttl_ST0(void)
3593{
3594 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003595 val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003596 return val;
3597}
3598
3599int64_t helper_fisttll_ST0(void)
3600{
3601 int64_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003602 val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003603 return val;
3604}
3605
3606void helper_fldt_ST0(target_ulong ptr)
3607{
3608 int new_fpstt;
3609 new_fpstt = (env->fpstt - 1) & 7;
3610 env->fpregs[new_fpstt].d = helper_fldt(ptr);
3611 env->fpstt = new_fpstt;
3612 env->fptags[new_fpstt] = 0; /* validate stack entry */
3613}
3614
3615void helper_fstt_ST0(target_ulong ptr)
3616{
3617 helper_fstt(ST0, ptr);
3618}
3619
3620void helper_fpush(void)
3621{
3622 fpush();
3623}
3624
3625void helper_fpop(void)
3626{
3627 fpop();
3628}
3629
3630void helper_fdecstp(void)
3631{
3632 env->fpstt = (env->fpstt - 1) & 7;
3633 env->fpus &= (~0x4700);
3634}
3635
3636void helper_fincstp(void)
3637{
3638 env->fpstt = (env->fpstt + 1) & 7;
3639 env->fpus &= (~0x4700);
3640}
3641
3642/* FPU move */
3643
3644void helper_ffree_STN(int st_index)
3645{
3646 env->fptags[(env->fpstt + st_index) & 7] = 1;
3647}
3648
3649void helper_fmov_ST0_FT0(void)
3650{
3651 ST0 = FT0;
3652}
3653
3654void helper_fmov_FT0_STN(int st_index)
3655{
3656 FT0 = ST(st_index);
3657}
3658
3659void helper_fmov_ST0_STN(int st_index)
3660{
3661 ST0 = ST(st_index);
3662}
3663
3664void helper_fmov_STN_ST0(int st_index)
3665{
3666 ST(st_index) = ST0;
3667}
3668
3669void helper_fxchg_ST0_STN(int st_index)
3670{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003671 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08003672 tmp = ST(st_index);
3673 ST(st_index) = ST0;
3674 ST0 = tmp;
3675}
3676
3677/* FPU operations */
3678
3679static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500};
3680
3681void helper_fcom_ST0_FT0(void)
3682{
3683 int ret;
3684
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003685 ret = floatx80_compare(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003686 env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
3687}
3688
3689void helper_fucom_ST0_FT0(void)
3690{
3691 int ret;
3692
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003693 ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003694 env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1];
3695}
3696
3697static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
3698
3699void helper_fcomi_ST0_FT0(void)
3700{
3701 int eflags;
3702 int ret;
3703
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003704 ret = floatx80_compare(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003705 eflags = helper_cc_compute_all(CC_OP);
3706 eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
3707 CC_SRC = eflags;
3708}
3709
3710void helper_fucomi_ST0_FT0(void)
3711{
3712 int eflags;
3713 int ret;
3714
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003715 ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003716 eflags = helper_cc_compute_all(CC_OP);
3717 eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
3718 CC_SRC = eflags;
3719}
3720
3721void helper_fadd_ST0_FT0(void)
3722{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003723 ST0 = floatx80_add(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003724}
3725
3726void helper_fmul_ST0_FT0(void)
3727{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003728 ST0 = floatx80_mul(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003729}
3730
3731void helper_fsub_ST0_FT0(void)
3732{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003733 ST0 = floatx80_sub(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003734}
3735
3736void helper_fsubr_ST0_FT0(void)
3737{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003738 ST0 = floatx80_sub(FT0, ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003739}
3740
3741void helper_fdiv_ST0_FT0(void)
3742{
3743 ST0 = helper_fdiv(ST0, FT0);
3744}
3745
3746void helper_fdivr_ST0_FT0(void)
3747{
3748 ST0 = helper_fdiv(FT0, ST0);
3749}
3750
3751/* fp operations between STN and ST0 */
3752
3753void helper_fadd_STN_ST0(int st_index)
3754{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003755 ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003756}
3757
3758void helper_fmul_STN_ST0(int st_index)
3759{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003760 ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003761}
3762
3763void helper_fsub_STN_ST0(int st_index)
3764{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003765 ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003766}
3767
3768void helper_fsubr_STN_ST0(int st_index)
3769{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003770 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003771 p = &ST(st_index);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003772 *p = floatx80_sub(ST0, *p, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003773}
3774
3775void helper_fdiv_STN_ST0(int st_index)
3776{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003777 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003778 p = &ST(st_index);
3779 *p = helper_fdiv(*p, ST0);
3780}
3781
3782void helper_fdivr_STN_ST0(int st_index)
3783{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003784 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003785 p = &ST(st_index);
3786 *p = helper_fdiv(ST0, *p);
3787}
3788
3789/* misc FPU operations */
3790void helper_fchs_ST0(void)
3791{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003792 ST0 = floatx80_chs(ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003793}
3794
3795void helper_fabs_ST0(void)
3796{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003797 ST0 = floatx80_abs(ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003798}
3799
3800void helper_fld1_ST0(void)
3801{
3802 ST0 = f15rk[1];
3803}
3804
3805void helper_fldl2t_ST0(void)
3806{
3807 ST0 = f15rk[6];
3808}
3809
3810void helper_fldl2e_ST0(void)
3811{
3812 ST0 = f15rk[5];
3813}
3814
3815void helper_fldpi_ST0(void)
3816{
3817 ST0 = f15rk[2];
3818}
3819
3820void helper_fldlg2_ST0(void)
3821{
3822 ST0 = f15rk[3];
3823}
3824
3825void helper_fldln2_ST0(void)
3826{
3827 ST0 = f15rk[4];
3828}
3829
3830void helper_fldz_ST0(void)
3831{
3832 ST0 = f15rk[0];
3833}
3834
3835void helper_fldz_FT0(void)
3836{
3837 FT0 = f15rk[0];
3838}
3839
3840uint32_t helper_fnstsw(void)
3841{
3842 return (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
3843}
3844
3845uint32_t helper_fnstcw(void)
3846{
3847 return env->fpuc;
3848}
3849
3850static void update_fp_status(void)
3851{
3852 int rnd_type;
3853
3854 /* set rounding mode */
3855 switch(env->fpuc & RC_MASK) {
3856 default:
3857 case RC_NEAR:
3858 rnd_type = float_round_nearest_even;
3859 break;
3860 case RC_DOWN:
3861 rnd_type = float_round_down;
3862 break;
3863 case RC_UP:
3864 rnd_type = float_round_up;
3865 break;
3866 case RC_CHOP:
3867 rnd_type = float_round_to_zero;
3868 break;
3869 }
3870 set_float_rounding_mode(rnd_type, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003871 switch((env->fpuc >> 8) & 3) {
3872 case 0:
3873 rnd_type = 32;
3874 break;
3875 case 2:
3876 rnd_type = 64;
3877 break;
3878 case 3:
3879 default:
3880 rnd_type = 80;
3881 break;
3882 }
3883 set_floatx80_rounding_precision(rnd_type, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003884}
3885
3886void helper_fldcw(uint32_t val)
3887{
3888 env->fpuc = val;
3889 update_fp_status();
3890}
3891
3892void helper_fclex(void)
3893{
3894 env->fpus &= 0x7f00;
3895}
3896
3897void helper_fwait(void)
3898{
3899 if (env->fpus & FPUS_SE)
3900 fpu_raise_exception();
3901}
3902
3903void helper_fninit(void)
3904{
3905 env->fpus = 0;
3906 env->fpstt = 0;
3907 env->fpuc = 0x37f;
3908 env->fptags[0] = 1;
3909 env->fptags[1] = 1;
3910 env->fptags[2] = 1;
3911 env->fptags[3] = 1;
3912 env->fptags[4] = 1;
3913 env->fptags[5] = 1;
3914 env->fptags[6] = 1;
3915 env->fptags[7] = 1;
3916}
3917
3918/* BCD ops */
3919
3920void helper_fbld_ST0(target_ulong ptr)
3921{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003922 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08003923 uint64_t val;
3924 unsigned int v;
3925 int i;
3926
3927 val = 0;
3928 for(i = 8; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003929 v = cpu_ldub_data(env, ptr + i);
Jun Nakajima86797932011-01-29 14:24:24 -08003930 val = (val * 100) + ((v >> 4) * 10) + (v & 0xf);
3931 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003932 tmp = int64_to_floatx80(val, &env->fp_status);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003933 if (cpu_ldub_data(env, ptr + 9) & 0x80) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003934 floatx80_chs(tmp);
3935 }
Jun Nakajima86797932011-01-29 14:24:24 -08003936 fpush();
3937 ST0 = tmp;
3938}
3939
3940void helper_fbst_ST0(target_ulong ptr)
3941{
3942 int v;
3943 target_ulong mem_ref, mem_end;
3944 int64_t val;
3945
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003946 val = floatx80_to_int64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003947 mem_ref = ptr;
3948 mem_end = mem_ref + 9;
3949 if (val < 0) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003950 cpu_stb_data(env, mem_end, 0x80);
Jun Nakajima86797932011-01-29 14:24:24 -08003951 val = -val;
3952 } else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003953 cpu_stb_data(env, mem_end, 0x00);
Jun Nakajima86797932011-01-29 14:24:24 -08003954 }
3955 while (mem_ref < mem_end) {
3956 if (val == 0)
3957 break;
3958 v = val % 100;
3959 val = val / 100;
3960 v = ((v / 10) << 4) | (v % 10);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003961 cpu_stb_data(env, mem_ref++, v);
Jun Nakajima86797932011-01-29 14:24:24 -08003962 }
3963 while (mem_ref < mem_end) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003964 cpu_stb_data(env, mem_ref++, 0);
Jun Nakajima86797932011-01-29 14:24:24 -08003965 }
3966}
3967
3968void helper_f2xm1(void)
3969{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003970 double val = floatx80_to_double(env, ST0);
3971 val = pow(2.0, val) - 1.0;
3972 ST0 = double_to_floatx80(env, val);
Jun Nakajima86797932011-01-29 14:24:24 -08003973}
3974
3975void helper_fyl2x(void)
3976{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003977 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003978
Jun Nakajima86797932011-01-29 14:24:24 -08003979 if (fptemp>0.0){
3980 fptemp = log(fptemp)/log(2.0); /* log2(ST) */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003981 fptemp *= floatx80_to_double(env, ST1);
3982 ST1 = double_to_floatx80(env, fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08003983 fpop();
3984 } else {
3985 env->fpus &= (~0x4700);
3986 env->fpus |= 0x400;
3987 }
3988}
3989
3990void helper_fptan(void)
3991{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003992 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003993
Jun Nakajima86797932011-01-29 14:24:24 -08003994 if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
3995 env->fpus |= 0x400;
3996 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003997 fptemp = tan(fptemp);
3998 ST0 = double_to_floatx80(env, fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08003999 fpush();
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004000 ST0 = floatx80_one;
Jun Nakajima86797932011-01-29 14:24:24 -08004001 env->fpus &= (~0x400); /* C2 <-- 0 */
4002 /* the above code is for |arg| < 2**52 only */
4003 }
4004}
4005
4006void helper_fpatan(void)
4007{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004008 double fptemp, fpsrcop;
Jun Nakajima86797932011-01-29 14:24:24 -08004009
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004010 fpsrcop = floatx80_to_double(env, ST1);
4011 fptemp = floatx80_to_double(env, ST0);
4012 ST1 = double_to_floatx80(env, atan2(fpsrcop,fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004013 fpop();
4014}
4015
4016void helper_fxtract(void)
4017{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004018 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004019 unsigned int expdif;
4020
4021 temp.d = ST0;
4022 expdif = EXPD(temp) - EXPBIAS;
4023 /*DP exponent bias*/
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004024 ST0 = int32_to_floatx80(expdif, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004025 fpush();
4026 BIASEXPONENT(temp);
4027 ST0 = temp.d;
4028}
4029
4030void helper_fprem1(void)
4031{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004032 double st0, st1, dblq, fpsrcop, fptemp;
4033 CPU_LDoubleU fpsrcop1, fptemp1;
Jun Nakajima86797932011-01-29 14:24:24 -08004034 int expdif;
4035 signed long long int q;
4036
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004037 st0 = floatx80_to_double(env, ST0);
4038 st1 = floatx80_to_double(env, ST1);
4039
4040 if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
4041 ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */
Jun Nakajima86797932011-01-29 14:24:24 -08004042 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4043 return;
4044 }
4045
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004046 fpsrcop = st0;
4047 fptemp = st1;
4048 fpsrcop1.d = ST0;
4049 fptemp1.d = ST1;
Jun Nakajima86797932011-01-29 14:24:24 -08004050 expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
4051
4052 if (expdif < 0) {
4053 /* optimisation? taken from the AMD docs */
4054 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4055 /* ST0 is unchanged */
4056 return;
4057 }
4058
4059 if (expdif < 53) {
4060 dblq = fpsrcop / fptemp;
4061 /* round dblq towards nearest integer */
4062 dblq = rint(dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004063 st0 = fpsrcop - fptemp * dblq;
Jun Nakajima86797932011-01-29 14:24:24 -08004064
4065 /* convert dblq to q by truncating towards zero */
4066 if (dblq < 0.0)
4067 q = (signed long long int)(-dblq);
4068 else
4069 q = (signed long long int)dblq;
4070
4071 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4072 /* (C0,C3,C1) <-- (q2,q1,q0) */
4073 env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
4074 env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
4075 env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
4076 } else {
4077 env->fpus |= 0x400; /* C2 <-- 1 */
4078 fptemp = pow(2.0, expdif - 50);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004079 fpsrcop = (st0 / st1) / fptemp;
Jun Nakajima86797932011-01-29 14:24:24 -08004080 /* fpsrcop = integer obtained by chopping */
4081 fpsrcop = (fpsrcop < 0.0) ?
4082 -(floor(fabs(fpsrcop))) : floor(fpsrcop);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004083 st0 -= (st1 * fpsrcop * fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004084 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004085 ST0 = double_to_floatx80(env, st0);
Jun Nakajima86797932011-01-29 14:24:24 -08004086}
4087
4088void helper_fprem(void)
4089{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004090 double st0, st1, dblq, fpsrcop, fptemp;
4091 CPU_LDoubleU fpsrcop1, fptemp1;
Jun Nakajima86797932011-01-29 14:24:24 -08004092 int expdif;
4093 signed long long int q;
4094
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004095 st0 = floatx80_to_double(env, ST0);
4096 st1 = floatx80_to_double(env, ST1);
4097
4098 if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
4099 ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */
Jun Nakajima86797932011-01-29 14:24:24 -08004100 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4101 return;
4102 }
4103
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004104 fpsrcop = st0;
4105 fptemp = st1;
4106 fpsrcop1.d = ST0;
4107 fptemp1.d = ST1;
Jun Nakajima86797932011-01-29 14:24:24 -08004108 expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
4109
4110 if (expdif < 0) {
4111 /* optimisation? taken from the AMD docs */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004112 env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */
Jun Nakajima86797932011-01-29 14:24:24 -08004113 /* ST0 is unchanged */
4114 return;
4115 }
4116
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004117 if (expdif < 53) {
4118 dblq = fpsrcop / fptemp; /* ST0 / ST1*/;
Jun Nakajima86797932011-01-29 14:24:24 -08004119 /* round dblq towards zero */
4120 dblq = (dblq < 0.0) ? ceil(dblq) : floor(dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004121 st0 = fpsrcop - fptemp * dblq; /* fpsrcop is ST0 */
Jun Nakajima86797932011-01-29 14:24:24 -08004122
4123 /* convert dblq to q by truncating towards zero */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004124 if (dblq < 0.0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004125 q = (signed long long int)(-dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004126 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004127 q = (signed long long int)dblq;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004128 }
Jun Nakajima86797932011-01-29 14:24:24 -08004129
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004130 env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */
4131 /* (C0,C3,C1) <-- (q2,q1,q0) */
Jun Nakajima86797932011-01-29 14:24:24 -08004132 env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
4133 env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
4134 env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
4135 } else {
4136 int N = 32 + (expdif % 32); /* as per AMD docs */
4137 env->fpus |= 0x400; /* C2 <-- 1 */
4138 fptemp = pow(2.0, (double)(expdif - N));
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004139 fpsrcop = (st0 / st1) / fptemp;
Jun Nakajima86797932011-01-29 14:24:24 -08004140 /* fpsrcop = integer obtained by chopping */
4141 fpsrcop = (fpsrcop < 0.0) ?
4142 -(floor(fabs(fpsrcop))) : floor(fpsrcop);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004143 st0 -= (st1 * fpsrcop * fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004144 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004145 ST0 = double_to_floatx80(env, st0);
Jun Nakajima86797932011-01-29 14:24:24 -08004146}
4147
4148void helper_fyl2xp1(void)
4149{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004150 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004151
Jun Nakajima86797932011-01-29 14:24:24 -08004152 if ((fptemp+1.0)>0.0) {
4153 fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004154 fptemp *= floatx80_to_double(env, ST1);
4155 ST1 = double_to_floatx80(env, fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004156 fpop();
4157 } else {
4158 env->fpus &= (~0x4700);
4159 env->fpus |= 0x400;
4160 }
4161}
4162
4163void helper_fsqrt(void)
4164{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004165 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004166
Jun Nakajima86797932011-01-29 14:24:24 -08004167 if (fptemp<0.0) {
4168 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4169 env->fpus |= 0x400;
4170 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004171 ST0 = floatx80_sqrt(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004172}
4173
4174void helper_fsincos(void)
4175{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004176 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004177
Jun Nakajima86797932011-01-29 14:24:24 -08004178 if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4179 env->fpus |= 0x400;
4180 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004181 ST0 = double_to_floatx80(env, sin(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004182 fpush();
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004183 ST0 = double_to_floatx80(env, cos(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004184 env->fpus &= (~0x400); /* C2 <-- 0 */
4185 /* the above code is for |arg| < 2**63 only */
4186 }
4187}
4188
4189void helper_frndint(void)
4190{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004191 ST0 = floatx80_round_to_int(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004192}
4193
4194void helper_fscale(void)
4195{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004196 double st0 = floatx80_to_double(env, ST0);
4197 double st1 = floatx80_to_double(env, ST1);
4198 double val = ldexp(st0, (int)st1);
4199 ST0 = double_to_floatx80(env, val);
Jun Nakajima86797932011-01-29 14:24:24 -08004200}
4201
4202void helper_fsin(void)
4203{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004204 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004205
Jun Nakajima86797932011-01-29 14:24:24 -08004206 if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4207 env->fpus |= 0x400;
4208 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004209 ST0 = double_to_floatx80(env, sin(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004210 env->fpus &= (~0x400); /* C2 <-- 0 */
4211 /* the above code is for |arg| < 2**53 only */
4212 }
4213}
4214
4215void helper_fcos(void)
4216{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004217 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004218
Jun Nakajima86797932011-01-29 14:24:24 -08004219 if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4220 env->fpus |= 0x400;
4221 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004222 ST0 = double_to_floatx80(env, cos(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004223 env->fpus &= (~0x400); /* C2 <-- 0 */
4224 /* the above code is for |arg5 < 2**63 only */
4225 }
4226}
4227
4228void helper_fxam_ST0(void)
4229{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004230 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004231 int expdif;
4232
4233 temp.d = ST0;
4234
4235 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4236 if (SIGND(temp))
4237 env->fpus |= 0x200; /* C1 <-- 1 */
4238
4239 /* XXX: test fptags too */
4240 expdif = EXPD(temp);
4241 if (expdif == MAXEXPD) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004242 if (MANTD(temp) == 0x8000000000000000ULL) {
Jun Nakajima86797932011-01-29 14:24:24 -08004243 env->fpus |= 0x500 /*Infinity*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004244 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004245 env->fpus |= 0x100 /*NaN*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004246 }
Jun Nakajima86797932011-01-29 14:24:24 -08004247 } else if (expdif == 0) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004248 if (MANTD(temp) == 0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004249 env->fpus |= 0x4000 /*Zero*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004250 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004251 env->fpus |= 0x4400 /*Denormal*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004252 }
Jun Nakajima86797932011-01-29 14:24:24 -08004253 } else {
4254 env->fpus |= 0x400;
4255 }
4256}
4257
4258void helper_fstenv(target_ulong ptr, int data32)
4259{
4260 int fpus, fptag, exp, i;
4261 uint64_t mant;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004262 CPU_LDoubleU tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004263
4264 fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
4265 fptag = 0;
4266 for (i=7; i>=0; i--) {
4267 fptag <<= 2;
4268 if (env->fptags[i]) {
4269 fptag |= 3;
4270 } else {
4271 tmp.d = env->fpregs[i].d;
4272 exp = EXPD(tmp);
4273 mant = MANTD(tmp);
4274 if (exp == 0 && mant == 0) {
4275 /* zero */
4276 fptag |= 1;
4277 } else if (exp == 0 || exp == MAXEXPD
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004278 || (mant & (1LL << 63)) == 0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004279 /* NaNs, infinity, denormal */
4280 fptag |= 2;
4281 }
4282 }
4283 }
4284 if (data32) {
4285 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004286 cpu_stl_data(env, ptr, env->fpuc);
4287 cpu_stl_data(env, ptr + 4, fpus);
4288 cpu_stl_data(env, ptr + 8, fptag);
4289 cpu_stl_data(env, ptr + 12, 0); /* fpip */
4290 cpu_stl_data(env, ptr + 16, 0); /* fpcs */
4291 cpu_stl_data(env, ptr + 20, 0); /* fpoo */
4292 cpu_stl_data(env, ptr + 24, 0); /* fpos */
Jun Nakajima86797932011-01-29 14:24:24 -08004293 } else {
4294 /* 16 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004295 cpu_stw_data(env, ptr, env->fpuc);
4296 cpu_stw_data(env, ptr + 2, fpus);
4297 cpu_stw_data(env, ptr + 4, fptag);
4298 cpu_stw_data(env, ptr + 6, 0);
4299 cpu_stw_data(env, ptr + 8, 0);
4300 cpu_stw_data(env, ptr + 10, 0);
4301 cpu_stw_data(env, ptr + 12, 0);
Jun Nakajima86797932011-01-29 14:24:24 -08004302 }
4303}
4304
4305void helper_fldenv(target_ulong ptr, int data32)
4306{
4307 int i, fpus, fptag;
4308
4309 if (data32) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004310 env->fpuc = cpu_lduw_data(env, ptr);
4311 fpus = cpu_lduw_data(env, ptr + 4);
4312 fptag = cpu_lduw_data(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08004313 }
4314 else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004315 env->fpuc = cpu_lduw_data(env, ptr);
4316 fpus = cpu_lduw_data(env, ptr + 2);
4317 fptag = cpu_lduw_data(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004318 }
4319 env->fpstt = (fpus >> 11) & 7;
4320 env->fpus = fpus & ~0x3800;
4321 for(i = 0;i < 8; i++) {
4322 env->fptags[i] = ((fptag & 3) == 3);
4323 fptag >>= 2;
4324 }
4325}
4326
4327void helper_fsave(target_ulong ptr, int data32)
4328{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004329 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004330 int i;
4331
4332 helper_fstenv(ptr, data32);
4333
4334 ptr += (14 << data32);
4335 for(i = 0;i < 8; i++) {
4336 tmp = ST(i);
4337 helper_fstt(tmp, ptr);
4338 ptr += 10;
4339 }
4340
4341 /* fninit */
4342 env->fpus = 0;
4343 env->fpstt = 0;
4344 env->fpuc = 0x37f;
4345 env->fptags[0] = 1;
4346 env->fptags[1] = 1;
4347 env->fptags[2] = 1;
4348 env->fptags[3] = 1;
4349 env->fptags[4] = 1;
4350 env->fptags[5] = 1;
4351 env->fptags[6] = 1;
4352 env->fptags[7] = 1;
4353}
4354
4355void helper_frstor(target_ulong ptr, int data32)
4356{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004357 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004358 int i;
4359
4360 helper_fldenv(ptr, data32);
4361 ptr += (14 << data32);
4362
4363 for(i = 0;i < 8; i++) {
4364 tmp = helper_fldt(ptr);
4365 ST(i) = tmp;
4366 ptr += 10;
4367 }
4368}
4369
4370void helper_fxsave(target_ulong ptr, int data64)
4371{
4372 int fpus, fptag, i, nb_xmm_regs;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004373 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004374 target_ulong addr;
4375
4376 fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
4377 fptag = 0;
4378 for(i = 0; i < 8; i++) {
4379 fptag |= (env->fptags[i] << i);
4380 }
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004381 cpu_stw_data(env, ptr, env->fpuc);
4382 cpu_stw_data(env, ptr + 2, fpus);
4383 cpu_stw_data(env, ptr + 4, fptag ^ 0xff);
Jun Nakajima86797932011-01-29 14:24:24 -08004384#ifdef TARGET_X86_64
4385 if (data64) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004386 cpu_stq_data(env, ptr + 0x08, 0); /* rip */
4387 cpu_stq_data(env, ptr + 0x10, 0); /* rdp */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004388 } else
Jun Nakajima86797932011-01-29 14:24:24 -08004389#endif
4390 {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004391 cpu_stl_data(env, ptr + 0x08, 0); /* eip */
4392 cpu_stl_data(env, ptr + 0x0c, 0); /* sel */
4393 cpu_stl_data(env, ptr + 0x10, 0); /* dp */
4394 cpu_stl_data(env, ptr + 0x14, 0); /* sel */
Jun Nakajima86797932011-01-29 14:24:24 -08004395 }
4396
4397 addr = ptr + 0x20;
4398 for(i = 0;i < 8; i++) {
4399 tmp = ST(i);
4400 helper_fstt(tmp, addr);
4401 addr += 16;
4402 }
4403
4404 if (env->cr[4] & CR4_OSFXSR_MASK) {
4405 /* XXX: finish it */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004406 cpu_stl_data(env, ptr + 0x18, env->mxcsr); /* mxcsr */
4407 cpu_stl_data(env, ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */
Jun Nakajima86797932011-01-29 14:24:24 -08004408 if (env->hflags & HF_CS64_MASK)
4409 nb_xmm_regs = 16;
4410 else
4411 nb_xmm_regs = 8;
4412 addr = ptr + 0xa0;
4413 /* Fast FXSAVE leaves out the XMM registers */
4414 if (!(env->efer & MSR_EFER_FFXSR)
4415 || (env->hflags & HF_CPL_MASK)
4416 || !(env->hflags & HF_LMA_MASK)) {
4417 for(i = 0; i < nb_xmm_regs; i++) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004418 cpu_stq_data(env, addr, env->xmm_regs[i].XMM_Q(0));
4419 cpu_stq_data(env, addr + 8, env->xmm_regs[i].XMM_Q(1));
Jun Nakajima86797932011-01-29 14:24:24 -08004420 addr += 16;
4421 }
4422 }
4423 }
4424}
4425
4426void helper_fxrstor(target_ulong ptr, int data64)
4427{
4428 int i, fpus, fptag, nb_xmm_regs;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004429 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004430 target_ulong addr;
4431
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004432 env->fpuc = cpu_lduw_data(env, ptr);
4433 fpus = cpu_lduw_data(env, ptr + 2);
4434 fptag = cpu_lduw_data(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004435 env->fpstt = (fpus >> 11) & 7;
4436 env->fpus = fpus & ~0x3800;
4437 fptag ^= 0xff;
4438 for(i = 0;i < 8; i++) {
4439 env->fptags[i] = ((fptag >> i) & 1);
4440 }
4441
4442 addr = ptr + 0x20;
4443 for(i = 0;i < 8; i++) {
4444 tmp = helper_fldt(addr);
4445 ST(i) = tmp;
4446 addr += 16;
4447 }
4448
4449 if (env->cr[4] & CR4_OSFXSR_MASK) {
4450 /* XXX: finish it */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004451 env->mxcsr = cpu_ldl_data(env, ptr + 0x18);
Jun Nakajima86797932011-01-29 14:24:24 -08004452 //ldl(ptr + 0x1c);
4453 if (env->hflags & HF_CS64_MASK)
4454 nb_xmm_regs = 16;
4455 else
4456 nb_xmm_regs = 8;
4457 addr = ptr + 0xa0;
4458 /* Fast FXRESTORE leaves out the XMM registers */
4459 if (!(env->efer & MSR_EFER_FFXSR)
4460 || (env->hflags & HF_CPL_MASK)
4461 || !(env->hflags & HF_LMA_MASK)) {
4462 for(i = 0; i < nb_xmm_regs; i++) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004463 env->xmm_regs[i].XMM_Q(0) = cpu_ldq_data(env, addr);
4464 env->xmm_regs[i].XMM_Q(1) = cpu_ldq_data(env, addr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08004465 addr += 16;
4466 }
4467 }
4468 }
4469}
4470
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004471void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f)
Jun Nakajima86797932011-01-29 14:24:24 -08004472{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004473 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004474
4475 temp.d = f;
4476 *pmant = temp.l.lower;
4477 *pexp = temp.l.upper;
4478}
4479
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004480floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper)
Jun Nakajima86797932011-01-29 14:24:24 -08004481{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004482 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004483
4484 temp.l.upper = upper;
4485 temp.l.lower = mant;
4486 return temp.d;
4487}
Jun Nakajima86797932011-01-29 14:24:24 -08004488
4489#ifdef TARGET_X86_64
4490
4491//#define DEBUG_MULDIV
4492
4493static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
4494{
4495 *plow += a;
4496 /* carry test */
4497 if (*plow < a)
4498 (*phigh)++;
4499 *phigh += b;
4500}
4501
4502static void neg128(uint64_t *plow, uint64_t *phigh)
4503{
4504 *plow = ~ *plow;
4505 *phigh = ~ *phigh;
4506 add128(plow, phigh, 1, 0);
4507}
4508
4509/* return TRUE if overflow */
4510static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
4511{
4512 uint64_t q, r, a1, a0;
4513 int i, qb, ab;
4514
4515 a0 = *plow;
4516 a1 = *phigh;
4517 if (a1 == 0) {
4518 q = a0 / b;
4519 r = a0 % b;
4520 *plow = q;
4521 *phigh = r;
4522 } else {
4523 if (a1 >= b)
4524 return 1;
4525 /* XXX: use a better algorithm */
4526 for(i = 0; i < 64; i++) {
4527 ab = a1 >> 63;
4528 a1 = (a1 << 1) | (a0 >> 63);
4529 if (ab || a1 >= b) {
4530 a1 -= b;
4531 qb = 1;
4532 } else {
4533 qb = 0;
4534 }
4535 a0 = (a0 << 1) | qb;
4536 }
4537#if defined(DEBUG_MULDIV)
4538 printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n",
4539 *phigh, *plow, b, a0, a1);
4540#endif
4541 *plow = a0;
4542 *phigh = a1;
4543 }
4544 return 0;
4545}
4546
4547/* return TRUE if overflow */
4548static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
4549{
4550 int sa, sb;
4551 sa = ((int64_t)*phigh < 0);
4552 if (sa)
4553 neg128(plow, phigh);
4554 sb = (b < 0);
4555 if (sb)
4556 b = -b;
4557 if (div64(plow, phigh, b) != 0)
4558 return 1;
4559 if (sa ^ sb) {
4560 if (*plow > (1ULL << 63))
4561 return 1;
4562 *plow = - *plow;
4563 } else {
4564 if (*plow >= (1ULL << 63))
4565 return 1;
4566 }
4567 if (sa)
4568 *phigh = - *phigh;
4569 return 0;
4570}
4571
4572void helper_mulq_EAX_T0(target_ulong t0)
4573{
4574 uint64_t r0, r1;
4575
4576 mulu64(&r0, &r1, EAX, t0);
4577 EAX = r0;
4578 EDX = r1;
4579 CC_DST = r0;
4580 CC_SRC = r1;
4581}
4582
4583void helper_imulq_EAX_T0(target_ulong t0)
4584{
4585 uint64_t r0, r1;
4586
4587 muls64(&r0, &r1, EAX, t0);
4588 EAX = r0;
4589 EDX = r1;
4590 CC_DST = r0;
4591 CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
4592}
4593
4594target_ulong helper_imulq_T0_T1(target_ulong t0, target_ulong t1)
4595{
4596 uint64_t r0, r1;
4597
4598 muls64(&r0, &r1, t0, t1);
4599 CC_DST = r0;
4600 CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
4601 return r0;
4602}
4603
4604void helper_divq_EAX(target_ulong t0)
4605{
4606 uint64_t r0, r1;
4607 if (t0 == 0) {
4608 raise_exception(EXCP00_DIVZ);
4609 }
4610 r0 = EAX;
4611 r1 = EDX;
4612 if (div64(&r0, &r1, t0))
4613 raise_exception(EXCP00_DIVZ);
4614 EAX = r0;
4615 EDX = r1;
4616}
4617
4618void helper_idivq_EAX(target_ulong t0)
4619{
4620 uint64_t r0, r1;
4621 if (t0 == 0) {
4622 raise_exception(EXCP00_DIVZ);
4623 }
4624 r0 = EAX;
4625 r1 = EDX;
4626 if (idiv64(&r0, &r1, t0))
4627 raise_exception(EXCP00_DIVZ);
4628 EAX = r0;
4629 EDX = r1;
4630}
4631#endif
4632
4633static void do_hlt(void)
4634{
4635 env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */
4636 env->halted = 1;
4637 env->exception_index = EXCP_HLT;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01004638 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004639}
4640
4641void helper_hlt(int next_eip_addend)
4642{
4643 helper_svm_check_intercept_param(SVM_EXIT_HLT, 0);
4644 EIP += next_eip_addend;
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004645
Jun Nakajima86797932011-01-29 14:24:24 -08004646 do_hlt();
4647}
4648
4649void helper_monitor(target_ulong ptr)
4650{
4651 if ((uint32_t)ECX != 0)
4652 raise_exception(EXCP0D_GPF);
4653 /* XXX: store address ? */
4654 helper_svm_check_intercept_param(SVM_EXIT_MONITOR, 0);
4655}
4656
4657void helper_mwait(int next_eip_addend)
4658{
4659 if ((uint32_t)ECX != 0)
4660 raise_exception(EXCP0D_GPF);
4661 helper_svm_check_intercept_param(SVM_EXIT_MWAIT, 0);
4662 EIP += next_eip_addend;
4663
4664 /* XXX: not complete but not completely erroneous */
4665 if (env->cpu_index != 0 || env->next_cpu != NULL) {
4666 /* more than one CPU: do not sleep because another CPU may
4667 wake this one */
4668 } else {
4669 do_hlt();
4670 }
4671}
4672
4673void helper_debug(void)
4674{
4675 env->exception_index = EXCP_DEBUG;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01004676 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004677}
4678
4679void helper_reset_rf(void)
4680{
4681 env->eflags &= ~RF_MASK;
4682}
4683
4684void helper_raise_interrupt(int intno, int next_eip_addend)
4685{
4686 raise_interrupt(intno, 1, 0, next_eip_addend);
4687}
4688
4689void helper_raise_exception(int exception_index)
4690{
4691 raise_exception(exception_index);
4692}
4693
4694void helper_cli(void)
4695{
4696 env->eflags &= ~IF_MASK;
4697}
4698
4699void helper_sti(void)
4700{
4701 env->eflags |= IF_MASK;
4702}
4703
4704#if 0
4705/* vm86plus instructions */
4706void helper_cli_vm(void)
4707{
4708 env->eflags &= ~VIF_MASK;
4709}
4710
4711void helper_sti_vm(void)
4712{
4713 env->eflags |= VIF_MASK;
4714 if (env->eflags & VIP_MASK) {
4715 raise_exception(EXCP0D_GPF);
4716 }
4717}
4718#endif
4719
4720void helper_set_inhibit_irq(void)
4721{
4722 env->hflags |= HF_INHIBIT_IRQ_MASK;
4723}
4724
4725void helper_reset_inhibit_irq(void)
4726{
4727 env->hflags &= ~HF_INHIBIT_IRQ_MASK;
4728}
4729
4730void helper_boundw(target_ulong a0, int v)
4731{
4732 int low, high;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004733 low = cpu_ldsw_data(env, a0);
4734 high = cpu_ldsw_data(env, a0 + 2);
Jun Nakajima86797932011-01-29 14:24:24 -08004735 v = (int16_t)v;
4736 if (v < low || v > high) {
4737 raise_exception(EXCP05_BOUND);
4738 }
4739}
4740
4741void helper_boundl(target_ulong a0, int v)
4742{
4743 int low, high;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004744 low = cpu_ldl_data(env, a0);
4745 high = cpu_ldl_data(env, a0 + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004746 if (v < low || v > high) {
4747 raise_exception(EXCP05_BOUND);
4748 }
4749}
4750
4751static float approx_rsqrt(float a)
4752{
4753 return 1.0 / sqrt(a);
4754}
4755
4756static float approx_rcp(float a)
4757{
4758 return 1.0 / a;
4759}
4760
4761#if !defined(CONFIG_USER_ONLY)
4762
4763#define MMUSUFFIX _mmu
4764
4765#define SHIFT 0
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004766#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004767
4768#define SHIFT 1
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004769#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004770
4771#define SHIFT 2
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004772#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004773
4774#define SHIFT 3
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004775#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004776
4777#endif
4778
4779#if !defined(CONFIG_USER_ONLY)
4780/* try to fill the TLB and return an exception if error. If retaddr is
4781 NULL, it means that the function was called in C code (i.e. not
4782 from generated code or from helper.c) */
4783/* XXX: fix it to restore all registers */
David 'Digit' Turner6d1afd32014-03-14 10:51:57 +01004784void tlb_fill(CPUX86State* env1, target_ulong addr, int is_write, int mmu_idx, void *retaddr)
Jun Nakajima86797932011-01-29 14:24:24 -08004785{
4786 TranslationBlock *tb;
4787 int ret;
4788 unsigned long pc;
4789 CPUX86State *saved_env;
4790
4791 /* XXX: hack to restore env in all cases, even if not called from
4792 generated code */
4793 saved_env = env;
David 'Digit' Turner6d1afd32014-03-14 10:51:57 +01004794 env = env1;
Jun Nakajima86797932011-01-29 14:24:24 -08004795 ret = cpu_x86_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
4796 if (ret) {
4797 if (retaddr) {
4798 /* now we have a real cpu fault */
4799 pc = (unsigned long)retaddr;
4800 tb = tb_find_pc(pc);
4801 if (tb) {
4802 /* the PC is inside the translated code. It means that we have
4803 a virtual CPU fault */
David 'Digit' Turner3e0677d2014-03-07 15:01:06 +01004804 cpu_restore_state(env, pc);
Jun Nakajima86797932011-01-29 14:24:24 -08004805 }
4806 }
4807 raise_exception_err(env->exception_index, env->error_code);
4808 }
4809 env = saved_env;
4810}
4811#endif
4812
4813/* Secure Virtual Machine helpers */
4814
4815#if defined(CONFIG_USER_ONLY)
4816
4817void helper_vmrun(int aflag, int next_eip_addend)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004818{
Jun Nakajima86797932011-01-29 14:24:24 -08004819}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004820void helper_vmmcall(void)
4821{
Jun Nakajima86797932011-01-29 14:24:24 -08004822}
4823void helper_vmload(int aflag)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004824{
Jun Nakajima86797932011-01-29 14:24:24 -08004825}
4826void helper_vmsave(int aflag)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004827{
Jun Nakajima86797932011-01-29 14:24:24 -08004828}
4829void helper_stgi(void)
4830{
4831}
4832void helper_clgi(void)
4833{
4834}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004835void helper_skinit(void)
4836{
Jun Nakajima86797932011-01-29 14:24:24 -08004837}
4838void helper_invlpga(int aflag)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004839{
Jun Nakajima86797932011-01-29 14:24:24 -08004840}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004841void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1)
4842{
Jun Nakajima86797932011-01-29 14:24:24 -08004843}
4844void helper_svm_check_intercept_param(uint32_t type, uint64_t param)
4845{
4846}
4847
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004848void helper_svm_check_io(uint32_t port, uint32_t param,
Jun Nakajima86797932011-01-29 14:24:24 -08004849 uint32_t next_eip_addend)
4850{
4851}
4852#else
4853
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +01004854static inline void svm_save_seg(hwaddr addr,
Jun Nakajima86797932011-01-29 14:24:24 -08004855 const SegmentCache *sc)
4856{
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004857 stw_phys(addr + offsetof(struct vmcb_seg, selector),
Jun Nakajima86797932011-01-29 14:24:24 -08004858 sc->selector);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004859 stq_phys(addr + offsetof(struct vmcb_seg, base),
Jun Nakajima86797932011-01-29 14:24:24 -08004860 sc->base);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004861 stl_phys(addr + offsetof(struct vmcb_seg, limit),
Jun Nakajima86797932011-01-29 14:24:24 -08004862 sc->limit);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004863 stw_phys(addr + offsetof(struct vmcb_seg, attrib),
Jun Nakajima86797932011-01-29 14:24:24 -08004864 ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00));
4865}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004866
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +01004867static inline void svm_load_seg(hwaddr addr, SegmentCache *sc)
Jun Nakajima86797932011-01-29 14:24:24 -08004868{
4869 unsigned int flags;
4870
4871 sc->selector = lduw_phys(addr + offsetof(struct vmcb_seg, selector));
4872 sc->base = ldq_phys(addr + offsetof(struct vmcb_seg, base));
4873 sc->limit = ldl_phys(addr + offsetof(struct vmcb_seg, limit));
4874 flags = lduw_phys(addr + offsetof(struct vmcb_seg, attrib));
4875 sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12);
4876}
4877
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +01004878static inline void svm_load_seg_cache(hwaddr addr,
David 'Digit' Turnere2678e12014-01-16 15:56:43 +01004879 CPUX86State *env, int seg_reg)
Jun Nakajima86797932011-01-29 14:24:24 -08004880{
4881 SegmentCache sc1, *sc = &sc1;
4882 svm_load_seg(addr, sc);
4883 cpu_x86_load_seg_cache(env, seg_reg, sc->selector,
4884 sc->base, sc->limit, sc->flags);
4885}
4886
4887void helper_vmrun(int aflag, int next_eip_addend)
4888{
4889 target_ulong addr;
4890 uint32_t event_inj;
4891 uint32_t int_ctl;
4892
4893 helper_svm_check_intercept_param(SVM_EXIT_VMRUN, 0);
4894
4895 if (aflag == 2)
4896 addr = EAX;
4897 else
4898 addr = (uint32_t)EAX;
4899
4900 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n", addr);
4901
4902 env->vm_vmcb = addr;
4903
4904 /* save the current CPU state in the hsave page */
4905 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), env->gdt.base);
4906 stl_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit);
4907
4908 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.base), env->idt.base);
4909 stl_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), env->idt.limit);
4910
4911 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]);
4912 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]);
4913 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]);
4914 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]);
4915 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]);
4916 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]);
4917
4918 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer);
4919 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags), compute_eflags());
4920
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004921 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.es),
Jun Nakajima86797932011-01-29 14:24:24 -08004922 &env->segs[R_ES]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004923 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.cs),
Jun Nakajima86797932011-01-29 14:24:24 -08004924 &env->segs[R_CS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004925 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ss),
Jun Nakajima86797932011-01-29 14:24:24 -08004926 &env->segs[R_SS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004927 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ds),
Jun Nakajima86797932011-01-29 14:24:24 -08004928 &env->segs[R_DS]);
4929
4930 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rip),
4931 EIP + next_eip_addend);
4932 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rsp), ESP);
4933 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rax), EAX);
4934
4935 /* load the interception bitmaps so we do not need to access the
4936 vmcb in svm mode */
4937 env->intercept = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept));
4938 env->intercept_cr_read = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_read));
4939 env->intercept_cr_write = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_write));
4940 env->intercept_dr_read = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_read));
4941 env->intercept_dr_write = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_write));
4942 env->intercept_exceptions = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_exceptions));
4943
4944 /* enable intercepts */
4945 env->hflags |= HF_SVMI_MASK;
4946
4947 env->tsc_offset = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.tsc_offset));
4948
4949 env->gdt.base = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base));
4950 env->gdt.limit = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit));
4951
4952 env->idt.base = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.base));
4953 env->idt.limit = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit));
4954
4955 /* clear exit_info_2 so we behave like the real hardware */
4956 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0);
4957
4958 cpu_x86_update_cr0(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr0)));
4959 cpu_x86_update_cr4(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr4)));
4960 cpu_x86_update_cr3(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr3)));
4961 env->cr[2] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr2));
4962 int_ctl = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
4963 env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
4964 if (int_ctl & V_INTR_MASKING_MASK) {
4965 env->v_tpr = int_ctl & V_TPR_MASK;
4966 env->hflags2 |= HF2_VINTR_MASK;
4967 if (env->eflags & IF_MASK)
4968 env->hflags2 |= HF2_HIF_MASK;
4969 }
4970
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004971 cpu_load_efer(env,
Jun Nakajima86797932011-01-29 14:24:24 -08004972 ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.efer)));
4973 env->eflags = 0;
4974 load_eflags(ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rflags)),
4975 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
4976 CC_OP = CC_OP_EFLAGS;
4977
4978 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.es),
4979 env, R_ES);
4980 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.cs),
4981 env, R_CS);
4982 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.ss),
4983 env, R_SS);
4984 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.ds),
4985 env, R_DS);
4986
4987 EIP = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rip));
4988 env->eip = EIP;
4989 ESP = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rsp));
4990 EAX = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rax));
4991 env->dr[7] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr7));
4992 env->dr[6] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr6));
4993 cpu_x86_set_cpl(env, ldub_phys(env->vm_vmcb + offsetof(struct vmcb, save.cpl)));
4994
4995 /* FIXME: guest state consistency checks */
4996
4997 switch(ldub_phys(env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) {
4998 case TLB_CONTROL_DO_NOTHING:
4999 break;
5000 case TLB_CONTROL_FLUSH_ALL_ASID:
5001 /* FIXME: this is not 100% correct but should work for now */
5002 tlb_flush(env, 1);
5003 break;
5004 }
5005
5006 env->hflags2 |= HF2_GIF_MASK;
5007
5008 if (int_ctl & V_IRQ_MASK) {
5009 env->interrupt_request |= CPU_INTERRUPT_VIRQ;
5010 }
5011
5012 /* maybe we need to inject an event */
5013 event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
5014 if (event_inj & SVM_EVTINJ_VALID) {
5015 uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK;
5016 uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR;
5017 uint32_t event_inj_err = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err));
5018
5019 qemu_log_mask(CPU_LOG_TB_IN_ASM, "Injecting(%#hx): ", valid_err);
5020 /* FIXME: need to implement valid_err */
5021 switch (event_inj & SVM_EVTINJ_TYPE_MASK) {
5022 case SVM_EVTINJ_TYPE_INTR:
5023 env->exception_index = vector;
5024 env->error_code = event_inj_err;
5025 env->exception_is_int = 0;
5026 env->exception_next_eip = -1;
5027 qemu_log_mask(CPU_LOG_TB_IN_ASM, "INTR");
5028 /* XXX: is it always correct ? */
5029 do_interrupt(vector, 0, 0, 0, 1);
5030 break;
5031 case SVM_EVTINJ_TYPE_NMI:
5032 env->exception_index = EXCP02_NMI;
5033 env->error_code = event_inj_err;
5034 env->exception_is_int = 0;
5035 env->exception_next_eip = EIP;
5036 qemu_log_mask(CPU_LOG_TB_IN_ASM, "NMI");
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005037 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005038 break;
5039 case SVM_EVTINJ_TYPE_EXEPT:
5040 env->exception_index = vector;
5041 env->error_code = event_inj_err;
5042 env->exception_is_int = 0;
5043 env->exception_next_eip = -1;
5044 qemu_log_mask(CPU_LOG_TB_IN_ASM, "EXEPT");
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005045 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005046 break;
5047 case SVM_EVTINJ_TYPE_SOFT:
5048 env->exception_index = vector;
5049 env->error_code = event_inj_err;
5050 env->exception_is_int = 1;
5051 env->exception_next_eip = EIP;
5052 qemu_log_mask(CPU_LOG_TB_IN_ASM, "SOFT");
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005053 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005054 break;
5055 }
5056 qemu_log_mask(CPU_LOG_TB_IN_ASM, " %#x %#x\n", env->exception_index, env->error_code);
5057 }
5058}
5059
5060void helper_vmmcall(void)
5061{
5062 helper_svm_check_intercept_param(SVM_EXIT_VMMCALL, 0);
5063 raise_exception(EXCP06_ILLOP);
5064}
5065
5066void helper_vmload(int aflag)
5067{
5068 target_ulong addr;
5069 helper_svm_check_intercept_param(SVM_EXIT_VMLOAD, 0);
5070
5071 if (aflag == 2)
5072 addr = EAX;
5073 else
5074 addr = (uint32_t)EAX;
5075
5076 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
5077 addr, ldq_phys(addr + offsetof(struct vmcb, save.fs.base)),
5078 env->segs[R_FS].base);
5079
5080 svm_load_seg_cache(addr + offsetof(struct vmcb, save.fs),
5081 env, R_FS);
5082 svm_load_seg_cache(addr + offsetof(struct vmcb, save.gs),
5083 env, R_GS);
5084 svm_load_seg(addr + offsetof(struct vmcb, save.tr),
5085 &env->tr);
5086 svm_load_seg(addr + offsetof(struct vmcb, save.ldtr),
5087 &env->ldt);
5088
5089#ifdef TARGET_X86_64
5090 env->kernelgsbase = ldq_phys(addr + offsetof(struct vmcb, save.kernel_gs_base));
5091 env->lstar = ldq_phys(addr + offsetof(struct vmcb, save.lstar));
5092 env->cstar = ldq_phys(addr + offsetof(struct vmcb, save.cstar));
5093 env->fmask = ldq_phys(addr + offsetof(struct vmcb, save.sfmask));
5094#endif
5095 env->star = ldq_phys(addr + offsetof(struct vmcb, save.star));
5096 env->sysenter_cs = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_cs));
5097 env->sysenter_esp = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_esp));
5098 env->sysenter_eip = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_eip));
5099}
5100
5101void helper_vmsave(int aflag)
5102{
5103 target_ulong addr;
5104 helper_svm_check_intercept_param(SVM_EXIT_VMSAVE, 0);
5105
5106 if (aflag == 2)
5107 addr = EAX;
5108 else
5109 addr = (uint32_t)EAX;
5110
5111 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
5112 addr, ldq_phys(addr + offsetof(struct vmcb, save.fs.base)),
5113 env->segs[R_FS].base);
5114
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005115 svm_save_seg(addr + offsetof(struct vmcb, save.fs),
Jun Nakajima86797932011-01-29 14:24:24 -08005116 &env->segs[R_FS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005117 svm_save_seg(addr + offsetof(struct vmcb, save.gs),
Jun Nakajima86797932011-01-29 14:24:24 -08005118 &env->segs[R_GS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005119 svm_save_seg(addr + offsetof(struct vmcb, save.tr),
Jun Nakajima86797932011-01-29 14:24:24 -08005120 &env->tr);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005121 svm_save_seg(addr + offsetof(struct vmcb, save.ldtr),
Jun Nakajima86797932011-01-29 14:24:24 -08005122 &env->ldt);
5123
5124#ifdef TARGET_X86_64
5125 stq_phys(addr + offsetof(struct vmcb, save.kernel_gs_base), env->kernelgsbase);
5126 stq_phys(addr + offsetof(struct vmcb, save.lstar), env->lstar);
5127 stq_phys(addr + offsetof(struct vmcb, save.cstar), env->cstar);
5128 stq_phys(addr + offsetof(struct vmcb, save.sfmask), env->fmask);
5129#endif
5130 stq_phys(addr + offsetof(struct vmcb, save.star), env->star);
5131 stq_phys(addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs);
5132 stq_phys(addr + offsetof(struct vmcb, save.sysenter_esp), env->sysenter_esp);
5133 stq_phys(addr + offsetof(struct vmcb, save.sysenter_eip), env->sysenter_eip);
5134}
5135
5136void helper_stgi(void)
5137{
5138 helper_svm_check_intercept_param(SVM_EXIT_STGI, 0);
5139 env->hflags2 |= HF2_GIF_MASK;
5140}
5141
5142void helper_clgi(void)
5143{
5144 helper_svm_check_intercept_param(SVM_EXIT_CLGI, 0);
5145 env->hflags2 &= ~HF2_GIF_MASK;
5146}
5147
5148void helper_skinit(void)
5149{
5150 helper_svm_check_intercept_param(SVM_EXIT_SKINIT, 0);
5151 /* XXX: not implemented */
5152 raise_exception(EXCP06_ILLOP);
5153}
5154
5155void helper_invlpga(int aflag)
5156{
5157 target_ulong addr;
5158 helper_svm_check_intercept_param(SVM_EXIT_INVLPGA, 0);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005159
Jun Nakajima86797932011-01-29 14:24:24 -08005160 if (aflag == 2)
5161 addr = EAX;
5162 else
5163 addr = (uint32_t)EAX;
5164
5165 /* XXX: could use the ASID to see if it is needed to do the
5166 flush */
5167 tlb_flush_page(env, addr);
5168}
5169
5170void helper_svm_check_intercept_param(uint32_t type, uint64_t param)
5171{
5172 if (likely(!(env->hflags & HF_SVMI_MASK)))
5173 return;
5174 switch(type) {
5175 case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8:
5176 if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) {
5177 helper_vmexit(type, param);
5178 }
5179 break;
5180 case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8:
5181 if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) {
5182 helper_vmexit(type, param);
5183 }
5184 break;
5185 case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7:
5186 if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) {
5187 helper_vmexit(type, param);
5188 }
5189 break;
5190 case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7:
5191 if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) {
5192 helper_vmexit(type, param);
5193 }
5194 break;
5195 case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31:
5196 if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) {
5197 helper_vmexit(type, param);
5198 }
5199 break;
5200 case SVM_EXIT_MSR:
5201 if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) {
5202 /* FIXME: this should be read in at vmrun (faster this way?) */
5203 uint64_t addr = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.msrpm_base_pa));
5204 uint32_t t0, t1;
5205 switch((uint32_t)ECX) {
5206 case 0 ... 0x1fff:
5207 t0 = (ECX * 2) % 8;
5208 t1 = ECX / 8;
5209 break;
5210 case 0xc0000000 ... 0xc0001fff:
5211 t0 = (8192 + ECX - 0xc0000000) * 2;
5212 t1 = (t0 / 8);
5213 t0 %= 8;
5214 break;
5215 case 0xc0010000 ... 0xc0011fff:
5216 t0 = (16384 + ECX - 0xc0010000) * 2;
5217 t1 = (t0 / 8);
5218 t0 %= 8;
5219 break;
5220 default:
5221 helper_vmexit(type, param);
5222 t0 = 0;
5223 t1 = 0;
5224 break;
5225 }
5226 if (ldub_phys(addr + t1) & ((1 << param) << t0))
5227 helper_vmexit(type, param);
5228 }
5229 break;
5230 default:
5231 if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) {
5232 helper_vmexit(type, param);
5233 }
5234 break;
5235 }
5236}
5237
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005238void helper_svm_check_io(uint32_t port, uint32_t param,
Jun Nakajima86797932011-01-29 14:24:24 -08005239 uint32_t next_eip_addend)
5240{
5241 if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) {
5242 /* FIXME: this should be read in at vmrun (faster this way?) */
5243 uint64_t addr = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.iopm_base_pa));
5244 uint16_t mask = (1 << ((param >> 4) & 7)) - 1;
5245 if(lduw_phys(addr + port / 8) & (mask << (port & 7))) {
5246 /* next EIP */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005247 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2),
Jun Nakajima86797932011-01-29 14:24:24 -08005248 env->eip + next_eip_addend);
5249 helper_vmexit(SVM_EXIT_IOIO, param | (port << 16));
5250 }
5251 }
5252}
5253
5254/* Note: currently only 32 bits of exit_code are used */
5255void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1)
5256{
5257 uint32_t int_ctl;
5258
5259 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" PRIx64 ", " TARGET_FMT_lx ")!\n",
5260 exit_code, exit_info_1,
5261 ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2)),
5262 EIP);
5263
5264 if(env->hflags & HF_INHIBIT_IRQ_MASK) {
5265 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_state), SVM_INTERRUPT_SHADOW_MASK);
5266 env->hflags &= ~HF_INHIBIT_IRQ_MASK;
5267 } else {
5268 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0);
5269 }
5270
5271 /* Save the VM state in the vmcb */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005272 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.es),
Jun Nakajima86797932011-01-29 14:24:24 -08005273 &env->segs[R_ES]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005274 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.cs),
Jun Nakajima86797932011-01-29 14:24:24 -08005275 &env->segs[R_CS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005276 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ss),
Jun Nakajima86797932011-01-29 14:24:24 -08005277 &env->segs[R_SS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005278 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ds),
Jun Nakajima86797932011-01-29 14:24:24 -08005279 &env->segs[R_DS]);
5280
5281 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), env->gdt.base);
5282 stl_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit);
5283
5284 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), env->idt.base);
5285 stl_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), env->idt.limit);
5286
5287 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer);
5288 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]);
5289 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]);
5290 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]);
5291 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]);
5292
5293 int_ctl = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
5294 int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK);
5295 int_ctl |= env->v_tpr & V_TPR_MASK;
5296 if (env->interrupt_request & CPU_INTERRUPT_VIRQ)
5297 int_ctl |= V_IRQ_MASK;
5298 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl);
5299
5300 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rflags), compute_eflags());
5301 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rip), env->eip);
5302 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rsp), ESP);
5303 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rax), EAX);
5304 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]);
5305 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]);
5306 stb_phys(env->vm_vmcb + offsetof(struct vmcb, save.cpl), env->hflags & HF_CPL_MASK);
5307
5308 /* Reload the host state from vm_hsave */
5309 env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
5310 env->hflags &= ~HF_SVMI_MASK;
5311 env->intercept = 0;
5312 env->intercept_exceptions = 0;
5313 env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
5314 env->tsc_offset = 0;
5315
5316 env->gdt.base = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.base));
5317 env->gdt.limit = ldl_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit));
5318
5319 env->idt.base = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.base));
5320 env->idt.limit = ldl_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.limit));
5321
5322 cpu_x86_update_cr0(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr0)) | CR0_PE_MASK);
5323 cpu_x86_update_cr4(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr4)));
5324 cpu_x86_update_cr3(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr3)));
5325 /* we need to set the efer after the crs so the hidden flags get
5326 set properly */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005327 cpu_load_efer(env,
Jun Nakajima86797932011-01-29 14:24:24 -08005328 ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer)));
5329 env->eflags = 0;
5330 load_eflags(ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags)),
5331 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
5332 CC_OP = CC_OP_EFLAGS;
5333
5334 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.es),
5335 env, R_ES);
5336 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.cs),
5337 env, R_CS);
5338 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.ss),
5339 env, R_SS);
5340 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.ds),
5341 env, R_DS);
5342
5343 EIP = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rip));
5344 ESP = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rsp));
5345 EAX = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rax));
5346
5347 env->dr[6] = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr6));
5348 env->dr[7] = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr7));
5349
5350 /* other setups */
5351 cpu_x86_set_cpl(env, 0);
5352 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_code), exit_code);
5353 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), exit_info_1);
5354
5355 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info),
5356 ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj)));
5357 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err),
5358 ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err)));
5359
5360 env->hflags2 &= ~HF2_GIF_MASK;
5361 /* FIXME: Resets the current ASID register to zero (host ASID). */
5362
5363 /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */
5364
5365 /* Clears the TSC_OFFSET inside the processor. */
5366
5367 /* If the host is in PAE mode, the processor reloads the host's PDPEs
5368 from the page table indicated the host's CR3. If the PDPEs contain
5369 illegal state, the processor causes a shutdown. */
5370
5371 /* Forces CR0.PE = 1, RFLAGS.VM = 0. */
5372 env->cr[0] |= CR0_PE_MASK;
5373 env->eflags &= ~VM_MASK;
5374
5375 /* Disables all breakpoints in the host DR7 register. */
5376
5377 /* Checks the reloaded host state for consistency. */
5378
5379 /* If the host's rIP reloaded by #VMEXIT is outside the limit of the
5380 host's code segment or non-canonical (in the case of long mode), a
5381 #GP fault is delivered inside the host.) */
5382
5383 /* remove any pending exception */
5384 env->exception_index = -1;
5385 env->error_code = 0;
5386 env->old_exception = -1;
5387
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005388 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005389}
5390
5391#endif
5392
5393/* MMX/SSE */
5394/* XXX: optimize by storing fptt and fptags in the static cpu state */
5395void helper_enter_mmx(void)
5396{
5397 env->fpstt = 0;
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +01005398 memset(env->fptags, 0, sizeof(env->fptags));
Jun Nakajima86797932011-01-29 14:24:24 -08005399}
5400
5401void helper_emms(void)
5402{
5403 /* set to empty state */
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +01005404 memset(env->fptags, 1, sizeof(env->fptags));
Jun Nakajima86797932011-01-29 14:24:24 -08005405}
5406
5407/* XXX: suppress */
5408void helper_movq(void *d, void *s)
5409{
5410 *(uint64_t *)d = *(uint64_t *)s;
5411}
5412
5413#define SHIFT 0
5414#include "ops_sse.h"
5415
5416#define SHIFT 1
5417#include "ops_sse.h"
5418
5419#define SHIFT 0
5420#include "helper_template.h"
5421#undef SHIFT
5422
5423#define SHIFT 1
5424#include "helper_template.h"
5425#undef SHIFT
5426
5427#define SHIFT 2
5428#include "helper_template.h"
5429#undef SHIFT
5430
5431#ifdef TARGET_X86_64
5432
5433#define SHIFT 3
5434#include "helper_template.h"
5435#undef SHIFT
5436
5437#endif
5438
5439/* bit operations */
5440target_ulong helper_bsf(target_ulong t0)
5441{
5442 int count;
5443 target_ulong res;
5444
5445 res = t0;
5446 count = 0;
5447 while ((res & 1) == 0) {
5448 count++;
5449 res >>= 1;
5450 }
5451 return count;
5452}
5453
5454target_ulong helper_bsr(target_ulong t0)
5455{
5456 int count;
5457 target_ulong res, mask;
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005458
Jun Nakajima86797932011-01-29 14:24:24 -08005459 res = t0;
5460 count = TARGET_LONG_BITS - 1;
5461 mask = (target_ulong)1 << (TARGET_LONG_BITS - 1);
5462 while ((res & mask) == 0) {
5463 count--;
5464 res <<= 1;
5465 }
5466 return count;
5467}
5468
5469
5470static int compute_all_eflags(void)
5471{
5472 return CC_SRC;
5473}
5474
5475static int compute_c_eflags(void)
5476{
5477 return CC_SRC & CC_C;
5478}
5479
5480uint32_t helper_cc_compute_all(int op)
5481{
5482 switch (op) {
5483 default: /* should never happen */ return 0;
5484
5485 case CC_OP_EFLAGS: return compute_all_eflags();
5486
5487 case CC_OP_MULB: return compute_all_mulb();
5488 case CC_OP_MULW: return compute_all_mulw();
5489 case CC_OP_MULL: return compute_all_mull();
5490
5491 case CC_OP_ADDB: return compute_all_addb();
5492 case CC_OP_ADDW: return compute_all_addw();
5493 case CC_OP_ADDL: return compute_all_addl();
5494
5495 case CC_OP_ADCB: return compute_all_adcb();
5496 case CC_OP_ADCW: return compute_all_adcw();
5497 case CC_OP_ADCL: return compute_all_adcl();
5498
5499 case CC_OP_SUBB: return compute_all_subb();
5500 case CC_OP_SUBW: return compute_all_subw();
5501 case CC_OP_SUBL: return compute_all_subl();
5502
5503 case CC_OP_SBBB: return compute_all_sbbb();
5504 case CC_OP_SBBW: return compute_all_sbbw();
5505 case CC_OP_SBBL: return compute_all_sbbl();
5506
5507 case CC_OP_LOGICB: return compute_all_logicb();
5508 case CC_OP_LOGICW: return compute_all_logicw();
5509 case CC_OP_LOGICL: return compute_all_logicl();
5510
5511 case CC_OP_INCB: return compute_all_incb();
5512 case CC_OP_INCW: return compute_all_incw();
5513 case CC_OP_INCL: return compute_all_incl();
5514
5515 case CC_OP_DECB: return compute_all_decb();
5516 case CC_OP_DECW: return compute_all_decw();
5517 case CC_OP_DECL: return compute_all_decl();
5518
5519 case CC_OP_SHLB: return compute_all_shlb();
5520 case CC_OP_SHLW: return compute_all_shlw();
5521 case CC_OP_SHLL: return compute_all_shll();
5522
5523 case CC_OP_SARB: return compute_all_sarb();
5524 case CC_OP_SARW: return compute_all_sarw();
5525 case CC_OP_SARL: return compute_all_sarl();
5526
5527#ifdef TARGET_X86_64
5528 case CC_OP_MULQ: return compute_all_mulq();
5529
5530 case CC_OP_ADDQ: return compute_all_addq();
5531
5532 case CC_OP_ADCQ: return compute_all_adcq();
5533
5534 case CC_OP_SUBQ: return compute_all_subq();
5535
5536 case CC_OP_SBBQ: return compute_all_sbbq();
5537
5538 case CC_OP_LOGICQ: return compute_all_logicq();
5539
5540 case CC_OP_INCQ: return compute_all_incq();
5541
5542 case CC_OP_DECQ: return compute_all_decq();
5543
5544 case CC_OP_SHLQ: return compute_all_shlq();
5545
5546 case CC_OP_SARQ: return compute_all_sarq();
5547#endif
5548 }
5549}
5550
5551uint32_t helper_cc_compute_c(int op)
5552{
5553 switch (op) {
5554 default: /* should never happen */ return 0;
5555
5556 case CC_OP_EFLAGS: return compute_c_eflags();
5557
5558 case CC_OP_MULB: return compute_c_mull();
5559 case CC_OP_MULW: return compute_c_mull();
5560 case CC_OP_MULL: return compute_c_mull();
5561
5562 case CC_OP_ADDB: return compute_c_addb();
5563 case CC_OP_ADDW: return compute_c_addw();
5564 case CC_OP_ADDL: return compute_c_addl();
5565
5566 case CC_OP_ADCB: return compute_c_adcb();
5567 case CC_OP_ADCW: return compute_c_adcw();
5568 case CC_OP_ADCL: return compute_c_adcl();
5569
5570 case CC_OP_SUBB: return compute_c_subb();
5571 case CC_OP_SUBW: return compute_c_subw();
5572 case CC_OP_SUBL: return compute_c_subl();
5573
5574 case CC_OP_SBBB: return compute_c_sbbb();
5575 case CC_OP_SBBW: return compute_c_sbbw();
5576 case CC_OP_SBBL: return compute_c_sbbl();
5577
5578 case CC_OP_LOGICB: return compute_c_logicb();
5579 case CC_OP_LOGICW: return compute_c_logicw();
5580 case CC_OP_LOGICL: return compute_c_logicl();
5581
5582 case CC_OP_INCB: return compute_c_incl();
5583 case CC_OP_INCW: return compute_c_incl();
5584 case CC_OP_INCL: return compute_c_incl();
5585
5586 case CC_OP_DECB: return compute_c_incl();
5587 case CC_OP_DECW: return compute_c_incl();
5588 case CC_OP_DECL: return compute_c_incl();
5589
5590 case CC_OP_SHLB: return compute_c_shlb();
5591 case CC_OP_SHLW: return compute_c_shlw();
5592 case CC_OP_SHLL: return compute_c_shll();
5593
5594 case CC_OP_SARB: return compute_c_sarl();
5595 case CC_OP_SARW: return compute_c_sarl();
5596 case CC_OP_SARL: return compute_c_sarl();
5597
5598#ifdef TARGET_X86_64
5599 case CC_OP_MULQ: return compute_c_mull();
5600
5601 case CC_OP_ADDQ: return compute_c_addq();
5602
5603 case CC_OP_ADCQ: return compute_c_adcq();
5604
5605 case CC_OP_SUBQ: return compute_c_subq();
5606
5607 case CC_OP_SBBQ: return compute_c_sbbq();
5608
5609 case CC_OP_LOGICQ: return compute_c_logicq();
5610
5611 case CC_OP_INCQ: return compute_c_incl();
5612
5613 case CC_OP_DECQ: return compute_c_incl();
5614
5615 case CC_OP_SHLQ: return compute_c_shlq();
5616
5617 case CC_OP_SARQ: return compute_c_sarl();
5618#endif
5619 }
5620}