blob: 2af7f7a8dd9ac970672e3fc54080a11604a9e79c [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
David 'Digit' Turnera889d352014-03-20 12:35:32 +010023#include "cpu.h"
24#include "dyngen-exec.h"
David 'Digit' Turnere90d6652013-12-14 14:55:12 +010025#include "qemu/host-utils.h"
David 'Digit' Turnera889d352014-03-20 12:35:32 +010026#include "exec/cpu-defs.h"
27#include "helper.h"
28
29#if !defined(CONFIG_USER_ONLY)
30#include "exec/softmmu_exec.h"
31#endif /* !defined(CONFIG_USER_ONLY) */
Jun Nakajima86797932011-01-29 14:24:24 -080032
33//#define DEBUG_PCALL
34
35
36#ifdef DEBUG_PCALL
37# define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__)
38# define LOG_PCALL_STATE(env) \
39 log_cpu_state_mask(CPU_LOG_PCALL, (env), X86_DUMP_CCOP)
40#else
41# define LOG_PCALL(...) do { } while (0)
42# define LOG_PCALL_STATE(env) do { } while (0)
43#endif
44
David 'Digit' Turnera889d352014-03-20 12:35:32 +010045/* n must be a constant to be efficient */
46static inline target_long lshift(target_long x, int n)
47{
48 if (n >= 0)
49 return x << n;
50 else
51 return x >> (-n);
52}
53
54#define RC_MASK 0xc00
55#define RC_NEAR 0x000
56#define RC_DOWN 0x400
57#define RC_UP 0x800
58#define RC_CHOP 0xc00
59
60#define MAXTAN 9223372036854775808.0
61
62/* the following deal with x86 long double-precision numbers */
63#define MAXEXPD 0x7fff
64#define EXPBIAS 16383
65#define EXPD(fp) (fp.l.upper & 0x7fff)
66#define SIGND(fp) ((fp.l.upper) & 0x8000)
67#define MANTD(fp) (fp.l.lower)
68#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS
69
70static inline void fpush(void)
71{
72 env->fpstt = (env->fpstt - 1) & 7;
73 env->fptags[env->fpstt] = 0; /* validate stack entry */
74}
75
76static inline void fpop(void)
77{
78 env->fptags[env->fpstt] = 1; /* invvalidate stack entry */
79 env->fpstt = (env->fpstt + 1) & 7;
80}
81
82static inline floatx80 helper_fldt(target_ulong ptr)
83{
84 floatx80 temp;
85
86 temp.low = cpu_ldq_data(env, ptr);
87 temp.high = cpu_lduw_data(env, ptr + 8);
88 return temp;
89}
90
91static inline void helper_fstt(floatx80 f, target_ulong ptr)
92{
93 cpu_stq_data(env, ptr, f.low);
94 cpu_stw_data(env, ptr + 8, f.high);
95}
96
97#define FPUS_IE (1 << 0)
98#define FPUS_DE (1 << 1)
99#define FPUS_ZE (1 << 2)
100#define FPUS_OE (1 << 3)
101#define FPUS_UE (1 << 4)
102#define FPUS_PE (1 << 5)
103#define FPUS_SF (1 << 6)
104#define FPUS_SE (1 << 7)
105#define FPUS_B (1 << 15)
106
107#define FPUC_EM 0x3f
108
109static inline uint32_t compute_eflags(void)
110{
111 return env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK);
112}
113
114/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */
115static inline void load_eflags(int eflags, int update_mask)
116{
117 CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
118 DF = 1 - (2 * ((eflags >> 10) & 1));
119 env->eflags = (env->eflags & ~update_mask) |
120 (eflags & update_mask) | 0x2;
121}
122
123/* load efer and update the corresponding hflags. XXX: do consistency
124 checks with cpuid bits ? */
125static inline void cpu_load_efer(CPUX86State *env, uint64_t val)
126{
127 env->efer = val;
128 env->hflags &= ~(HF_LMA_MASK | HF_SVME_MASK);
129 if (env->efer & MSR_EFER_LMA)
130 env->hflags |= HF_LMA_MASK;
131 if (env->efer & MSR_EFER_SVME)
132 env->hflags |= HF_SVME_MASK;
133}
Jun Nakajima86797932011-01-29 14:24:24 -0800134
135#if 0
136#define raise_exception_err(a, b)\
137do {\
138 qemu_log("raise_exception line=%d\n", __LINE__);\
139 (raise_exception_err)(a, b);\
140} while (0)
141#endif
142
David 'Digit' Turnera889d352014-03-20 12:35:32 +0100143static void QEMU_NORETURN raise_exception_err(int exception_index,
144 int error_code);
145
Jun Nakajima86797932011-01-29 14:24:24 -0800146static const uint8_t parity_table[256] = {
147 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
148 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
149 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
150 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
151 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
152 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
153 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
154 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
155 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
156 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
157 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
158 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
159 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
160 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
161 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
162 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
163 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
164 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
165 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
166 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
167 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
168 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
169 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
170 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
171 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
172 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
173 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
174 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
175 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
176 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
177 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
178 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
179};
180
181/* modulo 17 table */
182static const uint8_t rclw_table[32] = {
183 0, 1, 2, 3, 4, 5, 6, 7,
184 8, 9,10,11,12,13,14,15,
185 16, 0, 1, 2, 3, 4, 5, 6,
186 7, 8, 9,10,11,12,13,14,
187};
188
189/* modulo 9 table */
190static const uint8_t rclb_table[32] = {
191 0, 1, 2, 3, 4, 5, 6, 7,
192 8, 0, 1, 2, 3, 4, 5, 6,
193 7, 8, 0, 1, 2, 3, 4, 5,
194 6, 7, 8, 0, 1, 2, 3, 4,
195};
196
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +0100197#define floatx80_lg2 make_floatx80( 0x3ffd, 0x9a209a84fbcff799LL )
198#define floatx80_l2e make_floatx80( 0x3fff, 0xb8aa3b295c17f0bcLL )
199#define floatx80_l2t make_floatx80( 0x4000, 0xd49a784bcd1b8afeLL )
200
201static const floatx80 f15rk[7] =
Jun Nakajima86797932011-01-29 14:24:24 -0800202{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +0100203 floatx80_zero,
204 floatx80_one,
205 floatx80_pi,
206 floatx80_lg2,
207 floatx80_ln2,
208 floatx80_l2e,
209 floatx80_l2t,
Jun Nakajima86797932011-01-29 14:24:24 -0800210};
211
212/* broken thread support */
213
214static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
215
216void helper_lock(void)
217{
218 spin_lock(&global_cpu_lock);
219}
220
221void helper_unlock(void)
222{
223 spin_unlock(&global_cpu_lock);
224}
225
226void helper_write_eflags(target_ulong t0, uint32_t update_mask)
227{
228 load_eflags(t0, update_mask);
229}
230
231target_ulong helper_read_eflags(void)
232{
233 uint32_t eflags;
234 eflags = helper_cc_compute_all(CC_OP);
235 eflags |= (DF & DF_MASK);
236 eflags |= env->eflags & ~(VM_MASK | RF_MASK);
237 return eflags;
238}
239
240/* return non zero if error */
241static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr,
242 int selector)
243{
244 SegmentCache *dt;
245 int index;
246 target_ulong ptr;
247
248 if (selector & 0x4)
249 dt = &env->ldt;
250 else
251 dt = &env->gdt;
252 index = selector & ~7;
253 if ((index + 7) > dt->limit)
254 return -1;
255 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100256 *e1_ptr = cpu_ldl_kernel(env, ptr);
257 *e2_ptr = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800258 return 0;
259}
260
261static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
262{
263 unsigned int limit;
264 limit = (e1 & 0xffff) | (e2 & 0x000f0000);
265 if (e2 & DESC_G_MASK)
266 limit = (limit << 12) | 0xfff;
267 return limit;
268}
269
270static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
271{
272 return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
273}
274
275static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2)
276{
277 sc->base = get_seg_base(e1, e2);
278 sc->limit = get_seg_limit(e1, e2);
279 sc->flags = e2;
280}
281
282/* init the segment cache in vm86 mode. */
283static inline void load_seg_vm(int seg, int selector)
284{
285 selector &= 0xffff;
286 cpu_x86_load_seg_cache(env, seg, selector,
287 (selector << 4), 0xffff, 0);
288}
289
290static inline void get_ss_esp_from_tss(uint32_t *ss_ptr,
291 uint32_t *esp_ptr, int dpl)
292{
293 int type, index, shift;
294
295#if 0
296 {
297 int i;
298 printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
299 for(i=0;i<env->tr.limit;i++) {
300 printf("%02x ", env->tr.base[i]);
301 if ((i & 7) == 7) printf("\n");
302 }
303 printf("\n");
304 }
305#endif
306
307 if (!(env->tr.flags & DESC_P_MASK))
308 cpu_abort(env, "invalid tss");
309 type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
310 if ((type & 7) != 1)
311 cpu_abort(env, "invalid tss type");
312 shift = type >> 3;
313 index = (dpl * 4 + 2) << shift;
314 if (index + (4 << shift) - 1 > env->tr.limit)
315 raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
316 if (shift == 0) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100317 *esp_ptr = cpu_lduw_kernel(env, env->tr.base + index);
318 *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 2);
Jun Nakajima86797932011-01-29 14:24:24 -0800319 } else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100320 *esp_ptr = cpu_ldl_kernel(env, env->tr.base + index);
321 *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800322 }
323}
324
325/* XXX: merge with load_seg() */
326static void tss_load_seg(int seg_reg, int selector)
327{
328 uint32_t e1, e2;
329 int rpl, dpl, cpl;
330
331 if ((selector & 0xfffc) != 0) {
332 if (load_segment(&e1, &e2, selector) != 0)
333 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
334 if (!(e2 & DESC_S_MASK))
335 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
336 rpl = selector & 3;
337 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
338 cpl = env->hflags & HF_CPL_MASK;
339 if (seg_reg == R_CS) {
340 if (!(e2 & DESC_CS_MASK))
341 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
342 /* XXX: is it correct ? */
343 if (dpl != rpl)
344 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
345 if ((e2 & DESC_C_MASK) && dpl > rpl)
346 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
347 } else if (seg_reg == R_SS) {
348 /* SS must be writable data */
349 if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
350 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
351 if (dpl != cpl || dpl != rpl)
352 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
353 } else {
354 /* not readable code */
355 if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK))
356 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
357 /* if data or non conforming code, checks the rights */
358 if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) {
359 if (dpl < cpl || dpl < rpl)
360 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
361 }
362 }
363 if (!(e2 & DESC_P_MASK))
364 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
365 cpu_x86_load_seg_cache(env, seg_reg, selector,
366 get_seg_base(e1, e2),
367 get_seg_limit(e1, e2),
368 e2);
369 } else {
370 if (seg_reg == R_SS || seg_reg == R_CS)
371 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
372 }
373}
374
375#define SWITCH_TSS_JMP 0
376#define SWITCH_TSS_IRET 1
377#define SWITCH_TSS_CALL 2
378
379/* XXX: restore CPU state in registers (PowerPC case) */
380static void switch_tss(int tss_selector,
381 uint32_t e1, uint32_t e2, int source,
382 uint32_t next_eip)
383{
384 int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
385 target_ulong tss_base;
386 uint32_t new_regs[8], new_segs[6];
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +0100387 uint32_t new_eflags, new_eip, new_cr3, new_ldt;
Jun Nakajima86797932011-01-29 14:24:24 -0800388 uint32_t old_eflags, eflags_mask;
389 SegmentCache *dt;
390 int index;
391 target_ulong ptr;
392
393 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
394 LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source);
395
396 /* if task gate, we read the TSS segment and we load it */
397 if (type == 5) {
398 if (!(e2 & DESC_P_MASK))
399 raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
400 tss_selector = e1 >> 16;
401 if (tss_selector & 4)
402 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
403 if (load_segment(&e1, &e2, tss_selector) != 0)
404 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
405 if (e2 & DESC_S_MASK)
406 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
407 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
408 if ((type & 7) != 1)
409 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
410 }
411
412 if (!(e2 & DESC_P_MASK))
413 raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
414
415 if (type & 8)
416 tss_limit_max = 103;
417 else
418 tss_limit_max = 43;
419 tss_limit = get_seg_limit(e1, e2);
420 tss_base = get_seg_base(e1, e2);
421 if ((tss_selector & 4) != 0 ||
422 tss_limit < tss_limit_max)
423 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
424 old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
425 if (old_type & 8)
426 old_tss_limit_max = 103;
427 else
428 old_tss_limit_max = 43;
429
430 /* read all the registers from the new TSS */
431 if (type & 8) {
432 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100433 new_cr3 = cpu_ldl_kernel(env, tss_base + 0x1c);
434 new_eip = cpu_ldl_kernel(env, tss_base + 0x20);
435 new_eflags = cpu_ldl_kernel(env, tss_base + 0x24);
Jun Nakajima86797932011-01-29 14:24:24 -0800436 for(i = 0; i < 8; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100437 new_regs[i] = cpu_ldl_kernel(env, tss_base + (0x28 + i * 4));
Jun Nakajima86797932011-01-29 14:24:24 -0800438 for(i = 0; i < 6; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100439 new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x48 + i * 4));
440 new_ldt = cpu_lduw_kernel(env, tss_base + 0x60);
441 cpu_ldl_kernel(env, tss_base + 0x64);
Jun Nakajima86797932011-01-29 14:24:24 -0800442 } else {
443 /* 16 bit */
444 new_cr3 = 0;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100445 new_eip = cpu_lduw_kernel(env, tss_base + 0x0e);
446 new_eflags = cpu_lduw_kernel(env, tss_base + 0x10);
Jun Nakajima86797932011-01-29 14:24:24 -0800447 for(i = 0; i < 8; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100448 new_regs[i] = cpu_lduw_kernel(env, tss_base + (0x12 + i * 2)) | 0xffff0000;
Jun Nakajima86797932011-01-29 14:24:24 -0800449 for(i = 0; i < 4; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100450 new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x22 + i * 4));
451 new_ldt = cpu_lduw_kernel(env, tss_base + 0x2a);
Jun Nakajima86797932011-01-29 14:24:24 -0800452 new_segs[R_FS] = 0;
453 new_segs[R_GS] = 0;
Jun Nakajima86797932011-01-29 14:24:24 -0800454 }
455
456 /* NOTE: we must avoid memory exceptions during the task switch,
457 so we make dummy accesses before */
458 /* XXX: it can still fail in some cases, so a bigger hack is
459 necessary to valid the TLB after having done the accesses */
460
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100461 v1 = cpu_ldub_kernel(env, env->tr.base);
462 v2 = cpu_ldub_kernel(env, env->tr.base + old_tss_limit_max);
463 cpu_stb_kernel(env, env->tr.base, v1);
464 cpu_stb_kernel(env, env->tr.base + old_tss_limit_max, v2);
Jun Nakajima86797932011-01-29 14:24:24 -0800465
466 /* clear busy bit (it is restartable) */
467 if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
468 target_ulong ptr;
469 uint32_t e2;
470 ptr = env->gdt.base + (env->tr.selector & ~7);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100471 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800472 e2 &= ~DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100473 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -0800474 }
475 old_eflags = compute_eflags();
476 if (source == SWITCH_TSS_IRET)
477 old_eflags &= ~NT_MASK;
478
479 /* save the current state in the old TSS */
480 if (type & 8) {
481 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100482 cpu_stl_kernel(env, env->tr.base + 0x20, next_eip);
483 cpu_stl_kernel(env, env->tr.base + 0x24, old_eflags);
484 cpu_stl_kernel(env, env->tr.base + (0x28 + 0 * 4), EAX);
485 cpu_stl_kernel(env, env->tr.base + (0x28 + 1 * 4), ECX);
486 cpu_stl_kernel(env, env->tr.base + (0x28 + 2 * 4), EDX);
487 cpu_stl_kernel(env, env->tr.base + (0x28 + 3 * 4), EBX);
488 cpu_stl_kernel(env, env->tr.base + (0x28 + 4 * 4), ESP);
489 cpu_stl_kernel(env, env->tr.base + (0x28 + 5 * 4), EBP);
490 cpu_stl_kernel(env, env->tr.base + (0x28 + 6 * 4), ESI);
491 cpu_stl_kernel(env, env->tr.base + (0x28 + 7 * 4), EDI);
Jun Nakajima86797932011-01-29 14:24:24 -0800492 for(i = 0; i < 6; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100493 cpu_stw_kernel(env, env->tr.base + (0x48 + i * 4), env->segs[i].selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800494 } else {
495 /* 16 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100496 cpu_stw_kernel(env, env->tr.base + 0x0e, next_eip);
497 cpu_stw_kernel(env, env->tr.base + 0x10, old_eflags);
498 cpu_stw_kernel(env, env->tr.base + (0x12 + 0 * 2), EAX);
499 cpu_stw_kernel(env, env->tr.base + (0x12 + 1 * 2), ECX);
500 cpu_stw_kernel(env, env->tr.base + (0x12 + 2 * 2), EDX);
501 cpu_stw_kernel(env, env->tr.base + (0x12 + 3 * 2), EBX);
502 cpu_stw_kernel(env, env->tr.base + (0x12 + 4 * 2), ESP);
503 cpu_stw_kernel(env, env->tr.base + (0x12 + 5 * 2), EBP);
504 cpu_stw_kernel(env, env->tr.base + (0x12 + 6 * 2), ESI);
505 cpu_stw_kernel(env, env->tr.base + (0x12 + 7 * 2), EDI);
Jun Nakajima86797932011-01-29 14:24:24 -0800506 for(i = 0; i < 4; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100507 cpu_stw_kernel(env, env->tr.base + (0x22 + i * 4), env->segs[i].selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800508 }
509
510 /* now if an exception occurs, it will occurs in the next task
511 context */
512
513 if (source == SWITCH_TSS_CALL) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100514 cpu_stw_kernel(env, tss_base, env->tr.selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800515 new_eflags |= NT_MASK;
516 }
517
518 /* set busy bit */
519 if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
520 target_ulong ptr;
521 uint32_t e2;
522 ptr = env->gdt.base + (tss_selector & ~7);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100523 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800524 e2 |= DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100525 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -0800526 }
527
528 /* set the new CPU state */
529 /* from this point, any exception which occurs can give problems */
530 env->cr[0] |= CR0_TS_MASK;
531 env->hflags |= HF_TS_MASK;
532 env->tr.selector = tss_selector;
533 env->tr.base = tss_base;
534 env->tr.limit = tss_limit;
535 env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
536
537 if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
538 cpu_x86_update_cr3(env, new_cr3);
539 }
540
541 /* load all registers without an exception, then reload them with
542 possible exception */
543 env->eip = new_eip;
544 eflags_mask = TF_MASK | AC_MASK | ID_MASK |
545 IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
546 if (!(type & 8))
547 eflags_mask &= 0xffff;
548 load_eflags(new_eflags, eflags_mask);
549 /* XXX: what to do in 16 bit case ? */
550 EAX = new_regs[0];
551 ECX = new_regs[1];
552 EDX = new_regs[2];
553 EBX = new_regs[3];
554 ESP = new_regs[4];
555 EBP = new_regs[5];
556 ESI = new_regs[6];
557 EDI = new_regs[7];
558 if (new_eflags & VM_MASK) {
559 for(i = 0; i < 6; i++)
560 load_seg_vm(i, new_segs[i]);
561 /* in vm86, CPL is always 3 */
562 cpu_x86_set_cpl(env, 3);
563 } else {
564 /* CPL is set the RPL of CS */
565 cpu_x86_set_cpl(env, new_segs[R_CS] & 3);
566 /* first just selectors as the rest may trigger exceptions */
567 for(i = 0; i < 6; i++)
568 cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
569 }
570
571 env->ldt.selector = new_ldt & ~4;
572 env->ldt.base = 0;
573 env->ldt.limit = 0;
574 env->ldt.flags = 0;
575
576 /* load the LDT */
577 if (new_ldt & 4)
578 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
579
580 if ((new_ldt & 0xfffc) != 0) {
581 dt = &env->gdt;
582 index = new_ldt & ~7;
583 if ((index + 7) > dt->limit)
584 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
585 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100586 e1 = cpu_ldl_kernel(env, ptr);
587 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800588 if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
589 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
590 if (!(e2 & DESC_P_MASK))
591 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
592 load_seg_cache_raw_dt(&env->ldt, e1, e2);
593 }
594
595 /* load the segments */
596 if (!(new_eflags & VM_MASK)) {
597 tss_load_seg(R_CS, new_segs[R_CS]);
598 tss_load_seg(R_SS, new_segs[R_SS]);
599 tss_load_seg(R_ES, new_segs[R_ES]);
600 tss_load_seg(R_DS, new_segs[R_DS]);
601 tss_load_seg(R_FS, new_segs[R_FS]);
602 tss_load_seg(R_GS, new_segs[R_GS]);
603 }
604
605 /* check that EIP is in the CS segment limits */
606 if (new_eip > env->segs[R_CS].limit) {
607 /* XXX: different exception if CALL ? */
608 raise_exception_err(EXCP0D_GPF, 0);
609 }
610
611#ifndef CONFIG_USER_ONLY
612 /* reset local breakpoints */
613 if (env->dr[7] & 0x55) {
614 for (i = 0; i < 4; i++) {
615 if (hw_breakpoint_enabled(env->dr[7], i) == 0x1)
616 hw_breakpoint_remove(env, i);
617 }
618 env->dr[7] &= ~0x55;
619 }
620#endif
621}
622
623/* check if Port I/O is allowed in TSS */
624static inline void check_io(int addr, int size)
625{
626 int io_offset, val, mask;
627
628 /* TSS must be a valid 32 bit one */
629 if (!(env->tr.flags & DESC_P_MASK) ||
630 ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
631 env->tr.limit < 103)
632 goto fail;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100633 io_offset = cpu_lduw_kernel(env, env->tr.base + 0x66);
Jun Nakajima86797932011-01-29 14:24:24 -0800634 io_offset += (addr >> 3);
635 /* Note: the check needs two bytes */
636 if ((io_offset + 1) > env->tr.limit)
637 goto fail;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100638 val = cpu_lduw_kernel(env, env->tr.base + io_offset);
Jun Nakajima86797932011-01-29 14:24:24 -0800639 val >>= (addr & 7);
640 mask = (1 << size) - 1;
641 /* all bits must be zero to allow the I/O */
642 if ((val & mask) != 0) {
643 fail:
644 raise_exception_err(EXCP0D_GPF, 0);
645 }
646}
647
648void helper_check_iob(uint32_t t0)
649{
650 check_io(t0, 1);
651}
652
653void helper_check_iow(uint32_t t0)
654{
655 check_io(t0, 2);
656}
657
658void helper_check_iol(uint32_t t0)
659{
660 check_io(t0, 4);
661}
662
663void helper_outb(uint32_t port, uint32_t data)
664{
665 cpu_outb(port, data & 0xff);
666}
667
668target_ulong helper_inb(uint32_t port)
669{
670 return cpu_inb(port);
671}
672
673void helper_outw(uint32_t port, uint32_t data)
674{
675 cpu_outw(port, data & 0xffff);
676}
677
678target_ulong helper_inw(uint32_t port)
679{
680 return cpu_inw(port);
681}
682
683void helper_outl(uint32_t port, uint32_t data)
684{
685 cpu_outl(port, data);
686}
687
688target_ulong helper_inl(uint32_t port)
689{
690 return cpu_inl(port);
691}
692
693static inline unsigned int get_sp_mask(unsigned int e2)
694{
695 if (e2 & DESC_B_MASK)
696 return 0xffffffff;
697 else
698 return 0xffff;
699}
700
701static int exeption_has_error_code(int intno)
702{
703 switch(intno) {
704 case 8:
705 case 10:
706 case 11:
707 case 12:
708 case 13:
709 case 14:
710 case 17:
711 return 1;
712 }
713 return 0;
714}
715
716#ifdef TARGET_X86_64
717#define SET_ESP(val, sp_mask)\
718do {\
719 if ((sp_mask) == 0xffff)\
720 ESP = (ESP & ~0xffff) | ((val) & 0xffff);\
721 else if ((sp_mask) == 0xffffffffLL)\
722 ESP = (uint32_t)(val);\
723 else\
724 ESP = (val);\
725} while (0)
726#else
727#define SET_ESP(val, sp_mask) ESP = (ESP & ~(sp_mask)) | ((val) & (sp_mask))
728#endif
729
730/* in 64-bit machines, this can overflow. So this segment addition macro
731 * can be used to trim the value to 32-bit whenever needed */
732#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask))))
733
734/* XXX: add a is_user flag to have proper security support */
735#define PUSHW(ssp, sp, sp_mask, val)\
736{\
737 sp -= 2;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100738 cpu_stw_kernel(env, (ssp) + (sp & (sp_mask)), (val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800739}
740
741#define PUSHL(ssp, sp, sp_mask, val)\
742{\
743 sp -= 4;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100744 cpu_stl_kernel(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800745}
746
747#define POPW(ssp, sp, sp_mask, val)\
748{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100749 val = cpu_lduw_kernel(env, (ssp) + (sp & (sp_mask)));\
Jun Nakajima86797932011-01-29 14:24:24 -0800750 sp += 2;\
751}
752
753#define POPL(ssp, sp, sp_mask, val)\
754{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100755 val = (uint32_t)cpu_ldl_kernel(env, SEG_ADDL(ssp, sp, sp_mask));\
Jun Nakajima86797932011-01-29 14:24:24 -0800756 sp += 4;\
757}
758
759/* protected mode interrupt */
760static void do_interrupt_protected(int intno, int is_int, int error_code,
761 unsigned int next_eip, int is_hw)
762{
763 SegmentCache *dt;
764 target_ulong ptr, ssp;
765 int type, dpl, selector, ss_dpl, cpl;
766 int has_error_code, new_stack, shift;
767 uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
768 uint32_t old_eip, sp_mask;
769
770 has_error_code = 0;
771 if (!is_int && !is_hw)
772 has_error_code = exeption_has_error_code(intno);
773 if (is_int)
774 old_eip = next_eip;
775 else
776 old_eip = env->eip;
777
778 dt = &env->idt;
779 if (intno * 8 + 7 > dt->limit)
780 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
781 ptr = dt->base + intno * 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100782 e1 = cpu_ldl_kernel(env, ptr);
783 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800784 /* check gate type */
785 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
786 switch(type) {
787 case 5: /* task gate */
788 /* must do that check here to return the correct error code */
789 if (!(e2 & DESC_P_MASK))
790 raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
791 switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
792 if (has_error_code) {
793 int type;
794 uint32_t mask;
795 /* push the error code */
796 type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
797 shift = type >> 3;
798 if (env->segs[R_SS].flags & DESC_B_MASK)
799 mask = 0xffffffff;
800 else
801 mask = 0xffff;
802 esp = (ESP - (2 << shift)) & mask;
803 ssp = env->segs[R_SS].base + esp;
804 if (shift)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100805 cpu_stl_kernel(env, ssp, error_code);
Jun Nakajima86797932011-01-29 14:24:24 -0800806 else
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100807 cpu_stw_kernel(env, ssp, error_code);
Jun Nakajima86797932011-01-29 14:24:24 -0800808 SET_ESP(esp, mask);
809 }
810 return;
811 case 6: /* 286 interrupt gate */
812 case 7: /* 286 trap gate */
813 case 14: /* 386 interrupt gate */
814 case 15: /* 386 trap gate */
815 break;
816 default:
817 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
818 break;
819 }
820 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
821 cpl = env->hflags & HF_CPL_MASK;
822 /* check privilege if software int */
823 if (is_int && dpl < cpl)
824 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
825 /* check valid bit */
826 if (!(e2 & DESC_P_MASK))
827 raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
828 selector = e1 >> 16;
829 offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
830 if ((selector & 0xfffc) == 0)
831 raise_exception_err(EXCP0D_GPF, 0);
832
833 if (load_segment(&e1, &e2, selector) != 0)
834 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
835 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
836 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
837 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
838 if (dpl > cpl)
839 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
840 if (!(e2 & DESC_P_MASK))
841 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
842 if (!(e2 & DESC_C_MASK) && dpl < cpl) {
843 /* to inner privilege */
844 get_ss_esp_from_tss(&ss, &esp, dpl);
845 if ((ss & 0xfffc) == 0)
846 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
847 if ((ss & 3) != dpl)
848 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
849 if (load_segment(&ss_e1, &ss_e2, ss) != 0)
850 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
851 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
852 if (ss_dpl != dpl)
853 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
854 if (!(ss_e2 & DESC_S_MASK) ||
855 (ss_e2 & DESC_CS_MASK) ||
856 !(ss_e2 & DESC_W_MASK))
857 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
858 if (!(ss_e2 & DESC_P_MASK))
859 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
860 new_stack = 1;
861 sp_mask = get_sp_mask(ss_e2);
862 ssp = get_seg_base(ss_e1, ss_e2);
863 } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
864 /* to same privilege */
865 if (env->eflags & VM_MASK)
866 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
867 new_stack = 0;
868 sp_mask = get_sp_mask(env->segs[R_SS].flags);
869 ssp = env->segs[R_SS].base;
870 esp = ESP;
871 dpl = cpl;
872 } else {
873 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
874 new_stack = 0; /* avoid warning */
875 sp_mask = 0; /* avoid warning */
876 ssp = 0; /* avoid warning */
877 esp = 0; /* avoid warning */
878 }
879
880 shift = type >> 3;
881
882#if 0
883 /* XXX: check that enough room is available */
884 push_size = 6 + (new_stack << 2) + (has_error_code << 1);
885 if (env->eflags & VM_MASK)
886 push_size += 8;
887 push_size <<= shift;
888#endif
889 if (shift == 1) {
890 if (new_stack) {
891 if (env->eflags & VM_MASK) {
892 PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
893 PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
894 PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
895 PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
896 }
897 PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
898 PUSHL(ssp, esp, sp_mask, ESP);
899 }
900 PUSHL(ssp, esp, sp_mask, compute_eflags());
901 PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
902 PUSHL(ssp, esp, sp_mask, old_eip);
903 if (has_error_code) {
904 PUSHL(ssp, esp, sp_mask, error_code);
905 }
906 } else {
907 if (new_stack) {
908 if (env->eflags & VM_MASK) {
909 PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
910 PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
911 PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
912 PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector);
913 }
914 PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
915 PUSHW(ssp, esp, sp_mask, ESP);
916 }
917 PUSHW(ssp, esp, sp_mask, compute_eflags());
918 PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
919 PUSHW(ssp, esp, sp_mask, old_eip);
920 if (has_error_code) {
921 PUSHW(ssp, esp, sp_mask, error_code);
922 }
923 }
924
925 if (new_stack) {
926 if (env->eflags & VM_MASK) {
927 cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
928 cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
929 cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
930 cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
931 }
932 ss = (ss & ~3) | dpl;
933 cpu_x86_load_seg_cache(env, R_SS, ss,
934 ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
935 }
936 SET_ESP(esp, sp_mask);
937
938 selector = (selector & ~3) | dpl;
939 cpu_x86_load_seg_cache(env, R_CS, selector,
940 get_seg_base(e1, e2),
941 get_seg_limit(e1, e2),
942 e2);
943 cpu_x86_set_cpl(env, dpl);
944 env->eip = offset;
945
946 /* interrupt gate clear IF mask */
947 if ((type & 1) == 0) {
948 env->eflags &= ~IF_MASK;
949 }
950 env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
951}
952
953#ifdef TARGET_X86_64
954
955#define PUSHQ(sp, val)\
956{\
957 sp -= 8;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100958 cpu_stq_kernel(env, sp, (val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800959}
960
961#define POPQ(sp, val)\
962{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100963 val = cpu_ldq_kernel(env, sp);\
Jun Nakajima86797932011-01-29 14:24:24 -0800964 sp += 8;\
965}
966
967static inline target_ulong get_rsp_from_tss(int level)
968{
969 int index;
970
971#if 0
972 printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
973 env->tr.base, env->tr.limit);
974#endif
975
976 if (!(env->tr.flags & DESC_P_MASK))
977 cpu_abort(env, "invalid tss");
978 index = 8 * level + 4;
979 if ((index + 7) > env->tr.limit)
980 raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100981 return cpu_ldq_kernel(env, env->tr.base + index);
Jun Nakajima86797932011-01-29 14:24:24 -0800982}
983
984/* 64 bit interrupt */
985static void do_interrupt64(int intno, int is_int, int error_code,
986 target_ulong next_eip, int is_hw)
987{
988 SegmentCache *dt;
989 target_ulong ptr;
990 int type, dpl, selector, cpl, ist;
991 int has_error_code, new_stack;
992 uint32_t e1, e2, e3, ss;
993 target_ulong old_eip, esp, offset;
994
995 has_error_code = 0;
996 if (!is_int && !is_hw)
997 has_error_code = exeption_has_error_code(intno);
998 if (is_int)
999 old_eip = next_eip;
1000 else
1001 old_eip = env->eip;
1002
1003 dt = &env->idt;
1004 if (intno * 16 + 15 > dt->limit)
1005 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
1006 ptr = dt->base + intno * 16;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001007 e1 = cpu_ldl_kernel(env, ptr);
1008 e2 = cpu_ldl_kernel(env, ptr + 4);
1009 e3 = cpu_ldl_kernel(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08001010 /* check gate type */
1011 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
1012 switch(type) {
1013 case 14: /* 386 interrupt gate */
1014 case 15: /* 386 trap gate */
1015 break;
1016 default:
1017 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
1018 break;
1019 }
1020 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1021 cpl = env->hflags & HF_CPL_MASK;
1022 /* check privilege if software int */
1023 if (is_int && dpl < cpl)
1024 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
1025 /* check valid bit */
1026 if (!(e2 & DESC_P_MASK))
1027 raise_exception_err(EXCP0B_NOSEG, intno * 16 + 2);
1028 selector = e1 >> 16;
1029 offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
1030 ist = e2 & 7;
1031 if ((selector & 0xfffc) == 0)
1032 raise_exception_err(EXCP0D_GPF, 0);
1033
1034 if (load_segment(&e1, &e2, selector) != 0)
1035 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1036 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
1037 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1038 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1039 if (dpl > cpl)
1040 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1041 if (!(e2 & DESC_P_MASK))
1042 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
1043 if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK))
1044 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1045 if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {
1046 /* to inner privilege */
1047 if (ist != 0)
1048 esp = get_rsp_from_tss(ist + 3);
1049 else
1050 esp = get_rsp_from_tss(dpl);
1051 esp &= ~0xfLL; /* align stack */
1052 ss = 0;
1053 new_stack = 1;
1054 } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
1055 /* to same privilege */
1056 if (env->eflags & VM_MASK)
1057 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1058 new_stack = 0;
1059 if (ist != 0)
1060 esp = get_rsp_from_tss(ist + 3);
1061 else
1062 esp = ESP;
1063 esp &= ~0xfLL; /* align stack */
1064 dpl = cpl;
1065 } else {
1066 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1067 new_stack = 0; /* avoid warning */
1068 esp = 0; /* avoid warning */
1069 }
1070
1071 PUSHQ(esp, env->segs[R_SS].selector);
1072 PUSHQ(esp, ESP);
1073 PUSHQ(esp, compute_eflags());
1074 PUSHQ(esp, env->segs[R_CS].selector);
1075 PUSHQ(esp, old_eip);
1076 if (has_error_code) {
1077 PUSHQ(esp, error_code);
1078 }
1079
1080 if (new_stack) {
1081 ss = 0 | dpl;
1082 cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
1083 }
1084 ESP = esp;
1085
1086 selector = (selector & ~3) | dpl;
1087 cpu_x86_load_seg_cache(env, R_CS, selector,
1088 get_seg_base(e1, e2),
1089 get_seg_limit(e1, e2),
1090 e2);
1091 cpu_x86_set_cpl(env, dpl);
1092 env->eip = offset;
1093
1094 /* interrupt gate clear IF mask */
1095 if ((type & 1) == 0) {
1096 env->eflags &= ~IF_MASK;
1097 }
1098 env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
1099}
1100#endif
1101
1102#ifdef TARGET_X86_64
1103#if defined(CONFIG_USER_ONLY)
1104void helper_syscall(int next_eip_addend)
1105{
1106 env->exception_index = EXCP_SYSCALL;
1107 env->exception_next_eip = env->eip + next_eip_addend;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01001108 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08001109}
1110#else
1111void helper_syscall(int next_eip_addend)
1112{
1113 int selector;
1114
1115 if (!(env->efer & MSR_EFER_SCE)) {
1116 raise_exception_err(EXCP06_ILLOP, 0);
1117 }
1118 selector = (env->star >> 32) & 0xffff;
1119 if (env->hflags & HF_LMA_MASK) {
1120 int code64;
1121
1122 ECX = env->eip + next_eip_addend;
1123 env->regs[11] = compute_eflags();
1124
1125 code64 = env->hflags & HF_CS64_MASK;
1126
1127 cpu_x86_set_cpl(env, 0);
1128 cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1129 0, 0xffffffff,
1130 DESC_G_MASK | DESC_P_MASK |
1131 DESC_S_MASK |
1132 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
1133 cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1134 0, 0xffffffff,
1135 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1136 DESC_S_MASK |
1137 DESC_W_MASK | DESC_A_MASK);
1138 env->eflags &= ~env->fmask;
1139 load_eflags(env->eflags, 0);
1140 if (code64)
1141 env->eip = env->lstar;
1142 else
1143 env->eip = env->cstar;
1144 } else {
1145 ECX = (uint32_t)(env->eip + next_eip_addend);
1146
1147 cpu_x86_set_cpl(env, 0);
1148 cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1149 0, 0xffffffff,
1150 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1151 DESC_S_MASK |
1152 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1153 cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1154 0, 0xffffffff,
1155 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1156 DESC_S_MASK |
1157 DESC_W_MASK | DESC_A_MASK);
1158 env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
1159 env->eip = (uint32_t)env->star;
1160 }
1161}
1162#endif
1163#endif
1164
1165#ifdef TARGET_X86_64
1166void helper_sysret(int dflag)
1167{
1168 int cpl, selector;
1169
1170 if (!(env->efer & MSR_EFER_SCE)) {
1171 raise_exception_err(EXCP06_ILLOP, 0);
1172 }
1173 cpl = env->hflags & HF_CPL_MASK;
1174 if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
1175 raise_exception_err(EXCP0D_GPF, 0);
1176 }
1177 selector = (env->star >> 48) & 0xffff;
1178 if (env->hflags & HF_LMA_MASK) {
1179 if (dflag == 2) {
1180 cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
1181 0, 0xffffffff,
1182 DESC_G_MASK | DESC_P_MASK |
1183 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1184 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
1185 DESC_L_MASK);
1186 env->eip = ECX;
1187 } else {
1188 cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1189 0, 0xffffffff,
1190 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1191 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1192 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1193 env->eip = (uint32_t)ECX;
1194 }
1195 cpu_x86_load_seg_cache(env, R_SS, selector + 8,
1196 0, 0xffffffff,
1197 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1198 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1199 DESC_W_MASK | DESC_A_MASK);
1200 load_eflags((uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK |
1201 IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK);
1202 cpu_x86_set_cpl(env, 3);
1203 } else {
1204 cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1205 0, 0xffffffff,
1206 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1207 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1208 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1209 env->eip = (uint32_t)ECX;
1210 cpu_x86_load_seg_cache(env, R_SS, selector + 8,
1211 0, 0xffffffff,
1212 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1213 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1214 DESC_W_MASK | DESC_A_MASK);
1215 env->eflags |= IF_MASK;
1216 cpu_x86_set_cpl(env, 3);
1217 }
Jun Nakajima86797932011-01-29 14:24:24 -08001218}
1219#endif
1220
1221/* real mode interrupt */
1222static void do_interrupt_real(int intno, int is_int, int error_code,
1223 unsigned int next_eip)
1224{
1225 SegmentCache *dt;
1226 target_ulong ptr, ssp;
1227 int selector;
1228 uint32_t offset, esp;
1229 uint32_t old_cs, old_eip;
1230
1231 /* real mode (simpler !) */
1232 dt = &env->idt;
1233 if (intno * 4 + 3 > dt->limit)
1234 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
1235 ptr = dt->base + intno * 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001236 offset = cpu_lduw_kernel(env, ptr);
1237 selector = cpu_lduw_kernel(env, ptr + 2);
Jun Nakajima86797932011-01-29 14:24:24 -08001238 esp = ESP;
1239 ssp = env->segs[R_SS].base;
1240 if (is_int)
1241 old_eip = next_eip;
1242 else
1243 old_eip = env->eip;
1244 old_cs = env->segs[R_CS].selector;
1245 /* XXX: use SS segment size ? */
1246 PUSHW(ssp, esp, 0xffff, compute_eflags());
1247 PUSHW(ssp, esp, 0xffff, old_cs);
1248 PUSHW(ssp, esp, 0xffff, old_eip);
1249
1250 /* update processor state */
1251 ESP = (ESP & ~0xffff) | (esp & 0xffff);
1252 env->eip = offset;
1253 env->segs[R_CS].selector = selector;
1254 env->segs[R_CS].base = (selector << 4);
1255 env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
1256}
1257
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001258#if defined(CONFIG_USER_ONLY)
Jun Nakajima86797932011-01-29 14:24:24 -08001259/* fake user mode interrupt */
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001260static void do_interrupt_user(int intno, int is_int, int error_code,
1261 target_ulong next_eip)
Jun Nakajima86797932011-01-29 14:24:24 -08001262{
1263 SegmentCache *dt;
1264 target_ulong ptr;
1265 int dpl, cpl, shift;
1266 uint32_t e2;
1267
1268 dt = &env->idt;
1269 if (env->hflags & HF_LMA_MASK) {
1270 shift = 4;
1271 } else {
1272 shift = 3;
1273 }
1274 ptr = dt->base + (intno << shift);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001275 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08001276
1277 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1278 cpl = env->hflags & HF_CPL_MASK;
1279 /* check privilege if software int */
1280 if (is_int && dpl < cpl)
1281 raise_exception_err(EXCP0D_GPF, (intno << shift) + 2);
1282
1283 /* Since we emulate only user space, we cannot do more than
1284 exiting the emulation with the suitable exception and error
1285 code */
1286 if (is_int)
1287 EIP = next_eip;
1288}
1289
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001290#else
1291
Jun Nakajima86797932011-01-29 14:24:24 -08001292static void handle_even_inj(int intno, int is_int, int error_code,
1293 int is_hw, int rm)
1294{
1295 uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
1296 if (!(event_inj & SVM_EVTINJ_VALID)) {
1297 int type;
1298 if (is_int)
1299 type = SVM_EVTINJ_TYPE_SOFT;
1300 else
1301 type = SVM_EVTINJ_TYPE_EXEPT;
1302 event_inj = intno | type | SVM_EVTINJ_VALID;
1303 if (!rm && exeption_has_error_code(intno)) {
1304 event_inj |= SVM_EVTINJ_VALID_ERR;
1305 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err), error_code);
1306 }
1307 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj);
1308 }
1309}
1310#endif
1311
1312/*
1313 * Begin execution of an interruption. is_int is TRUE if coming from
1314 * the int instruction. next_eip is the EIP value AFTER the interrupt
1315 * instruction. It is only relevant if is_int is TRUE.
1316 */
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001317static void do_interrupt_all(int intno, int is_int, int error_code,
1318 target_ulong next_eip, int is_hw)
Jun Nakajima86797932011-01-29 14:24:24 -08001319{
1320 if (qemu_loglevel_mask(CPU_LOG_INT)) {
1321 if ((env->cr[0] & CR0_PE_MASK)) {
1322 static int count;
1323 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,
1324 count, intno, error_code, is_int,
1325 env->hflags & HF_CPL_MASK,
1326 env->segs[R_CS].selector, EIP,
1327 (int)env->segs[R_CS].base + EIP,
1328 env->segs[R_SS].selector, ESP);
1329 if (intno == 0x0e) {
1330 qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]);
1331 } else {
1332 qemu_log(" EAX=" TARGET_FMT_lx, EAX);
1333 }
1334 qemu_log("\n");
1335 log_cpu_state(env, X86_DUMP_CCOP);
1336#if 0
1337 {
1338 int i;
1339 uint8_t *ptr;
1340 qemu_log(" code=");
1341 ptr = env->segs[R_CS].base + env->eip;
1342 for(i = 0; i < 16; i++) {
1343 qemu_log(" %02x", ldub(ptr + i));
1344 }
1345 qemu_log("\n");
1346 }
1347#endif
1348 count++;
1349 }
1350 }
1351 if (env->cr[0] & CR0_PE_MASK) {
1352#if !defined(CONFIG_USER_ONLY)
1353 if (env->hflags & HF_SVMI_MASK)
1354 handle_even_inj(intno, is_int, error_code, is_hw, 0);
1355#endif
1356#ifdef TARGET_X86_64
1357 if (env->hflags & HF_LMA_MASK) {
1358 do_interrupt64(intno, is_int, error_code, next_eip, is_hw);
1359 } else
1360#endif
1361 {
1362 do_interrupt_protected(intno, is_int, error_code, next_eip, is_hw);
1363 }
1364 } else {
1365#if !defined(CONFIG_USER_ONLY)
1366 if (env->hflags & HF_SVMI_MASK)
1367 handle_even_inj(intno, is_int, error_code, is_hw, 1);
1368#endif
1369 do_interrupt_real(intno, is_int, error_code, next_eip);
1370 }
1371
1372#if !defined(CONFIG_USER_ONLY)
1373 if (env->hflags & HF_SVMI_MASK) {
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001374 uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
1375 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj & ~SVM_EVTINJ_VALID);
Jun Nakajima86797932011-01-29 14:24:24 -08001376 }
1377#endif
1378}
1379
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001380void do_interrupt(CPUArchState *env1)
1381{
1382 CPUArchState *saved_env;
1383
1384 saved_env = env;
1385 env = env1;
1386#if defined(CONFIG_USER_ONLY)
1387 /* if user mode only, we simulate a fake exception
1388 which will be handled outside the cpu execution
1389 loop */
1390 do_interrupt_user(env->exception_index,
1391 env->exception_is_int,
1392 env->error_code,
1393 env->exception_next_eip);
1394 /* successfully delivered */
1395 env->old_exception = -1;
1396#else
1397 /* simulate a real cpu exception. On i386, it can
1398 trigger new exceptions, but we do not handle
1399 double or triple faults yet. */
1400 do_interrupt_all(env->exception_index,
1401 env->exception_is_int,
1402 env->error_code,
1403 env->exception_next_eip, 0);
1404 /* successfully delivered */
1405 env->old_exception = -1;
1406#endif
1407 env = saved_env;
1408}
1409
1410void do_interrupt_x86_hardirq(CPUArchState *env1, int intno, int is_hw)
1411{
1412 CPUArchState *saved_env;
1413
1414 saved_env = env;
1415 env = env1;
1416 do_interrupt_all(intno, 0, 0, 0, is_hw);
1417 env = saved_env;
1418}
1419
Jun Nakajima86797932011-01-29 14:24:24 -08001420/* This should come from sysemu.h - if we could include it here... */
1421void qemu_system_reset_request(void);
1422
1423/*
1424 * Check nested exceptions and change to double or triple fault if
1425 * needed. It should only be called, if this is not an interrupt.
1426 * Returns the new exception number.
1427 */
1428static int check_exception(int intno, int *error_code)
1429{
1430 int first_contributory = env->old_exception == 0 ||
1431 (env->old_exception >= 10 &&
1432 env->old_exception <= 13);
1433 int second_contributory = intno == 0 ||
1434 (intno >= 10 && intno <= 13);
1435
1436 qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n",
1437 env->old_exception, intno);
1438
1439#if !defined(CONFIG_USER_ONLY)
1440 if (env->old_exception == EXCP08_DBLE) {
1441 if (env->hflags & HF_SVMI_MASK)
1442 helper_vmexit(SVM_EXIT_SHUTDOWN, 0); /* does not return */
1443
1444 qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
1445
1446 qemu_system_reset_request();
1447 return EXCP_HLT;
1448 }
1449#endif
1450
1451 if ((first_contributory && second_contributory)
1452 || (env->old_exception == EXCP0E_PAGE &&
1453 (second_contributory || (intno == EXCP0E_PAGE)))) {
1454 intno = EXCP08_DBLE;
1455 *error_code = 0;
1456 }
1457
1458 if (second_contributory || (intno == EXCP0E_PAGE) ||
1459 (intno == EXCP08_DBLE))
1460 env->old_exception = intno;
1461
1462 return intno;
1463}
1464
1465/*
1466 * Signal an interruption. It is executed in the main CPU loop.
1467 * is_int is TRUE if coming from the int instruction. next_eip is the
1468 * EIP value AFTER the interrupt instruction. It is only relevant if
1469 * is_int is TRUE.
1470 */
1471static void QEMU_NORETURN raise_interrupt(int intno, int is_int, int error_code,
1472 int next_eip_addend)
1473{
1474 if (!is_int) {
1475 helper_svm_check_intercept_param(SVM_EXIT_EXCP_BASE + intno, error_code);
1476 intno = check_exception(intno, &error_code);
1477 } else {
1478 helper_svm_check_intercept_param(SVM_EXIT_SWINT, 0);
1479 }
1480
1481 env->exception_index = intno;
1482 env->error_code = error_code;
1483 env->exception_is_int = is_int;
1484 env->exception_next_eip = env->eip + next_eip_addend;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01001485 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08001486}
1487
1488/* shortcuts to generate exceptions */
1489
1490void raise_exception_err(int exception_index, int error_code)
1491{
1492 raise_interrupt(exception_index, 0, error_code, 0);
1493}
1494
1495void raise_exception(int exception_index)
1496{
1497 raise_interrupt(exception_index, 0, 0, 0);
1498}
1499
1500/* SMM support */
1501
1502#if defined(CONFIG_USER_ONLY)
1503
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001504void do_smm_enter(CPUArchState *env1)
Jun Nakajima86797932011-01-29 14:24:24 -08001505{
1506}
1507
1508void helper_rsm(void)
1509{
1510}
1511
1512#else
1513
1514#ifdef TARGET_X86_64
1515#define SMM_REVISION_ID 0x00020064
1516#else
1517#define SMM_REVISION_ID 0x00020000
1518#endif
1519
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001520void do_smm_enter(CPUArchState *env1)
Jun Nakajima86797932011-01-29 14:24:24 -08001521{
1522 target_ulong sm_state;
1523 SegmentCache *dt;
1524 int i, offset;
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001525 CPUArchState *saved_env;
1526
1527 saved_env = env;
1528 env = env1;
Jun Nakajima86797932011-01-29 14:24:24 -08001529
1530 qemu_log_mask(CPU_LOG_INT, "SMM: enter\n");
1531 log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
1532
1533 env->hflags |= HF_SMM_MASK;
1534 cpu_smm_update(env);
1535
1536 sm_state = env->smbase + 0x8000;
1537
1538#ifdef TARGET_X86_64
1539 for(i = 0; i < 6; i++) {
1540 dt = &env->segs[i];
1541 offset = 0x7e00 + i * 16;
1542 stw_phys(sm_state + offset, dt->selector);
1543 stw_phys(sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff);
1544 stl_phys(sm_state + offset + 4, dt->limit);
1545 stq_phys(sm_state + offset + 8, dt->base);
1546 }
1547
1548 stq_phys(sm_state + 0x7e68, env->gdt.base);
1549 stl_phys(sm_state + 0x7e64, env->gdt.limit);
1550
1551 stw_phys(sm_state + 0x7e70, env->ldt.selector);
1552 stq_phys(sm_state + 0x7e78, env->ldt.base);
1553 stl_phys(sm_state + 0x7e74, env->ldt.limit);
1554 stw_phys(sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff);
1555
1556 stq_phys(sm_state + 0x7e88, env->idt.base);
1557 stl_phys(sm_state + 0x7e84, env->idt.limit);
1558
1559 stw_phys(sm_state + 0x7e90, env->tr.selector);
1560 stq_phys(sm_state + 0x7e98, env->tr.base);
1561 stl_phys(sm_state + 0x7e94, env->tr.limit);
1562 stw_phys(sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff);
1563
1564 stq_phys(sm_state + 0x7ed0, env->efer);
1565
1566 stq_phys(sm_state + 0x7ff8, EAX);
1567 stq_phys(sm_state + 0x7ff0, ECX);
1568 stq_phys(sm_state + 0x7fe8, EDX);
1569 stq_phys(sm_state + 0x7fe0, EBX);
1570 stq_phys(sm_state + 0x7fd8, ESP);
1571 stq_phys(sm_state + 0x7fd0, EBP);
1572 stq_phys(sm_state + 0x7fc8, ESI);
1573 stq_phys(sm_state + 0x7fc0, EDI);
1574 for(i = 8; i < 16; i++)
1575 stq_phys(sm_state + 0x7ff8 - i * 8, env->regs[i]);
1576 stq_phys(sm_state + 0x7f78, env->eip);
1577 stl_phys(sm_state + 0x7f70, compute_eflags());
1578 stl_phys(sm_state + 0x7f68, env->dr[6]);
1579 stl_phys(sm_state + 0x7f60, env->dr[7]);
1580
1581 stl_phys(sm_state + 0x7f48, env->cr[4]);
1582 stl_phys(sm_state + 0x7f50, env->cr[3]);
1583 stl_phys(sm_state + 0x7f58, env->cr[0]);
1584
1585 stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
1586 stl_phys(sm_state + 0x7f00, env->smbase);
1587#else
1588 stl_phys(sm_state + 0x7ffc, env->cr[0]);
1589 stl_phys(sm_state + 0x7ff8, env->cr[3]);
1590 stl_phys(sm_state + 0x7ff4, compute_eflags());
1591 stl_phys(sm_state + 0x7ff0, env->eip);
1592 stl_phys(sm_state + 0x7fec, EDI);
1593 stl_phys(sm_state + 0x7fe8, ESI);
1594 stl_phys(sm_state + 0x7fe4, EBP);
1595 stl_phys(sm_state + 0x7fe0, ESP);
1596 stl_phys(sm_state + 0x7fdc, EBX);
1597 stl_phys(sm_state + 0x7fd8, EDX);
1598 stl_phys(sm_state + 0x7fd4, ECX);
1599 stl_phys(sm_state + 0x7fd0, EAX);
1600 stl_phys(sm_state + 0x7fcc, env->dr[6]);
1601 stl_phys(sm_state + 0x7fc8, env->dr[7]);
1602
1603 stl_phys(sm_state + 0x7fc4, env->tr.selector);
1604 stl_phys(sm_state + 0x7f64, env->tr.base);
1605 stl_phys(sm_state + 0x7f60, env->tr.limit);
1606 stl_phys(sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff);
1607
1608 stl_phys(sm_state + 0x7fc0, env->ldt.selector);
1609 stl_phys(sm_state + 0x7f80, env->ldt.base);
1610 stl_phys(sm_state + 0x7f7c, env->ldt.limit);
1611 stl_phys(sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff);
1612
1613 stl_phys(sm_state + 0x7f74, env->gdt.base);
1614 stl_phys(sm_state + 0x7f70, env->gdt.limit);
1615
1616 stl_phys(sm_state + 0x7f58, env->idt.base);
1617 stl_phys(sm_state + 0x7f54, env->idt.limit);
1618
1619 for(i = 0; i < 6; i++) {
1620 dt = &env->segs[i];
1621 if (i < 3)
1622 offset = 0x7f84 + i * 12;
1623 else
1624 offset = 0x7f2c + (i - 3) * 12;
1625 stl_phys(sm_state + 0x7fa8 + i * 4, dt->selector);
1626 stl_phys(sm_state + offset + 8, dt->base);
1627 stl_phys(sm_state + offset + 4, dt->limit);
1628 stl_phys(sm_state + offset, (dt->flags >> 8) & 0xf0ff);
1629 }
1630 stl_phys(sm_state + 0x7f14, env->cr[4]);
1631
1632 stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
1633 stl_phys(sm_state + 0x7ef8, env->smbase);
1634#endif
1635 /* init SMM cpu state */
1636
1637#ifdef TARGET_X86_64
1638 cpu_load_efer(env, 0);
1639#endif
1640 load_eflags(0, ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1641 env->eip = 0x00008000;
1642 cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase,
1643 0xffffffff, 0);
1644 cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, 0);
1645 cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, 0);
1646 cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, 0);
1647 cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, 0);
1648 cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, 0);
1649
1650 cpu_x86_update_cr0(env,
1651 env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK | CR0_PG_MASK));
1652 cpu_x86_update_cr4(env, 0);
1653 env->dr[7] = 0x00000400;
1654 CC_OP = CC_OP_EFLAGS;
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001655 env = saved_env;
Jun Nakajima86797932011-01-29 14:24:24 -08001656}
1657
1658void helper_rsm(void)
1659{
1660 target_ulong sm_state;
1661 int i, offset;
1662 uint32_t val;
1663
1664 sm_state = env->smbase + 0x8000;
1665#ifdef TARGET_X86_64
1666 cpu_load_efer(env, ldq_phys(sm_state + 0x7ed0));
1667
1668 for(i = 0; i < 6; i++) {
1669 offset = 0x7e00 + i * 16;
1670 cpu_x86_load_seg_cache(env, i,
1671 lduw_phys(sm_state + offset),
1672 ldq_phys(sm_state + offset + 8),
1673 ldl_phys(sm_state + offset + 4),
1674 (lduw_phys(sm_state + offset + 2) & 0xf0ff) << 8);
1675 }
1676
1677 env->gdt.base = ldq_phys(sm_state + 0x7e68);
1678 env->gdt.limit = ldl_phys(sm_state + 0x7e64);
1679
1680 env->ldt.selector = lduw_phys(sm_state + 0x7e70);
1681 env->ldt.base = ldq_phys(sm_state + 0x7e78);
1682 env->ldt.limit = ldl_phys(sm_state + 0x7e74);
1683 env->ldt.flags = (lduw_phys(sm_state + 0x7e72) & 0xf0ff) << 8;
1684
1685 env->idt.base = ldq_phys(sm_state + 0x7e88);
1686 env->idt.limit = ldl_phys(sm_state + 0x7e84);
1687
1688 env->tr.selector = lduw_phys(sm_state + 0x7e90);
1689 env->tr.base = ldq_phys(sm_state + 0x7e98);
1690 env->tr.limit = ldl_phys(sm_state + 0x7e94);
1691 env->tr.flags = (lduw_phys(sm_state + 0x7e92) & 0xf0ff) << 8;
1692
1693 EAX = ldq_phys(sm_state + 0x7ff8);
1694 ECX = ldq_phys(sm_state + 0x7ff0);
1695 EDX = ldq_phys(sm_state + 0x7fe8);
1696 EBX = ldq_phys(sm_state + 0x7fe0);
1697 ESP = ldq_phys(sm_state + 0x7fd8);
1698 EBP = ldq_phys(sm_state + 0x7fd0);
1699 ESI = ldq_phys(sm_state + 0x7fc8);
1700 EDI = ldq_phys(sm_state + 0x7fc0);
1701 for(i = 8; i < 16; i++)
1702 env->regs[i] = ldq_phys(sm_state + 0x7ff8 - i * 8);
1703 env->eip = ldq_phys(sm_state + 0x7f78);
1704 load_eflags(ldl_phys(sm_state + 0x7f70),
1705 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1706 env->dr[6] = ldl_phys(sm_state + 0x7f68);
1707 env->dr[7] = ldl_phys(sm_state + 0x7f60);
1708
1709 cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f48));
1710 cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7f50));
1711 cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7f58));
1712
1713 val = ldl_phys(sm_state + 0x7efc); /* revision ID */
1714 if (val & 0x20000) {
1715 env->smbase = ldl_phys(sm_state + 0x7f00) & ~0x7fff;
1716 }
1717#else
1718 cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7ffc));
1719 cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7ff8));
1720 load_eflags(ldl_phys(sm_state + 0x7ff4),
1721 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1722 env->eip = ldl_phys(sm_state + 0x7ff0);
1723 EDI = ldl_phys(sm_state + 0x7fec);
1724 ESI = ldl_phys(sm_state + 0x7fe8);
1725 EBP = ldl_phys(sm_state + 0x7fe4);
1726 ESP = ldl_phys(sm_state + 0x7fe0);
1727 EBX = ldl_phys(sm_state + 0x7fdc);
1728 EDX = ldl_phys(sm_state + 0x7fd8);
1729 ECX = ldl_phys(sm_state + 0x7fd4);
1730 EAX = ldl_phys(sm_state + 0x7fd0);
1731 env->dr[6] = ldl_phys(sm_state + 0x7fcc);
1732 env->dr[7] = ldl_phys(sm_state + 0x7fc8);
1733
1734 env->tr.selector = ldl_phys(sm_state + 0x7fc4) & 0xffff;
1735 env->tr.base = ldl_phys(sm_state + 0x7f64);
1736 env->tr.limit = ldl_phys(sm_state + 0x7f60);
1737 env->tr.flags = (ldl_phys(sm_state + 0x7f5c) & 0xf0ff) << 8;
1738
1739 env->ldt.selector = ldl_phys(sm_state + 0x7fc0) & 0xffff;
1740 env->ldt.base = ldl_phys(sm_state + 0x7f80);
1741 env->ldt.limit = ldl_phys(sm_state + 0x7f7c);
1742 env->ldt.flags = (ldl_phys(sm_state + 0x7f78) & 0xf0ff) << 8;
1743
1744 env->gdt.base = ldl_phys(sm_state + 0x7f74);
1745 env->gdt.limit = ldl_phys(sm_state + 0x7f70);
1746
1747 env->idt.base = ldl_phys(sm_state + 0x7f58);
1748 env->idt.limit = ldl_phys(sm_state + 0x7f54);
1749
1750 for(i = 0; i < 6; i++) {
1751 if (i < 3)
1752 offset = 0x7f84 + i * 12;
1753 else
1754 offset = 0x7f2c + (i - 3) * 12;
1755 cpu_x86_load_seg_cache(env, i,
1756 ldl_phys(sm_state + 0x7fa8 + i * 4) & 0xffff,
1757 ldl_phys(sm_state + offset + 8),
1758 ldl_phys(sm_state + offset + 4),
1759 (ldl_phys(sm_state + offset) & 0xf0ff) << 8);
1760 }
1761 cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f14));
1762
1763 val = ldl_phys(sm_state + 0x7efc); /* revision ID */
1764 if (val & 0x20000) {
1765 env->smbase = ldl_phys(sm_state + 0x7ef8) & ~0x7fff;
1766 }
1767#endif
1768 CC_OP = CC_OP_EFLAGS;
1769 env->hflags &= ~HF_SMM_MASK;
1770 cpu_smm_update(env);
1771
1772 qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
1773 log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
1774}
1775
1776#endif /* !CONFIG_USER_ONLY */
1777
1778
1779/* division, flags are undefined */
1780
1781void helper_divb_AL(target_ulong t0)
1782{
1783 unsigned int num, den, q, r;
1784
1785 num = (EAX & 0xffff);
1786 den = (t0 & 0xff);
1787 if (den == 0) {
1788 raise_exception(EXCP00_DIVZ);
1789 }
1790 q = (num / den);
1791 if (q > 0xff)
1792 raise_exception(EXCP00_DIVZ);
1793 q &= 0xff;
1794 r = (num % den) & 0xff;
1795 EAX = (EAX & ~0xffff) | (r << 8) | q;
1796}
1797
1798void helper_idivb_AL(target_ulong t0)
1799{
1800 int num, den, q, r;
1801
1802 num = (int16_t)EAX;
1803 den = (int8_t)t0;
1804 if (den == 0) {
1805 raise_exception(EXCP00_DIVZ);
1806 }
1807 q = (num / den);
1808 if (q != (int8_t)q)
1809 raise_exception(EXCP00_DIVZ);
1810 q &= 0xff;
1811 r = (num % den) & 0xff;
1812 EAX = (EAX & ~0xffff) | (r << 8) | q;
1813}
1814
1815void helper_divw_AX(target_ulong t0)
1816{
1817 unsigned int num, den, q, r;
1818
1819 num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
1820 den = (t0 & 0xffff);
1821 if (den == 0) {
1822 raise_exception(EXCP00_DIVZ);
1823 }
1824 q = (num / den);
1825 if (q > 0xffff)
1826 raise_exception(EXCP00_DIVZ);
1827 q &= 0xffff;
1828 r = (num % den) & 0xffff;
1829 EAX = (EAX & ~0xffff) | q;
1830 EDX = (EDX & ~0xffff) | r;
1831}
1832
1833void helper_idivw_AX(target_ulong t0)
1834{
1835 int num, den, q, r;
1836
1837 num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
1838 den = (int16_t)t0;
1839 if (den == 0) {
1840 raise_exception(EXCP00_DIVZ);
1841 }
1842 q = (num / den);
1843 if (q != (int16_t)q)
1844 raise_exception(EXCP00_DIVZ);
1845 q &= 0xffff;
1846 r = (num % den) & 0xffff;
1847 EAX = (EAX & ~0xffff) | q;
1848 EDX = (EDX & ~0xffff) | r;
1849}
1850
1851void helper_divl_EAX(target_ulong t0)
1852{
1853 unsigned int den, r;
1854 uint64_t num, q;
1855
1856 num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
1857 den = t0;
1858 if (den == 0) {
1859 raise_exception(EXCP00_DIVZ);
1860 }
1861 q = (num / den);
1862 r = (num % den);
1863 if (q > 0xffffffff)
1864 raise_exception(EXCP00_DIVZ);
1865 EAX = (uint32_t)q;
1866 EDX = (uint32_t)r;
1867}
1868
1869void helper_idivl_EAX(target_ulong t0)
1870{
1871 int den, r;
1872 int64_t num, q;
1873
1874 num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
1875 den = t0;
1876 if (den == 0) {
1877 raise_exception(EXCP00_DIVZ);
1878 }
1879 q = (num / den);
1880 r = (num % den);
1881 if (q != (int32_t)q)
1882 raise_exception(EXCP00_DIVZ);
1883 EAX = (uint32_t)q;
1884 EDX = (uint32_t)r;
1885}
1886
1887/* bcd */
1888
1889/* XXX: exception */
1890void helper_aam(int base)
1891{
1892 int al, ah;
1893 al = EAX & 0xff;
1894 ah = al / base;
1895 al = al % base;
1896 EAX = (EAX & ~0xffff) | al | (ah << 8);
1897 CC_DST = al;
1898}
1899
1900void helper_aad(int base)
1901{
1902 int al, ah;
1903 al = EAX & 0xff;
1904 ah = (EAX >> 8) & 0xff;
1905 al = ((ah * base) + al) & 0xff;
1906 EAX = (EAX & ~0xffff) | al;
1907 CC_DST = al;
1908}
1909
1910void helper_aaa(void)
1911{
1912 int icarry;
1913 int al, ah, af;
1914 int eflags;
1915
1916 eflags = helper_cc_compute_all(CC_OP);
1917 af = eflags & CC_A;
1918 al = EAX & 0xff;
1919 ah = (EAX >> 8) & 0xff;
1920
1921 icarry = (al > 0xf9);
1922 if (((al & 0x0f) > 9 ) || af) {
1923 al = (al + 6) & 0x0f;
1924 ah = (ah + 1 + icarry) & 0xff;
1925 eflags |= CC_C | CC_A;
1926 } else {
1927 eflags &= ~(CC_C | CC_A);
1928 al &= 0x0f;
1929 }
1930 EAX = (EAX & ~0xffff) | al | (ah << 8);
1931 CC_SRC = eflags;
1932}
1933
1934void helper_aas(void)
1935{
1936 int icarry;
1937 int al, ah, af;
1938 int eflags;
1939
1940 eflags = helper_cc_compute_all(CC_OP);
1941 af = eflags & CC_A;
1942 al = EAX & 0xff;
1943 ah = (EAX >> 8) & 0xff;
1944
1945 icarry = (al < 6);
1946 if (((al & 0x0f) > 9 ) || af) {
1947 al = (al - 6) & 0x0f;
1948 ah = (ah - 1 - icarry) & 0xff;
1949 eflags |= CC_C | CC_A;
1950 } else {
1951 eflags &= ~(CC_C | CC_A);
1952 al &= 0x0f;
1953 }
1954 EAX = (EAX & ~0xffff) | al | (ah << 8);
1955 CC_SRC = eflags;
1956}
1957
1958void helper_daa(void)
1959{
1960 int al, af, cf;
1961 int eflags;
1962
1963 eflags = helper_cc_compute_all(CC_OP);
1964 cf = eflags & CC_C;
1965 af = eflags & CC_A;
1966 al = EAX & 0xff;
1967
1968 eflags = 0;
1969 if (((al & 0x0f) > 9 ) || af) {
1970 al = (al + 6) & 0xff;
1971 eflags |= CC_A;
1972 }
1973 if ((al > 0x9f) || cf) {
1974 al = (al + 0x60) & 0xff;
1975 eflags |= CC_C;
1976 }
1977 EAX = (EAX & ~0xff) | al;
1978 /* well, speed is not an issue here, so we compute the flags by hand */
1979 eflags |= (al == 0) << 6; /* zf */
1980 eflags |= parity_table[al]; /* pf */
1981 eflags |= (al & 0x80); /* sf */
1982 CC_SRC = eflags;
1983}
1984
1985void helper_das(void)
1986{
1987 int al, al1, af, cf;
1988 int eflags;
1989
1990 eflags = helper_cc_compute_all(CC_OP);
1991 cf = eflags & CC_C;
1992 af = eflags & CC_A;
1993 al = EAX & 0xff;
1994
1995 eflags = 0;
1996 al1 = al;
1997 if (((al & 0x0f) > 9 ) || af) {
1998 eflags |= CC_A;
1999 if (al < 6 || cf)
2000 eflags |= CC_C;
2001 al = (al - 6) & 0xff;
2002 }
2003 if ((al1 > 0x99) || cf) {
2004 al = (al - 0x60) & 0xff;
2005 eflags |= CC_C;
2006 }
2007 EAX = (EAX & ~0xff) | al;
2008 /* well, speed is not an issue here, so we compute the flags by hand */
2009 eflags |= (al == 0) << 6; /* zf */
2010 eflags |= parity_table[al]; /* pf */
2011 eflags |= (al & 0x80); /* sf */
2012 CC_SRC = eflags;
2013}
2014
2015void helper_into(int next_eip_addend)
2016{
2017 int eflags;
2018 eflags = helper_cc_compute_all(CC_OP);
2019 if (eflags & CC_O) {
2020 raise_interrupt(EXCP04_INTO, 1, 0, next_eip_addend);
2021 }
2022}
2023
2024void helper_cmpxchg8b(target_ulong a0)
2025{
2026 uint64_t d;
2027 int eflags;
2028
2029 eflags = helper_cc_compute_all(CC_OP);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002030 d = cpu_ldq_data(env, a0);
Jun Nakajima86797932011-01-29 14:24:24 -08002031 if (d == (((uint64_t)EDX << 32) | (uint32_t)EAX)) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002032 cpu_stq_data(env, a0, ((uint64_t)ECX << 32) | (uint32_t)EBX);
Jun Nakajima86797932011-01-29 14:24:24 -08002033 eflags |= CC_Z;
2034 } else {
2035 /* always do the store */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002036 cpu_stq_data(env, a0, d);
Jun Nakajima86797932011-01-29 14:24:24 -08002037 EDX = (uint32_t)(d >> 32);
2038 EAX = (uint32_t)d;
2039 eflags &= ~CC_Z;
2040 }
2041 CC_SRC = eflags;
2042}
2043
2044#ifdef TARGET_X86_64
2045void helper_cmpxchg16b(target_ulong a0)
2046{
2047 uint64_t d0, d1;
2048 int eflags;
2049
2050 if ((a0 & 0xf) != 0)
2051 raise_exception(EXCP0D_GPF);
2052 eflags = helper_cc_compute_all(CC_OP);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002053 d0 = cpu_ldq_data(env, a0);
2054 d1 = cpu_ldq_data(env, a0 + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08002055 if (d0 == EAX && d1 == EDX) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002056 cpu_stq_data(env, a0, EBX);
2057 cpu_stq_data(env, a0 + 8, ECX);
Jun Nakajima86797932011-01-29 14:24:24 -08002058 eflags |= CC_Z;
2059 } else {
2060 /* always do the store */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002061 cpu_stq_data(env, a0, d0);
2062 cpu_stq_data(env, a0 + 8, d1);
Jun Nakajima86797932011-01-29 14:24:24 -08002063 EDX = d1;
2064 EAX = d0;
2065 eflags &= ~CC_Z;
2066 }
2067 CC_SRC = eflags;
2068}
2069#endif
2070
2071void helper_single_step(void)
2072{
2073#ifndef CONFIG_USER_ONLY
2074 check_hw_breakpoints(env, 1);
2075 env->dr[6] |= DR6_BS;
2076#endif
2077 raise_exception(EXCP01_DB);
2078}
2079
2080void helper_cpuid(void)
2081{
2082 uint32_t eax, ebx, ecx, edx;
2083
2084 helper_svm_check_intercept_param(SVM_EXIT_CPUID, 0);
2085
2086 cpu_x86_cpuid(env, (uint32_t)EAX, (uint32_t)ECX, &eax, &ebx, &ecx, &edx);
2087 EAX = eax;
2088 EBX = ebx;
2089 ECX = ecx;
2090 EDX = edx;
2091}
2092
2093void helper_enter_level(int level, int data32, target_ulong t1)
2094{
2095 target_ulong ssp;
2096 uint32_t esp_mask, esp, ebp;
2097
2098 esp_mask = get_sp_mask(env->segs[R_SS].flags);
2099 ssp = env->segs[R_SS].base;
2100 ebp = EBP;
2101 esp = ESP;
2102 if (data32) {
2103 /* 32 bit */
2104 esp -= 4;
2105 while (--level) {
2106 esp -= 4;
2107 ebp -= 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002108 cpu_stl_data(env, ssp + (esp & esp_mask),
2109 cpu_ldl_data(env, ssp + (ebp & esp_mask)));
Jun Nakajima86797932011-01-29 14:24:24 -08002110 }
2111 esp -= 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002112 cpu_stl_data(env, ssp + (esp & esp_mask), t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002113 } else {
2114 /* 16 bit */
2115 esp -= 2;
2116 while (--level) {
2117 esp -= 2;
2118 ebp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002119 cpu_stw_data(env, ssp + (esp & esp_mask),
2120 cpu_lduw_data(env, ssp + (ebp & esp_mask)));
Jun Nakajima86797932011-01-29 14:24:24 -08002121 }
2122 esp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002123 cpu_stw_data(env, ssp + (esp & esp_mask), t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002124 }
2125}
2126
2127#ifdef TARGET_X86_64
2128void helper_enter64_level(int level, int data64, target_ulong t1)
2129{
2130 target_ulong esp, ebp;
2131 ebp = EBP;
2132 esp = ESP;
2133
2134 if (data64) {
2135 /* 64 bit */
2136 esp -= 8;
2137 while (--level) {
2138 esp -= 8;
2139 ebp -= 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002140 cpu_stq_data(env, esp, cpu_ldq_data(env, ebp));
Jun Nakajima86797932011-01-29 14:24:24 -08002141 }
2142 esp -= 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002143 cpu_stq_data(env, esp, t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002144 } else {
2145 /* 16 bit */
2146 esp -= 2;
2147 while (--level) {
2148 esp -= 2;
2149 ebp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002150 cpu_stw_data(env, esp, cpu_lduw_data(env, ebp));
Jun Nakajima86797932011-01-29 14:24:24 -08002151 }
2152 esp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002153 cpu_stw_data(env, esp, t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002154 }
2155}
2156#endif
2157
2158void helper_lldt(int selector)
2159{
2160 SegmentCache *dt;
2161 uint32_t e1, e2;
2162 int index, entry_limit;
2163 target_ulong ptr;
2164
2165 selector &= 0xffff;
2166 if ((selector & 0xfffc) == 0) {
2167 /* XXX: NULL selector case: invalid LDT */
2168 env->ldt.base = 0;
2169 env->ldt.limit = 0;
2170 } else {
2171 if (selector & 0x4)
2172 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2173 dt = &env->gdt;
2174 index = selector & ~7;
2175#ifdef TARGET_X86_64
2176 if (env->hflags & HF_LMA_MASK)
2177 entry_limit = 15;
2178 else
2179#endif
2180 entry_limit = 7;
2181 if ((index + entry_limit) > dt->limit)
2182 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2183 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002184 e1 = cpu_ldl_kernel(env, ptr);
2185 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002186 if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
2187 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2188 if (!(e2 & DESC_P_MASK))
2189 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2190#ifdef TARGET_X86_64
2191 if (env->hflags & HF_LMA_MASK) {
2192 uint32_t e3;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002193 e3 = cpu_ldl_kernel(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08002194 load_seg_cache_raw_dt(&env->ldt, e1, e2);
2195 env->ldt.base |= (target_ulong)e3 << 32;
2196 } else
2197#endif
2198 {
2199 load_seg_cache_raw_dt(&env->ldt, e1, e2);
2200 }
2201 }
2202 env->ldt.selector = selector;
2203}
2204
2205void helper_ltr(int selector)
2206{
2207 SegmentCache *dt;
2208 uint32_t e1, e2;
2209 int index, type, entry_limit;
2210 target_ulong ptr;
2211
2212 selector &= 0xffff;
2213 if ((selector & 0xfffc) == 0) {
2214 /* NULL selector case: invalid TR */
2215 env->tr.base = 0;
2216 env->tr.limit = 0;
2217 env->tr.flags = 0;
2218 } else {
2219 if (selector & 0x4)
2220 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2221 dt = &env->gdt;
2222 index = selector & ~7;
2223#ifdef TARGET_X86_64
2224 if (env->hflags & HF_LMA_MASK)
2225 entry_limit = 15;
2226 else
2227#endif
2228 entry_limit = 7;
2229 if ((index + entry_limit) > dt->limit)
2230 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2231 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002232 e1 = cpu_ldl_kernel(env, ptr);
2233 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002234 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2235 if ((e2 & DESC_S_MASK) ||
2236 (type != 1 && type != 9))
2237 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2238 if (!(e2 & DESC_P_MASK))
2239 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2240#ifdef TARGET_X86_64
2241 if (env->hflags & HF_LMA_MASK) {
2242 uint32_t e3, e4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002243 e3 = cpu_ldl_kernel(env, ptr + 8);
2244 e4 = cpu_ldl_kernel(env, ptr + 12);
Jun Nakajima86797932011-01-29 14:24:24 -08002245 if ((e4 >> DESC_TYPE_SHIFT) & 0xf)
2246 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2247 load_seg_cache_raw_dt(&env->tr, e1, e2);
2248 env->tr.base |= (target_ulong)e3 << 32;
2249 } else
2250#endif
2251 {
2252 load_seg_cache_raw_dt(&env->tr, e1, e2);
2253 }
2254 e2 |= DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002255 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -08002256 }
2257 env->tr.selector = selector;
2258}
2259
2260/* only works if protected mode and not VM86. seg_reg must be != R_CS */
2261void helper_load_seg(int seg_reg, int selector)
2262{
2263 uint32_t e1, e2;
2264 int cpl, dpl, rpl;
2265 SegmentCache *dt;
2266 int index;
2267 target_ulong ptr;
2268
2269 selector &= 0xffff;
2270 cpl = env->hflags & HF_CPL_MASK;
2271 if ((selector & 0xfffc) == 0) {
2272 /* null selector case */
2273 if (seg_reg == R_SS
2274#ifdef TARGET_X86_64
2275 && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
2276#endif
2277 )
2278 raise_exception_err(EXCP0D_GPF, 0);
2279 cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
2280 } else {
2281
2282 if (selector & 0x4)
2283 dt = &env->ldt;
2284 else
2285 dt = &env->gdt;
2286 index = selector & ~7;
2287 if ((index + 7) > dt->limit)
2288 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2289 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002290 e1 = cpu_ldl_kernel(env, ptr);
2291 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002292
2293 if (!(e2 & DESC_S_MASK))
2294 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2295 rpl = selector & 3;
2296 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2297 if (seg_reg == R_SS) {
2298 /* must be writable segment */
2299 if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
2300 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2301 if (rpl != cpl || dpl != cpl)
2302 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2303 } else {
2304 /* must be readable segment */
2305 if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK)
2306 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2307
2308 if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2309 /* if not conforming code, test rights */
2310 if (dpl < cpl || dpl < rpl)
2311 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2312 }
2313 }
2314
2315 if (!(e2 & DESC_P_MASK)) {
2316 if (seg_reg == R_SS)
2317 raise_exception_err(EXCP0C_STACK, selector & 0xfffc);
2318 else
2319 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2320 }
2321
2322 /* set the access bit if not already set */
2323 if (!(e2 & DESC_A_MASK)) {
2324 e2 |= DESC_A_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002325 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -08002326 }
2327
2328 cpu_x86_load_seg_cache(env, seg_reg, selector,
2329 get_seg_base(e1, e2),
2330 get_seg_limit(e1, e2),
2331 e2);
2332#if 0
2333 qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
2334 selector, (unsigned long)sc->base, sc->limit, sc->flags);
2335#endif
2336 }
2337}
2338
2339/* protected mode jump */
2340void helper_ljmp_protected(int new_cs, target_ulong new_eip,
2341 int next_eip_addend)
2342{
2343 int gate_cs, type;
2344 uint32_t e1, e2, cpl, dpl, rpl, limit;
2345 target_ulong next_eip;
2346
2347 if ((new_cs & 0xfffc) == 0)
2348 raise_exception_err(EXCP0D_GPF, 0);
2349 if (load_segment(&e1, &e2, new_cs) != 0)
2350 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2351 cpl = env->hflags & HF_CPL_MASK;
2352 if (e2 & DESC_S_MASK) {
2353 if (!(e2 & DESC_CS_MASK))
2354 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2355 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2356 if (e2 & DESC_C_MASK) {
2357 /* conforming code segment */
2358 if (dpl > cpl)
2359 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2360 } else {
2361 /* non conforming code segment */
2362 rpl = new_cs & 3;
2363 if (rpl > cpl)
2364 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2365 if (dpl != cpl)
2366 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2367 }
2368 if (!(e2 & DESC_P_MASK))
2369 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2370 limit = get_seg_limit(e1, e2);
2371 if (new_eip > limit &&
2372 !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK))
2373 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2374 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2375 get_seg_base(e1, e2), limit, e2);
2376 EIP = new_eip;
2377 } else {
2378 /* jump to call or task gate */
2379 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2380 rpl = new_cs & 3;
2381 cpl = env->hflags & HF_CPL_MASK;
2382 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2383 switch(type) {
2384 case 1: /* 286 TSS */
2385 case 9: /* 386 TSS */
2386 case 5: /* task gate */
2387 if (dpl < cpl || dpl < rpl)
2388 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2389 next_eip = env->eip + next_eip_addend;
2390 switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
2391 CC_OP = CC_OP_EFLAGS;
2392 break;
2393 case 4: /* 286 call gate */
2394 case 12: /* 386 call gate */
2395 if ((dpl < cpl) || (dpl < rpl))
2396 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2397 if (!(e2 & DESC_P_MASK))
2398 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2399 gate_cs = e1 >> 16;
2400 new_eip = (e1 & 0xffff);
2401 if (type == 12)
2402 new_eip |= (e2 & 0xffff0000);
2403 if (load_segment(&e1, &e2, gate_cs) != 0)
2404 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2405 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2406 /* must be code segment */
2407 if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
2408 (DESC_S_MASK | DESC_CS_MASK)))
2409 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2410 if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
2411 (!(e2 & DESC_C_MASK) && (dpl != cpl)))
2412 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2413 if (!(e2 & DESC_P_MASK))
2414 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2415 limit = get_seg_limit(e1, e2);
2416 if (new_eip > limit)
2417 raise_exception_err(EXCP0D_GPF, 0);
2418 cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl,
2419 get_seg_base(e1, e2), limit, e2);
2420 EIP = new_eip;
2421 break;
2422 default:
2423 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2424 break;
2425 }
2426 }
2427}
2428
2429/* real mode call */
2430void helper_lcall_real(int new_cs, target_ulong new_eip1,
2431 int shift, int next_eip)
2432{
2433 int new_eip;
2434 uint32_t esp, esp_mask;
2435 target_ulong ssp;
2436
2437 new_eip = new_eip1;
2438 esp = ESP;
2439 esp_mask = get_sp_mask(env->segs[R_SS].flags);
2440 ssp = env->segs[R_SS].base;
2441 if (shift) {
2442 PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector);
2443 PUSHL(ssp, esp, esp_mask, next_eip);
2444 } else {
2445 PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector);
2446 PUSHW(ssp, esp, esp_mask, next_eip);
2447 }
2448
2449 SET_ESP(esp, esp_mask);
2450 env->eip = new_eip;
2451 env->segs[R_CS].selector = new_cs;
2452 env->segs[R_CS].base = (new_cs << 4);
2453}
2454
2455/* protected mode call */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02002456void helper_lcall_protected(int new_cs, target_ulong new_eip,
Jun Nakajima86797932011-01-29 14:24:24 -08002457 int shift, int next_eip_addend)
2458{
2459 int new_stack, i;
2460 uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
2461 uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask;
2462 uint32_t val, limit, old_sp_mask;
2463 target_ulong ssp, old_ssp, next_eip;
2464
2465 next_eip = env->eip + next_eip_addend;
2466 LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift);
2467 LOG_PCALL_STATE(env);
2468 if ((new_cs & 0xfffc) == 0)
2469 raise_exception_err(EXCP0D_GPF, 0);
2470 if (load_segment(&e1, &e2, new_cs) != 0)
2471 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2472 cpl = env->hflags & HF_CPL_MASK;
2473 LOG_PCALL("desc=%08x:%08x\n", e1, e2);
2474 if (e2 & DESC_S_MASK) {
2475 if (!(e2 & DESC_CS_MASK))
2476 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2477 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2478 if (e2 & DESC_C_MASK) {
2479 /* conforming code segment */
2480 if (dpl > cpl)
2481 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2482 } else {
2483 /* non conforming code segment */
2484 rpl = new_cs & 3;
2485 if (rpl > cpl)
2486 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2487 if (dpl != cpl)
2488 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2489 }
2490 if (!(e2 & DESC_P_MASK))
2491 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2492
2493#ifdef TARGET_X86_64
2494 /* XXX: check 16/32 bit cases in long mode */
2495 if (shift == 2) {
2496 target_ulong rsp;
2497 /* 64 bit case */
2498 rsp = ESP;
2499 PUSHQ(rsp, env->segs[R_CS].selector);
2500 PUSHQ(rsp, next_eip);
2501 /* from this point, not restartable */
2502 ESP = rsp;
2503 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2504 get_seg_base(e1, e2),
2505 get_seg_limit(e1, e2), e2);
2506 EIP = new_eip;
2507 } else
2508#endif
2509 {
2510 sp = ESP;
2511 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2512 ssp = env->segs[R_SS].base;
2513 if (shift) {
2514 PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
2515 PUSHL(ssp, sp, sp_mask, next_eip);
2516 } else {
2517 PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
2518 PUSHW(ssp, sp, sp_mask, next_eip);
2519 }
2520
2521 limit = get_seg_limit(e1, e2);
2522 if (new_eip > limit)
2523 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2524 /* from this point, not restartable */
2525 SET_ESP(sp, sp_mask);
2526 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2527 get_seg_base(e1, e2), limit, e2);
2528 EIP = new_eip;
2529 }
2530 } else {
2531 /* check gate type */
2532 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
2533 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2534 rpl = new_cs & 3;
2535 switch(type) {
2536 case 1: /* available 286 TSS */
2537 case 9: /* available 386 TSS */
2538 case 5: /* task gate */
2539 if (dpl < cpl || dpl < rpl)
2540 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2541 switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
2542 CC_OP = CC_OP_EFLAGS;
2543 return;
2544 case 4: /* 286 call gate */
2545 case 12: /* 386 call gate */
2546 break;
2547 default:
2548 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2549 break;
2550 }
2551 shift = type >> 3;
2552
2553 if (dpl < cpl || dpl < rpl)
2554 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2555 /* check valid bit */
2556 if (!(e2 & DESC_P_MASK))
2557 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2558 selector = e1 >> 16;
2559 offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
2560 param_count = e2 & 0x1f;
2561 if ((selector & 0xfffc) == 0)
2562 raise_exception_err(EXCP0D_GPF, 0);
2563
2564 if (load_segment(&e1, &e2, selector) != 0)
2565 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2566 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
2567 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2568 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2569 if (dpl > cpl)
2570 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2571 if (!(e2 & DESC_P_MASK))
2572 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2573
2574 if (!(e2 & DESC_C_MASK) && dpl < cpl) {
2575 /* to inner privilege */
2576 get_ss_esp_from_tss(&ss, &sp, dpl);
2577 LOG_PCALL("new ss:esp=%04x:%08x param_count=%d ESP=" TARGET_FMT_lx "\n",
2578 ss, sp, param_count, ESP);
2579 if ((ss & 0xfffc) == 0)
2580 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2581 if ((ss & 3) != dpl)
2582 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2583 if (load_segment(&ss_e1, &ss_e2, ss) != 0)
2584 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2585 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
2586 if (ss_dpl != dpl)
2587 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2588 if (!(ss_e2 & DESC_S_MASK) ||
2589 (ss_e2 & DESC_CS_MASK) ||
2590 !(ss_e2 & DESC_W_MASK))
2591 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2592 if (!(ss_e2 & DESC_P_MASK))
2593 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2594
2595 // push_size = ((param_count * 2) + 8) << shift;
2596
2597 old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
2598 old_ssp = env->segs[R_SS].base;
2599
2600 sp_mask = get_sp_mask(ss_e2);
2601 ssp = get_seg_base(ss_e1, ss_e2);
2602 if (shift) {
2603 PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector);
2604 PUSHL(ssp, sp, sp_mask, ESP);
2605 for(i = param_count - 1; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002606 val = cpu_ldl_kernel(env, old_ssp + ((ESP + i * 4) & old_sp_mask));
Jun Nakajima86797932011-01-29 14:24:24 -08002607 PUSHL(ssp, sp, sp_mask, val);
2608 }
2609 } else {
2610 PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector);
2611 PUSHW(ssp, sp, sp_mask, ESP);
2612 for(i = param_count - 1; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002613 val = cpu_lduw_kernel(env, old_ssp + ((ESP + i * 2) & old_sp_mask));
Jun Nakajima86797932011-01-29 14:24:24 -08002614 PUSHW(ssp, sp, sp_mask, val);
2615 }
2616 }
2617 new_stack = 1;
2618 } else {
2619 /* to same privilege */
2620 sp = ESP;
2621 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2622 ssp = env->segs[R_SS].base;
2623 // push_size = (4 << shift);
2624 new_stack = 0;
2625 }
2626
2627 if (shift) {
2628 PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
2629 PUSHL(ssp, sp, sp_mask, next_eip);
2630 } else {
2631 PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
2632 PUSHW(ssp, sp, sp_mask, next_eip);
2633 }
2634
2635 /* from this point, not restartable */
2636
2637 if (new_stack) {
2638 ss = (ss & ~3) | dpl;
2639 cpu_x86_load_seg_cache(env, R_SS, ss,
2640 ssp,
2641 get_seg_limit(ss_e1, ss_e2),
2642 ss_e2);
2643 }
2644
2645 selector = (selector & ~3) | dpl;
2646 cpu_x86_load_seg_cache(env, R_CS, selector,
2647 get_seg_base(e1, e2),
2648 get_seg_limit(e1, e2),
2649 e2);
2650 cpu_x86_set_cpl(env, dpl);
2651 SET_ESP(sp, sp_mask);
2652 EIP = offset;
2653 }
Jun Nakajima86797932011-01-29 14:24:24 -08002654}
2655
2656/* real and vm86 mode iret */
2657void helper_iret_real(int shift)
2658{
2659 uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
2660 target_ulong ssp;
2661 int eflags_mask;
2662
2663 sp_mask = 0xffff; /* XXXX: use SS segment size ? */
2664 sp = ESP;
2665 ssp = env->segs[R_SS].base;
2666 if (shift == 1) {
2667 /* 32 bits */
2668 POPL(ssp, sp, sp_mask, new_eip);
2669 POPL(ssp, sp, sp_mask, new_cs);
2670 new_cs &= 0xffff;
2671 POPL(ssp, sp, sp_mask, new_eflags);
2672 } else {
2673 /* 16 bits */
2674 POPW(ssp, sp, sp_mask, new_eip);
2675 POPW(ssp, sp, sp_mask, new_cs);
2676 POPW(ssp, sp, sp_mask, new_eflags);
2677 }
2678 ESP = (ESP & ~sp_mask) | (sp & sp_mask);
2679 env->segs[R_CS].selector = new_cs;
2680 env->segs[R_CS].base = (new_cs << 4);
2681 env->eip = new_eip;
2682 if (env->eflags & VM_MASK)
2683 eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK;
2684 else
2685 eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK | NT_MASK;
2686 if (shift == 0)
2687 eflags_mask &= 0xffff;
2688 load_eflags(new_eflags, eflags_mask);
2689 env->hflags2 &= ~HF2_NMI_MASK;
2690}
2691
2692static inline void validate_seg(int seg_reg, int cpl)
2693{
2694 int dpl;
2695 uint32_t e2;
2696
2697 /* XXX: on x86_64, we do not want to nullify FS and GS because
2698 they may still contain a valid base. I would be interested to
2699 know how a real x86_64 CPU behaves */
2700 if ((seg_reg == R_FS || seg_reg == R_GS) &&
2701 (env->segs[seg_reg].selector & 0xfffc) == 0)
2702 return;
2703
2704 e2 = env->segs[seg_reg].flags;
2705 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2706 if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2707 /* data or non conforming code segment */
2708 if (dpl < cpl) {
2709 cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0);
2710 }
2711 }
2712}
2713
2714/* protected mode iret */
2715static inline void helper_ret_protected(int shift, int is_iret, int addend)
2716{
2717 uint32_t new_cs, new_eflags, new_ss;
2718 uint32_t new_es, new_ds, new_fs, new_gs;
2719 uint32_t e1, e2, ss_e1, ss_e2;
2720 int cpl, dpl, rpl, eflags_mask, iopl;
2721 target_ulong ssp, sp, new_eip, new_esp, sp_mask;
2722
2723#ifdef TARGET_X86_64
2724 if (shift == 2)
2725 sp_mask = -1;
2726 else
2727#endif
2728 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2729 sp = ESP;
2730 ssp = env->segs[R_SS].base;
2731 new_eflags = 0; /* avoid warning */
2732#ifdef TARGET_X86_64
2733 if (shift == 2) {
2734 POPQ(sp, new_eip);
2735 POPQ(sp, new_cs);
2736 new_cs &= 0xffff;
2737 if (is_iret) {
2738 POPQ(sp, new_eflags);
2739 }
2740 } else
2741#endif
2742 if (shift == 1) {
2743 /* 32 bits */
2744 POPL(ssp, sp, sp_mask, new_eip);
2745 POPL(ssp, sp, sp_mask, new_cs);
2746 new_cs &= 0xffff;
2747 if (is_iret) {
2748 POPL(ssp, sp, sp_mask, new_eflags);
2749 if (new_eflags & VM_MASK)
2750 goto return_to_vm86;
2751 }
2752 } else {
2753 /* 16 bits */
2754 POPW(ssp, sp, sp_mask, new_eip);
2755 POPW(ssp, sp, sp_mask, new_cs);
2756 if (is_iret)
2757 POPW(ssp, sp, sp_mask, new_eflags);
2758 }
2759 LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
2760 new_cs, new_eip, shift, addend);
2761 LOG_PCALL_STATE(env);
2762 if ((new_cs & 0xfffc) == 0)
2763 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2764 if (load_segment(&e1, &e2, new_cs) != 0)
2765 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2766 if (!(e2 & DESC_S_MASK) ||
2767 !(e2 & DESC_CS_MASK))
2768 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2769 cpl = env->hflags & HF_CPL_MASK;
2770 rpl = new_cs & 3;
2771 if (rpl < cpl)
2772 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2773 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2774 if (e2 & DESC_C_MASK) {
2775 if (dpl > rpl)
2776 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2777 } else {
2778 if (dpl != rpl)
2779 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2780 }
2781 if (!(e2 & DESC_P_MASK))
2782 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2783
2784 sp += addend;
2785 if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
2786 ((env->hflags & HF_CS64_MASK) && !is_iret))) {
2787 /* return to same privilege level */
2788 cpu_x86_load_seg_cache(env, R_CS, new_cs,
2789 get_seg_base(e1, e2),
2790 get_seg_limit(e1, e2),
2791 e2);
2792 } else {
2793 /* return to different privilege level */
2794#ifdef TARGET_X86_64
2795 if (shift == 2) {
2796 POPQ(sp, new_esp);
2797 POPQ(sp, new_ss);
2798 new_ss &= 0xffff;
2799 } else
2800#endif
2801 if (shift == 1) {
2802 /* 32 bits */
2803 POPL(ssp, sp, sp_mask, new_esp);
2804 POPL(ssp, sp, sp_mask, new_ss);
2805 new_ss &= 0xffff;
2806 } else {
2807 /* 16 bits */
2808 POPW(ssp, sp, sp_mask, new_esp);
2809 POPW(ssp, sp, sp_mask, new_ss);
2810 }
2811 LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n",
2812 new_ss, new_esp);
2813 if ((new_ss & 0xfffc) == 0) {
2814#ifdef TARGET_X86_64
2815 /* NULL ss is allowed in long mode if cpl != 3*/
2816 /* XXX: test CS64 ? */
2817 if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
2818 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2819 0, 0xffffffff,
2820 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2821 DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
2822 DESC_W_MASK | DESC_A_MASK);
2823 ss_e2 = DESC_B_MASK; /* XXX: should not be needed ? */
2824 } else
2825#endif
2826 {
2827 raise_exception_err(EXCP0D_GPF, 0);
2828 }
2829 } else {
2830 if ((new_ss & 3) != rpl)
2831 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2832 if (load_segment(&ss_e1, &ss_e2, new_ss) != 0)
2833 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2834 if (!(ss_e2 & DESC_S_MASK) ||
2835 (ss_e2 & DESC_CS_MASK) ||
2836 !(ss_e2 & DESC_W_MASK))
2837 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2838 dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
2839 if (dpl != rpl)
2840 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2841 if (!(ss_e2 & DESC_P_MASK))
2842 raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
2843 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2844 get_seg_base(ss_e1, ss_e2),
2845 get_seg_limit(ss_e1, ss_e2),
2846 ss_e2);
2847 }
2848
2849 cpu_x86_load_seg_cache(env, R_CS, new_cs,
2850 get_seg_base(e1, e2),
2851 get_seg_limit(e1, e2),
2852 e2);
2853 cpu_x86_set_cpl(env, rpl);
2854 sp = new_esp;
2855#ifdef TARGET_X86_64
2856 if (env->hflags & HF_CS64_MASK)
2857 sp_mask = -1;
2858 else
2859#endif
2860 sp_mask = get_sp_mask(ss_e2);
2861
2862 /* validate data segments */
2863 validate_seg(R_ES, rpl);
2864 validate_seg(R_DS, rpl);
2865 validate_seg(R_FS, rpl);
2866 validate_seg(R_GS, rpl);
2867
2868 sp += addend;
2869 }
2870 SET_ESP(sp, sp_mask);
2871 env->eip = new_eip;
2872 if (is_iret) {
2873 /* NOTE: 'cpl' is the _old_ CPL */
2874 eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
2875 if (cpl == 0)
2876 eflags_mask |= IOPL_MASK;
2877 iopl = (env->eflags >> IOPL_SHIFT) & 3;
2878 if (cpl <= iopl)
2879 eflags_mask |= IF_MASK;
2880 if (shift == 0)
2881 eflags_mask &= 0xffff;
2882 load_eflags(new_eflags, eflags_mask);
2883 }
2884 return;
2885
2886 return_to_vm86:
2887 POPL(ssp, sp, sp_mask, new_esp);
2888 POPL(ssp, sp, sp_mask, new_ss);
2889 POPL(ssp, sp, sp_mask, new_es);
2890 POPL(ssp, sp, sp_mask, new_ds);
2891 POPL(ssp, sp, sp_mask, new_fs);
2892 POPL(ssp, sp, sp_mask, new_gs);
2893
2894 /* modify processor state */
2895 load_eflags(new_eflags, TF_MASK | AC_MASK | ID_MASK |
2896 IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | VIP_MASK);
2897 load_seg_vm(R_CS, new_cs & 0xffff);
2898 cpu_x86_set_cpl(env, 3);
2899 load_seg_vm(R_SS, new_ss & 0xffff);
2900 load_seg_vm(R_ES, new_es & 0xffff);
2901 load_seg_vm(R_DS, new_ds & 0xffff);
2902 load_seg_vm(R_FS, new_fs & 0xffff);
2903 load_seg_vm(R_GS, new_gs & 0xffff);
2904
2905 env->eip = new_eip & 0xffff;
2906 ESP = new_esp;
2907}
2908
2909void helper_iret_protected(int shift, int next_eip)
2910{
2911 int tss_selector, type;
2912 uint32_t e1, e2;
2913
2914 /* specific case for TSS */
2915 if (env->eflags & NT_MASK) {
2916#ifdef TARGET_X86_64
2917 if (env->hflags & HF_LMA_MASK)
2918 raise_exception_err(EXCP0D_GPF, 0);
2919#endif
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002920 tss_selector = cpu_lduw_kernel(env, env->tr.base + 0);
Jun Nakajima86797932011-01-29 14:24:24 -08002921 if (tss_selector & 4)
2922 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2923 if (load_segment(&e1, &e2, tss_selector) != 0)
2924 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2925 type = (e2 >> DESC_TYPE_SHIFT) & 0x17;
2926 /* NOTE: we check both segment and busy TSS */
2927 if (type != 3)
2928 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2929 switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip);
2930 } else {
2931 helper_ret_protected(shift, 1, 0);
2932 }
2933 env->hflags2 &= ~HF2_NMI_MASK;
Jun Nakajima86797932011-01-29 14:24:24 -08002934}
2935
2936void helper_lret_protected(int shift, int addend)
2937{
2938 helper_ret_protected(shift, 0, addend);
Jun Nakajima86797932011-01-29 14:24:24 -08002939}
2940
2941void helper_sysenter(void)
2942{
2943 if (env->sysenter_cs == 0) {
2944 raise_exception_err(EXCP0D_GPF, 0);
2945 }
2946 env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
2947 cpu_x86_set_cpl(env, 0);
2948
2949#ifdef TARGET_X86_64
2950 if (env->hflags & HF_LMA_MASK) {
2951 cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2952 0, 0xffffffff,
2953 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2954 DESC_S_MASK |
2955 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
2956 } else
2957#endif
2958 {
2959 cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2960 0, 0xffffffff,
2961 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2962 DESC_S_MASK |
2963 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
2964 }
2965 cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
2966 0, 0xffffffff,
2967 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2968 DESC_S_MASK |
2969 DESC_W_MASK | DESC_A_MASK);
2970 ESP = env->sysenter_esp;
2971 EIP = env->sysenter_eip;
2972}
2973
2974void helper_sysexit(int dflag)
2975{
2976 int cpl;
2977
2978 cpl = env->hflags & HF_CPL_MASK;
2979 if (env->sysenter_cs == 0 || cpl != 0) {
2980 raise_exception_err(EXCP0D_GPF, 0);
2981 }
2982 cpu_x86_set_cpl(env, 3);
2983#ifdef TARGET_X86_64
2984 if (dflag == 2) {
2985 cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | 3,
2986 0, 0xffffffff,
2987 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2988 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2989 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
2990 cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | 3,
2991 0, 0xffffffff,
2992 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2993 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2994 DESC_W_MASK | DESC_A_MASK);
2995 } else
2996#endif
2997 {
2998 cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3,
2999 0, 0xffffffff,
3000 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
3001 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
3002 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
3003 cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3,
3004 0, 0xffffffff,
3005 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
3006 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
3007 DESC_W_MASK | DESC_A_MASK);
3008 }
3009 ESP = ECX;
3010 EIP = EDX;
Jun Nakajima86797932011-01-29 14:24:24 -08003011}
3012
3013#if defined(CONFIG_USER_ONLY)
3014target_ulong helper_read_crN(int reg)
3015{
3016 return 0;
3017}
3018
3019void helper_write_crN(int reg, target_ulong t0)
3020{
3021}
3022
3023void helper_movl_drN_T0(int reg, target_ulong t0)
3024{
3025}
3026#else
3027target_ulong helper_read_crN(int reg)
3028{
3029 target_ulong val;
3030
3031 helper_svm_check_intercept_param(SVM_EXIT_READ_CR0 + reg, 0);
3032 switch(reg) {
3033 default:
3034 val = env->cr[reg];
3035 break;
3036 case 8:
3037 if (!(env->hflags2 & HF2_VINTR_MASK)) {
3038 val = cpu_get_apic_tpr(env);
3039 } else {
3040 val = env->v_tpr;
3041 }
3042 break;
3043 }
3044 return val;
3045}
3046
3047void helper_write_crN(int reg, target_ulong t0)
3048{
3049 helper_svm_check_intercept_param(SVM_EXIT_WRITE_CR0 + reg, 0);
3050 switch(reg) {
3051 case 0:
3052 cpu_x86_update_cr0(env, t0);
3053 break;
3054 case 3:
3055 cpu_x86_update_cr3(env, t0);
3056 break;
3057 case 4:
3058 cpu_x86_update_cr4(env, t0);
3059 break;
3060 case 8:
3061 if (!(env->hflags2 & HF2_VINTR_MASK)) {
3062 cpu_set_apic_tpr(env, t0);
3063 }
3064 env->v_tpr = t0 & 0x0f;
3065 break;
3066 default:
3067 env->cr[reg] = t0;
3068 break;
3069 }
3070}
3071
3072void helper_movl_drN_T0(int reg, target_ulong t0)
3073{
3074 int i;
3075
3076 if (reg < 4) {
3077 hw_breakpoint_remove(env, reg);
3078 env->dr[reg] = t0;
3079 hw_breakpoint_insert(env, reg);
3080 } else if (reg == 7) {
3081 for (i = 0; i < 4; i++)
3082 hw_breakpoint_remove(env, i);
3083 env->dr[7] = t0;
3084 for (i = 0; i < 4; i++)
3085 hw_breakpoint_insert(env, i);
3086 } else
3087 env->dr[reg] = t0;
3088}
3089#endif
3090
3091void helper_lmsw(target_ulong t0)
3092{
3093 /* only 4 lower bits of CR0 are modified. PE cannot be set to zero
3094 if already set to one. */
3095 t0 = (env->cr[0] & ~0xe) | (t0 & 0xf);
3096 helper_write_crN(0, t0);
3097}
3098
3099void helper_clts(void)
3100{
3101 env->cr[0] &= ~CR0_TS_MASK;
3102 env->hflags &= ~HF_TS_MASK;
3103}
3104
3105void helper_invlpg(target_ulong addr)
3106{
3107 helper_svm_check_intercept_param(SVM_EXIT_INVLPG, 0);
3108 tlb_flush_page(env, addr);
3109}
3110
3111void helper_rdtsc(void)
3112{
3113 uint64_t val;
3114
3115 if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
3116 raise_exception(EXCP0D_GPF);
3117 }
3118 helper_svm_check_intercept_param(SVM_EXIT_RDTSC, 0);
3119
3120 val = cpu_get_tsc(env) + env->tsc_offset;
3121 EAX = (uint32_t)(val);
3122 EDX = (uint32_t)(val >> 32);
3123}
3124
3125void helper_rdpmc(void)
3126{
3127 if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
3128 raise_exception(EXCP0D_GPF);
3129 }
3130 helper_svm_check_intercept_param(SVM_EXIT_RDPMC, 0);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02003131
Jun Nakajima86797932011-01-29 14:24:24 -08003132 /* currently unimplemented */
3133 raise_exception_err(EXCP06_ILLOP, 0);
3134}
3135
3136#if defined(CONFIG_USER_ONLY)
3137void helper_wrmsr(void)
3138{
3139}
3140
3141void helper_rdmsr(void)
3142{
3143}
3144#else
3145void helper_wrmsr(void)
3146{
3147 uint64_t val;
3148
3149 helper_svm_check_intercept_param(SVM_EXIT_MSR, 1);
3150
3151 val = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
3152
3153 switch((uint32_t)ECX) {
3154 case MSR_IA32_SYSENTER_CS:
3155 env->sysenter_cs = val & 0xffff;
3156 break;
3157 case MSR_IA32_SYSENTER_ESP:
3158 env->sysenter_esp = val;
3159 break;
3160 case MSR_IA32_SYSENTER_EIP:
3161 env->sysenter_eip = val;
3162 break;
3163 case MSR_IA32_APICBASE:
3164 cpu_set_apic_base(env, val);
3165 break;
3166 case MSR_EFER:
3167 {
3168 uint64_t update_mask;
3169 update_mask = 0;
3170 if (env->cpuid_ext2_features & CPUID_EXT2_SYSCALL)
3171 update_mask |= MSR_EFER_SCE;
3172 if (env->cpuid_ext2_features & CPUID_EXT2_LM)
3173 update_mask |= MSR_EFER_LME;
3174 if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
3175 update_mask |= MSR_EFER_FFXSR;
3176 if (env->cpuid_ext2_features & CPUID_EXT2_NX)
3177 update_mask |= MSR_EFER_NXE;
3178 if (env->cpuid_ext3_features & CPUID_EXT3_SVM)
3179 update_mask |= MSR_EFER_SVME;
3180 if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
3181 update_mask |= MSR_EFER_FFXSR;
3182 cpu_load_efer(env, (env->efer & ~update_mask) |
3183 (val & update_mask));
3184 }
3185 break;
3186 case MSR_STAR:
3187 env->star = val;
3188 break;
3189 case MSR_PAT:
3190 env->pat = val;
3191 break;
3192 case MSR_VM_HSAVE_PA:
3193 env->vm_hsave = val;
3194 break;
3195#ifdef TARGET_X86_64
3196 case MSR_LSTAR:
3197 env->lstar = val;
3198 break;
3199 case MSR_CSTAR:
3200 env->cstar = val;
3201 break;
3202 case MSR_FMASK:
3203 env->fmask = val;
3204 break;
3205 case MSR_FSBASE:
3206 env->segs[R_FS].base = val;
3207 break;
3208 case MSR_GSBASE:
3209 env->segs[R_GS].base = val;
3210 break;
3211 case MSR_KERNELGSBASE:
3212 env->kernelgsbase = val;
3213 break;
3214#endif
3215 case MSR_MTRRphysBase(0):
3216 case MSR_MTRRphysBase(1):
3217 case MSR_MTRRphysBase(2):
3218 case MSR_MTRRphysBase(3):
3219 case MSR_MTRRphysBase(4):
3220 case MSR_MTRRphysBase(5):
3221 case MSR_MTRRphysBase(6):
3222 case MSR_MTRRphysBase(7):
3223 env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base = val;
3224 break;
3225 case MSR_MTRRphysMask(0):
3226 case MSR_MTRRphysMask(1):
3227 case MSR_MTRRphysMask(2):
3228 case MSR_MTRRphysMask(3):
3229 case MSR_MTRRphysMask(4):
3230 case MSR_MTRRphysMask(5):
3231 case MSR_MTRRphysMask(6):
3232 case MSR_MTRRphysMask(7):
3233 env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask = val;
3234 break;
3235 case MSR_MTRRfix64K_00000:
3236 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix64K_00000] = val;
3237 break;
3238 case MSR_MTRRfix16K_80000:
3239 case MSR_MTRRfix16K_A0000:
3240 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1] = val;
3241 break;
3242 case MSR_MTRRfix4K_C0000:
3243 case MSR_MTRRfix4K_C8000:
3244 case MSR_MTRRfix4K_D0000:
3245 case MSR_MTRRfix4K_D8000:
3246 case MSR_MTRRfix4K_E0000:
3247 case MSR_MTRRfix4K_E8000:
3248 case MSR_MTRRfix4K_F0000:
3249 case MSR_MTRRfix4K_F8000:
3250 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3] = val;
3251 break;
3252 case MSR_MTRRdefType:
3253 env->mtrr_deftype = val;
3254 break;
3255 case MSR_MCG_STATUS:
3256 env->mcg_status = val;
3257 break;
3258 case MSR_MCG_CTL:
3259 if ((env->mcg_cap & MCG_CTL_P)
3260 && (val == 0 || val == ~(uint64_t)0))
3261 env->mcg_ctl = val;
3262 break;
3263 default:
3264 if ((uint32_t)ECX >= MSR_MC0_CTL
3265 && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
3266 uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
3267 if ((offset & 0x3) != 0
3268 || (val == 0 || val == ~(uint64_t)0))
3269 env->mce_banks[offset] = val;
3270 break;
3271 }
3272 /* XXX: exception ? */
3273 break;
3274 }
3275}
3276
3277void helper_rdmsr(void)
3278{
3279 uint64_t val;
3280
3281 helper_svm_check_intercept_param(SVM_EXIT_MSR, 0);
3282
3283 switch((uint32_t)ECX) {
3284 case MSR_IA32_SYSENTER_CS:
3285 val = env->sysenter_cs;
3286 break;
3287 case MSR_IA32_SYSENTER_ESP:
3288 val = env->sysenter_esp;
3289 break;
3290 case MSR_IA32_SYSENTER_EIP:
3291 val = env->sysenter_eip;
3292 break;
3293 case MSR_IA32_APICBASE:
3294 val = cpu_get_apic_base(env);
3295 break;
3296 case MSR_EFER:
3297 val = env->efer;
3298 break;
3299 case MSR_STAR:
3300 val = env->star;
3301 break;
3302 case MSR_PAT:
3303 val = env->pat;
3304 break;
3305 case MSR_VM_HSAVE_PA:
3306 val = env->vm_hsave;
3307 break;
3308 case MSR_IA32_PERF_STATUS:
3309 /* tsc_increment_by_tick */
3310 val = 1000ULL;
3311 /* CPU multiplier */
3312 val |= (((uint64_t)4ULL) << 40);
3313 break;
3314#ifdef TARGET_X86_64
3315 case MSR_LSTAR:
3316 val = env->lstar;
3317 break;
3318 case MSR_CSTAR:
3319 val = env->cstar;
3320 break;
3321 case MSR_FMASK:
3322 val = env->fmask;
3323 break;
3324 case MSR_FSBASE:
3325 val = env->segs[R_FS].base;
3326 break;
3327 case MSR_GSBASE:
3328 val = env->segs[R_GS].base;
3329 break;
3330 case MSR_KERNELGSBASE:
3331 val = env->kernelgsbase;
3332 break;
3333#endif
Jun Nakajima86797932011-01-29 14:24:24 -08003334 case MSR_MTRRphysBase(0):
3335 case MSR_MTRRphysBase(1):
3336 case MSR_MTRRphysBase(2):
3337 case MSR_MTRRphysBase(3):
3338 case MSR_MTRRphysBase(4):
3339 case MSR_MTRRphysBase(5):
3340 case MSR_MTRRphysBase(6):
3341 case MSR_MTRRphysBase(7):
3342 val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base;
3343 break;
3344 case MSR_MTRRphysMask(0):
3345 case MSR_MTRRphysMask(1):
3346 case MSR_MTRRphysMask(2):
3347 case MSR_MTRRphysMask(3):
3348 case MSR_MTRRphysMask(4):
3349 case MSR_MTRRphysMask(5):
3350 case MSR_MTRRphysMask(6):
3351 case MSR_MTRRphysMask(7):
3352 val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask;
3353 break;
3354 case MSR_MTRRfix64K_00000:
3355 val = env->mtrr_fixed[0];
3356 break;
3357 case MSR_MTRRfix16K_80000:
3358 case MSR_MTRRfix16K_A0000:
3359 val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1];
3360 break;
3361 case MSR_MTRRfix4K_C0000:
3362 case MSR_MTRRfix4K_C8000:
3363 case MSR_MTRRfix4K_D0000:
3364 case MSR_MTRRfix4K_D8000:
3365 case MSR_MTRRfix4K_E0000:
3366 case MSR_MTRRfix4K_E8000:
3367 case MSR_MTRRfix4K_F0000:
3368 case MSR_MTRRfix4K_F8000:
3369 val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3];
3370 break;
3371 case MSR_MTRRdefType:
3372 val = env->mtrr_deftype;
3373 break;
3374 case MSR_MTRRcap:
3375 if (env->cpuid_features & CPUID_MTRR)
3376 val = MSR_MTRRcap_VCNT | MSR_MTRRcap_FIXRANGE_SUPPORT | MSR_MTRRcap_WC_SUPPORTED;
3377 else
3378 /* XXX: exception ? */
3379 val = 0;
3380 break;
3381 case MSR_MCG_CAP:
3382 val = env->mcg_cap;
3383 break;
3384 case MSR_MCG_CTL:
3385 if (env->mcg_cap & MCG_CTL_P)
3386 val = env->mcg_ctl;
3387 else
3388 val = 0;
3389 break;
3390 case MSR_MCG_STATUS:
3391 val = env->mcg_status;
3392 break;
3393 default:
3394 if ((uint32_t)ECX >= MSR_MC0_CTL
3395 && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
3396 uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
3397 val = env->mce_banks[offset];
3398 break;
3399 }
3400 /* XXX: exception ? */
3401 val = 0;
3402 break;
3403 }
3404 EAX = (uint32_t)(val);
3405 EDX = (uint32_t)(val >> 32);
3406}
3407#endif
3408
3409target_ulong helper_lsl(target_ulong selector1)
3410{
3411 unsigned int limit;
3412 uint32_t e1, e2, eflags, selector;
3413 int rpl, dpl, cpl, type;
3414
3415 selector = selector1 & 0xffff;
3416 eflags = helper_cc_compute_all(CC_OP);
3417 if ((selector & 0xfffc) == 0)
3418 goto fail;
3419 if (load_segment(&e1, &e2, selector) != 0)
3420 goto fail;
3421 rpl = selector & 3;
3422 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3423 cpl = env->hflags & HF_CPL_MASK;
3424 if (e2 & DESC_S_MASK) {
3425 if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
3426 /* conforming */
3427 } else {
3428 if (dpl < cpl || dpl < rpl)
3429 goto fail;
3430 }
3431 } else {
3432 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
3433 switch(type) {
3434 case 1:
3435 case 2:
3436 case 3:
3437 case 9:
3438 case 11:
3439 break;
3440 default:
3441 goto fail;
3442 }
3443 if (dpl < cpl || dpl < rpl) {
3444 fail:
3445 CC_SRC = eflags & ~CC_Z;
3446 return 0;
3447 }
3448 }
3449 limit = get_seg_limit(e1, e2);
3450 CC_SRC = eflags | CC_Z;
3451 return limit;
3452}
3453
3454target_ulong helper_lar(target_ulong selector1)
3455{
3456 uint32_t e1, e2, eflags, selector;
3457 int rpl, dpl, cpl, type;
3458
3459 selector = selector1 & 0xffff;
3460 eflags = helper_cc_compute_all(CC_OP);
3461 if ((selector & 0xfffc) == 0)
3462 goto fail;
3463 if (load_segment(&e1, &e2, selector) != 0)
3464 goto fail;
3465 rpl = selector & 3;
3466 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3467 cpl = env->hflags & HF_CPL_MASK;
3468 if (e2 & DESC_S_MASK) {
3469 if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
3470 /* conforming */
3471 } else {
3472 if (dpl < cpl || dpl < rpl)
3473 goto fail;
3474 }
3475 } else {
3476 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
3477 switch(type) {
3478 case 1:
3479 case 2:
3480 case 3:
3481 case 4:
3482 case 5:
3483 case 9:
3484 case 11:
3485 case 12:
3486 break;
3487 default:
3488 goto fail;
3489 }
3490 if (dpl < cpl || dpl < rpl) {
3491 fail:
3492 CC_SRC = eflags & ~CC_Z;
3493 return 0;
3494 }
3495 }
3496 CC_SRC = eflags | CC_Z;
3497 return e2 & 0x00f0ff00;
3498}
3499
3500void helper_verr(target_ulong selector1)
3501{
3502 uint32_t e1, e2, eflags, selector;
3503 int rpl, dpl, cpl;
3504
3505 selector = selector1 & 0xffff;
3506 eflags = helper_cc_compute_all(CC_OP);
3507 if ((selector & 0xfffc) == 0)
3508 goto fail;
3509 if (load_segment(&e1, &e2, selector) != 0)
3510 goto fail;
3511 if (!(e2 & DESC_S_MASK))
3512 goto fail;
3513 rpl = selector & 3;
3514 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3515 cpl = env->hflags & HF_CPL_MASK;
3516 if (e2 & DESC_CS_MASK) {
3517 if (!(e2 & DESC_R_MASK))
3518 goto fail;
3519 if (!(e2 & DESC_C_MASK)) {
3520 if (dpl < cpl || dpl < rpl)
3521 goto fail;
3522 }
3523 } else {
3524 if (dpl < cpl || dpl < rpl) {
3525 fail:
3526 CC_SRC = eflags & ~CC_Z;
3527 return;
3528 }
3529 }
3530 CC_SRC = eflags | CC_Z;
3531}
3532
3533void helper_verw(target_ulong selector1)
3534{
3535 uint32_t e1, e2, eflags, selector;
3536 int rpl, dpl, cpl;
3537
3538 selector = selector1 & 0xffff;
3539 eflags = helper_cc_compute_all(CC_OP);
3540 if ((selector & 0xfffc) == 0)
3541 goto fail;
3542 if (load_segment(&e1, &e2, selector) != 0)
3543 goto fail;
3544 if (!(e2 & DESC_S_MASK))
3545 goto fail;
3546 rpl = selector & 3;
3547 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3548 cpl = env->hflags & HF_CPL_MASK;
3549 if (e2 & DESC_CS_MASK) {
3550 goto fail;
3551 } else {
3552 if (dpl < cpl || dpl < rpl)
3553 goto fail;
3554 if (!(e2 & DESC_W_MASK)) {
3555 fail:
3556 CC_SRC = eflags & ~CC_Z;
3557 return;
3558 }
3559 }
3560 CC_SRC = eflags | CC_Z;
3561}
3562
3563/* x87 FPU helpers */
3564
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003565static inline double floatx80_to_double(CPUX86State *env, floatx80 a)
3566{
3567 union {
3568 float64 f64;
3569 double d;
3570 } u;
3571
3572 u.f64 = floatx80_to_float64(a, &env->fp_status);
3573 return u.d;
3574}
3575
3576static inline floatx80 double_to_floatx80(CPUX86State *env, double a)
3577{
3578 union {
3579 float64 f64;
3580 double d;
3581 } u;
3582
3583 u.d = a;
3584 return float64_to_floatx80(u.f64, &env->fp_status);
3585}
3586
Jun Nakajima86797932011-01-29 14:24:24 -08003587static void fpu_set_exception(int mask)
3588{
3589 env->fpus |= mask;
3590 if (env->fpus & (~env->fpuc & FPUC_EM))
3591 env->fpus |= FPUS_SE | FPUS_B;
3592}
3593
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003594static inline floatx80 helper_fdiv(floatx80 a, floatx80 b)
Jun Nakajima86797932011-01-29 14:24:24 -08003595{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003596 if (floatx80_is_zero(b)) {
Jun Nakajima86797932011-01-29 14:24:24 -08003597 fpu_set_exception(FPUS_ZE);
David 'Digit' Turner763b5972014-03-26 17:10:52 +01003598 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003599 return floatx80_div(a, b, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003600}
3601
3602static void fpu_raise_exception(void)
3603{
3604 if (env->cr[0] & CR0_NE_MASK) {
3605 raise_exception(EXCP10_COPR);
3606 }
3607#if !defined(CONFIG_USER_ONLY)
3608 else {
3609 cpu_set_ferr(env);
3610 }
3611#endif
3612}
3613
3614void helper_flds_FT0(uint32_t val)
3615{
3616 union {
3617 float32 f;
3618 uint32_t i;
3619 } u;
3620 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003621 FT0 = float32_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003622}
3623
3624void helper_fldl_FT0(uint64_t val)
3625{
3626 union {
3627 float64 f;
3628 uint64_t i;
3629 } u;
3630 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003631 FT0 = float64_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003632}
3633
3634void helper_fildl_FT0(int32_t val)
3635{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003636 FT0 = int32_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003637}
3638
3639void helper_flds_ST0(uint32_t val)
3640{
3641 int new_fpstt;
3642 union {
3643 float32 f;
3644 uint32_t i;
3645 } u;
3646 new_fpstt = (env->fpstt - 1) & 7;
3647 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003648 env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003649 env->fpstt = new_fpstt;
3650 env->fptags[new_fpstt] = 0; /* validate stack entry */
3651}
3652
3653void helper_fldl_ST0(uint64_t val)
3654{
3655 int new_fpstt;
3656 union {
3657 float64 f;
3658 uint64_t i;
3659 } u;
3660 new_fpstt = (env->fpstt - 1) & 7;
3661 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003662 env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003663 env->fpstt = new_fpstt;
3664 env->fptags[new_fpstt] = 0; /* validate stack entry */
3665}
3666
3667void helper_fildl_ST0(int32_t val)
3668{
3669 int new_fpstt;
3670 new_fpstt = (env->fpstt - 1) & 7;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003671 env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003672 env->fpstt = new_fpstt;
3673 env->fptags[new_fpstt] = 0; /* validate stack entry */
3674}
3675
3676void helper_fildll_ST0(int64_t val)
3677{
3678 int new_fpstt;
3679 new_fpstt = (env->fpstt - 1) & 7;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003680 env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003681 env->fpstt = new_fpstt;
3682 env->fptags[new_fpstt] = 0; /* validate stack entry */
3683}
3684
3685uint32_t helper_fsts_ST0(void)
3686{
3687 union {
3688 float32 f;
3689 uint32_t i;
3690 } u;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003691 u.f = floatx80_to_float32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003692 return u.i;
3693}
3694
3695uint64_t helper_fstl_ST0(void)
3696{
3697 union {
3698 float64 f;
3699 uint64_t i;
3700 } u;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003701 u.f = floatx80_to_float64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003702 return u.i;
3703}
3704
3705int32_t helper_fist_ST0(void)
3706{
3707 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003708 val = floatx80_to_int32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003709 if (val != (int16_t)val)
3710 val = -32768;
3711 return val;
3712}
3713
3714int32_t helper_fistl_ST0(void)
3715{
3716 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003717 val = floatx80_to_int32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003718 return val;
3719}
3720
3721int64_t helper_fistll_ST0(void)
3722{
3723 int64_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003724 val = floatx80_to_int64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003725 return val;
3726}
3727
3728int32_t helper_fistt_ST0(void)
3729{
3730 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003731 val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003732 if (val != (int16_t)val)
3733 val = -32768;
3734 return val;
3735}
3736
3737int32_t helper_fisttl_ST0(void)
3738{
3739 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003740 val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003741 return val;
3742}
3743
3744int64_t helper_fisttll_ST0(void)
3745{
3746 int64_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003747 val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003748 return val;
3749}
3750
3751void helper_fldt_ST0(target_ulong ptr)
3752{
3753 int new_fpstt;
3754 new_fpstt = (env->fpstt - 1) & 7;
3755 env->fpregs[new_fpstt].d = helper_fldt(ptr);
3756 env->fpstt = new_fpstt;
3757 env->fptags[new_fpstt] = 0; /* validate stack entry */
3758}
3759
3760void helper_fstt_ST0(target_ulong ptr)
3761{
3762 helper_fstt(ST0, ptr);
3763}
3764
3765void helper_fpush(void)
3766{
3767 fpush();
3768}
3769
3770void helper_fpop(void)
3771{
3772 fpop();
3773}
3774
3775void helper_fdecstp(void)
3776{
3777 env->fpstt = (env->fpstt - 1) & 7;
3778 env->fpus &= (~0x4700);
3779}
3780
3781void helper_fincstp(void)
3782{
3783 env->fpstt = (env->fpstt + 1) & 7;
3784 env->fpus &= (~0x4700);
3785}
3786
3787/* FPU move */
3788
3789void helper_ffree_STN(int st_index)
3790{
3791 env->fptags[(env->fpstt + st_index) & 7] = 1;
3792}
3793
3794void helper_fmov_ST0_FT0(void)
3795{
3796 ST0 = FT0;
3797}
3798
3799void helper_fmov_FT0_STN(int st_index)
3800{
3801 FT0 = ST(st_index);
3802}
3803
3804void helper_fmov_ST0_STN(int st_index)
3805{
3806 ST0 = ST(st_index);
3807}
3808
3809void helper_fmov_STN_ST0(int st_index)
3810{
3811 ST(st_index) = ST0;
3812}
3813
3814void helper_fxchg_ST0_STN(int st_index)
3815{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003816 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08003817 tmp = ST(st_index);
3818 ST(st_index) = ST0;
3819 ST0 = tmp;
3820}
3821
3822/* FPU operations */
3823
3824static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500};
3825
3826void helper_fcom_ST0_FT0(void)
3827{
3828 int ret;
3829
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003830 ret = floatx80_compare(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003831 env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
3832}
3833
3834void helper_fucom_ST0_FT0(void)
3835{
3836 int ret;
3837
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003838 ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003839 env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1];
3840}
3841
3842static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
3843
3844void helper_fcomi_ST0_FT0(void)
3845{
3846 int eflags;
3847 int ret;
3848
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003849 ret = floatx80_compare(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003850 eflags = helper_cc_compute_all(CC_OP);
3851 eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
3852 CC_SRC = eflags;
3853}
3854
3855void helper_fucomi_ST0_FT0(void)
3856{
3857 int eflags;
3858 int ret;
3859
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003860 ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003861 eflags = helper_cc_compute_all(CC_OP);
3862 eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
3863 CC_SRC = eflags;
3864}
3865
3866void helper_fadd_ST0_FT0(void)
3867{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003868 ST0 = floatx80_add(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003869}
3870
3871void helper_fmul_ST0_FT0(void)
3872{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003873 ST0 = floatx80_mul(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003874}
3875
3876void helper_fsub_ST0_FT0(void)
3877{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003878 ST0 = floatx80_sub(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003879}
3880
3881void helper_fsubr_ST0_FT0(void)
3882{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003883 ST0 = floatx80_sub(FT0, ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003884}
3885
3886void helper_fdiv_ST0_FT0(void)
3887{
3888 ST0 = helper_fdiv(ST0, FT0);
3889}
3890
3891void helper_fdivr_ST0_FT0(void)
3892{
3893 ST0 = helper_fdiv(FT0, ST0);
3894}
3895
3896/* fp operations between STN and ST0 */
3897
3898void helper_fadd_STN_ST0(int st_index)
3899{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003900 ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003901}
3902
3903void helper_fmul_STN_ST0(int st_index)
3904{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003905 ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003906}
3907
3908void helper_fsub_STN_ST0(int st_index)
3909{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003910 ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003911}
3912
3913void helper_fsubr_STN_ST0(int st_index)
3914{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003915 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003916 p = &ST(st_index);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003917 *p = floatx80_sub(ST0, *p, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003918}
3919
3920void helper_fdiv_STN_ST0(int st_index)
3921{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003922 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003923 p = &ST(st_index);
3924 *p = helper_fdiv(*p, ST0);
3925}
3926
3927void helper_fdivr_STN_ST0(int st_index)
3928{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003929 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003930 p = &ST(st_index);
3931 *p = helper_fdiv(ST0, *p);
3932}
3933
3934/* misc FPU operations */
3935void helper_fchs_ST0(void)
3936{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003937 ST0 = floatx80_chs(ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003938}
3939
3940void helper_fabs_ST0(void)
3941{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003942 ST0 = floatx80_abs(ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003943}
3944
3945void helper_fld1_ST0(void)
3946{
3947 ST0 = f15rk[1];
3948}
3949
3950void helper_fldl2t_ST0(void)
3951{
3952 ST0 = f15rk[6];
3953}
3954
3955void helper_fldl2e_ST0(void)
3956{
3957 ST0 = f15rk[5];
3958}
3959
3960void helper_fldpi_ST0(void)
3961{
3962 ST0 = f15rk[2];
3963}
3964
3965void helper_fldlg2_ST0(void)
3966{
3967 ST0 = f15rk[3];
3968}
3969
3970void helper_fldln2_ST0(void)
3971{
3972 ST0 = f15rk[4];
3973}
3974
3975void helper_fldz_ST0(void)
3976{
3977 ST0 = f15rk[0];
3978}
3979
3980void helper_fldz_FT0(void)
3981{
3982 FT0 = f15rk[0];
3983}
3984
3985uint32_t helper_fnstsw(void)
3986{
3987 return (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
3988}
3989
3990uint32_t helper_fnstcw(void)
3991{
3992 return env->fpuc;
3993}
3994
3995static void update_fp_status(void)
3996{
3997 int rnd_type;
3998
3999 /* set rounding mode */
4000 switch(env->fpuc & RC_MASK) {
4001 default:
4002 case RC_NEAR:
4003 rnd_type = float_round_nearest_even;
4004 break;
4005 case RC_DOWN:
4006 rnd_type = float_round_down;
4007 break;
4008 case RC_UP:
4009 rnd_type = float_round_up;
4010 break;
4011 case RC_CHOP:
4012 rnd_type = float_round_to_zero;
4013 break;
4014 }
4015 set_float_rounding_mode(rnd_type, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004016 switch((env->fpuc >> 8) & 3) {
4017 case 0:
4018 rnd_type = 32;
4019 break;
4020 case 2:
4021 rnd_type = 64;
4022 break;
4023 case 3:
4024 default:
4025 rnd_type = 80;
4026 break;
4027 }
4028 set_floatx80_rounding_precision(rnd_type, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004029}
4030
4031void helper_fldcw(uint32_t val)
4032{
4033 env->fpuc = val;
4034 update_fp_status();
4035}
4036
4037void helper_fclex(void)
4038{
4039 env->fpus &= 0x7f00;
4040}
4041
4042void helper_fwait(void)
4043{
4044 if (env->fpus & FPUS_SE)
4045 fpu_raise_exception();
4046}
4047
4048void helper_fninit(void)
4049{
4050 env->fpus = 0;
4051 env->fpstt = 0;
4052 env->fpuc = 0x37f;
4053 env->fptags[0] = 1;
4054 env->fptags[1] = 1;
4055 env->fptags[2] = 1;
4056 env->fptags[3] = 1;
4057 env->fptags[4] = 1;
4058 env->fptags[5] = 1;
4059 env->fptags[6] = 1;
4060 env->fptags[7] = 1;
4061}
4062
4063/* BCD ops */
4064
4065void helper_fbld_ST0(target_ulong ptr)
4066{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004067 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004068 uint64_t val;
4069 unsigned int v;
4070 int i;
4071
4072 val = 0;
4073 for(i = 8; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004074 v = cpu_ldub_data(env, ptr + i);
Jun Nakajima86797932011-01-29 14:24:24 -08004075 val = (val * 100) + ((v >> 4) * 10) + (v & 0xf);
4076 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004077 tmp = int64_to_floatx80(val, &env->fp_status);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004078 if (cpu_ldub_data(env, ptr + 9) & 0x80) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004079 floatx80_chs(tmp);
4080 }
Jun Nakajima86797932011-01-29 14:24:24 -08004081 fpush();
4082 ST0 = tmp;
4083}
4084
4085void helper_fbst_ST0(target_ulong ptr)
4086{
4087 int v;
4088 target_ulong mem_ref, mem_end;
4089 int64_t val;
4090
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004091 val = floatx80_to_int64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004092 mem_ref = ptr;
4093 mem_end = mem_ref + 9;
4094 if (val < 0) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004095 cpu_stb_data(env, mem_end, 0x80);
Jun Nakajima86797932011-01-29 14:24:24 -08004096 val = -val;
4097 } else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004098 cpu_stb_data(env, mem_end, 0x00);
Jun Nakajima86797932011-01-29 14:24:24 -08004099 }
4100 while (mem_ref < mem_end) {
4101 if (val == 0)
4102 break;
4103 v = val % 100;
4104 val = val / 100;
4105 v = ((v / 10) << 4) | (v % 10);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004106 cpu_stb_data(env, mem_ref++, v);
Jun Nakajima86797932011-01-29 14:24:24 -08004107 }
4108 while (mem_ref < mem_end) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004109 cpu_stb_data(env, mem_ref++, 0);
Jun Nakajima86797932011-01-29 14:24:24 -08004110 }
4111}
4112
4113void helper_f2xm1(void)
4114{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004115 double val = floatx80_to_double(env, ST0);
4116 val = pow(2.0, val) - 1.0;
4117 ST0 = double_to_floatx80(env, val);
Jun Nakajima86797932011-01-29 14:24:24 -08004118}
4119
4120void helper_fyl2x(void)
4121{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004122 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004123
Jun Nakajima86797932011-01-29 14:24:24 -08004124 if (fptemp>0.0){
4125 fptemp = log(fptemp)/log(2.0); /* log2(ST) */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004126 fptemp *= floatx80_to_double(env, ST1);
4127 ST1 = double_to_floatx80(env, fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004128 fpop();
4129 } else {
4130 env->fpus &= (~0x4700);
4131 env->fpus |= 0x400;
4132 }
4133}
4134
4135void helper_fptan(void)
4136{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004137 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004138
Jun Nakajima86797932011-01-29 14:24:24 -08004139 if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4140 env->fpus |= 0x400;
4141 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004142 fptemp = tan(fptemp);
4143 ST0 = double_to_floatx80(env, fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004144 fpush();
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004145 ST0 = floatx80_one;
Jun Nakajima86797932011-01-29 14:24:24 -08004146 env->fpus &= (~0x400); /* C2 <-- 0 */
4147 /* the above code is for |arg| < 2**52 only */
4148 }
4149}
4150
4151void helper_fpatan(void)
4152{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004153 double fptemp, fpsrcop;
Jun Nakajima86797932011-01-29 14:24:24 -08004154
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004155 fpsrcop = floatx80_to_double(env, ST1);
4156 fptemp = floatx80_to_double(env, ST0);
4157 ST1 = double_to_floatx80(env, atan2(fpsrcop,fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004158 fpop();
4159}
4160
4161void helper_fxtract(void)
4162{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004163 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004164 unsigned int expdif;
4165
4166 temp.d = ST0;
4167 expdif = EXPD(temp) - EXPBIAS;
4168 /*DP exponent bias*/
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004169 ST0 = int32_to_floatx80(expdif, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004170 fpush();
4171 BIASEXPONENT(temp);
4172 ST0 = temp.d;
4173}
4174
4175void helper_fprem1(void)
4176{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004177 double st0, st1, dblq, fpsrcop, fptemp;
4178 CPU_LDoubleU fpsrcop1, fptemp1;
Jun Nakajima86797932011-01-29 14:24:24 -08004179 int expdif;
4180 signed long long int q;
4181
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004182 st0 = floatx80_to_double(env, ST0);
4183 st1 = floatx80_to_double(env, ST1);
4184
4185 if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
4186 ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */
Jun Nakajima86797932011-01-29 14:24:24 -08004187 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4188 return;
4189 }
4190
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004191 fpsrcop = st0;
4192 fptemp = st1;
4193 fpsrcop1.d = ST0;
4194 fptemp1.d = ST1;
Jun Nakajima86797932011-01-29 14:24:24 -08004195 expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
4196
4197 if (expdif < 0) {
4198 /* optimisation? taken from the AMD docs */
4199 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4200 /* ST0 is unchanged */
4201 return;
4202 }
4203
4204 if (expdif < 53) {
4205 dblq = fpsrcop / fptemp;
4206 /* round dblq towards nearest integer */
4207 dblq = rint(dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004208 st0 = fpsrcop - fptemp * dblq;
Jun Nakajima86797932011-01-29 14:24:24 -08004209
4210 /* convert dblq to q by truncating towards zero */
4211 if (dblq < 0.0)
4212 q = (signed long long int)(-dblq);
4213 else
4214 q = (signed long long int)dblq;
4215
4216 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4217 /* (C0,C3,C1) <-- (q2,q1,q0) */
4218 env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
4219 env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
4220 env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
4221 } else {
4222 env->fpus |= 0x400; /* C2 <-- 1 */
4223 fptemp = pow(2.0, expdif - 50);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004224 fpsrcop = (st0 / st1) / fptemp;
Jun Nakajima86797932011-01-29 14:24:24 -08004225 /* fpsrcop = integer obtained by chopping */
4226 fpsrcop = (fpsrcop < 0.0) ?
4227 -(floor(fabs(fpsrcop))) : floor(fpsrcop);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004228 st0 -= (st1 * fpsrcop * fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004229 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004230 ST0 = double_to_floatx80(env, st0);
Jun Nakajima86797932011-01-29 14:24:24 -08004231}
4232
4233void helper_fprem(void)
4234{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004235 double st0, st1, dblq, fpsrcop, fptemp;
4236 CPU_LDoubleU fpsrcop1, fptemp1;
Jun Nakajima86797932011-01-29 14:24:24 -08004237 int expdif;
4238 signed long long int q;
4239
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004240 st0 = floatx80_to_double(env, ST0);
4241 st1 = floatx80_to_double(env, ST1);
4242
4243 if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
4244 ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */
Jun Nakajima86797932011-01-29 14:24:24 -08004245 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4246 return;
4247 }
4248
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004249 fpsrcop = st0;
4250 fptemp = st1;
4251 fpsrcop1.d = ST0;
4252 fptemp1.d = ST1;
Jun Nakajima86797932011-01-29 14:24:24 -08004253 expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
4254
4255 if (expdif < 0) {
4256 /* optimisation? taken from the AMD docs */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004257 env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */
Jun Nakajima86797932011-01-29 14:24:24 -08004258 /* ST0 is unchanged */
4259 return;
4260 }
4261
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004262 if (expdif < 53) {
4263 dblq = fpsrcop / fptemp; /* ST0 / ST1*/;
Jun Nakajima86797932011-01-29 14:24:24 -08004264 /* round dblq towards zero */
4265 dblq = (dblq < 0.0) ? ceil(dblq) : floor(dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004266 st0 = fpsrcop - fptemp * dblq; /* fpsrcop is ST0 */
Jun Nakajima86797932011-01-29 14:24:24 -08004267
4268 /* convert dblq to q by truncating towards zero */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004269 if (dblq < 0.0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004270 q = (signed long long int)(-dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004271 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004272 q = (signed long long int)dblq;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004273 }
Jun Nakajima86797932011-01-29 14:24:24 -08004274
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004275 env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */
4276 /* (C0,C3,C1) <-- (q2,q1,q0) */
Jun Nakajima86797932011-01-29 14:24:24 -08004277 env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
4278 env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
4279 env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
4280 } else {
4281 int N = 32 + (expdif % 32); /* as per AMD docs */
4282 env->fpus |= 0x400; /* C2 <-- 1 */
4283 fptemp = pow(2.0, (double)(expdif - N));
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004284 fpsrcop = (st0 / st1) / fptemp;
Jun Nakajima86797932011-01-29 14:24:24 -08004285 /* fpsrcop = integer obtained by chopping */
4286 fpsrcop = (fpsrcop < 0.0) ?
4287 -(floor(fabs(fpsrcop))) : floor(fpsrcop);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004288 st0 -= (st1 * fpsrcop * fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004289 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004290 ST0 = double_to_floatx80(env, st0);
Jun Nakajima86797932011-01-29 14:24:24 -08004291}
4292
4293void helper_fyl2xp1(void)
4294{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004295 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004296
Jun Nakajima86797932011-01-29 14:24:24 -08004297 if ((fptemp+1.0)>0.0) {
4298 fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004299 fptemp *= floatx80_to_double(env, ST1);
4300 ST1 = double_to_floatx80(env, fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004301 fpop();
4302 } else {
4303 env->fpus &= (~0x4700);
4304 env->fpus |= 0x400;
4305 }
4306}
4307
4308void helper_fsqrt(void)
4309{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004310 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004311
Jun Nakajima86797932011-01-29 14:24:24 -08004312 if (fptemp<0.0) {
4313 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4314 env->fpus |= 0x400;
4315 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004316 ST0 = floatx80_sqrt(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004317}
4318
4319void helper_fsincos(void)
4320{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004321 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004322
Jun Nakajima86797932011-01-29 14:24:24 -08004323 if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4324 env->fpus |= 0x400;
4325 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004326 ST0 = double_to_floatx80(env, sin(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004327 fpush();
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004328 ST0 = double_to_floatx80(env, cos(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004329 env->fpus &= (~0x400); /* C2 <-- 0 */
4330 /* the above code is for |arg| < 2**63 only */
4331 }
4332}
4333
4334void helper_frndint(void)
4335{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004336 ST0 = floatx80_round_to_int(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004337}
4338
4339void helper_fscale(void)
4340{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004341 double st0 = floatx80_to_double(env, ST0);
4342 double st1 = floatx80_to_double(env, ST1);
4343 double val = ldexp(st0, (int)st1);
4344 ST0 = double_to_floatx80(env, val);
Jun Nakajima86797932011-01-29 14:24:24 -08004345}
4346
4347void helper_fsin(void)
4348{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004349 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004350
Jun Nakajima86797932011-01-29 14:24:24 -08004351 if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4352 env->fpus |= 0x400;
4353 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004354 ST0 = double_to_floatx80(env, sin(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004355 env->fpus &= (~0x400); /* C2 <-- 0 */
4356 /* the above code is for |arg| < 2**53 only */
4357 }
4358}
4359
4360void helper_fcos(void)
4361{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004362 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004363
Jun Nakajima86797932011-01-29 14:24:24 -08004364 if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4365 env->fpus |= 0x400;
4366 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004367 ST0 = double_to_floatx80(env, cos(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004368 env->fpus &= (~0x400); /* C2 <-- 0 */
4369 /* the above code is for |arg5 < 2**63 only */
4370 }
4371}
4372
4373void helper_fxam_ST0(void)
4374{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004375 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004376 int expdif;
4377
4378 temp.d = ST0;
4379
4380 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4381 if (SIGND(temp))
4382 env->fpus |= 0x200; /* C1 <-- 1 */
4383
4384 /* XXX: test fptags too */
4385 expdif = EXPD(temp);
4386 if (expdif == MAXEXPD) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004387 if (MANTD(temp) == 0x8000000000000000ULL) {
Jun Nakajima86797932011-01-29 14:24:24 -08004388 env->fpus |= 0x500 /*Infinity*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004389 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004390 env->fpus |= 0x100 /*NaN*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004391 }
Jun Nakajima86797932011-01-29 14:24:24 -08004392 } else if (expdif == 0) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004393 if (MANTD(temp) == 0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004394 env->fpus |= 0x4000 /*Zero*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004395 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004396 env->fpus |= 0x4400 /*Denormal*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004397 }
Jun Nakajima86797932011-01-29 14:24:24 -08004398 } else {
4399 env->fpus |= 0x400;
4400 }
4401}
4402
4403void helper_fstenv(target_ulong ptr, int data32)
4404{
4405 int fpus, fptag, exp, i;
4406 uint64_t mant;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004407 CPU_LDoubleU tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004408
4409 fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
4410 fptag = 0;
4411 for (i=7; i>=0; i--) {
4412 fptag <<= 2;
4413 if (env->fptags[i]) {
4414 fptag |= 3;
4415 } else {
4416 tmp.d = env->fpregs[i].d;
4417 exp = EXPD(tmp);
4418 mant = MANTD(tmp);
4419 if (exp == 0 && mant == 0) {
4420 /* zero */
4421 fptag |= 1;
4422 } else if (exp == 0 || exp == MAXEXPD
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004423 || (mant & (1LL << 63)) == 0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004424 /* NaNs, infinity, denormal */
4425 fptag |= 2;
4426 }
4427 }
4428 }
4429 if (data32) {
4430 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004431 cpu_stl_data(env, ptr, env->fpuc);
4432 cpu_stl_data(env, ptr + 4, fpus);
4433 cpu_stl_data(env, ptr + 8, fptag);
4434 cpu_stl_data(env, ptr + 12, 0); /* fpip */
4435 cpu_stl_data(env, ptr + 16, 0); /* fpcs */
4436 cpu_stl_data(env, ptr + 20, 0); /* fpoo */
4437 cpu_stl_data(env, ptr + 24, 0); /* fpos */
Jun Nakajima86797932011-01-29 14:24:24 -08004438 } else {
4439 /* 16 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004440 cpu_stw_data(env, ptr, env->fpuc);
4441 cpu_stw_data(env, ptr + 2, fpus);
4442 cpu_stw_data(env, ptr + 4, fptag);
4443 cpu_stw_data(env, ptr + 6, 0);
4444 cpu_stw_data(env, ptr + 8, 0);
4445 cpu_stw_data(env, ptr + 10, 0);
4446 cpu_stw_data(env, ptr + 12, 0);
Jun Nakajima86797932011-01-29 14:24:24 -08004447 }
4448}
4449
4450void helper_fldenv(target_ulong ptr, int data32)
4451{
4452 int i, fpus, fptag;
4453
4454 if (data32) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004455 env->fpuc = cpu_lduw_data(env, ptr);
4456 fpus = cpu_lduw_data(env, ptr + 4);
4457 fptag = cpu_lduw_data(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08004458 }
4459 else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004460 env->fpuc = cpu_lduw_data(env, ptr);
4461 fpus = cpu_lduw_data(env, ptr + 2);
4462 fptag = cpu_lduw_data(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004463 }
4464 env->fpstt = (fpus >> 11) & 7;
4465 env->fpus = fpus & ~0x3800;
4466 for(i = 0;i < 8; i++) {
4467 env->fptags[i] = ((fptag & 3) == 3);
4468 fptag >>= 2;
4469 }
4470}
4471
4472void helper_fsave(target_ulong ptr, int data32)
4473{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004474 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004475 int i;
4476
4477 helper_fstenv(ptr, data32);
4478
4479 ptr += (14 << data32);
4480 for(i = 0;i < 8; i++) {
4481 tmp = ST(i);
4482 helper_fstt(tmp, ptr);
4483 ptr += 10;
4484 }
4485
4486 /* fninit */
4487 env->fpus = 0;
4488 env->fpstt = 0;
4489 env->fpuc = 0x37f;
4490 env->fptags[0] = 1;
4491 env->fptags[1] = 1;
4492 env->fptags[2] = 1;
4493 env->fptags[3] = 1;
4494 env->fptags[4] = 1;
4495 env->fptags[5] = 1;
4496 env->fptags[6] = 1;
4497 env->fptags[7] = 1;
4498}
4499
4500void helper_frstor(target_ulong ptr, int data32)
4501{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004502 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004503 int i;
4504
4505 helper_fldenv(ptr, data32);
4506 ptr += (14 << data32);
4507
4508 for(i = 0;i < 8; i++) {
4509 tmp = helper_fldt(ptr);
4510 ST(i) = tmp;
4511 ptr += 10;
4512 }
4513}
4514
4515void helper_fxsave(target_ulong ptr, int data64)
4516{
4517 int fpus, fptag, i, nb_xmm_regs;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004518 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004519 target_ulong addr;
4520
4521 fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
4522 fptag = 0;
4523 for(i = 0; i < 8; i++) {
4524 fptag |= (env->fptags[i] << i);
4525 }
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004526 cpu_stw_data(env, ptr, env->fpuc);
4527 cpu_stw_data(env, ptr + 2, fpus);
4528 cpu_stw_data(env, ptr + 4, fptag ^ 0xff);
Jun Nakajima86797932011-01-29 14:24:24 -08004529#ifdef TARGET_X86_64
4530 if (data64) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004531 cpu_stq_data(env, ptr + 0x08, 0); /* rip */
4532 cpu_stq_data(env, ptr + 0x10, 0); /* rdp */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004533 } else
Jun Nakajima86797932011-01-29 14:24:24 -08004534#endif
4535 {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004536 cpu_stl_data(env, ptr + 0x08, 0); /* eip */
4537 cpu_stl_data(env, ptr + 0x0c, 0); /* sel */
4538 cpu_stl_data(env, ptr + 0x10, 0); /* dp */
4539 cpu_stl_data(env, ptr + 0x14, 0); /* sel */
Jun Nakajima86797932011-01-29 14:24:24 -08004540 }
4541
4542 addr = ptr + 0x20;
4543 for(i = 0;i < 8; i++) {
4544 tmp = ST(i);
4545 helper_fstt(tmp, addr);
4546 addr += 16;
4547 }
4548
4549 if (env->cr[4] & CR4_OSFXSR_MASK) {
4550 /* XXX: finish it */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004551 cpu_stl_data(env, ptr + 0x18, env->mxcsr); /* mxcsr */
4552 cpu_stl_data(env, ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */
Jun Nakajima86797932011-01-29 14:24:24 -08004553 if (env->hflags & HF_CS64_MASK)
4554 nb_xmm_regs = 16;
4555 else
4556 nb_xmm_regs = 8;
4557 addr = ptr + 0xa0;
4558 /* Fast FXSAVE leaves out the XMM registers */
4559 if (!(env->efer & MSR_EFER_FFXSR)
4560 || (env->hflags & HF_CPL_MASK)
4561 || !(env->hflags & HF_LMA_MASK)) {
4562 for(i = 0; i < nb_xmm_regs; i++) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004563 cpu_stq_data(env, addr, env->xmm_regs[i].XMM_Q(0));
4564 cpu_stq_data(env, addr + 8, env->xmm_regs[i].XMM_Q(1));
Jun Nakajima86797932011-01-29 14:24:24 -08004565 addr += 16;
4566 }
4567 }
4568 }
4569}
4570
4571void helper_fxrstor(target_ulong ptr, int data64)
4572{
4573 int i, fpus, fptag, nb_xmm_regs;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004574 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004575 target_ulong addr;
4576
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004577 env->fpuc = cpu_lduw_data(env, ptr);
4578 fpus = cpu_lduw_data(env, ptr + 2);
4579 fptag = cpu_lduw_data(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004580 env->fpstt = (fpus >> 11) & 7;
4581 env->fpus = fpus & ~0x3800;
4582 fptag ^= 0xff;
4583 for(i = 0;i < 8; i++) {
4584 env->fptags[i] = ((fptag >> i) & 1);
4585 }
4586
4587 addr = ptr + 0x20;
4588 for(i = 0;i < 8; i++) {
4589 tmp = helper_fldt(addr);
4590 ST(i) = tmp;
4591 addr += 16;
4592 }
4593
4594 if (env->cr[4] & CR4_OSFXSR_MASK) {
4595 /* XXX: finish it */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004596 env->mxcsr = cpu_ldl_data(env, ptr + 0x18);
Jun Nakajima86797932011-01-29 14:24:24 -08004597 //ldl(ptr + 0x1c);
4598 if (env->hflags & HF_CS64_MASK)
4599 nb_xmm_regs = 16;
4600 else
4601 nb_xmm_regs = 8;
4602 addr = ptr + 0xa0;
4603 /* Fast FXRESTORE leaves out the XMM registers */
4604 if (!(env->efer & MSR_EFER_FFXSR)
4605 || (env->hflags & HF_CPL_MASK)
4606 || !(env->hflags & HF_LMA_MASK)) {
4607 for(i = 0; i < nb_xmm_regs; i++) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004608 env->xmm_regs[i].XMM_Q(0) = cpu_ldq_data(env, addr);
4609 env->xmm_regs[i].XMM_Q(1) = cpu_ldq_data(env, addr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08004610 addr += 16;
4611 }
4612 }
4613 }
4614}
4615
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004616void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f)
Jun Nakajima86797932011-01-29 14:24:24 -08004617{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004618 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004619
4620 temp.d = f;
4621 *pmant = temp.l.lower;
4622 *pexp = temp.l.upper;
4623}
4624
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004625floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper)
Jun Nakajima86797932011-01-29 14:24:24 -08004626{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004627 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004628
4629 temp.l.upper = upper;
4630 temp.l.lower = mant;
4631 return temp.d;
4632}
Jun Nakajima86797932011-01-29 14:24:24 -08004633
4634#ifdef TARGET_X86_64
4635
4636//#define DEBUG_MULDIV
4637
4638static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
4639{
4640 *plow += a;
4641 /* carry test */
4642 if (*plow < a)
4643 (*phigh)++;
4644 *phigh += b;
4645}
4646
4647static void neg128(uint64_t *plow, uint64_t *phigh)
4648{
4649 *plow = ~ *plow;
4650 *phigh = ~ *phigh;
4651 add128(plow, phigh, 1, 0);
4652}
4653
4654/* return TRUE if overflow */
4655static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
4656{
4657 uint64_t q, r, a1, a0;
4658 int i, qb, ab;
4659
4660 a0 = *plow;
4661 a1 = *phigh;
4662 if (a1 == 0) {
4663 q = a0 / b;
4664 r = a0 % b;
4665 *plow = q;
4666 *phigh = r;
4667 } else {
4668 if (a1 >= b)
4669 return 1;
4670 /* XXX: use a better algorithm */
4671 for(i = 0; i < 64; i++) {
4672 ab = a1 >> 63;
4673 a1 = (a1 << 1) | (a0 >> 63);
4674 if (ab || a1 >= b) {
4675 a1 -= b;
4676 qb = 1;
4677 } else {
4678 qb = 0;
4679 }
4680 a0 = (a0 << 1) | qb;
4681 }
4682#if defined(DEBUG_MULDIV)
4683 printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n",
4684 *phigh, *plow, b, a0, a1);
4685#endif
4686 *plow = a0;
4687 *phigh = a1;
4688 }
4689 return 0;
4690}
4691
4692/* return TRUE if overflow */
4693static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
4694{
4695 int sa, sb;
4696 sa = ((int64_t)*phigh < 0);
4697 if (sa)
4698 neg128(plow, phigh);
4699 sb = (b < 0);
4700 if (sb)
4701 b = -b;
4702 if (div64(plow, phigh, b) != 0)
4703 return 1;
4704 if (sa ^ sb) {
4705 if (*plow > (1ULL << 63))
4706 return 1;
4707 *plow = - *plow;
4708 } else {
4709 if (*plow >= (1ULL << 63))
4710 return 1;
4711 }
4712 if (sa)
4713 *phigh = - *phigh;
4714 return 0;
4715}
4716
4717void helper_mulq_EAX_T0(target_ulong t0)
4718{
4719 uint64_t r0, r1;
4720
4721 mulu64(&r0, &r1, EAX, t0);
4722 EAX = r0;
4723 EDX = r1;
4724 CC_DST = r0;
4725 CC_SRC = r1;
4726}
4727
4728void helper_imulq_EAX_T0(target_ulong t0)
4729{
4730 uint64_t r0, r1;
4731
4732 muls64(&r0, &r1, EAX, t0);
4733 EAX = r0;
4734 EDX = r1;
4735 CC_DST = r0;
4736 CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
4737}
4738
4739target_ulong helper_imulq_T0_T1(target_ulong t0, target_ulong t1)
4740{
4741 uint64_t r0, r1;
4742
4743 muls64(&r0, &r1, t0, t1);
4744 CC_DST = r0;
4745 CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
4746 return r0;
4747}
4748
4749void helper_divq_EAX(target_ulong t0)
4750{
4751 uint64_t r0, r1;
4752 if (t0 == 0) {
4753 raise_exception(EXCP00_DIVZ);
4754 }
4755 r0 = EAX;
4756 r1 = EDX;
4757 if (div64(&r0, &r1, t0))
4758 raise_exception(EXCP00_DIVZ);
4759 EAX = r0;
4760 EDX = r1;
4761}
4762
4763void helper_idivq_EAX(target_ulong t0)
4764{
4765 uint64_t r0, r1;
4766 if (t0 == 0) {
4767 raise_exception(EXCP00_DIVZ);
4768 }
4769 r0 = EAX;
4770 r1 = EDX;
4771 if (idiv64(&r0, &r1, t0))
4772 raise_exception(EXCP00_DIVZ);
4773 EAX = r0;
4774 EDX = r1;
4775}
4776#endif
4777
4778static void do_hlt(void)
4779{
4780 env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */
4781 env->halted = 1;
4782 env->exception_index = EXCP_HLT;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01004783 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004784}
4785
4786void helper_hlt(int next_eip_addend)
4787{
4788 helper_svm_check_intercept_param(SVM_EXIT_HLT, 0);
4789 EIP += next_eip_addend;
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004790
Jun Nakajima86797932011-01-29 14:24:24 -08004791 do_hlt();
4792}
4793
4794void helper_monitor(target_ulong ptr)
4795{
4796 if ((uint32_t)ECX != 0)
4797 raise_exception(EXCP0D_GPF);
4798 /* XXX: store address ? */
4799 helper_svm_check_intercept_param(SVM_EXIT_MONITOR, 0);
4800}
4801
4802void helper_mwait(int next_eip_addend)
4803{
4804 if ((uint32_t)ECX != 0)
4805 raise_exception(EXCP0D_GPF);
4806 helper_svm_check_intercept_param(SVM_EXIT_MWAIT, 0);
4807 EIP += next_eip_addend;
4808
4809 /* XXX: not complete but not completely erroneous */
4810 if (env->cpu_index != 0 || env->next_cpu != NULL) {
4811 /* more than one CPU: do not sleep because another CPU may
4812 wake this one */
4813 } else {
4814 do_hlt();
4815 }
4816}
4817
4818void helper_debug(void)
4819{
4820 env->exception_index = EXCP_DEBUG;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01004821 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004822}
4823
4824void helper_reset_rf(void)
4825{
4826 env->eflags &= ~RF_MASK;
4827}
4828
4829void helper_raise_interrupt(int intno, int next_eip_addend)
4830{
4831 raise_interrupt(intno, 1, 0, next_eip_addend);
4832}
4833
4834void helper_raise_exception(int exception_index)
4835{
4836 raise_exception(exception_index);
4837}
4838
4839void helper_cli(void)
4840{
4841 env->eflags &= ~IF_MASK;
4842}
4843
4844void helper_sti(void)
4845{
4846 env->eflags |= IF_MASK;
4847}
4848
4849#if 0
4850/* vm86plus instructions */
4851void helper_cli_vm(void)
4852{
4853 env->eflags &= ~VIF_MASK;
4854}
4855
4856void helper_sti_vm(void)
4857{
4858 env->eflags |= VIF_MASK;
4859 if (env->eflags & VIP_MASK) {
4860 raise_exception(EXCP0D_GPF);
4861 }
4862}
4863#endif
4864
4865void helper_set_inhibit_irq(void)
4866{
4867 env->hflags |= HF_INHIBIT_IRQ_MASK;
4868}
4869
4870void helper_reset_inhibit_irq(void)
4871{
4872 env->hflags &= ~HF_INHIBIT_IRQ_MASK;
4873}
4874
4875void helper_boundw(target_ulong a0, int v)
4876{
4877 int low, high;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004878 low = cpu_ldsw_data(env, a0);
4879 high = cpu_ldsw_data(env, a0 + 2);
Jun Nakajima86797932011-01-29 14:24:24 -08004880 v = (int16_t)v;
4881 if (v < low || v > high) {
4882 raise_exception(EXCP05_BOUND);
4883 }
4884}
4885
4886void helper_boundl(target_ulong a0, int v)
4887{
4888 int low, high;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004889 low = cpu_ldl_data(env, a0);
4890 high = cpu_ldl_data(env, a0 + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004891 if (v < low || v > high) {
4892 raise_exception(EXCP05_BOUND);
4893 }
4894}
4895
4896static float approx_rsqrt(float a)
4897{
4898 return 1.0 / sqrt(a);
4899}
4900
4901static float approx_rcp(float a)
4902{
4903 return 1.0 / a;
4904}
4905
4906#if !defined(CONFIG_USER_ONLY)
4907
4908#define MMUSUFFIX _mmu
4909
4910#define SHIFT 0
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004911#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004912
4913#define SHIFT 1
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004914#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004915
4916#define SHIFT 2
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004917#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004918
4919#define SHIFT 3
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004920#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004921
4922#endif
4923
4924#if !defined(CONFIG_USER_ONLY)
4925/* try to fill the TLB and return an exception if error. If retaddr is
4926 NULL, it means that the function was called in C code (i.e. not
4927 from generated code or from helper.c) */
4928/* XXX: fix it to restore all registers */
David 'Digit' Turner6d1afd32014-03-14 10:51:57 +01004929void tlb_fill(CPUX86State* env1, target_ulong addr, int is_write, int mmu_idx, void *retaddr)
Jun Nakajima86797932011-01-29 14:24:24 -08004930{
4931 TranslationBlock *tb;
4932 int ret;
4933 unsigned long pc;
4934 CPUX86State *saved_env;
4935
4936 /* XXX: hack to restore env in all cases, even if not called from
4937 generated code */
4938 saved_env = env;
David 'Digit' Turner6d1afd32014-03-14 10:51:57 +01004939 env = env1;
Jun Nakajima86797932011-01-29 14:24:24 -08004940 ret = cpu_x86_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
4941 if (ret) {
4942 if (retaddr) {
4943 /* now we have a real cpu fault */
4944 pc = (unsigned long)retaddr;
4945 tb = tb_find_pc(pc);
4946 if (tb) {
4947 /* the PC is inside the translated code. It means that we have
4948 a virtual CPU fault */
David 'Digit' Turner3e0677d2014-03-07 15:01:06 +01004949 cpu_restore_state(env, pc);
Jun Nakajima86797932011-01-29 14:24:24 -08004950 }
4951 }
4952 raise_exception_err(env->exception_index, env->error_code);
4953 }
4954 env = saved_env;
4955}
4956#endif
4957
4958/* Secure Virtual Machine helpers */
4959
4960#if defined(CONFIG_USER_ONLY)
4961
4962void helper_vmrun(int aflag, int next_eip_addend)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004963{
Jun Nakajima86797932011-01-29 14:24:24 -08004964}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004965void helper_vmmcall(void)
4966{
Jun Nakajima86797932011-01-29 14:24:24 -08004967}
4968void helper_vmload(int aflag)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004969{
Jun Nakajima86797932011-01-29 14:24:24 -08004970}
4971void helper_vmsave(int aflag)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004972{
Jun Nakajima86797932011-01-29 14:24:24 -08004973}
4974void helper_stgi(void)
4975{
4976}
4977void helper_clgi(void)
4978{
4979}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004980void helper_skinit(void)
4981{
Jun Nakajima86797932011-01-29 14:24:24 -08004982}
4983void helper_invlpga(int aflag)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004984{
Jun Nakajima86797932011-01-29 14:24:24 -08004985}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004986void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1)
4987{
Jun Nakajima86797932011-01-29 14:24:24 -08004988}
4989void helper_svm_check_intercept_param(uint32_t type, uint64_t param)
4990{
4991}
4992
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01004993void svm_check_intercept(CPUArchState *env1, uint32_t type)
4994{
4995}
4996
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004997void helper_svm_check_io(uint32_t port, uint32_t param,
Jun Nakajima86797932011-01-29 14:24:24 -08004998 uint32_t next_eip_addend)
4999{
5000}
5001#else
5002
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +01005003static inline void svm_save_seg(hwaddr addr,
Jun Nakajima86797932011-01-29 14:24:24 -08005004 const SegmentCache *sc)
5005{
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005006 stw_phys(addr + offsetof(struct vmcb_seg, selector),
Jun Nakajima86797932011-01-29 14:24:24 -08005007 sc->selector);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005008 stq_phys(addr + offsetof(struct vmcb_seg, base),
Jun Nakajima86797932011-01-29 14:24:24 -08005009 sc->base);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005010 stl_phys(addr + offsetof(struct vmcb_seg, limit),
Jun Nakajima86797932011-01-29 14:24:24 -08005011 sc->limit);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005012 stw_phys(addr + offsetof(struct vmcb_seg, attrib),
Jun Nakajima86797932011-01-29 14:24:24 -08005013 ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00));
5014}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005015
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +01005016static inline void svm_load_seg(hwaddr addr, SegmentCache *sc)
Jun Nakajima86797932011-01-29 14:24:24 -08005017{
5018 unsigned int flags;
5019
5020 sc->selector = lduw_phys(addr + offsetof(struct vmcb_seg, selector));
5021 sc->base = ldq_phys(addr + offsetof(struct vmcb_seg, base));
5022 sc->limit = ldl_phys(addr + offsetof(struct vmcb_seg, limit));
5023 flags = lduw_phys(addr + offsetof(struct vmcb_seg, attrib));
5024 sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12);
5025}
5026
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +01005027static inline void svm_load_seg_cache(hwaddr addr,
David 'Digit' Turnere2678e12014-01-16 15:56:43 +01005028 CPUX86State *env, int seg_reg)
Jun Nakajima86797932011-01-29 14:24:24 -08005029{
5030 SegmentCache sc1, *sc = &sc1;
5031 svm_load_seg(addr, sc);
5032 cpu_x86_load_seg_cache(env, seg_reg, sc->selector,
5033 sc->base, sc->limit, sc->flags);
5034}
5035
5036void helper_vmrun(int aflag, int next_eip_addend)
5037{
5038 target_ulong addr;
5039 uint32_t event_inj;
5040 uint32_t int_ctl;
5041
5042 helper_svm_check_intercept_param(SVM_EXIT_VMRUN, 0);
5043
5044 if (aflag == 2)
5045 addr = EAX;
5046 else
5047 addr = (uint32_t)EAX;
5048
5049 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n", addr);
5050
5051 env->vm_vmcb = addr;
5052
5053 /* save the current CPU state in the hsave page */
5054 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), env->gdt.base);
5055 stl_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit);
5056
5057 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.base), env->idt.base);
5058 stl_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), env->idt.limit);
5059
5060 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]);
5061 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]);
5062 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]);
5063 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]);
5064 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]);
5065 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]);
5066
5067 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer);
5068 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags), compute_eflags());
5069
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005070 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.es),
Jun Nakajima86797932011-01-29 14:24:24 -08005071 &env->segs[R_ES]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005072 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.cs),
Jun Nakajima86797932011-01-29 14:24:24 -08005073 &env->segs[R_CS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005074 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ss),
Jun Nakajima86797932011-01-29 14:24:24 -08005075 &env->segs[R_SS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005076 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ds),
Jun Nakajima86797932011-01-29 14:24:24 -08005077 &env->segs[R_DS]);
5078
5079 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rip),
5080 EIP + next_eip_addend);
5081 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rsp), ESP);
5082 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rax), EAX);
5083
5084 /* load the interception bitmaps so we do not need to access the
5085 vmcb in svm mode */
5086 env->intercept = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept));
5087 env->intercept_cr_read = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_read));
5088 env->intercept_cr_write = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_write));
5089 env->intercept_dr_read = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_read));
5090 env->intercept_dr_write = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_write));
5091 env->intercept_exceptions = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_exceptions));
5092
5093 /* enable intercepts */
5094 env->hflags |= HF_SVMI_MASK;
5095
5096 env->tsc_offset = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.tsc_offset));
5097
5098 env->gdt.base = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base));
5099 env->gdt.limit = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit));
5100
5101 env->idt.base = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.base));
5102 env->idt.limit = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit));
5103
5104 /* clear exit_info_2 so we behave like the real hardware */
5105 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0);
5106
5107 cpu_x86_update_cr0(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr0)));
5108 cpu_x86_update_cr4(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr4)));
5109 cpu_x86_update_cr3(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr3)));
5110 env->cr[2] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr2));
5111 int_ctl = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
5112 env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
5113 if (int_ctl & V_INTR_MASKING_MASK) {
5114 env->v_tpr = int_ctl & V_TPR_MASK;
5115 env->hflags2 |= HF2_VINTR_MASK;
5116 if (env->eflags & IF_MASK)
5117 env->hflags2 |= HF2_HIF_MASK;
5118 }
5119
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005120 cpu_load_efer(env,
Jun Nakajima86797932011-01-29 14:24:24 -08005121 ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.efer)));
5122 env->eflags = 0;
5123 load_eflags(ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rflags)),
5124 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
5125 CC_OP = CC_OP_EFLAGS;
5126
5127 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.es),
5128 env, R_ES);
5129 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.cs),
5130 env, R_CS);
5131 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.ss),
5132 env, R_SS);
5133 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.ds),
5134 env, R_DS);
5135
5136 EIP = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rip));
5137 env->eip = EIP;
5138 ESP = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rsp));
5139 EAX = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rax));
5140 env->dr[7] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr7));
5141 env->dr[6] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr6));
5142 cpu_x86_set_cpl(env, ldub_phys(env->vm_vmcb + offsetof(struct vmcb, save.cpl)));
5143
5144 /* FIXME: guest state consistency checks */
5145
5146 switch(ldub_phys(env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) {
5147 case TLB_CONTROL_DO_NOTHING:
5148 break;
5149 case TLB_CONTROL_FLUSH_ALL_ASID:
5150 /* FIXME: this is not 100% correct but should work for now */
5151 tlb_flush(env, 1);
5152 break;
5153 }
5154
5155 env->hflags2 |= HF2_GIF_MASK;
5156
5157 if (int_ctl & V_IRQ_MASK) {
5158 env->interrupt_request |= CPU_INTERRUPT_VIRQ;
5159 }
5160
5161 /* maybe we need to inject an event */
5162 event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
5163 if (event_inj & SVM_EVTINJ_VALID) {
5164 uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK;
5165 uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR;
5166 uint32_t event_inj_err = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err));
5167
5168 qemu_log_mask(CPU_LOG_TB_IN_ASM, "Injecting(%#hx): ", valid_err);
5169 /* FIXME: need to implement valid_err */
5170 switch (event_inj & SVM_EVTINJ_TYPE_MASK) {
5171 case SVM_EVTINJ_TYPE_INTR:
5172 env->exception_index = vector;
5173 env->error_code = event_inj_err;
5174 env->exception_is_int = 0;
5175 env->exception_next_eip = -1;
5176 qemu_log_mask(CPU_LOG_TB_IN_ASM, "INTR");
5177 /* XXX: is it always correct ? */
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01005178 do_interrupt_all(vector, 0, 0, 0, 1);
Jun Nakajima86797932011-01-29 14:24:24 -08005179 break;
5180 case SVM_EVTINJ_TYPE_NMI:
5181 env->exception_index = EXCP02_NMI;
5182 env->error_code = event_inj_err;
5183 env->exception_is_int = 0;
5184 env->exception_next_eip = EIP;
5185 qemu_log_mask(CPU_LOG_TB_IN_ASM, "NMI");
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005186 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005187 break;
5188 case SVM_EVTINJ_TYPE_EXEPT:
5189 env->exception_index = vector;
5190 env->error_code = event_inj_err;
5191 env->exception_is_int = 0;
5192 env->exception_next_eip = -1;
5193 qemu_log_mask(CPU_LOG_TB_IN_ASM, "EXEPT");
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005194 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005195 break;
5196 case SVM_EVTINJ_TYPE_SOFT:
5197 env->exception_index = vector;
5198 env->error_code = event_inj_err;
5199 env->exception_is_int = 1;
5200 env->exception_next_eip = EIP;
5201 qemu_log_mask(CPU_LOG_TB_IN_ASM, "SOFT");
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005202 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005203 break;
5204 }
5205 qemu_log_mask(CPU_LOG_TB_IN_ASM, " %#x %#x\n", env->exception_index, env->error_code);
5206 }
5207}
5208
5209void helper_vmmcall(void)
5210{
5211 helper_svm_check_intercept_param(SVM_EXIT_VMMCALL, 0);
5212 raise_exception(EXCP06_ILLOP);
5213}
5214
5215void helper_vmload(int aflag)
5216{
5217 target_ulong addr;
5218 helper_svm_check_intercept_param(SVM_EXIT_VMLOAD, 0);
5219
5220 if (aflag == 2)
5221 addr = EAX;
5222 else
5223 addr = (uint32_t)EAX;
5224
5225 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
5226 addr, ldq_phys(addr + offsetof(struct vmcb, save.fs.base)),
5227 env->segs[R_FS].base);
5228
5229 svm_load_seg_cache(addr + offsetof(struct vmcb, save.fs),
5230 env, R_FS);
5231 svm_load_seg_cache(addr + offsetof(struct vmcb, save.gs),
5232 env, R_GS);
5233 svm_load_seg(addr + offsetof(struct vmcb, save.tr),
5234 &env->tr);
5235 svm_load_seg(addr + offsetof(struct vmcb, save.ldtr),
5236 &env->ldt);
5237
5238#ifdef TARGET_X86_64
5239 env->kernelgsbase = ldq_phys(addr + offsetof(struct vmcb, save.kernel_gs_base));
5240 env->lstar = ldq_phys(addr + offsetof(struct vmcb, save.lstar));
5241 env->cstar = ldq_phys(addr + offsetof(struct vmcb, save.cstar));
5242 env->fmask = ldq_phys(addr + offsetof(struct vmcb, save.sfmask));
5243#endif
5244 env->star = ldq_phys(addr + offsetof(struct vmcb, save.star));
5245 env->sysenter_cs = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_cs));
5246 env->sysenter_esp = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_esp));
5247 env->sysenter_eip = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_eip));
5248}
5249
5250void helper_vmsave(int aflag)
5251{
5252 target_ulong addr;
5253 helper_svm_check_intercept_param(SVM_EXIT_VMSAVE, 0);
5254
5255 if (aflag == 2)
5256 addr = EAX;
5257 else
5258 addr = (uint32_t)EAX;
5259
5260 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
5261 addr, ldq_phys(addr + offsetof(struct vmcb, save.fs.base)),
5262 env->segs[R_FS].base);
5263
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005264 svm_save_seg(addr + offsetof(struct vmcb, save.fs),
Jun Nakajima86797932011-01-29 14:24:24 -08005265 &env->segs[R_FS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005266 svm_save_seg(addr + offsetof(struct vmcb, save.gs),
Jun Nakajima86797932011-01-29 14:24:24 -08005267 &env->segs[R_GS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005268 svm_save_seg(addr + offsetof(struct vmcb, save.tr),
Jun Nakajima86797932011-01-29 14:24:24 -08005269 &env->tr);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005270 svm_save_seg(addr + offsetof(struct vmcb, save.ldtr),
Jun Nakajima86797932011-01-29 14:24:24 -08005271 &env->ldt);
5272
5273#ifdef TARGET_X86_64
5274 stq_phys(addr + offsetof(struct vmcb, save.kernel_gs_base), env->kernelgsbase);
5275 stq_phys(addr + offsetof(struct vmcb, save.lstar), env->lstar);
5276 stq_phys(addr + offsetof(struct vmcb, save.cstar), env->cstar);
5277 stq_phys(addr + offsetof(struct vmcb, save.sfmask), env->fmask);
5278#endif
5279 stq_phys(addr + offsetof(struct vmcb, save.star), env->star);
5280 stq_phys(addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs);
5281 stq_phys(addr + offsetof(struct vmcb, save.sysenter_esp), env->sysenter_esp);
5282 stq_phys(addr + offsetof(struct vmcb, save.sysenter_eip), env->sysenter_eip);
5283}
5284
5285void helper_stgi(void)
5286{
5287 helper_svm_check_intercept_param(SVM_EXIT_STGI, 0);
5288 env->hflags2 |= HF2_GIF_MASK;
5289}
5290
5291void helper_clgi(void)
5292{
5293 helper_svm_check_intercept_param(SVM_EXIT_CLGI, 0);
5294 env->hflags2 &= ~HF2_GIF_MASK;
5295}
5296
5297void helper_skinit(void)
5298{
5299 helper_svm_check_intercept_param(SVM_EXIT_SKINIT, 0);
5300 /* XXX: not implemented */
5301 raise_exception(EXCP06_ILLOP);
5302}
5303
5304void helper_invlpga(int aflag)
5305{
5306 target_ulong addr;
5307 helper_svm_check_intercept_param(SVM_EXIT_INVLPGA, 0);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005308
Jun Nakajima86797932011-01-29 14:24:24 -08005309 if (aflag == 2)
5310 addr = EAX;
5311 else
5312 addr = (uint32_t)EAX;
5313
5314 /* XXX: could use the ASID to see if it is needed to do the
5315 flush */
5316 tlb_flush_page(env, addr);
5317}
5318
5319void helper_svm_check_intercept_param(uint32_t type, uint64_t param)
5320{
5321 if (likely(!(env->hflags & HF_SVMI_MASK)))
5322 return;
5323 switch(type) {
5324 case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8:
5325 if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) {
5326 helper_vmexit(type, param);
5327 }
5328 break;
5329 case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8:
5330 if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) {
5331 helper_vmexit(type, param);
5332 }
5333 break;
5334 case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7:
5335 if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) {
5336 helper_vmexit(type, param);
5337 }
5338 break;
5339 case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7:
5340 if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) {
5341 helper_vmexit(type, param);
5342 }
5343 break;
5344 case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31:
5345 if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) {
5346 helper_vmexit(type, param);
5347 }
5348 break;
5349 case SVM_EXIT_MSR:
5350 if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) {
5351 /* FIXME: this should be read in at vmrun (faster this way?) */
5352 uint64_t addr = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.msrpm_base_pa));
5353 uint32_t t0, t1;
5354 switch((uint32_t)ECX) {
5355 case 0 ... 0x1fff:
5356 t0 = (ECX * 2) % 8;
5357 t1 = ECX / 8;
5358 break;
5359 case 0xc0000000 ... 0xc0001fff:
5360 t0 = (8192 + ECX - 0xc0000000) * 2;
5361 t1 = (t0 / 8);
5362 t0 %= 8;
5363 break;
5364 case 0xc0010000 ... 0xc0011fff:
5365 t0 = (16384 + ECX - 0xc0010000) * 2;
5366 t1 = (t0 / 8);
5367 t0 %= 8;
5368 break;
5369 default:
5370 helper_vmexit(type, param);
5371 t0 = 0;
5372 t1 = 0;
5373 break;
5374 }
5375 if (ldub_phys(addr + t1) & ((1 << param) << t0))
5376 helper_vmexit(type, param);
5377 }
5378 break;
5379 default:
5380 if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) {
5381 helper_vmexit(type, param);
5382 }
5383 break;
5384 }
5385}
5386
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01005387void svm_check_intercept(CPUArchState *env1, uint32_t type)
5388{
5389 CPUArchState *saved_env;
5390
5391 saved_env = env;
5392 env = env1;
5393 helper_svm_check_intercept_param(type, 0);
5394 env = saved_env;
5395}
5396
5397
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005398void helper_svm_check_io(uint32_t port, uint32_t param,
Jun Nakajima86797932011-01-29 14:24:24 -08005399 uint32_t next_eip_addend)
5400{
5401 if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) {
5402 /* FIXME: this should be read in at vmrun (faster this way?) */
5403 uint64_t addr = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.iopm_base_pa));
5404 uint16_t mask = (1 << ((param >> 4) & 7)) - 1;
5405 if(lduw_phys(addr + port / 8) & (mask << (port & 7))) {
5406 /* next EIP */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005407 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2),
Jun Nakajima86797932011-01-29 14:24:24 -08005408 env->eip + next_eip_addend);
5409 helper_vmexit(SVM_EXIT_IOIO, param | (port << 16));
5410 }
5411 }
5412}
5413
5414/* Note: currently only 32 bits of exit_code are used */
5415void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1)
5416{
5417 uint32_t int_ctl;
5418
5419 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" PRIx64 ", " TARGET_FMT_lx ")!\n",
5420 exit_code, exit_info_1,
5421 ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2)),
5422 EIP);
5423
5424 if(env->hflags & HF_INHIBIT_IRQ_MASK) {
5425 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_state), SVM_INTERRUPT_SHADOW_MASK);
5426 env->hflags &= ~HF_INHIBIT_IRQ_MASK;
5427 } else {
5428 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0);
5429 }
5430
5431 /* Save the VM state in the vmcb */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005432 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.es),
Jun Nakajima86797932011-01-29 14:24:24 -08005433 &env->segs[R_ES]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005434 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.cs),
Jun Nakajima86797932011-01-29 14:24:24 -08005435 &env->segs[R_CS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005436 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ss),
Jun Nakajima86797932011-01-29 14:24:24 -08005437 &env->segs[R_SS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005438 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ds),
Jun Nakajima86797932011-01-29 14:24:24 -08005439 &env->segs[R_DS]);
5440
5441 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), env->gdt.base);
5442 stl_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit);
5443
5444 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), env->idt.base);
5445 stl_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), env->idt.limit);
5446
5447 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer);
5448 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]);
5449 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]);
5450 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]);
5451 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]);
5452
5453 int_ctl = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
5454 int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK);
5455 int_ctl |= env->v_tpr & V_TPR_MASK;
5456 if (env->interrupt_request & CPU_INTERRUPT_VIRQ)
5457 int_ctl |= V_IRQ_MASK;
5458 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl);
5459
5460 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rflags), compute_eflags());
5461 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rip), env->eip);
5462 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rsp), ESP);
5463 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rax), EAX);
5464 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]);
5465 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]);
5466 stb_phys(env->vm_vmcb + offsetof(struct vmcb, save.cpl), env->hflags & HF_CPL_MASK);
5467
5468 /* Reload the host state from vm_hsave */
5469 env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
5470 env->hflags &= ~HF_SVMI_MASK;
5471 env->intercept = 0;
5472 env->intercept_exceptions = 0;
5473 env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
5474 env->tsc_offset = 0;
5475
5476 env->gdt.base = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.base));
5477 env->gdt.limit = ldl_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit));
5478
5479 env->idt.base = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.base));
5480 env->idt.limit = ldl_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.limit));
5481
5482 cpu_x86_update_cr0(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr0)) | CR0_PE_MASK);
5483 cpu_x86_update_cr4(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr4)));
5484 cpu_x86_update_cr3(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr3)));
5485 /* we need to set the efer after the crs so the hidden flags get
5486 set properly */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005487 cpu_load_efer(env,
Jun Nakajima86797932011-01-29 14:24:24 -08005488 ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer)));
5489 env->eflags = 0;
5490 load_eflags(ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags)),
5491 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
5492 CC_OP = CC_OP_EFLAGS;
5493
5494 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.es),
5495 env, R_ES);
5496 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.cs),
5497 env, R_CS);
5498 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.ss),
5499 env, R_SS);
5500 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.ds),
5501 env, R_DS);
5502
5503 EIP = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rip));
5504 ESP = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rsp));
5505 EAX = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rax));
5506
5507 env->dr[6] = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr6));
5508 env->dr[7] = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr7));
5509
5510 /* other setups */
5511 cpu_x86_set_cpl(env, 0);
5512 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_code), exit_code);
5513 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), exit_info_1);
5514
5515 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info),
5516 ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj)));
5517 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err),
5518 ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err)));
5519
5520 env->hflags2 &= ~HF2_GIF_MASK;
5521 /* FIXME: Resets the current ASID register to zero (host ASID). */
5522
5523 /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */
5524
5525 /* Clears the TSC_OFFSET inside the processor. */
5526
5527 /* If the host is in PAE mode, the processor reloads the host's PDPEs
5528 from the page table indicated the host's CR3. If the PDPEs contain
5529 illegal state, the processor causes a shutdown. */
5530
5531 /* Forces CR0.PE = 1, RFLAGS.VM = 0. */
5532 env->cr[0] |= CR0_PE_MASK;
5533 env->eflags &= ~VM_MASK;
5534
5535 /* Disables all breakpoints in the host DR7 register. */
5536
5537 /* Checks the reloaded host state for consistency. */
5538
5539 /* If the host's rIP reloaded by #VMEXIT is outside the limit of the
5540 host's code segment or non-canonical (in the case of long mode), a
5541 #GP fault is delivered inside the host.) */
5542
5543 /* remove any pending exception */
5544 env->exception_index = -1;
5545 env->error_code = 0;
5546 env->old_exception = -1;
5547
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005548 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005549}
5550
5551#endif
5552
5553/* MMX/SSE */
5554/* XXX: optimize by storing fptt and fptags in the static cpu state */
5555void helper_enter_mmx(void)
5556{
5557 env->fpstt = 0;
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +01005558 memset(env->fptags, 0, sizeof(env->fptags));
Jun Nakajima86797932011-01-29 14:24:24 -08005559}
5560
5561void helper_emms(void)
5562{
5563 /* set to empty state */
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +01005564 memset(env->fptags, 1, sizeof(env->fptags));
Jun Nakajima86797932011-01-29 14:24:24 -08005565}
5566
5567/* XXX: suppress */
5568void helper_movq(void *d, void *s)
5569{
5570 *(uint64_t *)d = *(uint64_t *)s;
5571}
5572
5573#define SHIFT 0
5574#include "ops_sse.h"
5575
5576#define SHIFT 1
5577#include "ops_sse.h"
5578
5579#define SHIFT 0
5580#include "helper_template.h"
5581#undef SHIFT
5582
5583#define SHIFT 1
5584#include "helper_template.h"
5585#undef SHIFT
5586
5587#define SHIFT 2
5588#include "helper_template.h"
5589#undef SHIFT
5590
5591#ifdef TARGET_X86_64
5592
5593#define SHIFT 3
5594#include "helper_template.h"
5595#undef SHIFT
5596
5597#endif
5598
5599/* bit operations */
5600target_ulong helper_bsf(target_ulong t0)
5601{
5602 int count;
5603 target_ulong res;
5604
5605 res = t0;
5606 count = 0;
5607 while ((res & 1) == 0) {
5608 count++;
5609 res >>= 1;
5610 }
5611 return count;
5612}
5613
5614target_ulong helper_bsr(target_ulong t0)
5615{
5616 int count;
5617 target_ulong res, mask;
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005618
Jun Nakajima86797932011-01-29 14:24:24 -08005619 res = t0;
5620 count = TARGET_LONG_BITS - 1;
5621 mask = (target_ulong)1 << (TARGET_LONG_BITS - 1);
5622 while ((res & mask) == 0) {
5623 count--;
5624 res <<= 1;
5625 }
5626 return count;
5627}
5628
5629
5630static int compute_all_eflags(void)
5631{
5632 return CC_SRC;
5633}
5634
5635static int compute_c_eflags(void)
5636{
5637 return CC_SRC & CC_C;
5638}
5639
5640uint32_t helper_cc_compute_all(int op)
5641{
5642 switch (op) {
5643 default: /* should never happen */ return 0;
5644
5645 case CC_OP_EFLAGS: return compute_all_eflags();
5646
5647 case CC_OP_MULB: return compute_all_mulb();
5648 case CC_OP_MULW: return compute_all_mulw();
5649 case CC_OP_MULL: return compute_all_mull();
5650
5651 case CC_OP_ADDB: return compute_all_addb();
5652 case CC_OP_ADDW: return compute_all_addw();
5653 case CC_OP_ADDL: return compute_all_addl();
5654
5655 case CC_OP_ADCB: return compute_all_adcb();
5656 case CC_OP_ADCW: return compute_all_adcw();
5657 case CC_OP_ADCL: return compute_all_adcl();
5658
5659 case CC_OP_SUBB: return compute_all_subb();
5660 case CC_OP_SUBW: return compute_all_subw();
5661 case CC_OP_SUBL: return compute_all_subl();
5662
5663 case CC_OP_SBBB: return compute_all_sbbb();
5664 case CC_OP_SBBW: return compute_all_sbbw();
5665 case CC_OP_SBBL: return compute_all_sbbl();
5666
5667 case CC_OP_LOGICB: return compute_all_logicb();
5668 case CC_OP_LOGICW: return compute_all_logicw();
5669 case CC_OP_LOGICL: return compute_all_logicl();
5670
5671 case CC_OP_INCB: return compute_all_incb();
5672 case CC_OP_INCW: return compute_all_incw();
5673 case CC_OP_INCL: return compute_all_incl();
5674
5675 case CC_OP_DECB: return compute_all_decb();
5676 case CC_OP_DECW: return compute_all_decw();
5677 case CC_OP_DECL: return compute_all_decl();
5678
5679 case CC_OP_SHLB: return compute_all_shlb();
5680 case CC_OP_SHLW: return compute_all_shlw();
5681 case CC_OP_SHLL: return compute_all_shll();
5682
5683 case CC_OP_SARB: return compute_all_sarb();
5684 case CC_OP_SARW: return compute_all_sarw();
5685 case CC_OP_SARL: return compute_all_sarl();
5686
5687#ifdef TARGET_X86_64
5688 case CC_OP_MULQ: return compute_all_mulq();
5689
5690 case CC_OP_ADDQ: return compute_all_addq();
5691
5692 case CC_OP_ADCQ: return compute_all_adcq();
5693
5694 case CC_OP_SUBQ: return compute_all_subq();
5695
5696 case CC_OP_SBBQ: return compute_all_sbbq();
5697
5698 case CC_OP_LOGICQ: return compute_all_logicq();
5699
5700 case CC_OP_INCQ: return compute_all_incq();
5701
5702 case CC_OP_DECQ: return compute_all_decq();
5703
5704 case CC_OP_SHLQ: return compute_all_shlq();
5705
5706 case CC_OP_SARQ: return compute_all_sarq();
5707#endif
5708 }
5709}
5710
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01005711uint32_t cpu_cc_compute_all(CPUArchState *env1, int op)
5712{
5713 CPUArchState *saved_env;
5714 uint32_t ret;
5715
5716 saved_env = env;
5717 env = env1;
5718 ret = helper_cc_compute_all(op);
5719 env = saved_env;
5720 return ret;
5721}
5722
Jun Nakajima86797932011-01-29 14:24:24 -08005723uint32_t helper_cc_compute_c(int op)
5724{
5725 switch (op) {
5726 default: /* should never happen */ return 0;
5727
5728 case CC_OP_EFLAGS: return compute_c_eflags();
5729
5730 case CC_OP_MULB: return compute_c_mull();
5731 case CC_OP_MULW: return compute_c_mull();
5732 case CC_OP_MULL: return compute_c_mull();
5733
5734 case CC_OP_ADDB: return compute_c_addb();
5735 case CC_OP_ADDW: return compute_c_addw();
5736 case CC_OP_ADDL: return compute_c_addl();
5737
5738 case CC_OP_ADCB: return compute_c_adcb();
5739 case CC_OP_ADCW: return compute_c_adcw();
5740 case CC_OP_ADCL: return compute_c_adcl();
5741
5742 case CC_OP_SUBB: return compute_c_subb();
5743 case CC_OP_SUBW: return compute_c_subw();
5744 case CC_OP_SUBL: return compute_c_subl();
5745
5746 case CC_OP_SBBB: return compute_c_sbbb();
5747 case CC_OP_SBBW: return compute_c_sbbw();
5748 case CC_OP_SBBL: return compute_c_sbbl();
5749
5750 case CC_OP_LOGICB: return compute_c_logicb();
5751 case CC_OP_LOGICW: return compute_c_logicw();
5752 case CC_OP_LOGICL: return compute_c_logicl();
5753
5754 case CC_OP_INCB: return compute_c_incl();
5755 case CC_OP_INCW: return compute_c_incl();
5756 case CC_OP_INCL: return compute_c_incl();
5757
5758 case CC_OP_DECB: return compute_c_incl();
5759 case CC_OP_DECW: return compute_c_incl();
5760 case CC_OP_DECL: return compute_c_incl();
5761
5762 case CC_OP_SHLB: return compute_c_shlb();
5763 case CC_OP_SHLW: return compute_c_shlw();
5764 case CC_OP_SHLL: return compute_c_shll();
5765
5766 case CC_OP_SARB: return compute_c_sarl();
5767 case CC_OP_SARW: return compute_c_sarl();
5768 case CC_OP_SARL: return compute_c_sarl();
5769
5770#ifdef TARGET_X86_64
5771 case CC_OP_MULQ: return compute_c_mull();
5772
5773 case CC_OP_ADDQ: return compute_c_addq();
5774
5775 case CC_OP_ADCQ: return compute_c_adcq();
5776
5777 case CC_OP_SUBQ: return compute_c_subq();
5778
5779 case CC_OP_SBBQ: return compute_c_sbbq();
5780
5781 case CC_OP_LOGICQ: return compute_c_logicq();
5782
5783 case CC_OP_INCQ: return compute_c_incl();
5784
5785 case CC_OP_DECQ: return compute_c_incl();
5786
5787 case CC_OP_SHLQ: return compute_c_shlq();
5788
5789 case CC_OP_SARQ: return compute_c_sarl();
5790#endif
5791 }
5792}