blob: 4285cac8e7618d3e1e885dd4ac413b27a10de277 [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
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +020070static inline void fpush(CPUX86State *env)
David 'Digit' Turnera889d352014-03-20 12:35:32 +010071{
72 env->fpstt = (env->fpstt - 1) & 7;
73 env->fptags[env->fpstt] = 0; /* validate stack entry */
74}
75
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +020076static inline void fpop(CPUX86State *env)
David 'Digit' Turnera889d352014-03-20 12:35:32 +010077{
78 env->fptags[env->fpstt] = 1; /* invvalidate stack entry */
79 env->fpstt = (env->fpstt + 1) & 7;
80}
81
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +020082static inline floatx80 helper_fldt(CPUX86State *env, target_ulong ptr)
David 'Digit' Turnera889d352014-03-20 12:35:32 +010083{
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
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +020091static inline void helper_fstt(CPUX86State *env, floatx80 f, target_ulong ptr)
David 'Digit' Turnera889d352014-03-20 12:35:32 +010092{
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
David 'Digit' Turner90edd952014-04-03 17:40:22 +0200226void helper_write_eflags(CPUX86State *env,
227 target_ulong t0, uint32_t update_mask)
Jun Nakajima86797932011-01-29 14:24:24 -0800228{
229 load_eflags(t0, update_mask);
230}
231
David 'Digit' Turner90edd952014-04-03 17:40:22 +0200232target_ulong helper_read_eflags(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -0800233{
234 uint32_t eflags;
235 eflags = helper_cc_compute_all(CC_OP);
236 eflags |= (DF & DF_MASK);
237 eflags |= env->eflags & ~(VM_MASK | RF_MASK);
238 return eflags;
239}
240
241/* return non zero if error */
242static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr,
243 int selector)
244{
245 SegmentCache *dt;
246 int index;
247 target_ulong ptr;
248
249 if (selector & 0x4)
250 dt = &env->ldt;
251 else
252 dt = &env->gdt;
253 index = selector & ~7;
254 if ((index + 7) > dt->limit)
255 return -1;
256 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100257 *e1_ptr = cpu_ldl_kernel(env, ptr);
258 *e2_ptr = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800259 return 0;
260}
261
262static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
263{
264 unsigned int limit;
265 limit = (e1 & 0xffff) | (e2 & 0x000f0000);
266 if (e2 & DESC_G_MASK)
267 limit = (limit << 12) | 0xfff;
268 return limit;
269}
270
271static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
272{
273 return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
274}
275
276static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2)
277{
278 sc->base = get_seg_base(e1, e2);
279 sc->limit = get_seg_limit(e1, e2);
280 sc->flags = e2;
281}
282
283/* init the segment cache in vm86 mode. */
284static inline void load_seg_vm(int seg, int selector)
285{
286 selector &= 0xffff;
287 cpu_x86_load_seg_cache(env, seg, selector,
288 (selector << 4), 0xffff, 0);
289}
290
291static inline void get_ss_esp_from_tss(uint32_t *ss_ptr,
292 uint32_t *esp_ptr, int dpl)
293{
294 int type, index, shift;
295
296#if 0
297 {
298 int i;
299 printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
300 for(i=0;i<env->tr.limit;i++) {
301 printf("%02x ", env->tr.base[i]);
302 if ((i & 7) == 7) printf("\n");
303 }
304 printf("\n");
305 }
306#endif
307
308 if (!(env->tr.flags & DESC_P_MASK))
309 cpu_abort(env, "invalid tss");
310 type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
311 if ((type & 7) != 1)
312 cpu_abort(env, "invalid tss type");
313 shift = type >> 3;
314 index = (dpl * 4 + 2) << shift;
315 if (index + (4 << shift) - 1 > env->tr.limit)
316 raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
317 if (shift == 0) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100318 *esp_ptr = cpu_lduw_kernel(env, env->tr.base + index);
319 *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 2);
Jun Nakajima86797932011-01-29 14:24:24 -0800320 } else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100321 *esp_ptr = cpu_ldl_kernel(env, env->tr.base + index);
322 *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800323 }
324}
325
326/* XXX: merge with load_seg() */
327static void tss_load_seg(int seg_reg, int selector)
328{
329 uint32_t e1, e2;
330 int rpl, dpl, cpl;
331
332 if ((selector & 0xfffc) != 0) {
333 if (load_segment(&e1, &e2, selector) != 0)
334 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
335 if (!(e2 & DESC_S_MASK))
336 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
337 rpl = selector & 3;
338 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
339 cpl = env->hflags & HF_CPL_MASK;
340 if (seg_reg == R_CS) {
341 if (!(e2 & DESC_CS_MASK))
342 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
343 /* XXX: is it correct ? */
344 if (dpl != rpl)
345 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
346 if ((e2 & DESC_C_MASK) && dpl > rpl)
347 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
348 } else if (seg_reg == R_SS) {
349 /* SS must be writable data */
350 if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
351 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
352 if (dpl != cpl || dpl != rpl)
353 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
354 } else {
355 /* not readable code */
356 if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK))
357 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
358 /* if data or non conforming code, checks the rights */
359 if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) {
360 if (dpl < cpl || dpl < rpl)
361 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
362 }
363 }
364 if (!(e2 & DESC_P_MASK))
365 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
366 cpu_x86_load_seg_cache(env, seg_reg, selector,
367 get_seg_base(e1, e2),
368 get_seg_limit(e1, e2),
369 e2);
370 } else {
371 if (seg_reg == R_SS || seg_reg == R_CS)
372 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
373 }
374}
375
376#define SWITCH_TSS_JMP 0
377#define SWITCH_TSS_IRET 1
378#define SWITCH_TSS_CALL 2
379
380/* XXX: restore CPU state in registers (PowerPC case) */
381static void switch_tss(int tss_selector,
382 uint32_t e1, uint32_t e2, int source,
383 uint32_t next_eip)
384{
385 int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
386 target_ulong tss_base;
387 uint32_t new_regs[8], new_segs[6];
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +0100388 uint32_t new_eflags, new_eip, new_cr3, new_ldt;
Jun Nakajima86797932011-01-29 14:24:24 -0800389 uint32_t old_eflags, eflags_mask;
390 SegmentCache *dt;
391 int index;
392 target_ulong ptr;
393
394 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
395 LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source);
396
397 /* if task gate, we read the TSS segment and we load it */
398 if (type == 5) {
399 if (!(e2 & DESC_P_MASK))
400 raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
401 tss_selector = e1 >> 16;
402 if (tss_selector & 4)
403 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
404 if (load_segment(&e1, &e2, tss_selector) != 0)
405 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
406 if (e2 & DESC_S_MASK)
407 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
408 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
409 if ((type & 7) != 1)
410 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
411 }
412
413 if (!(e2 & DESC_P_MASK))
414 raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
415
416 if (type & 8)
417 tss_limit_max = 103;
418 else
419 tss_limit_max = 43;
420 tss_limit = get_seg_limit(e1, e2);
421 tss_base = get_seg_base(e1, e2);
422 if ((tss_selector & 4) != 0 ||
423 tss_limit < tss_limit_max)
424 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
425 old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
426 if (old_type & 8)
427 old_tss_limit_max = 103;
428 else
429 old_tss_limit_max = 43;
430
431 /* read all the registers from the new TSS */
432 if (type & 8) {
433 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100434 new_cr3 = cpu_ldl_kernel(env, tss_base + 0x1c);
435 new_eip = cpu_ldl_kernel(env, tss_base + 0x20);
436 new_eflags = cpu_ldl_kernel(env, tss_base + 0x24);
Jun Nakajima86797932011-01-29 14:24:24 -0800437 for(i = 0; i < 8; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100438 new_regs[i] = cpu_ldl_kernel(env, tss_base + (0x28 + i * 4));
Jun Nakajima86797932011-01-29 14:24:24 -0800439 for(i = 0; i < 6; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100440 new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x48 + i * 4));
441 new_ldt = cpu_lduw_kernel(env, tss_base + 0x60);
442 cpu_ldl_kernel(env, tss_base + 0x64);
Jun Nakajima86797932011-01-29 14:24:24 -0800443 } else {
444 /* 16 bit */
445 new_cr3 = 0;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100446 new_eip = cpu_lduw_kernel(env, tss_base + 0x0e);
447 new_eflags = cpu_lduw_kernel(env, tss_base + 0x10);
Jun Nakajima86797932011-01-29 14:24:24 -0800448 for(i = 0; i < 8; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100449 new_regs[i] = cpu_lduw_kernel(env, tss_base + (0x12 + i * 2)) | 0xffff0000;
Jun Nakajima86797932011-01-29 14:24:24 -0800450 for(i = 0; i < 4; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100451 new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x22 + i * 4));
452 new_ldt = cpu_lduw_kernel(env, tss_base + 0x2a);
Jun Nakajima86797932011-01-29 14:24:24 -0800453 new_segs[R_FS] = 0;
454 new_segs[R_GS] = 0;
Jun Nakajima86797932011-01-29 14:24:24 -0800455 }
456
457 /* NOTE: we must avoid memory exceptions during the task switch,
458 so we make dummy accesses before */
459 /* XXX: it can still fail in some cases, so a bigger hack is
460 necessary to valid the TLB after having done the accesses */
461
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100462 v1 = cpu_ldub_kernel(env, env->tr.base);
463 v2 = cpu_ldub_kernel(env, env->tr.base + old_tss_limit_max);
464 cpu_stb_kernel(env, env->tr.base, v1);
465 cpu_stb_kernel(env, env->tr.base + old_tss_limit_max, v2);
Jun Nakajima86797932011-01-29 14:24:24 -0800466
467 /* clear busy bit (it is restartable) */
468 if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
469 target_ulong ptr;
470 uint32_t e2;
471 ptr = env->gdt.base + (env->tr.selector & ~7);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100472 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800473 e2 &= ~DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100474 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -0800475 }
476 old_eflags = compute_eflags();
477 if (source == SWITCH_TSS_IRET)
478 old_eflags &= ~NT_MASK;
479
480 /* save the current state in the old TSS */
481 if (type & 8) {
482 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100483 cpu_stl_kernel(env, env->tr.base + 0x20, next_eip);
484 cpu_stl_kernel(env, env->tr.base + 0x24, old_eflags);
485 cpu_stl_kernel(env, env->tr.base + (0x28 + 0 * 4), EAX);
486 cpu_stl_kernel(env, env->tr.base + (0x28 + 1 * 4), ECX);
487 cpu_stl_kernel(env, env->tr.base + (0x28 + 2 * 4), EDX);
488 cpu_stl_kernel(env, env->tr.base + (0x28 + 3 * 4), EBX);
489 cpu_stl_kernel(env, env->tr.base + (0x28 + 4 * 4), ESP);
490 cpu_stl_kernel(env, env->tr.base + (0x28 + 5 * 4), EBP);
491 cpu_stl_kernel(env, env->tr.base + (0x28 + 6 * 4), ESI);
492 cpu_stl_kernel(env, env->tr.base + (0x28 + 7 * 4), EDI);
Jun Nakajima86797932011-01-29 14:24:24 -0800493 for(i = 0; i < 6; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100494 cpu_stw_kernel(env, env->tr.base + (0x48 + i * 4), env->segs[i].selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800495 } else {
496 /* 16 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100497 cpu_stw_kernel(env, env->tr.base + 0x0e, next_eip);
498 cpu_stw_kernel(env, env->tr.base + 0x10, old_eflags);
499 cpu_stw_kernel(env, env->tr.base + (0x12 + 0 * 2), EAX);
500 cpu_stw_kernel(env, env->tr.base + (0x12 + 1 * 2), ECX);
501 cpu_stw_kernel(env, env->tr.base + (0x12 + 2 * 2), EDX);
502 cpu_stw_kernel(env, env->tr.base + (0x12 + 3 * 2), EBX);
503 cpu_stw_kernel(env, env->tr.base + (0x12 + 4 * 2), ESP);
504 cpu_stw_kernel(env, env->tr.base + (0x12 + 5 * 2), EBP);
505 cpu_stw_kernel(env, env->tr.base + (0x12 + 6 * 2), ESI);
506 cpu_stw_kernel(env, env->tr.base + (0x12 + 7 * 2), EDI);
Jun Nakajima86797932011-01-29 14:24:24 -0800507 for(i = 0; i < 4; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100508 cpu_stw_kernel(env, env->tr.base + (0x22 + i * 4), env->segs[i].selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800509 }
510
511 /* now if an exception occurs, it will occurs in the next task
512 context */
513
514 if (source == SWITCH_TSS_CALL) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100515 cpu_stw_kernel(env, tss_base, env->tr.selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800516 new_eflags |= NT_MASK;
517 }
518
519 /* set busy bit */
520 if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
521 target_ulong ptr;
522 uint32_t e2;
523 ptr = env->gdt.base + (tss_selector & ~7);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100524 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800525 e2 |= DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100526 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -0800527 }
528
529 /* set the new CPU state */
530 /* from this point, any exception which occurs can give problems */
531 env->cr[0] |= CR0_TS_MASK;
532 env->hflags |= HF_TS_MASK;
533 env->tr.selector = tss_selector;
534 env->tr.base = tss_base;
535 env->tr.limit = tss_limit;
536 env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
537
538 if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
539 cpu_x86_update_cr3(env, new_cr3);
540 }
541
542 /* load all registers without an exception, then reload them with
543 possible exception */
544 env->eip = new_eip;
545 eflags_mask = TF_MASK | AC_MASK | ID_MASK |
546 IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
547 if (!(type & 8))
548 eflags_mask &= 0xffff;
549 load_eflags(new_eflags, eflags_mask);
550 /* XXX: what to do in 16 bit case ? */
551 EAX = new_regs[0];
552 ECX = new_regs[1];
553 EDX = new_regs[2];
554 EBX = new_regs[3];
555 ESP = new_regs[4];
556 EBP = new_regs[5];
557 ESI = new_regs[6];
558 EDI = new_regs[7];
559 if (new_eflags & VM_MASK) {
560 for(i = 0; i < 6; i++)
561 load_seg_vm(i, new_segs[i]);
562 /* in vm86, CPL is always 3 */
563 cpu_x86_set_cpl(env, 3);
564 } else {
565 /* CPL is set the RPL of CS */
566 cpu_x86_set_cpl(env, new_segs[R_CS] & 3);
567 /* first just selectors as the rest may trigger exceptions */
568 for(i = 0; i < 6; i++)
569 cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
570 }
571
572 env->ldt.selector = new_ldt & ~4;
573 env->ldt.base = 0;
574 env->ldt.limit = 0;
575 env->ldt.flags = 0;
576
577 /* load the LDT */
578 if (new_ldt & 4)
579 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
580
581 if ((new_ldt & 0xfffc) != 0) {
582 dt = &env->gdt;
583 index = new_ldt & ~7;
584 if ((index + 7) > dt->limit)
585 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
586 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100587 e1 = cpu_ldl_kernel(env, ptr);
588 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800589 if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
590 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
591 if (!(e2 & DESC_P_MASK))
592 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
593 load_seg_cache_raw_dt(&env->ldt, e1, e2);
594 }
595
596 /* load the segments */
597 if (!(new_eflags & VM_MASK)) {
598 tss_load_seg(R_CS, new_segs[R_CS]);
599 tss_load_seg(R_SS, new_segs[R_SS]);
600 tss_load_seg(R_ES, new_segs[R_ES]);
601 tss_load_seg(R_DS, new_segs[R_DS]);
602 tss_load_seg(R_FS, new_segs[R_FS]);
603 tss_load_seg(R_GS, new_segs[R_GS]);
604 }
605
606 /* check that EIP is in the CS segment limits */
607 if (new_eip > env->segs[R_CS].limit) {
608 /* XXX: different exception if CALL ? */
609 raise_exception_err(EXCP0D_GPF, 0);
610 }
611
612#ifndef CONFIG_USER_ONLY
613 /* reset local breakpoints */
614 if (env->dr[7] & 0x55) {
615 for (i = 0; i < 4; i++) {
616 if (hw_breakpoint_enabled(env->dr[7], i) == 0x1)
617 hw_breakpoint_remove(env, i);
618 }
619 env->dr[7] &= ~0x55;
620 }
621#endif
622}
623
624/* check if Port I/O is allowed in TSS */
625static inline void check_io(int addr, int size)
626{
627 int io_offset, val, mask;
628
629 /* TSS must be a valid 32 bit one */
630 if (!(env->tr.flags & DESC_P_MASK) ||
631 ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
632 env->tr.limit < 103)
633 goto fail;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100634 io_offset = cpu_lduw_kernel(env, env->tr.base + 0x66);
Jun Nakajima86797932011-01-29 14:24:24 -0800635 io_offset += (addr >> 3);
636 /* Note: the check needs two bytes */
637 if ((io_offset + 1) > env->tr.limit)
638 goto fail;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100639 val = cpu_lduw_kernel(env, env->tr.base + io_offset);
Jun Nakajima86797932011-01-29 14:24:24 -0800640 val >>= (addr & 7);
641 mask = (1 << size) - 1;
642 /* all bits must be zero to allow the I/O */
643 if ((val & mask) != 0) {
644 fail:
645 raise_exception_err(EXCP0D_GPF, 0);
646 }
647}
648
649void helper_check_iob(uint32_t t0)
650{
651 check_io(t0, 1);
652}
653
654void helper_check_iow(uint32_t t0)
655{
656 check_io(t0, 2);
657}
658
659void helper_check_iol(uint32_t t0)
660{
661 check_io(t0, 4);
662}
663
664void helper_outb(uint32_t port, uint32_t data)
665{
666 cpu_outb(port, data & 0xff);
667}
668
669target_ulong helper_inb(uint32_t port)
670{
671 return cpu_inb(port);
672}
673
674void helper_outw(uint32_t port, uint32_t data)
675{
676 cpu_outw(port, data & 0xffff);
677}
678
679target_ulong helper_inw(uint32_t port)
680{
681 return cpu_inw(port);
682}
683
684void helper_outl(uint32_t port, uint32_t data)
685{
686 cpu_outl(port, data);
687}
688
689target_ulong helper_inl(uint32_t port)
690{
691 return cpu_inl(port);
692}
693
694static inline unsigned int get_sp_mask(unsigned int e2)
695{
696 if (e2 & DESC_B_MASK)
697 return 0xffffffff;
698 else
699 return 0xffff;
700}
701
702static int exeption_has_error_code(int intno)
703{
704 switch(intno) {
705 case 8:
706 case 10:
707 case 11:
708 case 12:
709 case 13:
710 case 14:
711 case 17:
712 return 1;
713 }
714 return 0;
715}
716
717#ifdef TARGET_X86_64
718#define SET_ESP(val, sp_mask)\
719do {\
720 if ((sp_mask) == 0xffff)\
721 ESP = (ESP & ~0xffff) | ((val) & 0xffff);\
722 else if ((sp_mask) == 0xffffffffLL)\
723 ESP = (uint32_t)(val);\
724 else\
725 ESP = (val);\
726} while (0)
727#else
728#define SET_ESP(val, sp_mask) ESP = (ESP & ~(sp_mask)) | ((val) & (sp_mask))
729#endif
730
731/* in 64-bit machines, this can overflow. So this segment addition macro
732 * can be used to trim the value to 32-bit whenever needed */
733#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask))))
734
735/* XXX: add a is_user flag to have proper security support */
736#define PUSHW(ssp, sp, sp_mask, val)\
737{\
738 sp -= 2;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100739 cpu_stw_kernel(env, (ssp) + (sp & (sp_mask)), (val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800740}
741
742#define PUSHL(ssp, sp, sp_mask, val)\
743{\
744 sp -= 4;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100745 cpu_stl_kernel(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800746}
747
748#define POPW(ssp, sp, sp_mask, val)\
749{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100750 val = cpu_lduw_kernel(env, (ssp) + (sp & (sp_mask)));\
Jun Nakajima86797932011-01-29 14:24:24 -0800751 sp += 2;\
752}
753
754#define POPL(ssp, sp, sp_mask, val)\
755{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100756 val = (uint32_t)cpu_ldl_kernel(env, SEG_ADDL(ssp, sp, sp_mask));\
Jun Nakajima86797932011-01-29 14:24:24 -0800757 sp += 4;\
758}
759
760/* protected mode interrupt */
761static void do_interrupt_protected(int intno, int is_int, int error_code,
762 unsigned int next_eip, int is_hw)
763{
764 SegmentCache *dt;
765 target_ulong ptr, ssp;
766 int type, dpl, selector, ss_dpl, cpl;
767 int has_error_code, new_stack, shift;
768 uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
769 uint32_t old_eip, sp_mask;
770
771 has_error_code = 0;
772 if (!is_int && !is_hw)
773 has_error_code = exeption_has_error_code(intno);
774 if (is_int)
775 old_eip = next_eip;
776 else
777 old_eip = env->eip;
778
779 dt = &env->idt;
780 if (intno * 8 + 7 > dt->limit)
781 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
782 ptr = dt->base + intno * 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100783 e1 = cpu_ldl_kernel(env, ptr);
784 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800785 /* check gate type */
786 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
787 switch(type) {
788 case 5: /* task gate */
789 /* must do that check here to return the correct error code */
790 if (!(e2 & DESC_P_MASK))
791 raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
792 switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
793 if (has_error_code) {
794 int type;
795 uint32_t mask;
796 /* push the error code */
797 type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
798 shift = type >> 3;
799 if (env->segs[R_SS].flags & DESC_B_MASK)
800 mask = 0xffffffff;
801 else
802 mask = 0xffff;
803 esp = (ESP - (2 << shift)) & mask;
804 ssp = env->segs[R_SS].base + esp;
805 if (shift)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100806 cpu_stl_kernel(env, ssp, error_code);
Jun Nakajima86797932011-01-29 14:24:24 -0800807 else
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100808 cpu_stw_kernel(env, ssp, error_code);
Jun Nakajima86797932011-01-29 14:24:24 -0800809 SET_ESP(esp, mask);
810 }
811 return;
812 case 6: /* 286 interrupt gate */
813 case 7: /* 286 trap gate */
814 case 14: /* 386 interrupt gate */
815 case 15: /* 386 trap gate */
816 break;
817 default:
818 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
819 break;
820 }
821 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
822 cpl = env->hflags & HF_CPL_MASK;
823 /* check privilege if software int */
824 if (is_int && dpl < cpl)
825 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
826 /* check valid bit */
827 if (!(e2 & DESC_P_MASK))
828 raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
829 selector = e1 >> 16;
830 offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
831 if ((selector & 0xfffc) == 0)
832 raise_exception_err(EXCP0D_GPF, 0);
833
834 if (load_segment(&e1, &e2, selector) != 0)
835 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
836 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
837 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
838 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
839 if (dpl > cpl)
840 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
841 if (!(e2 & DESC_P_MASK))
842 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
843 if (!(e2 & DESC_C_MASK) && dpl < cpl) {
844 /* to inner privilege */
845 get_ss_esp_from_tss(&ss, &esp, dpl);
846 if ((ss & 0xfffc) == 0)
847 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
848 if ((ss & 3) != dpl)
849 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
850 if (load_segment(&ss_e1, &ss_e2, ss) != 0)
851 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
852 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
853 if (ss_dpl != dpl)
854 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
855 if (!(ss_e2 & DESC_S_MASK) ||
856 (ss_e2 & DESC_CS_MASK) ||
857 !(ss_e2 & DESC_W_MASK))
858 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
859 if (!(ss_e2 & DESC_P_MASK))
860 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
861 new_stack = 1;
862 sp_mask = get_sp_mask(ss_e2);
863 ssp = get_seg_base(ss_e1, ss_e2);
864 } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
865 /* to same privilege */
866 if (env->eflags & VM_MASK)
867 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
868 new_stack = 0;
869 sp_mask = get_sp_mask(env->segs[R_SS].flags);
870 ssp = env->segs[R_SS].base;
871 esp = ESP;
872 dpl = cpl;
873 } else {
874 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
875 new_stack = 0; /* avoid warning */
876 sp_mask = 0; /* avoid warning */
877 ssp = 0; /* avoid warning */
878 esp = 0; /* avoid warning */
879 }
880
881 shift = type >> 3;
882
883#if 0
884 /* XXX: check that enough room is available */
885 push_size = 6 + (new_stack << 2) + (has_error_code << 1);
886 if (env->eflags & VM_MASK)
887 push_size += 8;
888 push_size <<= shift;
889#endif
890 if (shift == 1) {
891 if (new_stack) {
892 if (env->eflags & VM_MASK) {
893 PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
894 PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
895 PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
896 PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
897 }
898 PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
899 PUSHL(ssp, esp, sp_mask, ESP);
900 }
901 PUSHL(ssp, esp, sp_mask, compute_eflags());
902 PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
903 PUSHL(ssp, esp, sp_mask, old_eip);
904 if (has_error_code) {
905 PUSHL(ssp, esp, sp_mask, error_code);
906 }
907 } else {
908 if (new_stack) {
909 if (env->eflags & VM_MASK) {
910 PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
911 PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
912 PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
913 PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector);
914 }
915 PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
916 PUSHW(ssp, esp, sp_mask, ESP);
917 }
918 PUSHW(ssp, esp, sp_mask, compute_eflags());
919 PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
920 PUSHW(ssp, esp, sp_mask, old_eip);
921 if (has_error_code) {
922 PUSHW(ssp, esp, sp_mask, error_code);
923 }
924 }
925
926 if (new_stack) {
927 if (env->eflags & VM_MASK) {
928 cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
929 cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
930 cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
931 cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
932 }
933 ss = (ss & ~3) | dpl;
934 cpu_x86_load_seg_cache(env, R_SS, ss,
935 ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
936 }
937 SET_ESP(esp, sp_mask);
938
939 selector = (selector & ~3) | dpl;
940 cpu_x86_load_seg_cache(env, R_CS, selector,
941 get_seg_base(e1, e2),
942 get_seg_limit(e1, e2),
943 e2);
944 cpu_x86_set_cpl(env, dpl);
945 env->eip = offset;
946
947 /* interrupt gate clear IF mask */
948 if ((type & 1) == 0) {
949 env->eflags &= ~IF_MASK;
950 }
951 env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
952}
953
954#ifdef TARGET_X86_64
955
956#define PUSHQ(sp, val)\
957{\
958 sp -= 8;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100959 cpu_stq_kernel(env, sp, (val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800960}
961
962#define POPQ(sp, val)\
963{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100964 val = cpu_ldq_kernel(env, sp);\
Jun Nakajima86797932011-01-29 14:24:24 -0800965 sp += 8;\
966}
967
968static inline target_ulong get_rsp_from_tss(int level)
969{
970 int index;
971
972#if 0
973 printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
974 env->tr.base, env->tr.limit);
975#endif
976
977 if (!(env->tr.flags & DESC_P_MASK))
978 cpu_abort(env, "invalid tss");
979 index = 8 * level + 4;
980 if ((index + 7) > env->tr.limit)
981 raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100982 return cpu_ldq_kernel(env, env->tr.base + index);
Jun Nakajima86797932011-01-29 14:24:24 -0800983}
984
985/* 64 bit interrupt */
986static void do_interrupt64(int intno, int is_int, int error_code,
987 target_ulong next_eip, int is_hw)
988{
989 SegmentCache *dt;
990 target_ulong ptr;
991 int type, dpl, selector, cpl, ist;
992 int has_error_code, new_stack;
993 uint32_t e1, e2, e3, ss;
994 target_ulong old_eip, esp, offset;
995
996 has_error_code = 0;
997 if (!is_int && !is_hw)
998 has_error_code = exeption_has_error_code(intno);
999 if (is_int)
1000 old_eip = next_eip;
1001 else
1002 old_eip = env->eip;
1003
1004 dt = &env->idt;
1005 if (intno * 16 + 15 > dt->limit)
1006 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
1007 ptr = dt->base + intno * 16;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001008 e1 = cpu_ldl_kernel(env, ptr);
1009 e2 = cpu_ldl_kernel(env, ptr + 4);
1010 e3 = cpu_ldl_kernel(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08001011 /* check gate type */
1012 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
1013 switch(type) {
1014 case 14: /* 386 interrupt gate */
1015 case 15: /* 386 trap gate */
1016 break;
1017 default:
1018 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
1019 break;
1020 }
1021 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1022 cpl = env->hflags & HF_CPL_MASK;
1023 /* check privilege if software int */
1024 if (is_int && dpl < cpl)
1025 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
1026 /* check valid bit */
1027 if (!(e2 & DESC_P_MASK))
1028 raise_exception_err(EXCP0B_NOSEG, intno * 16 + 2);
1029 selector = e1 >> 16;
1030 offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
1031 ist = e2 & 7;
1032 if ((selector & 0xfffc) == 0)
1033 raise_exception_err(EXCP0D_GPF, 0);
1034
1035 if (load_segment(&e1, &e2, selector) != 0)
1036 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1037 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
1038 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1039 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1040 if (dpl > cpl)
1041 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1042 if (!(e2 & DESC_P_MASK))
1043 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
1044 if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK))
1045 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1046 if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {
1047 /* to inner privilege */
1048 if (ist != 0)
1049 esp = get_rsp_from_tss(ist + 3);
1050 else
1051 esp = get_rsp_from_tss(dpl);
1052 esp &= ~0xfLL; /* align stack */
1053 ss = 0;
1054 new_stack = 1;
1055 } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
1056 /* to same privilege */
1057 if (env->eflags & VM_MASK)
1058 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1059 new_stack = 0;
1060 if (ist != 0)
1061 esp = get_rsp_from_tss(ist + 3);
1062 else
1063 esp = ESP;
1064 esp &= ~0xfLL; /* align stack */
1065 dpl = cpl;
1066 } else {
1067 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
1068 new_stack = 0; /* avoid warning */
1069 esp = 0; /* avoid warning */
1070 }
1071
1072 PUSHQ(esp, env->segs[R_SS].selector);
1073 PUSHQ(esp, ESP);
1074 PUSHQ(esp, compute_eflags());
1075 PUSHQ(esp, env->segs[R_CS].selector);
1076 PUSHQ(esp, old_eip);
1077 if (has_error_code) {
1078 PUSHQ(esp, error_code);
1079 }
1080
1081 if (new_stack) {
1082 ss = 0 | dpl;
1083 cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
1084 }
1085 ESP = esp;
1086
1087 selector = (selector & ~3) | dpl;
1088 cpu_x86_load_seg_cache(env, R_CS, selector,
1089 get_seg_base(e1, e2),
1090 get_seg_limit(e1, e2),
1091 e2);
1092 cpu_x86_set_cpl(env, dpl);
1093 env->eip = offset;
1094
1095 /* interrupt gate clear IF mask */
1096 if ((type & 1) == 0) {
1097 env->eflags &= ~IF_MASK;
1098 }
1099 env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
1100}
1101#endif
1102
1103#ifdef TARGET_X86_64
1104#if defined(CONFIG_USER_ONLY)
1105void helper_syscall(int next_eip_addend)
1106{
1107 env->exception_index = EXCP_SYSCALL;
1108 env->exception_next_eip = env->eip + next_eip_addend;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01001109 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08001110}
1111#else
1112void helper_syscall(int next_eip_addend)
1113{
1114 int selector;
1115
1116 if (!(env->efer & MSR_EFER_SCE)) {
1117 raise_exception_err(EXCP06_ILLOP, 0);
1118 }
1119 selector = (env->star >> 32) & 0xffff;
1120 if (env->hflags & HF_LMA_MASK) {
1121 int code64;
1122
1123 ECX = env->eip + next_eip_addend;
1124 env->regs[11] = compute_eflags();
1125
1126 code64 = env->hflags & HF_CS64_MASK;
1127
1128 cpu_x86_set_cpl(env, 0);
1129 cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1130 0, 0xffffffff,
1131 DESC_G_MASK | DESC_P_MASK |
1132 DESC_S_MASK |
1133 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
1134 cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1135 0, 0xffffffff,
1136 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1137 DESC_S_MASK |
1138 DESC_W_MASK | DESC_A_MASK);
1139 env->eflags &= ~env->fmask;
1140 load_eflags(env->eflags, 0);
1141 if (code64)
1142 env->eip = env->lstar;
1143 else
1144 env->eip = env->cstar;
1145 } else {
1146 ECX = (uint32_t)(env->eip + next_eip_addend);
1147
1148 cpu_x86_set_cpl(env, 0);
1149 cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1150 0, 0xffffffff,
1151 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1152 DESC_S_MASK |
1153 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1154 cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1155 0, 0xffffffff,
1156 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1157 DESC_S_MASK |
1158 DESC_W_MASK | DESC_A_MASK);
1159 env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
1160 env->eip = (uint32_t)env->star;
1161 }
1162}
1163#endif
1164#endif
1165
1166#ifdef TARGET_X86_64
1167void helper_sysret(int dflag)
1168{
1169 int cpl, selector;
1170
1171 if (!(env->efer & MSR_EFER_SCE)) {
1172 raise_exception_err(EXCP06_ILLOP, 0);
1173 }
1174 cpl = env->hflags & HF_CPL_MASK;
1175 if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
1176 raise_exception_err(EXCP0D_GPF, 0);
1177 }
1178 selector = (env->star >> 48) & 0xffff;
1179 if (env->hflags & HF_LMA_MASK) {
1180 if (dflag == 2) {
1181 cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
1182 0, 0xffffffff,
1183 DESC_G_MASK | DESC_P_MASK |
1184 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1185 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
1186 DESC_L_MASK);
1187 env->eip = ECX;
1188 } else {
1189 cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1190 0, 0xffffffff,
1191 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1192 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1193 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1194 env->eip = (uint32_t)ECX;
1195 }
1196 cpu_x86_load_seg_cache(env, R_SS, selector + 8,
1197 0, 0xffffffff,
1198 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1199 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1200 DESC_W_MASK | DESC_A_MASK);
1201 load_eflags((uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK |
1202 IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK);
1203 cpu_x86_set_cpl(env, 3);
1204 } else {
1205 cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1206 0, 0xffffffff,
1207 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1208 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1209 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1210 env->eip = (uint32_t)ECX;
1211 cpu_x86_load_seg_cache(env, R_SS, selector + 8,
1212 0, 0xffffffff,
1213 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1214 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1215 DESC_W_MASK | DESC_A_MASK);
1216 env->eflags |= IF_MASK;
1217 cpu_x86_set_cpl(env, 3);
1218 }
Jun Nakajima86797932011-01-29 14:24:24 -08001219}
1220#endif
1221
1222/* real mode interrupt */
1223static void do_interrupt_real(int intno, int is_int, int error_code,
1224 unsigned int next_eip)
1225{
1226 SegmentCache *dt;
1227 target_ulong ptr, ssp;
1228 int selector;
1229 uint32_t offset, esp;
1230 uint32_t old_cs, old_eip;
1231
1232 /* real mode (simpler !) */
1233 dt = &env->idt;
1234 if (intno * 4 + 3 > dt->limit)
1235 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
1236 ptr = dt->base + intno * 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001237 offset = cpu_lduw_kernel(env, ptr);
1238 selector = cpu_lduw_kernel(env, ptr + 2);
Jun Nakajima86797932011-01-29 14:24:24 -08001239 esp = ESP;
1240 ssp = env->segs[R_SS].base;
1241 if (is_int)
1242 old_eip = next_eip;
1243 else
1244 old_eip = env->eip;
1245 old_cs = env->segs[R_CS].selector;
1246 /* XXX: use SS segment size ? */
1247 PUSHW(ssp, esp, 0xffff, compute_eflags());
1248 PUSHW(ssp, esp, 0xffff, old_cs);
1249 PUSHW(ssp, esp, 0xffff, old_eip);
1250
1251 /* update processor state */
1252 ESP = (ESP & ~0xffff) | (esp & 0xffff);
1253 env->eip = offset;
1254 env->segs[R_CS].selector = selector;
1255 env->segs[R_CS].base = (selector << 4);
1256 env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
1257}
1258
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001259#if defined(CONFIG_USER_ONLY)
Jun Nakajima86797932011-01-29 14:24:24 -08001260/* fake user mode interrupt */
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001261static void do_interrupt_user(int intno, int is_int, int error_code,
1262 target_ulong next_eip)
Jun Nakajima86797932011-01-29 14:24:24 -08001263{
1264 SegmentCache *dt;
1265 target_ulong ptr;
1266 int dpl, cpl, shift;
1267 uint32_t e2;
1268
1269 dt = &env->idt;
1270 if (env->hflags & HF_LMA_MASK) {
1271 shift = 4;
1272 } else {
1273 shift = 3;
1274 }
1275 ptr = dt->base + (intno << shift);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001276 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08001277
1278 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1279 cpl = env->hflags & HF_CPL_MASK;
1280 /* check privilege if software int */
1281 if (is_int && dpl < cpl)
1282 raise_exception_err(EXCP0D_GPF, (intno << shift) + 2);
1283
1284 /* Since we emulate only user space, we cannot do more than
1285 exiting the emulation with the suitable exception and error
1286 code */
1287 if (is_int)
1288 EIP = next_eip;
1289}
1290
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001291#else
1292
Jun Nakajima86797932011-01-29 14:24:24 -08001293static void handle_even_inj(int intno, int is_int, int error_code,
1294 int is_hw, int rm)
1295{
1296 uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
1297 if (!(event_inj & SVM_EVTINJ_VALID)) {
1298 int type;
1299 if (is_int)
1300 type = SVM_EVTINJ_TYPE_SOFT;
1301 else
1302 type = SVM_EVTINJ_TYPE_EXEPT;
1303 event_inj = intno | type | SVM_EVTINJ_VALID;
1304 if (!rm && exeption_has_error_code(intno)) {
1305 event_inj |= SVM_EVTINJ_VALID_ERR;
1306 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err), error_code);
1307 }
1308 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj);
1309 }
1310}
1311#endif
1312
1313/*
1314 * Begin execution of an interruption. is_int is TRUE if coming from
1315 * the int instruction. next_eip is the EIP value AFTER the interrupt
1316 * instruction. It is only relevant if is_int is TRUE.
1317 */
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001318static void do_interrupt_all(int intno, int is_int, int error_code,
1319 target_ulong next_eip, int is_hw)
Jun Nakajima86797932011-01-29 14:24:24 -08001320{
1321 if (qemu_loglevel_mask(CPU_LOG_INT)) {
1322 if ((env->cr[0] & CR0_PE_MASK)) {
1323 static int count;
1324 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,
1325 count, intno, error_code, is_int,
1326 env->hflags & HF_CPL_MASK,
1327 env->segs[R_CS].selector, EIP,
1328 (int)env->segs[R_CS].base + EIP,
1329 env->segs[R_SS].selector, ESP);
1330 if (intno == 0x0e) {
1331 qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]);
1332 } else {
1333 qemu_log(" EAX=" TARGET_FMT_lx, EAX);
1334 }
1335 qemu_log("\n");
1336 log_cpu_state(env, X86_DUMP_CCOP);
1337#if 0
1338 {
1339 int i;
1340 uint8_t *ptr;
1341 qemu_log(" code=");
1342 ptr = env->segs[R_CS].base + env->eip;
1343 for(i = 0; i < 16; i++) {
1344 qemu_log(" %02x", ldub(ptr + i));
1345 }
1346 qemu_log("\n");
1347 }
1348#endif
1349 count++;
1350 }
1351 }
1352 if (env->cr[0] & CR0_PE_MASK) {
1353#if !defined(CONFIG_USER_ONLY)
1354 if (env->hflags & HF_SVMI_MASK)
1355 handle_even_inj(intno, is_int, error_code, is_hw, 0);
1356#endif
1357#ifdef TARGET_X86_64
1358 if (env->hflags & HF_LMA_MASK) {
1359 do_interrupt64(intno, is_int, error_code, next_eip, is_hw);
1360 } else
1361#endif
1362 {
1363 do_interrupt_protected(intno, is_int, error_code, next_eip, is_hw);
1364 }
1365 } else {
1366#if !defined(CONFIG_USER_ONLY)
1367 if (env->hflags & HF_SVMI_MASK)
1368 handle_even_inj(intno, is_int, error_code, is_hw, 1);
1369#endif
1370 do_interrupt_real(intno, is_int, error_code, next_eip);
1371 }
1372
1373#if !defined(CONFIG_USER_ONLY)
1374 if (env->hflags & HF_SVMI_MASK) {
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001375 uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
1376 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj & ~SVM_EVTINJ_VALID);
Jun Nakajima86797932011-01-29 14:24:24 -08001377 }
1378#endif
1379}
1380
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001381void do_interrupt(CPUArchState *env1)
1382{
1383 CPUArchState *saved_env;
1384
1385 saved_env = env;
1386 env = env1;
1387#if defined(CONFIG_USER_ONLY)
1388 /* if user mode only, we simulate a fake exception
1389 which will be handled outside the cpu execution
1390 loop */
1391 do_interrupt_user(env->exception_index,
1392 env->exception_is_int,
1393 env->error_code,
1394 env->exception_next_eip);
1395 /* successfully delivered */
1396 env->old_exception = -1;
1397#else
1398 /* simulate a real cpu exception. On i386, it can
1399 trigger new exceptions, but we do not handle
1400 double or triple faults yet. */
1401 do_interrupt_all(env->exception_index,
1402 env->exception_is_int,
1403 env->error_code,
1404 env->exception_next_eip, 0);
1405 /* successfully delivered */
1406 env->old_exception = -1;
1407#endif
1408 env = saved_env;
1409}
1410
1411void do_interrupt_x86_hardirq(CPUArchState *env1, int intno, int is_hw)
1412{
1413 CPUArchState *saved_env;
1414
1415 saved_env = env;
1416 env = env1;
1417 do_interrupt_all(intno, 0, 0, 0, is_hw);
1418 env = saved_env;
1419}
1420
Jun Nakajima86797932011-01-29 14:24:24 -08001421/* This should come from sysemu.h - if we could include it here... */
1422void qemu_system_reset_request(void);
1423
1424/*
1425 * Check nested exceptions and change to double or triple fault if
1426 * needed. It should only be called, if this is not an interrupt.
1427 * Returns the new exception number.
1428 */
1429static int check_exception(int intno, int *error_code)
1430{
1431 int first_contributory = env->old_exception == 0 ||
1432 (env->old_exception >= 10 &&
1433 env->old_exception <= 13);
1434 int second_contributory = intno == 0 ||
1435 (intno >= 10 && intno <= 13);
1436
1437 qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n",
1438 env->old_exception, intno);
1439
1440#if !defined(CONFIG_USER_ONLY)
1441 if (env->old_exception == EXCP08_DBLE) {
1442 if (env->hflags & HF_SVMI_MASK)
1443 helper_vmexit(SVM_EXIT_SHUTDOWN, 0); /* does not return */
1444
1445 qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
1446
1447 qemu_system_reset_request();
1448 return EXCP_HLT;
1449 }
1450#endif
1451
1452 if ((first_contributory && second_contributory)
1453 || (env->old_exception == EXCP0E_PAGE &&
1454 (second_contributory || (intno == EXCP0E_PAGE)))) {
1455 intno = EXCP08_DBLE;
1456 *error_code = 0;
1457 }
1458
1459 if (second_contributory || (intno == EXCP0E_PAGE) ||
1460 (intno == EXCP08_DBLE))
1461 env->old_exception = intno;
1462
1463 return intno;
1464}
1465
1466/*
1467 * Signal an interruption. It is executed in the main CPU loop.
1468 * is_int is TRUE if coming from the int instruction. next_eip is the
1469 * EIP value AFTER the interrupt instruction. It is only relevant if
1470 * is_int is TRUE.
1471 */
1472static void QEMU_NORETURN raise_interrupt(int intno, int is_int, int error_code,
1473 int next_eip_addend)
1474{
1475 if (!is_int) {
1476 helper_svm_check_intercept_param(SVM_EXIT_EXCP_BASE + intno, error_code);
1477 intno = check_exception(intno, &error_code);
1478 } else {
1479 helper_svm_check_intercept_param(SVM_EXIT_SWINT, 0);
1480 }
1481
1482 env->exception_index = intno;
1483 env->error_code = error_code;
1484 env->exception_is_int = is_int;
1485 env->exception_next_eip = env->eip + next_eip_addend;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01001486 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08001487}
1488
1489/* shortcuts to generate exceptions */
1490
1491void raise_exception_err(int exception_index, int error_code)
1492{
1493 raise_interrupt(exception_index, 0, error_code, 0);
1494}
1495
1496void raise_exception(int exception_index)
1497{
1498 raise_interrupt(exception_index, 0, 0, 0);
1499}
1500
1501/* SMM support */
1502
1503#if defined(CONFIG_USER_ONLY)
1504
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001505void do_smm_enter(CPUArchState *env1)
Jun Nakajima86797932011-01-29 14:24:24 -08001506{
1507}
1508
1509void helper_rsm(void)
1510{
1511}
1512
1513#else
1514
1515#ifdef TARGET_X86_64
1516#define SMM_REVISION_ID 0x00020064
1517#else
1518#define SMM_REVISION_ID 0x00020000
1519#endif
1520
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001521void do_smm_enter(CPUArchState *env1)
Jun Nakajima86797932011-01-29 14:24:24 -08001522{
1523 target_ulong sm_state;
1524 SegmentCache *dt;
1525 int i, offset;
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001526 CPUArchState *saved_env;
1527
1528 saved_env = env;
1529 env = env1;
Jun Nakajima86797932011-01-29 14:24:24 -08001530
1531 qemu_log_mask(CPU_LOG_INT, "SMM: enter\n");
1532 log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
1533
1534 env->hflags |= HF_SMM_MASK;
1535 cpu_smm_update(env);
1536
1537 sm_state = env->smbase + 0x8000;
1538
1539#ifdef TARGET_X86_64
1540 for(i = 0; i < 6; i++) {
1541 dt = &env->segs[i];
1542 offset = 0x7e00 + i * 16;
1543 stw_phys(sm_state + offset, dt->selector);
1544 stw_phys(sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff);
1545 stl_phys(sm_state + offset + 4, dt->limit);
1546 stq_phys(sm_state + offset + 8, dt->base);
1547 }
1548
1549 stq_phys(sm_state + 0x7e68, env->gdt.base);
1550 stl_phys(sm_state + 0x7e64, env->gdt.limit);
1551
1552 stw_phys(sm_state + 0x7e70, env->ldt.selector);
1553 stq_phys(sm_state + 0x7e78, env->ldt.base);
1554 stl_phys(sm_state + 0x7e74, env->ldt.limit);
1555 stw_phys(sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff);
1556
1557 stq_phys(sm_state + 0x7e88, env->idt.base);
1558 stl_phys(sm_state + 0x7e84, env->idt.limit);
1559
1560 stw_phys(sm_state + 0x7e90, env->tr.selector);
1561 stq_phys(sm_state + 0x7e98, env->tr.base);
1562 stl_phys(sm_state + 0x7e94, env->tr.limit);
1563 stw_phys(sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff);
1564
1565 stq_phys(sm_state + 0x7ed0, env->efer);
1566
1567 stq_phys(sm_state + 0x7ff8, EAX);
1568 stq_phys(sm_state + 0x7ff0, ECX);
1569 stq_phys(sm_state + 0x7fe8, EDX);
1570 stq_phys(sm_state + 0x7fe0, EBX);
1571 stq_phys(sm_state + 0x7fd8, ESP);
1572 stq_phys(sm_state + 0x7fd0, EBP);
1573 stq_phys(sm_state + 0x7fc8, ESI);
1574 stq_phys(sm_state + 0x7fc0, EDI);
1575 for(i = 8; i < 16; i++)
1576 stq_phys(sm_state + 0x7ff8 - i * 8, env->regs[i]);
1577 stq_phys(sm_state + 0x7f78, env->eip);
1578 stl_phys(sm_state + 0x7f70, compute_eflags());
1579 stl_phys(sm_state + 0x7f68, env->dr[6]);
1580 stl_phys(sm_state + 0x7f60, env->dr[7]);
1581
1582 stl_phys(sm_state + 0x7f48, env->cr[4]);
1583 stl_phys(sm_state + 0x7f50, env->cr[3]);
1584 stl_phys(sm_state + 0x7f58, env->cr[0]);
1585
1586 stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
1587 stl_phys(sm_state + 0x7f00, env->smbase);
1588#else
1589 stl_phys(sm_state + 0x7ffc, env->cr[0]);
1590 stl_phys(sm_state + 0x7ff8, env->cr[3]);
1591 stl_phys(sm_state + 0x7ff4, compute_eflags());
1592 stl_phys(sm_state + 0x7ff0, env->eip);
1593 stl_phys(sm_state + 0x7fec, EDI);
1594 stl_phys(sm_state + 0x7fe8, ESI);
1595 stl_phys(sm_state + 0x7fe4, EBP);
1596 stl_phys(sm_state + 0x7fe0, ESP);
1597 stl_phys(sm_state + 0x7fdc, EBX);
1598 stl_phys(sm_state + 0x7fd8, EDX);
1599 stl_phys(sm_state + 0x7fd4, ECX);
1600 stl_phys(sm_state + 0x7fd0, EAX);
1601 stl_phys(sm_state + 0x7fcc, env->dr[6]);
1602 stl_phys(sm_state + 0x7fc8, env->dr[7]);
1603
1604 stl_phys(sm_state + 0x7fc4, env->tr.selector);
1605 stl_phys(sm_state + 0x7f64, env->tr.base);
1606 stl_phys(sm_state + 0x7f60, env->tr.limit);
1607 stl_phys(sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff);
1608
1609 stl_phys(sm_state + 0x7fc0, env->ldt.selector);
1610 stl_phys(sm_state + 0x7f80, env->ldt.base);
1611 stl_phys(sm_state + 0x7f7c, env->ldt.limit);
1612 stl_phys(sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff);
1613
1614 stl_phys(sm_state + 0x7f74, env->gdt.base);
1615 stl_phys(sm_state + 0x7f70, env->gdt.limit);
1616
1617 stl_phys(sm_state + 0x7f58, env->idt.base);
1618 stl_phys(sm_state + 0x7f54, env->idt.limit);
1619
1620 for(i = 0; i < 6; i++) {
1621 dt = &env->segs[i];
1622 if (i < 3)
1623 offset = 0x7f84 + i * 12;
1624 else
1625 offset = 0x7f2c + (i - 3) * 12;
1626 stl_phys(sm_state + 0x7fa8 + i * 4, dt->selector);
1627 stl_phys(sm_state + offset + 8, dt->base);
1628 stl_phys(sm_state + offset + 4, dt->limit);
1629 stl_phys(sm_state + offset, (dt->flags >> 8) & 0xf0ff);
1630 }
1631 stl_phys(sm_state + 0x7f14, env->cr[4]);
1632
1633 stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
1634 stl_phys(sm_state + 0x7ef8, env->smbase);
1635#endif
1636 /* init SMM cpu state */
1637
1638#ifdef TARGET_X86_64
1639 cpu_load_efer(env, 0);
1640#endif
1641 load_eflags(0, ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1642 env->eip = 0x00008000;
1643 cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase,
1644 0xffffffff, 0);
1645 cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, 0);
1646 cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, 0);
1647 cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, 0);
1648 cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, 0);
1649 cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, 0);
1650
1651 cpu_x86_update_cr0(env,
1652 env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK | CR0_PG_MASK));
1653 cpu_x86_update_cr4(env, 0);
1654 env->dr[7] = 0x00000400;
1655 CC_OP = CC_OP_EFLAGS;
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001656 env = saved_env;
Jun Nakajima86797932011-01-29 14:24:24 -08001657}
1658
1659void helper_rsm(void)
1660{
1661 target_ulong sm_state;
1662 int i, offset;
1663 uint32_t val;
1664
1665 sm_state = env->smbase + 0x8000;
1666#ifdef TARGET_X86_64
1667 cpu_load_efer(env, ldq_phys(sm_state + 0x7ed0));
1668
1669 for(i = 0; i < 6; i++) {
1670 offset = 0x7e00 + i * 16;
1671 cpu_x86_load_seg_cache(env, i,
1672 lduw_phys(sm_state + offset),
1673 ldq_phys(sm_state + offset + 8),
1674 ldl_phys(sm_state + offset + 4),
1675 (lduw_phys(sm_state + offset + 2) & 0xf0ff) << 8);
1676 }
1677
1678 env->gdt.base = ldq_phys(sm_state + 0x7e68);
1679 env->gdt.limit = ldl_phys(sm_state + 0x7e64);
1680
1681 env->ldt.selector = lduw_phys(sm_state + 0x7e70);
1682 env->ldt.base = ldq_phys(sm_state + 0x7e78);
1683 env->ldt.limit = ldl_phys(sm_state + 0x7e74);
1684 env->ldt.flags = (lduw_phys(sm_state + 0x7e72) & 0xf0ff) << 8;
1685
1686 env->idt.base = ldq_phys(sm_state + 0x7e88);
1687 env->idt.limit = ldl_phys(sm_state + 0x7e84);
1688
1689 env->tr.selector = lduw_phys(sm_state + 0x7e90);
1690 env->tr.base = ldq_phys(sm_state + 0x7e98);
1691 env->tr.limit = ldl_phys(sm_state + 0x7e94);
1692 env->tr.flags = (lduw_phys(sm_state + 0x7e92) & 0xf0ff) << 8;
1693
1694 EAX = ldq_phys(sm_state + 0x7ff8);
1695 ECX = ldq_phys(sm_state + 0x7ff0);
1696 EDX = ldq_phys(sm_state + 0x7fe8);
1697 EBX = ldq_phys(sm_state + 0x7fe0);
1698 ESP = ldq_phys(sm_state + 0x7fd8);
1699 EBP = ldq_phys(sm_state + 0x7fd0);
1700 ESI = ldq_phys(sm_state + 0x7fc8);
1701 EDI = ldq_phys(sm_state + 0x7fc0);
1702 for(i = 8; i < 16; i++)
1703 env->regs[i] = ldq_phys(sm_state + 0x7ff8 - i * 8);
1704 env->eip = ldq_phys(sm_state + 0x7f78);
1705 load_eflags(ldl_phys(sm_state + 0x7f70),
1706 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1707 env->dr[6] = ldl_phys(sm_state + 0x7f68);
1708 env->dr[7] = ldl_phys(sm_state + 0x7f60);
1709
1710 cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f48));
1711 cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7f50));
1712 cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7f58));
1713
1714 val = ldl_phys(sm_state + 0x7efc); /* revision ID */
1715 if (val & 0x20000) {
1716 env->smbase = ldl_phys(sm_state + 0x7f00) & ~0x7fff;
1717 }
1718#else
1719 cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7ffc));
1720 cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7ff8));
1721 load_eflags(ldl_phys(sm_state + 0x7ff4),
1722 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1723 env->eip = ldl_phys(sm_state + 0x7ff0);
1724 EDI = ldl_phys(sm_state + 0x7fec);
1725 ESI = ldl_phys(sm_state + 0x7fe8);
1726 EBP = ldl_phys(sm_state + 0x7fe4);
1727 ESP = ldl_phys(sm_state + 0x7fe0);
1728 EBX = ldl_phys(sm_state + 0x7fdc);
1729 EDX = ldl_phys(sm_state + 0x7fd8);
1730 ECX = ldl_phys(sm_state + 0x7fd4);
1731 EAX = ldl_phys(sm_state + 0x7fd0);
1732 env->dr[6] = ldl_phys(sm_state + 0x7fcc);
1733 env->dr[7] = ldl_phys(sm_state + 0x7fc8);
1734
1735 env->tr.selector = ldl_phys(sm_state + 0x7fc4) & 0xffff;
1736 env->tr.base = ldl_phys(sm_state + 0x7f64);
1737 env->tr.limit = ldl_phys(sm_state + 0x7f60);
1738 env->tr.flags = (ldl_phys(sm_state + 0x7f5c) & 0xf0ff) << 8;
1739
1740 env->ldt.selector = ldl_phys(sm_state + 0x7fc0) & 0xffff;
1741 env->ldt.base = ldl_phys(sm_state + 0x7f80);
1742 env->ldt.limit = ldl_phys(sm_state + 0x7f7c);
1743 env->ldt.flags = (ldl_phys(sm_state + 0x7f78) & 0xf0ff) << 8;
1744
1745 env->gdt.base = ldl_phys(sm_state + 0x7f74);
1746 env->gdt.limit = ldl_phys(sm_state + 0x7f70);
1747
1748 env->idt.base = ldl_phys(sm_state + 0x7f58);
1749 env->idt.limit = ldl_phys(sm_state + 0x7f54);
1750
1751 for(i = 0; i < 6; i++) {
1752 if (i < 3)
1753 offset = 0x7f84 + i * 12;
1754 else
1755 offset = 0x7f2c + (i - 3) * 12;
1756 cpu_x86_load_seg_cache(env, i,
1757 ldl_phys(sm_state + 0x7fa8 + i * 4) & 0xffff,
1758 ldl_phys(sm_state + offset + 8),
1759 ldl_phys(sm_state + offset + 4),
1760 (ldl_phys(sm_state + offset) & 0xf0ff) << 8);
1761 }
1762 cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f14));
1763
1764 val = ldl_phys(sm_state + 0x7efc); /* revision ID */
1765 if (val & 0x20000) {
1766 env->smbase = ldl_phys(sm_state + 0x7ef8) & ~0x7fff;
1767 }
1768#endif
1769 CC_OP = CC_OP_EFLAGS;
1770 env->hflags &= ~HF_SMM_MASK;
1771 cpu_smm_update(env);
1772
1773 qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
1774 log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
1775}
1776
1777#endif /* !CONFIG_USER_ONLY */
1778
1779
1780/* division, flags are undefined */
1781
David 'Digit' Turner90edd952014-04-03 17:40:22 +02001782void helper_divb_AL(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08001783{
1784 unsigned int num, den, q, r;
1785
1786 num = (EAX & 0xffff);
1787 den = (t0 & 0xff);
1788 if (den == 0) {
1789 raise_exception(EXCP00_DIVZ);
1790 }
1791 q = (num / den);
1792 if (q > 0xff)
1793 raise_exception(EXCP00_DIVZ);
1794 q &= 0xff;
1795 r = (num % den) & 0xff;
1796 EAX = (EAX & ~0xffff) | (r << 8) | q;
1797}
1798
David 'Digit' Turner90edd952014-04-03 17:40:22 +02001799void helper_idivb_AL(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08001800{
1801 int num, den, q, r;
1802
1803 num = (int16_t)EAX;
1804 den = (int8_t)t0;
1805 if (den == 0) {
1806 raise_exception(EXCP00_DIVZ);
1807 }
1808 q = (num / den);
1809 if (q != (int8_t)q)
1810 raise_exception(EXCP00_DIVZ);
1811 q &= 0xff;
1812 r = (num % den) & 0xff;
1813 EAX = (EAX & ~0xffff) | (r << 8) | q;
1814}
1815
David 'Digit' Turner90edd952014-04-03 17:40:22 +02001816void helper_divw_AX(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08001817{
1818 unsigned int num, den, q, r;
1819
1820 num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
1821 den = (t0 & 0xffff);
1822 if (den == 0) {
1823 raise_exception(EXCP00_DIVZ);
1824 }
1825 q = (num / den);
1826 if (q > 0xffff)
1827 raise_exception(EXCP00_DIVZ);
1828 q &= 0xffff;
1829 r = (num % den) & 0xffff;
1830 EAX = (EAX & ~0xffff) | q;
1831 EDX = (EDX & ~0xffff) | r;
1832}
1833
David 'Digit' Turner90edd952014-04-03 17:40:22 +02001834void helper_idivw_AX(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08001835{
1836 int num, den, q, r;
1837
1838 num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
1839 den = (int16_t)t0;
1840 if (den == 0) {
1841 raise_exception(EXCP00_DIVZ);
1842 }
1843 q = (num / den);
1844 if (q != (int16_t)q)
1845 raise_exception(EXCP00_DIVZ);
1846 q &= 0xffff;
1847 r = (num % den) & 0xffff;
1848 EAX = (EAX & ~0xffff) | q;
1849 EDX = (EDX & ~0xffff) | r;
1850}
1851
David 'Digit' Turner90edd952014-04-03 17:40:22 +02001852void helper_divl_EAX(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08001853{
1854 unsigned int den, r;
1855 uint64_t num, q;
1856
1857 num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
1858 den = t0;
1859 if (den == 0) {
1860 raise_exception(EXCP00_DIVZ);
1861 }
1862 q = (num / den);
1863 r = (num % den);
1864 if (q > 0xffffffff)
1865 raise_exception(EXCP00_DIVZ);
1866 EAX = (uint32_t)q;
1867 EDX = (uint32_t)r;
1868}
1869
David 'Digit' Turner90edd952014-04-03 17:40:22 +02001870void helper_idivl_EAX(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08001871{
1872 int den, r;
1873 int64_t num, q;
1874
1875 num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
1876 den = t0;
1877 if (den == 0) {
1878 raise_exception(EXCP00_DIVZ);
1879 }
1880 q = (num / den);
1881 r = (num % den);
1882 if (q != (int32_t)q)
1883 raise_exception(EXCP00_DIVZ);
1884 EAX = (uint32_t)q;
1885 EDX = (uint32_t)r;
1886}
1887
1888/* bcd */
1889
1890/* XXX: exception */
1891void helper_aam(int base)
1892{
1893 int al, ah;
1894 al = EAX & 0xff;
1895 ah = al / base;
1896 al = al % base;
1897 EAX = (EAX & ~0xffff) | al | (ah << 8);
1898 CC_DST = al;
1899}
1900
1901void helper_aad(int base)
1902{
1903 int al, ah;
1904 al = EAX & 0xff;
1905 ah = (EAX >> 8) & 0xff;
1906 al = ((ah * base) + al) & 0xff;
1907 EAX = (EAX & ~0xffff) | al;
1908 CC_DST = al;
1909}
1910
1911void helper_aaa(void)
1912{
1913 int icarry;
1914 int al, ah, af;
1915 int eflags;
1916
1917 eflags = helper_cc_compute_all(CC_OP);
1918 af = eflags & CC_A;
1919 al = EAX & 0xff;
1920 ah = (EAX >> 8) & 0xff;
1921
1922 icarry = (al > 0xf9);
1923 if (((al & 0x0f) > 9 ) || af) {
1924 al = (al + 6) & 0x0f;
1925 ah = (ah + 1 + icarry) & 0xff;
1926 eflags |= CC_C | CC_A;
1927 } else {
1928 eflags &= ~(CC_C | CC_A);
1929 al &= 0x0f;
1930 }
1931 EAX = (EAX & ~0xffff) | al | (ah << 8);
1932 CC_SRC = eflags;
1933}
1934
1935void helper_aas(void)
1936{
1937 int icarry;
1938 int al, ah, af;
1939 int eflags;
1940
1941 eflags = helper_cc_compute_all(CC_OP);
1942 af = eflags & CC_A;
1943 al = EAX & 0xff;
1944 ah = (EAX >> 8) & 0xff;
1945
1946 icarry = (al < 6);
1947 if (((al & 0x0f) > 9 ) || af) {
1948 al = (al - 6) & 0x0f;
1949 ah = (ah - 1 - icarry) & 0xff;
1950 eflags |= CC_C | CC_A;
1951 } else {
1952 eflags &= ~(CC_C | CC_A);
1953 al &= 0x0f;
1954 }
1955 EAX = (EAX & ~0xffff) | al | (ah << 8);
1956 CC_SRC = eflags;
1957}
1958
1959void helper_daa(void)
1960{
1961 int al, af, cf;
1962 int eflags;
1963
1964 eflags = helper_cc_compute_all(CC_OP);
1965 cf = eflags & CC_C;
1966 af = eflags & CC_A;
1967 al = EAX & 0xff;
1968
1969 eflags = 0;
1970 if (((al & 0x0f) > 9 ) || af) {
1971 al = (al + 6) & 0xff;
1972 eflags |= CC_A;
1973 }
1974 if ((al > 0x9f) || cf) {
1975 al = (al + 0x60) & 0xff;
1976 eflags |= CC_C;
1977 }
1978 EAX = (EAX & ~0xff) | al;
1979 /* well, speed is not an issue here, so we compute the flags by hand */
1980 eflags |= (al == 0) << 6; /* zf */
1981 eflags |= parity_table[al]; /* pf */
1982 eflags |= (al & 0x80); /* sf */
1983 CC_SRC = eflags;
1984}
1985
1986void helper_das(void)
1987{
1988 int al, al1, af, cf;
1989 int eflags;
1990
1991 eflags = helper_cc_compute_all(CC_OP);
1992 cf = eflags & CC_C;
1993 af = eflags & CC_A;
1994 al = EAX & 0xff;
1995
1996 eflags = 0;
1997 al1 = al;
1998 if (((al & 0x0f) > 9 ) || af) {
1999 eflags |= CC_A;
2000 if (al < 6 || cf)
2001 eflags |= CC_C;
2002 al = (al - 6) & 0xff;
2003 }
2004 if ((al1 > 0x99) || cf) {
2005 al = (al - 0x60) & 0xff;
2006 eflags |= CC_C;
2007 }
2008 EAX = (EAX & ~0xff) | al;
2009 /* well, speed is not an issue here, so we compute the flags by hand */
2010 eflags |= (al == 0) << 6; /* zf */
2011 eflags |= parity_table[al]; /* pf */
2012 eflags |= (al & 0x80); /* sf */
2013 CC_SRC = eflags;
2014}
2015
2016void helper_into(int next_eip_addend)
2017{
2018 int eflags;
2019 eflags = helper_cc_compute_all(CC_OP);
2020 if (eflags & CC_O) {
2021 raise_interrupt(EXCP04_INTO, 1, 0, next_eip_addend);
2022 }
2023}
2024
2025void helper_cmpxchg8b(target_ulong a0)
2026{
2027 uint64_t d;
2028 int eflags;
2029
2030 eflags = helper_cc_compute_all(CC_OP);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002031 d = cpu_ldq_data(env, a0);
Jun Nakajima86797932011-01-29 14:24:24 -08002032 if (d == (((uint64_t)EDX << 32) | (uint32_t)EAX)) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002033 cpu_stq_data(env, a0, ((uint64_t)ECX << 32) | (uint32_t)EBX);
Jun Nakajima86797932011-01-29 14:24:24 -08002034 eflags |= CC_Z;
2035 } else {
2036 /* always do the store */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002037 cpu_stq_data(env, a0, d);
Jun Nakajima86797932011-01-29 14:24:24 -08002038 EDX = (uint32_t)(d >> 32);
2039 EAX = (uint32_t)d;
2040 eflags &= ~CC_Z;
2041 }
2042 CC_SRC = eflags;
2043}
2044
2045#ifdef TARGET_X86_64
2046void helper_cmpxchg16b(target_ulong a0)
2047{
2048 uint64_t d0, d1;
2049 int eflags;
2050
2051 if ((a0 & 0xf) != 0)
2052 raise_exception(EXCP0D_GPF);
2053 eflags = helper_cc_compute_all(CC_OP);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002054 d0 = cpu_ldq_data(env, a0);
2055 d1 = cpu_ldq_data(env, a0 + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08002056 if (d0 == EAX && d1 == EDX) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002057 cpu_stq_data(env, a0, EBX);
2058 cpu_stq_data(env, a0 + 8, ECX);
Jun Nakajima86797932011-01-29 14:24:24 -08002059 eflags |= CC_Z;
2060 } else {
2061 /* always do the store */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002062 cpu_stq_data(env, a0, d0);
2063 cpu_stq_data(env, a0 + 8, d1);
Jun Nakajima86797932011-01-29 14:24:24 -08002064 EDX = d1;
2065 EAX = d0;
2066 eflags &= ~CC_Z;
2067 }
2068 CC_SRC = eflags;
2069}
2070#endif
2071
2072void helper_single_step(void)
2073{
2074#ifndef CONFIG_USER_ONLY
2075 check_hw_breakpoints(env, 1);
2076 env->dr[6] |= DR6_BS;
2077#endif
2078 raise_exception(EXCP01_DB);
2079}
2080
2081void helper_cpuid(void)
2082{
2083 uint32_t eax, ebx, ecx, edx;
2084
2085 helper_svm_check_intercept_param(SVM_EXIT_CPUID, 0);
2086
2087 cpu_x86_cpuid(env, (uint32_t)EAX, (uint32_t)ECX, &eax, &ebx, &ecx, &edx);
2088 EAX = eax;
2089 EBX = ebx;
2090 ECX = ecx;
2091 EDX = edx;
2092}
2093
2094void helper_enter_level(int level, int data32, target_ulong t1)
2095{
2096 target_ulong ssp;
2097 uint32_t esp_mask, esp, ebp;
2098
2099 esp_mask = get_sp_mask(env->segs[R_SS].flags);
2100 ssp = env->segs[R_SS].base;
2101 ebp = EBP;
2102 esp = ESP;
2103 if (data32) {
2104 /* 32 bit */
2105 esp -= 4;
2106 while (--level) {
2107 esp -= 4;
2108 ebp -= 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002109 cpu_stl_data(env, ssp + (esp & esp_mask),
2110 cpu_ldl_data(env, ssp + (ebp & esp_mask)));
Jun Nakajima86797932011-01-29 14:24:24 -08002111 }
2112 esp -= 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002113 cpu_stl_data(env, ssp + (esp & esp_mask), t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002114 } else {
2115 /* 16 bit */
2116 esp -= 2;
2117 while (--level) {
2118 esp -= 2;
2119 ebp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002120 cpu_stw_data(env, ssp + (esp & esp_mask),
2121 cpu_lduw_data(env, ssp + (ebp & esp_mask)));
Jun Nakajima86797932011-01-29 14:24:24 -08002122 }
2123 esp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002124 cpu_stw_data(env, ssp + (esp & esp_mask), t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002125 }
2126}
2127
2128#ifdef TARGET_X86_64
2129void helper_enter64_level(int level, int data64, target_ulong t1)
2130{
2131 target_ulong esp, ebp;
2132 ebp = EBP;
2133 esp = ESP;
2134
2135 if (data64) {
2136 /* 64 bit */
2137 esp -= 8;
2138 while (--level) {
2139 esp -= 8;
2140 ebp -= 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002141 cpu_stq_data(env, esp, cpu_ldq_data(env, ebp));
Jun Nakajima86797932011-01-29 14:24:24 -08002142 }
2143 esp -= 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002144 cpu_stq_data(env, esp, t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002145 } else {
2146 /* 16 bit */
2147 esp -= 2;
2148 while (--level) {
2149 esp -= 2;
2150 ebp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002151 cpu_stw_data(env, esp, cpu_lduw_data(env, ebp));
Jun Nakajima86797932011-01-29 14:24:24 -08002152 }
2153 esp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002154 cpu_stw_data(env, esp, t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002155 }
2156}
2157#endif
2158
2159void helper_lldt(int selector)
2160{
2161 SegmentCache *dt;
2162 uint32_t e1, e2;
2163 int index, entry_limit;
2164 target_ulong ptr;
2165
2166 selector &= 0xffff;
2167 if ((selector & 0xfffc) == 0) {
2168 /* XXX: NULL selector case: invalid LDT */
2169 env->ldt.base = 0;
2170 env->ldt.limit = 0;
2171 } else {
2172 if (selector & 0x4)
2173 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2174 dt = &env->gdt;
2175 index = selector & ~7;
2176#ifdef TARGET_X86_64
2177 if (env->hflags & HF_LMA_MASK)
2178 entry_limit = 15;
2179 else
2180#endif
2181 entry_limit = 7;
2182 if ((index + entry_limit) > dt->limit)
2183 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2184 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002185 e1 = cpu_ldl_kernel(env, ptr);
2186 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002187 if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
2188 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2189 if (!(e2 & DESC_P_MASK))
2190 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2191#ifdef TARGET_X86_64
2192 if (env->hflags & HF_LMA_MASK) {
2193 uint32_t e3;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002194 e3 = cpu_ldl_kernel(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08002195 load_seg_cache_raw_dt(&env->ldt, e1, e2);
2196 env->ldt.base |= (target_ulong)e3 << 32;
2197 } else
2198#endif
2199 {
2200 load_seg_cache_raw_dt(&env->ldt, e1, e2);
2201 }
2202 }
2203 env->ldt.selector = selector;
2204}
2205
2206void helper_ltr(int selector)
2207{
2208 SegmentCache *dt;
2209 uint32_t e1, e2;
2210 int index, type, entry_limit;
2211 target_ulong ptr;
2212
2213 selector &= 0xffff;
2214 if ((selector & 0xfffc) == 0) {
2215 /* NULL selector case: invalid TR */
2216 env->tr.base = 0;
2217 env->tr.limit = 0;
2218 env->tr.flags = 0;
2219 } else {
2220 if (selector & 0x4)
2221 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2222 dt = &env->gdt;
2223 index = selector & ~7;
2224#ifdef TARGET_X86_64
2225 if (env->hflags & HF_LMA_MASK)
2226 entry_limit = 15;
2227 else
2228#endif
2229 entry_limit = 7;
2230 if ((index + entry_limit) > dt->limit)
2231 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2232 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002233 e1 = cpu_ldl_kernel(env, ptr);
2234 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002235 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2236 if ((e2 & DESC_S_MASK) ||
2237 (type != 1 && type != 9))
2238 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2239 if (!(e2 & DESC_P_MASK))
2240 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2241#ifdef TARGET_X86_64
2242 if (env->hflags & HF_LMA_MASK) {
2243 uint32_t e3, e4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002244 e3 = cpu_ldl_kernel(env, ptr + 8);
2245 e4 = cpu_ldl_kernel(env, ptr + 12);
Jun Nakajima86797932011-01-29 14:24:24 -08002246 if ((e4 >> DESC_TYPE_SHIFT) & 0xf)
2247 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2248 load_seg_cache_raw_dt(&env->tr, e1, e2);
2249 env->tr.base |= (target_ulong)e3 << 32;
2250 } else
2251#endif
2252 {
2253 load_seg_cache_raw_dt(&env->tr, e1, e2);
2254 }
2255 e2 |= DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002256 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -08002257 }
2258 env->tr.selector = selector;
2259}
2260
2261/* only works if protected mode and not VM86. seg_reg must be != R_CS */
2262void helper_load_seg(int seg_reg, int selector)
2263{
2264 uint32_t e1, e2;
2265 int cpl, dpl, rpl;
2266 SegmentCache *dt;
2267 int index;
2268 target_ulong ptr;
2269
2270 selector &= 0xffff;
2271 cpl = env->hflags & HF_CPL_MASK;
2272 if ((selector & 0xfffc) == 0) {
2273 /* null selector case */
2274 if (seg_reg == R_SS
2275#ifdef TARGET_X86_64
2276 && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
2277#endif
2278 )
2279 raise_exception_err(EXCP0D_GPF, 0);
2280 cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
2281 } else {
2282
2283 if (selector & 0x4)
2284 dt = &env->ldt;
2285 else
2286 dt = &env->gdt;
2287 index = selector & ~7;
2288 if ((index + 7) > dt->limit)
2289 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2290 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002291 e1 = cpu_ldl_kernel(env, ptr);
2292 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002293
2294 if (!(e2 & DESC_S_MASK))
2295 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2296 rpl = selector & 3;
2297 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2298 if (seg_reg == R_SS) {
2299 /* must be writable segment */
2300 if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
2301 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2302 if (rpl != cpl || dpl != cpl)
2303 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2304 } else {
2305 /* must be readable segment */
2306 if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK)
2307 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2308
2309 if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2310 /* if not conforming code, test rights */
2311 if (dpl < cpl || dpl < rpl)
2312 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2313 }
2314 }
2315
2316 if (!(e2 & DESC_P_MASK)) {
2317 if (seg_reg == R_SS)
2318 raise_exception_err(EXCP0C_STACK, selector & 0xfffc);
2319 else
2320 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2321 }
2322
2323 /* set the access bit if not already set */
2324 if (!(e2 & DESC_A_MASK)) {
2325 e2 |= DESC_A_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002326 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -08002327 }
2328
2329 cpu_x86_load_seg_cache(env, seg_reg, selector,
2330 get_seg_base(e1, e2),
2331 get_seg_limit(e1, e2),
2332 e2);
2333#if 0
2334 qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
2335 selector, (unsigned long)sc->base, sc->limit, sc->flags);
2336#endif
2337 }
2338}
2339
2340/* protected mode jump */
2341void helper_ljmp_protected(int new_cs, target_ulong new_eip,
2342 int next_eip_addend)
2343{
2344 int gate_cs, type;
2345 uint32_t e1, e2, cpl, dpl, rpl, limit;
2346 target_ulong next_eip;
2347
2348 if ((new_cs & 0xfffc) == 0)
2349 raise_exception_err(EXCP0D_GPF, 0);
2350 if (load_segment(&e1, &e2, new_cs) != 0)
2351 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2352 cpl = env->hflags & HF_CPL_MASK;
2353 if (e2 & DESC_S_MASK) {
2354 if (!(e2 & DESC_CS_MASK))
2355 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2356 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2357 if (e2 & DESC_C_MASK) {
2358 /* conforming code segment */
2359 if (dpl > cpl)
2360 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2361 } else {
2362 /* non conforming code segment */
2363 rpl = new_cs & 3;
2364 if (rpl > cpl)
2365 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2366 if (dpl != cpl)
2367 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2368 }
2369 if (!(e2 & DESC_P_MASK))
2370 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2371 limit = get_seg_limit(e1, e2);
2372 if (new_eip > limit &&
2373 !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK))
2374 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2375 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2376 get_seg_base(e1, e2), limit, e2);
2377 EIP = new_eip;
2378 } else {
2379 /* jump to call or task gate */
2380 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2381 rpl = new_cs & 3;
2382 cpl = env->hflags & HF_CPL_MASK;
2383 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2384 switch(type) {
2385 case 1: /* 286 TSS */
2386 case 9: /* 386 TSS */
2387 case 5: /* task gate */
2388 if (dpl < cpl || dpl < rpl)
2389 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2390 next_eip = env->eip + next_eip_addend;
2391 switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
2392 CC_OP = CC_OP_EFLAGS;
2393 break;
2394 case 4: /* 286 call gate */
2395 case 12: /* 386 call gate */
2396 if ((dpl < cpl) || (dpl < rpl))
2397 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2398 if (!(e2 & DESC_P_MASK))
2399 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2400 gate_cs = e1 >> 16;
2401 new_eip = (e1 & 0xffff);
2402 if (type == 12)
2403 new_eip |= (e2 & 0xffff0000);
2404 if (load_segment(&e1, &e2, gate_cs) != 0)
2405 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2406 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2407 /* must be code segment */
2408 if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
2409 (DESC_S_MASK | DESC_CS_MASK)))
2410 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2411 if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
2412 (!(e2 & DESC_C_MASK) && (dpl != cpl)))
2413 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2414 if (!(e2 & DESC_P_MASK))
2415 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2416 limit = get_seg_limit(e1, e2);
2417 if (new_eip > limit)
2418 raise_exception_err(EXCP0D_GPF, 0);
2419 cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl,
2420 get_seg_base(e1, e2), limit, e2);
2421 EIP = new_eip;
2422 break;
2423 default:
2424 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2425 break;
2426 }
2427 }
2428}
2429
2430/* real mode call */
2431void helper_lcall_real(int new_cs, target_ulong new_eip1,
2432 int shift, int next_eip)
2433{
2434 int new_eip;
2435 uint32_t esp, esp_mask;
2436 target_ulong ssp;
2437
2438 new_eip = new_eip1;
2439 esp = ESP;
2440 esp_mask = get_sp_mask(env->segs[R_SS].flags);
2441 ssp = env->segs[R_SS].base;
2442 if (shift) {
2443 PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector);
2444 PUSHL(ssp, esp, esp_mask, next_eip);
2445 } else {
2446 PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector);
2447 PUSHW(ssp, esp, esp_mask, next_eip);
2448 }
2449
2450 SET_ESP(esp, esp_mask);
2451 env->eip = new_eip;
2452 env->segs[R_CS].selector = new_cs;
2453 env->segs[R_CS].base = (new_cs << 4);
2454}
2455
2456/* protected mode call */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02002457void helper_lcall_protected(int new_cs, target_ulong new_eip,
Jun Nakajima86797932011-01-29 14:24:24 -08002458 int shift, int next_eip_addend)
2459{
2460 int new_stack, i;
2461 uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
2462 uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask;
2463 uint32_t val, limit, old_sp_mask;
2464 target_ulong ssp, old_ssp, next_eip;
2465
2466 next_eip = env->eip + next_eip_addend;
2467 LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift);
2468 LOG_PCALL_STATE(env);
2469 if ((new_cs & 0xfffc) == 0)
2470 raise_exception_err(EXCP0D_GPF, 0);
2471 if (load_segment(&e1, &e2, new_cs) != 0)
2472 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2473 cpl = env->hflags & HF_CPL_MASK;
2474 LOG_PCALL("desc=%08x:%08x\n", e1, e2);
2475 if (e2 & DESC_S_MASK) {
2476 if (!(e2 & DESC_CS_MASK))
2477 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2478 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2479 if (e2 & DESC_C_MASK) {
2480 /* conforming code segment */
2481 if (dpl > cpl)
2482 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2483 } else {
2484 /* non conforming code segment */
2485 rpl = new_cs & 3;
2486 if (rpl > cpl)
2487 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2488 if (dpl != cpl)
2489 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2490 }
2491 if (!(e2 & DESC_P_MASK))
2492 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2493
2494#ifdef TARGET_X86_64
2495 /* XXX: check 16/32 bit cases in long mode */
2496 if (shift == 2) {
2497 target_ulong rsp;
2498 /* 64 bit case */
2499 rsp = ESP;
2500 PUSHQ(rsp, env->segs[R_CS].selector);
2501 PUSHQ(rsp, next_eip);
2502 /* from this point, not restartable */
2503 ESP = rsp;
2504 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2505 get_seg_base(e1, e2),
2506 get_seg_limit(e1, e2), e2);
2507 EIP = new_eip;
2508 } else
2509#endif
2510 {
2511 sp = ESP;
2512 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2513 ssp = env->segs[R_SS].base;
2514 if (shift) {
2515 PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
2516 PUSHL(ssp, sp, sp_mask, next_eip);
2517 } else {
2518 PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
2519 PUSHW(ssp, sp, sp_mask, next_eip);
2520 }
2521
2522 limit = get_seg_limit(e1, e2);
2523 if (new_eip > limit)
2524 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2525 /* from this point, not restartable */
2526 SET_ESP(sp, sp_mask);
2527 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2528 get_seg_base(e1, e2), limit, e2);
2529 EIP = new_eip;
2530 }
2531 } else {
2532 /* check gate type */
2533 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
2534 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2535 rpl = new_cs & 3;
2536 switch(type) {
2537 case 1: /* available 286 TSS */
2538 case 9: /* available 386 TSS */
2539 case 5: /* task gate */
2540 if (dpl < cpl || dpl < rpl)
2541 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2542 switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
2543 CC_OP = CC_OP_EFLAGS;
2544 return;
2545 case 4: /* 286 call gate */
2546 case 12: /* 386 call gate */
2547 break;
2548 default:
2549 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2550 break;
2551 }
2552 shift = type >> 3;
2553
2554 if (dpl < cpl || dpl < rpl)
2555 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2556 /* check valid bit */
2557 if (!(e2 & DESC_P_MASK))
2558 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2559 selector = e1 >> 16;
2560 offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
2561 param_count = e2 & 0x1f;
2562 if ((selector & 0xfffc) == 0)
2563 raise_exception_err(EXCP0D_GPF, 0);
2564
2565 if (load_segment(&e1, &e2, selector) != 0)
2566 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2567 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
2568 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2569 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2570 if (dpl > cpl)
2571 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2572 if (!(e2 & DESC_P_MASK))
2573 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2574
2575 if (!(e2 & DESC_C_MASK) && dpl < cpl) {
2576 /* to inner privilege */
2577 get_ss_esp_from_tss(&ss, &sp, dpl);
2578 LOG_PCALL("new ss:esp=%04x:%08x param_count=%d ESP=" TARGET_FMT_lx "\n",
2579 ss, sp, param_count, ESP);
2580 if ((ss & 0xfffc) == 0)
2581 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2582 if ((ss & 3) != dpl)
2583 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2584 if (load_segment(&ss_e1, &ss_e2, ss) != 0)
2585 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2586 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
2587 if (ss_dpl != dpl)
2588 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2589 if (!(ss_e2 & DESC_S_MASK) ||
2590 (ss_e2 & DESC_CS_MASK) ||
2591 !(ss_e2 & DESC_W_MASK))
2592 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2593 if (!(ss_e2 & DESC_P_MASK))
2594 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2595
2596 // push_size = ((param_count * 2) + 8) << shift;
2597
2598 old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
2599 old_ssp = env->segs[R_SS].base;
2600
2601 sp_mask = get_sp_mask(ss_e2);
2602 ssp = get_seg_base(ss_e1, ss_e2);
2603 if (shift) {
2604 PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector);
2605 PUSHL(ssp, sp, sp_mask, ESP);
2606 for(i = param_count - 1; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002607 val = cpu_ldl_kernel(env, old_ssp + ((ESP + i * 4) & old_sp_mask));
Jun Nakajima86797932011-01-29 14:24:24 -08002608 PUSHL(ssp, sp, sp_mask, val);
2609 }
2610 } else {
2611 PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector);
2612 PUSHW(ssp, sp, sp_mask, ESP);
2613 for(i = param_count - 1; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002614 val = cpu_lduw_kernel(env, old_ssp + ((ESP + i * 2) & old_sp_mask));
Jun Nakajima86797932011-01-29 14:24:24 -08002615 PUSHW(ssp, sp, sp_mask, val);
2616 }
2617 }
2618 new_stack = 1;
2619 } else {
2620 /* to same privilege */
2621 sp = ESP;
2622 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2623 ssp = env->segs[R_SS].base;
2624 // push_size = (4 << shift);
2625 new_stack = 0;
2626 }
2627
2628 if (shift) {
2629 PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
2630 PUSHL(ssp, sp, sp_mask, next_eip);
2631 } else {
2632 PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
2633 PUSHW(ssp, sp, sp_mask, next_eip);
2634 }
2635
2636 /* from this point, not restartable */
2637
2638 if (new_stack) {
2639 ss = (ss & ~3) | dpl;
2640 cpu_x86_load_seg_cache(env, R_SS, ss,
2641 ssp,
2642 get_seg_limit(ss_e1, ss_e2),
2643 ss_e2);
2644 }
2645
2646 selector = (selector & ~3) | dpl;
2647 cpu_x86_load_seg_cache(env, R_CS, selector,
2648 get_seg_base(e1, e2),
2649 get_seg_limit(e1, e2),
2650 e2);
2651 cpu_x86_set_cpl(env, dpl);
2652 SET_ESP(sp, sp_mask);
2653 EIP = offset;
2654 }
Jun Nakajima86797932011-01-29 14:24:24 -08002655}
2656
2657/* real and vm86 mode iret */
2658void helper_iret_real(int shift)
2659{
2660 uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
2661 target_ulong ssp;
2662 int eflags_mask;
2663
2664 sp_mask = 0xffff; /* XXXX: use SS segment size ? */
2665 sp = ESP;
2666 ssp = env->segs[R_SS].base;
2667 if (shift == 1) {
2668 /* 32 bits */
2669 POPL(ssp, sp, sp_mask, new_eip);
2670 POPL(ssp, sp, sp_mask, new_cs);
2671 new_cs &= 0xffff;
2672 POPL(ssp, sp, sp_mask, new_eflags);
2673 } else {
2674 /* 16 bits */
2675 POPW(ssp, sp, sp_mask, new_eip);
2676 POPW(ssp, sp, sp_mask, new_cs);
2677 POPW(ssp, sp, sp_mask, new_eflags);
2678 }
2679 ESP = (ESP & ~sp_mask) | (sp & sp_mask);
2680 env->segs[R_CS].selector = new_cs;
2681 env->segs[R_CS].base = (new_cs << 4);
2682 env->eip = new_eip;
2683 if (env->eflags & VM_MASK)
2684 eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK;
2685 else
2686 eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK | NT_MASK;
2687 if (shift == 0)
2688 eflags_mask &= 0xffff;
2689 load_eflags(new_eflags, eflags_mask);
2690 env->hflags2 &= ~HF2_NMI_MASK;
2691}
2692
2693static inline void validate_seg(int seg_reg, int cpl)
2694{
2695 int dpl;
2696 uint32_t e2;
2697
2698 /* XXX: on x86_64, we do not want to nullify FS and GS because
2699 they may still contain a valid base. I would be interested to
2700 know how a real x86_64 CPU behaves */
2701 if ((seg_reg == R_FS || seg_reg == R_GS) &&
2702 (env->segs[seg_reg].selector & 0xfffc) == 0)
2703 return;
2704
2705 e2 = env->segs[seg_reg].flags;
2706 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2707 if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2708 /* data or non conforming code segment */
2709 if (dpl < cpl) {
2710 cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0);
2711 }
2712 }
2713}
2714
2715/* protected mode iret */
2716static inline void helper_ret_protected(int shift, int is_iret, int addend)
2717{
2718 uint32_t new_cs, new_eflags, new_ss;
2719 uint32_t new_es, new_ds, new_fs, new_gs;
2720 uint32_t e1, e2, ss_e1, ss_e2;
2721 int cpl, dpl, rpl, eflags_mask, iopl;
2722 target_ulong ssp, sp, new_eip, new_esp, sp_mask;
2723
2724#ifdef TARGET_X86_64
2725 if (shift == 2)
2726 sp_mask = -1;
2727 else
2728#endif
2729 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2730 sp = ESP;
2731 ssp = env->segs[R_SS].base;
2732 new_eflags = 0; /* avoid warning */
2733#ifdef TARGET_X86_64
2734 if (shift == 2) {
2735 POPQ(sp, new_eip);
2736 POPQ(sp, new_cs);
2737 new_cs &= 0xffff;
2738 if (is_iret) {
2739 POPQ(sp, new_eflags);
2740 }
2741 } else
2742#endif
2743 if (shift == 1) {
2744 /* 32 bits */
2745 POPL(ssp, sp, sp_mask, new_eip);
2746 POPL(ssp, sp, sp_mask, new_cs);
2747 new_cs &= 0xffff;
2748 if (is_iret) {
2749 POPL(ssp, sp, sp_mask, new_eflags);
2750 if (new_eflags & VM_MASK)
2751 goto return_to_vm86;
2752 }
2753 } else {
2754 /* 16 bits */
2755 POPW(ssp, sp, sp_mask, new_eip);
2756 POPW(ssp, sp, sp_mask, new_cs);
2757 if (is_iret)
2758 POPW(ssp, sp, sp_mask, new_eflags);
2759 }
2760 LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
2761 new_cs, new_eip, shift, addend);
2762 LOG_PCALL_STATE(env);
2763 if ((new_cs & 0xfffc) == 0)
2764 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2765 if (load_segment(&e1, &e2, new_cs) != 0)
2766 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2767 if (!(e2 & DESC_S_MASK) ||
2768 !(e2 & DESC_CS_MASK))
2769 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2770 cpl = env->hflags & HF_CPL_MASK;
2771 rpl = new_cs & 3;
2772 if (rpl < cpl)
2773 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2774 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2775 if (e2 & DESC_C_MASK) {
2776 if (dpl > rpl)
2777 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2778 } else {
2779 if (dpl != rpl)
2780 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2781 }
2782 if (!(e2 & DESC_P_MASK))
2783 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2784
2785 sp += addend;
2786 if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
2787 ((env->hflags & HF_CS64_MASK) && !is_iret))) {
2788 /* return to same privilege level */
2789 cpu_x86_load_seg_cache(env, R_CS, new_cs,
2790 get_seg_base(e1, e2),
2791 get_seg_limit(e1, e2),
2792 e2);
2793 } else {
2794 /* return to different privilege level */
2795#ifdef TARGET_X86_64
2796 if (shift == 2) {
2797 POPQ(sp, new_esp);
2798 POPQ(sp, new_ss);
2799 new_ss &= 0xffff;
2800 } else
2801#endif
2802 if (shift == 1) {
2803 /* 32 bits */
2804 POPL(ssp, sp, sp_mask, new_esp);
2805 POPL(ssp, sp, sp_mask, new_ss);
2806 new_ss &= 0xffff;
2807 } else {
2808 /* 16 bits */
2809 POPW(ssp, sp, sp_mask, new_esp);
2810 POPW(ssp, sp, sp_mask, new_ss);
2811 }
2812 LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n",
2813 new_ss, new_esp);
2814 if ((new_ss & 0xfffc) == 0) {
2815#ifdef TARGET_X86_64
2816 /* NULL ss is allowed in long mode if cpl != 3*/
2817 /* XXX: test CS64 ? */
2818 if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
2819 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2820 0, 0xffffffff,
2821 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2822 DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
2823 DESC_W_MASK | DESC_A_MASK);
2824 ss_e2 = DESC_B_MASK; /* XXX: should not be needed ? */
2825 } else
2826#endif
2827 {
2828 raise_exception_err(EXCP0D_GPF, 0);
2829 }
2830 } else {
2831 if ((new_ss & 3) != rpl)
2832 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2833 if (load_segment(&ss_e1, &ss_e2, new_ss) != 0)
2834 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2835 if (!(ss_e2 & DESC_S_MASK) ||
2836 (ss_e2 & DESC_CS_MASK) ||
2837 !(ss_e2 & DESC_W_MASK))
2838 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2839 dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
2840 if (dpl != rpl)
2841 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2842 if (!(ss_e2 & DESC_P_MASK))
2843 raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
2844 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2845 get_seg_base(ss_e1, ss_e2),
2846 get_seg_limit(ss_e1, ss_e2),
2847 ss_e2);
2848 }
2849
2850 cpu_x86_load_seg_cache(env, R_CS, new_cs,
2851 get_seg_base(e1, e2),
2852 get_seg_limit(e1, e2),
2853 e2);
2854 cpu_x86_set_cpl(env, rpl);
2855 sp = new_esp;
2856#ifdef TARGET_X86_64
2857 if (env->hflags & HF_CS64_MASK)
2858 sp_mask = -1;
2859 else
2860#endif
2861 sp_mask = get_sp_mask(ss_e2);
2862
2863 /* validate data segments */
2864 validate_seg(R_ES, rpl);
2865 validate_seg(R_DS, rpl);
2866 validate_seg(R_FS, rpl);
2867 validate_seg(R_GS, rpl);
2868
2869 sp += addend;
2870 }
2871 SET_ESP(sp, sp_mask);
2872 env->eip = new_eip;
2873 if (is_iret) {
2874 /* NOTE: 'cpl' is the _old_ CPL */
2875 eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
2876 if (cpl == 0)
2877 eflags_mask |= IOPL_MASK;
2878 iopl = (env->eflags >> IOPL_SHIFT) & 3;
2879 if (cpl <= iopl)
2880 eflags_mask |= IF_MASK;
2881 if (shift == 0)
2882 eflags_mask &= 0xffff;
2883 load_eflags(new_eflags, eflags_mask);
2884 }
2885 return;
2886
2887 return_to_vm86:
2888 POPL(ssp, sp, sp_mask, new_esp);
2889 POPL(ssp, sp, sp_mask, new_ss);
2890 POPL(ssp, sp, sp_mask, new_es);
2891 POPL(ssp, sp, sp_mask, new_ds);
2892 POPL(ssp, sp, sp_mask, new_fs);
2893 POPL(ssp, sp, sp_mask, new_gs);
2894
2895 /* modify processor state */
2896 load_eflags(new_eflags, TF_MASK | AC_MASK | ID_MASK |
2897 IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | VIP_MASK);
2898 load_seg_vm(R_CS, new_cs & 0xffff);
2899 cpu_x86_set_cpl(env, 3);
2900 load_seg_vm(R_SS, new_ss & 0xffff);
2901 load_seg_vm(R_ES, new_es & 0xffff);
2902 load_seg_vm(R_DS, new_ds & 0xffff);
2903 load_seg_vm(R_FS, new_fs & 0xffff);
2904 load_seg_vm(R_GS, new_gs & 0xffff);
2905
2906 env->eip = new_eip & 0xffff;
2907 ESP = new_esp;
2908}
2909
2910void helper_iret_protected(int shift, int next_eip)
2911{
2912 int tss_selector, type;
2913 uint32_t e1, e2;
2914
2915 /* specific case for TSS */
2916 if (env->eflags & NT_MASK) {
2917#ifdef TARGET_X86_64
2918 if (env->hflags & HF_LMA_MASK)
2919 raise_exception_err(EXCP0D_GPF, 0);
2920#endif
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002921 tss_selector = cpu_lduw_kernel(env, env->tr.base + 0);
Jun Nakajima86797932011-01-29 14:24:24 -08002922 if (tss_selector & 4)
2923 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2924 if (load_segment(&e1, &e2, tss_selector) != 0)
2925 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2926 type = (e2 >> DESC_TYPE_SHIFT) & 0x17;
2927 /* NOTE: we check both segment and busy TSS */
2928 if (type != 3)
2929 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2930 switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip);
2931 } else {
2932 helper_ret_protected(shift, 1, 0);
2933 }
2934 env->hflags2 &= ~HF2_NMI_MASK;
Jun Nakajima86797932011-01-29 14:24:24 -08002935}
2936
2937void helper_lret_protected(int shift, int addend)
2938{
2939 helper_ret_protected(shift, 0, addend);
Jun Nakajima86797932011-01-29 14:24:24 -08002940}
2941
2942void helper_sysenter(void)
2943{
2944 if (env->sysenter_cs == 0) {
2945 raise_exception_err(EXCP0D_GPF, 0);
2946 }
2947 env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
2948 cpu_x86_set_cpl(env, 0);
2949
2950#ifdef TARGET_X86_64
2951 if (env->hflags & HF_LMA_MASK) {
2952 cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2953 0, 0xffffffff,
2954 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2955 DESC_S_MASK |
2956 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
2957 } else
2958#endif
2959 {
2960 cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2961 0, 0xffffffff,
2962 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2963 DESC_S_MASK |
2964 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
2965 }
2966 cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
2967 0, 0xffffffff,
2968 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2969 DESC_S_MASK |
2970 DESC_W_MASK | DESC_A_MASK);
2971 ESP = env->sysenter_esp;
2972 EIP = env->sysenter_eip;
2973}
2974
2975void helper_sysexit(int dflag)
2976{
2977 int cpl;
2978
2979 cpl = env->hflags & HF_CPL_MASK;
2980 if (env->sysenter_cs == 0 || cpl != 0) {
2981 raise_exception_err(EXCP0D_GPF, 0);
2982 }
2983 cpu_x86_set_cpl(env, 3);
2984#ifdef TARGET_X86_64
2985 if (dflag == 2) {
2986 cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | 3,
2987 0, 0xffffffff,
2988 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2989 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2990 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
2991 cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | 3,
2992 0, 0xffffffff,
2993 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2994 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2995 DESC_W_MASK | DESC_A_MASK);
2996 } else
2997#endif
2998 {
2999 cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3,
3000 0, 0xffffffff,
3001 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
3002 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
3003 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
3004 cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3,
3005 0, 0xffffffff,
3006 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
3007 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
3008 DESC_W_MASK | DESC_A_MASK);
3009 }
3010 ESP = ECX;
3011 EIP = EDX;
Jun Nakajima86797932011-01-29 14:24:24 -08003012}
3013
3014#if defined(CONFIG_USER_ONLY)
3015target_ulong helper_read_crN(int reg)
3016{
3017 return 0;
3018}
3019
3020void helper_write_crN(int reg, target_ulong t0)
3021{
3022}
3023
3024void helper_movl_drN_T0(int reg, target_ulong t0)
3025{
3026}
3027#else
3028target_ulong helper_read_crN(int reg)
3029{
3030 target_ulong val;
3031
3032 helper_svm_check_intercept_param(SVM_EXIT_READ_CR0 + reg, 0);
3033 switch(reg) {
3034 default:
3035 val = env->cr[reg];
3036 break;
3037 case 8:
3038 if (!(env->hflags2 & HF2_VINTR_MASK)) {
3039 val = cpu_get_apic_tpr(env);
3040 } else {
3041 val = env->v_tpr;
3042 }
3043 break;
3044 }
3045 return val;
3046}
3047
3048void helper_write_crN(int reg, target_ulong t0)
3049{
3050 helper_svm_check_intercept_param(SVM_EXIT_WRITE_CR0 + reg, 0);
3051 switch(reg) {
3052 case 0:
3053 cpu_x86_update_cr0(env, t0);
3054 break;
3055 case 3:
3056 cpu_x86_update_cr3(env, t0);
3057 break;
3058 case 4:
3059 cpu_x86_update_cr4(env, t0);
3060 break;
3061 case 8:
3062 if (!(env->hflags2 & HF2_VINTR_MASK)) {
3063 cpu_set_apic_tpr(env, t0);
3064 }
3065 env->v_tpr = t0 & 0x0f;
3066 break;
3067 default:
3068 env->cr[reg] = t0;
3069 break;
3070 }
3071}
3072
3073void helper_movl_drN_T0(int reg, target_ulong t0)
3074{
3075 int i;
3076
3077 if (reg < 4) {
3078 hw_breakpoint_remove(env, reg);
3079 env->dr[reg] = t0;
3080 hw_breakpoint_insert(env, reg);
3081 } else if (reg == 7) {
3082 for (i = 0; i < 4; i++)
3083 hw_breakpoint_remove(env, i);
3084 env->dr[7] = t0;
3085 for (i = 0; i < 4; i++)
3086 hw_breakpoint_insert(env, i);
3087 } else
3088 env->dr[reg] = t0;
3089}
3090#endif
3091
3092void helper_lmsw(target_ulong t0)
3093{
3094 /* only 4 lower bits of CR0 are modified. PE cannot be set to zero
3095 if already set to one. */
3096 t0 = (env->cr[0] & ~0xe) | (t0 & 0xf);
3097 helper_write_crN(0, t0);
3098}
3099
3100void helper_clts(void)
3101{
3102 env->cr[0] &= ~CR0_TS_MASK;
3103 env->hflags &= ~HF_TS_MASK;
3104}
3105
3106void helper_invlpg(target_ulong addr)
3107{
3108 helper_svm_check_intercept_param(SVM_EXIT_INVLPG, 0);
3109 tlb_flush_page(env, addr);
3110}
3111
3112void helper_rdtsc(void)
3113{
3114 uint64_t val;
3115
3116 if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
3117 raise_exception(EXCP0D_GPF);
3118 }
3119 helper_svm_check_intercept_param(SVM_EXIT_RDTSC, 0);
3120
3121 val = cpu_get_tsc(env) + env->tsc_offset;
3122 EAX = (uint32_t)(val);
3123 EDX = (uint32_t)(val >> 32);
3124}
3125
3126void helper_rdpmc(void)
3127{
3128 if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
3129 raise_exception(EXCP0D_GPF);
3130 }
3131 helper_svm_check_intercept_param(SVM_EXIT_RDPMC, 0);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02003132
Jun Nakajima86797932011-01-29 14:24:24 -08003133 /* currently unimplemented */
3134 raise_exception_err(EXCP06_ILLOP, 0);
3135}
3136
3137#if defined(CONFIG_USER_ONLY)
3138void helper_wrmsr(void)
3139{
3140}
3141
3142void helper_rdmsr(void)
3143{
3144}
3145#else
3146void helper_wrmsr(void)
3147{
3148 uint64_t val;
3149
3150 helper_svm_check_intercept_param(SVM_EXIT_MSR, 1);
3151
3152 val = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
3153
3154 switch((uint32_t)ECX) {
3155 case MSR_IA32_SYSENTER_CS:
3156 env->sysenter_cs = val & 0xffff;
3157 break;
3158 case MSR_IA32_SYSENTER_ESP:
3159 env->sysenter_esp = val;
3160 break;
3161 case MSR_IA32_SYSENTER_EIP:
3162 env->sysenter_eip = val;
3163 break;
3164 case MSR_IA32_APICBASE:
3165 cpu_set_apic_base(env, val);
3166 break;
3167 case MSR_EFER:
3168 {
3169 uint64_t update_mask;
3170 update_mask = 0;
3171 if (env->cpuid_ext2_features & CPUID_EXT2_SYSCALL)
3172 update_mask |= MSR_EFER_SCE;
3173 if (env->cpuid_ext2_features & CPUID_EXT2_LM)
3174 update_mask |= MSR_EFER_LME;
3175 if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
3176 update_mask |= MSR_EFER_FFXSR;
3177 if (env->cpuid_ext2_features & CPUID_EXT2_NX)
3178 update_mask |= MSR_EFER_NXE;
3179 if (env->cpuid_ext3_features & CPUID_EXT3_SVM)
3180 update_mask |= MSR_EFER_SVME;
3181 if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
3182 update_mask |= MSR_EFER_FFXSR;
3183 cpu_load_efer(env, (env->efer & ~update_mask) |
3184 (val & update_mask));
3185 }
3186 break;
3187 case MSR_STAR:
3188 env->star = val;
3189 break;
3190 case MSR_PAT:
3191 env->pat = val;
3192 break;
3193 case MSR_VM_HSAVE_PA:
3194 env->vm_hsave = val;
3195 break;
3196#ifdef TARGET_X86_64
3197 case MSR_LSTAR:
3198 env->lstar = val;
3199 break;
3200 case MSR_CSTAR:
3201 env->cstar = val;
3202 break;
3203 case MSR_FMASK:
3204 env->fmask = val;
3205 break;
3206 case MSR_FSBASE:
3207 env->segs[R_FS].base = val;
3208 break;
3209 case MSR_GSBASE:
3210 env->segs[R_GS].base = val;
3211 break;
3212 case MSR_KERNELGSBASE:
3213 env->kernelgsbase = val;
3214 break;
3215#endif
3216 case MSR_MTRRphysBase(0):
3217 case MSR_MTRRphysBase(1):
3218 case MSR_MTRRphysBase(2):
3219 case MSR_MTRRphysBase(3):
3220 case MSR_MTRRphysBase(4):
3221 case MSR_MTRRphysBase(5):
3222 case MSR_MTRRphysBase(6):
3223 case MSR_MTRRphysBase(7):
3224 env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base = val;
3225 break;
3226 case MSR_MTRRphysMask(0):
3227 case MSR_MTRRphysMask(1):
3228 case MSR_MTRRphysMask(2):
3229 case MSR_MTRRphysMask(3):
3230 case MSR_MTRRphysMask(4):
3231 case MSR_MTRRphysMask(5):
3232 case MSR_MTRRphysMask(6):
3233 case MSR_MTRRphysMask(7):
3234 env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask = val;
3235 break;
3236 case MSR_MTRRfix64K_00000:
3237 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix64K_00000] = val;
3238 break;
3239 case MSR_MTRRfix16K_80000:
3240 case MSR_MTRRfix16K_A0000:
3241 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1] = val;
3242 break;
3243 case MSR_MTRRfix4K_C0000:
3244 case MSR_MTRRfix4K_C8000:
3245 case MSR_MTRRfix4K_D0000:
3246 case MSR_MTRRfix4K_D8000:
3247 case MSR_MTRRfix4K_E0000:
3248 case MSR_MTRRfix4K_E8000:
3249 case MSR_MTRRfix4K_F0000:
3250 case MSR_MTRRfix4K_F8000:
3251 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3] = val;
3252 break;
3253 case MSR_MTRRdefType:
3254 env->mtrr_deftype = val;
3255 break;
3256 case MSR_MCG_STATUS:
3257 env->mcg_status = val;
3258 break;
3259 case MSR_MCG_CTL:
3260 if ((env->mcg_cap & MCG_CTL_P)
3261 && (val == 0 || val == ~(uint64_t)0))
3262 env->mcg_ctl = val;
3263 break;
3264 default:
3265 if ((uint32_t)ECX >= MSR_MC0_CTL
3266 && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
3267 uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
3268 if ((offset & 0x3) != 0
3269 || (val == 0 || val == ~(uint64_t)0))
3270 env->mce_banks[offset] = val;
3271 break;
3272 }
3273 /* XXX: exception ? */
3274 break;
3275 }
3276}
3277
3278void helper_rdmsr(void)
3279{
3280 uint64_t val;
3281
3282 helper_svm_check_intercept_param(SVM_EXIT_MSR, 0);
3283
3284 switch((uint32_t)ECX) {
3285 case MSR_IA32_SYSENTER_CS:
3286 val = env->sysenter_cs;
3287 break;
3288 case MSR_IA32_SYSENTER_ESP:
3289 val = env->sysenter_esp;
3290 break;
3291 case MSR_IA32_SYSENTER_EIP:
3292 val = env->sysenter_eip;
3293 break;
3294 case MSR_IA32_APICBASE:
3295 val = cpu_get_apic_base(env);
3296 break;
3297 case MSR_EFER:
3298 val = env->efer;
3299 break;
3300 case MSR_STAR:
3301 val = env->star;
3302 break;
3303 case MSR_PAT:
3304 val = env->pat;
3305 break;
3306 case MSR_VM_HSAVE_PA:
3307 val = env->vm_hsave;
3308 break;
3309 case MSR_IA32_PERF_STATUS:
3310 /* tsc_increment_by_tick */
3311 val = 1000ULL;
3312 /* CPU multiplier */
3313 val |= (((uint64_t)4ULL) << 40);
3314 break;
3315#ifdef TARGET_X86_64
3316 case MSR_LSTAR:
3317 val = env->lstar;
3318 break;
3319 case MSR_CSTAR:
3320 val = env->cstar;
3321 break;
3322 case MSR_FMASK:
3323 val = env->fmask;
3324 break;
3325 case MSR_FSBASE:
3326 val = env->segs[R_FS].base;
3327 break;
3328 case MSR_GSBASE:
3329 val = env->segs[R_GS].base;
3330 break;
3331 case MSR_KERNELGSBASE:
3332 val = env->kernelgsbase;
3333 break;
3334#endif
Jun Nakajima86797932011-01-29 14:24:24 -08003335 case MSR_MTRRphysBase(0):
3336 case MSR_MTRRphysBase(1):
3337 case MSR_MTRRphysBase(2):
3338 case MSR_MTRRphysBase(3):
3339 case MSR_MTRRphysBase(4):
3340 case MSR_MTRRphysBase(5):
3341 case MSR_MTRRphysBase(6):
3342 case MSR_MTRRphysBase(7):
3343 val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base;
3344 break;
3345 case MSR_MTRRphysMask(0):
3346 case MSR_MTRRphysMask(1):
3347 case MSR_MTRRphysMask(2):
3348 case MSR_MTRRphysMask(3):
3349 case MSR_MTRRphysMask(4):
3350 case MSR_MTRRphysMask(5):
3351 case MSR_MTRRphysMask(6):
3352 case MSR_MTRRphysMask(7):
3353 val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask;
3354 break;
3355 case MSR_MTRRfix64K_00000:
3356 val = env->mtrr_fixed[0];
3357 break;
3358 case MSR_MTRRfix16K_80000:
3359 case MSR_MTRRfix16K_A0000:
3360 val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1];
3361 break;
3362 case MSR_MTRRfix4K_C0000:
3363 case MSR_MTRRfix4K_C8000:
3364 case MSR_MTRRfix4K_D0000:
3365 case MSR_MTRRfix4K_D8000:
3366 case MSR_MTRRfix4K_E0000:
3367 case MSR_MTRRfix4K_E8000:
3368 case MSR_MTRRfix4K_F0000:
3369 case MSR_MTRRfix4K_F8000:
3370 val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3];
3371 break;
3372 case MSR_MTRRdefType:
3373 val = env->mtrr_deftype;
3374 break;
3375 case MSR_MTRRcap:
3376 if (env->cpuid_features & CPUID_MTRR)
3377 val = MSR_MTRRcap_VCNT | MSR_MTRRcap_FIXRANGE_SUPPORT | MSR_MTRRcap_WC_SUPPORTED;
3378 else
3379 /* XXX: exception ? */
3380 val = 0;
3381 break;
3382 case MSR_MCG_CAP:
3383 val = env->mcg_cap;
3384 break;
3385 case MSR_MCG_CTL:
3386 if (env->mcg_cap & MCG_CTL_P)
3387 val = env->mcg_ctl;
3388 else
3389 val = 0;
3390 break;
3391 case MSR_MCG_STATUS:
3392 val = env->mcg_status;
3393 break;
3394 default:
3395 if ((uint32_t)ECX >= MSR_MC0_CTL
3396 && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
3397 uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
3398 val = env->mce_banks[offset];
3399 break;
3400 }
3401 /* XXX: exception ? */
3402 val = 0;
3403 break;
3404 }
3405 EAX = (uint32_t)(val);
3406 EDX = (uint32_t)(val >> 32);
3407}
3408#endif
3409
3410target_ulong helper_lsl(target_ulong selector1)
3411{
3412 unsigned int limit;
3413 uint32_t e1, e2, eflags, selector;
3414 int rpl, dpl, cpl, type;
3415
3416 selector = selector1 & 0xffff;
3417 eflags = helper_cc_compute_all(CC_OP);
3418 if ((selector & 0xfffc) == 0)
3419 goto fail;
3420 if (load_segment(&e1, &e2, selector) != 0)
3421 goto fail;
3422 rpl = selector & 3;
3423 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3424 cpl = env->hflags & HF_CPL_MASK;
3425 if (e2 & DESC_S_MASK) {
3426 if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
3427 /* conforming */
3428 } else {
3429 if (dpl < cpl || dpl < rpl)
3430 goto fail;
3431 }
3432 } else {
3433 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
3434 switch(type) {
3435 case 1:
3436 case 2:
3437 case 3:
3438 case 9:
3439 case 11:
3440 break;
3441 default:
3442 goto fail;
3443 }
3444 if (dpl < cpl || dpl < rpl) {
3445 fail:
3446 CC_SRC = eflags & ~CC_Z;
3447 return 0;
3448 }
3449 }
3450 limit = get_seg_limit(e1, e2);
3451 CC_SRC = eflags | CC_Z;
3452 return limit;
3453}
3454
3455target_ulong helper_lar(target_ulong selector1)
3456{
3457 uint32_t e1, e2, eflags, selector;
3458 int rpl, dpl, cpl, type;
3459
3460 selector = selector1 & 0xffff;
3461 eflags = helper_cc_compute_all(CC_OP);
3462 if ((selector & 0xfffc) == 0)
3463 goto fail;
3464 if (load_segment(&e1, &e2, selector) != 0)
3465 goto fail;
3466 rpl = selector & 3;
3467 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3468 cpl = env->hflags & HF_CPL_MASK;
3469 if (e2 & DESC_S_MASK) {
3470 if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
3471 /* conforming */
3472 } else {
3473 if (dpl < cpl || dpl < rpl)
3474 goto fail;
3475 }
3476 } else {
3477 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
3478 switch(type) {
3479 case 1:
3480 case 2:
3481 case 3:
3482 case 4:
3483 case 5:
3484 case 9:
3485 case 11:
3486 case 12:
3487 break;
3488 default:
3489 goto fail;
3490 }
3491 if (dpl < cpl || dpl < rpl) {
3492 fail:
3493 CC_SRC = eflags & ~CC_Z;
3494 return 0;
3495 }
3496 }
3497 CC_SRC = eflags | CC_Z;
3498 return e2 & 0x00f0ff00;
3499}
3500
3501void helper_verr(target_ulong selector1)
3502{
3503 uint32_t e1, e2, eflags, selector;
3504 int rpl, dpl, cpl;
3505
3506 selector = selector1 & 0xffff;
3507 eflags = helper_cc_compute_all(CC_OP);
3508 if ((selector & 0xfffc) == 0)
3509 goto fail;
3510 if (load_segment(&e1, &e2, selector) != 0)
3511 goto fail;
3512 if (!(e2 & DESC_S_MASK))
3513 goto fail;
3514 rpl = selector & 3;
3515 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3516 cpl = env->hflags & HF_CPL_MASK;
3517 if (e2 & DESC_CS_MASK) {
3518 if (!(e2 & DESC_R_MASK))
3519 goto fail;
3520 if (!(e2 & DESC_C_MASK)) {
3521 if (dpl < cpl || dpl < rpl)
3522 goto fail;
3523 }
3524 } else {
3525 if (dpl < cpl || dpl < rpl) {
3526 fail:
3527 CC_SRC = eflags & ~CC_Z;
3528 return;
3529 }
3530 }
3531 CC_SRC = eflags | CC_Z;
3532}
3533
3534void helper_verw(target_ulong selector1)
3535{
3536 uint32_t e1, e2, eflags, selector;
3537 int rpl, dpl, cpl;
3538
3539 selector = selector1 & 0xffff;
3540 eflags = helper_cc_compute_all(CC_OP);
3541 if ((selector & 0xfffc) == 0)
3542 goto fail;
3543 if (load_segment(&e1, &e2, selector) != 0)
3544 goto fail;
3545 if (!(e2 & DESC_S_MASK))
3546 goto fail;
3547 rpl = selector & 3;
3548 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3549 cpl = env->hflags & HF_CPL_MASK;
3550 if (e2 & DESC_CS_MASK) {
3551 goto fail;
3552 } else {
3553 if (dpl < cpl || dpl < rpl)
3554 goto fail;
3555 if (!(e2 & DESC_W_MASK)) {
3556 fail:
3557 CC_SRC = eflags & ~CC_Z;
3558 return;
3559 }
3560 }
3561 CC_SRC = eflags | CC_Z;
3562}
3563
3564/* x87 FPU helpers */
3565
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003566static inline double floatx80_to_double(CPUX86State *env, floatx80 a)
3567{
3568 union {
3569 float64 f64;
3570 double d;
3571 } u;
3572
3573 u.f64 = floatx80_to_float64(a, &env->fp_status);
3574 return u.d;
3575}
3576
3577static inline floatx80 double_to_floatx80(CPUX86State *env, double a)
3578{
3579 union {
3580 float64 f64;
3581 double d;
3582 } u;
3583
3584 u.d = a;
3585 return float64_to_floatx80(u.f64, &env->fp_status);
3586}
3587
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003588static void fpu_set_exception(CPUX86State *env, int mask)
Jun Nakajima86797932011-01-29 14:24:24 -08003589{
3590 env->fpus |= mask;
3591 if (env->fpus & (~env->fpuc & FPUC_EM))
3592 env->fpus |= FPUS_SE | FPUS_B;
3593}
3594
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003595static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b)
Jun Nakajima86797932011-01-29 14:24:24 -08003596{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003597 if (floatx80_is_zero(b)) {
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003598 fpu_set_exception(env, FPUS_ZE);
David 'Digit' Turner763b5972014-03-26 17:10:52 +01003599 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003600 return floatx80_div(a, b, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003601}
3602
3603static void fpu_raise_exception(void)
3604{
3605 if (env->cr[0] & CR0_NE_MASK) {
3606 raise_exception(EXCP10_COPR);
3607 }
3608#if !defined(CONFIG_USER_ONLY)
3609 else {
3610 cpu_set_ferr(env);
3611 }
3612#endif
3613}
3614
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003615void helper_flds_FT0(CPUX86State *env, uint32_t val)
Jun Nakajima86797932011-01-29 14:24:24 -08003616{
3617 union {
3618 float32 f;
3619 uint32_t i;
3620 } u;
3621 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003622 FT0 = float32_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003623}
3624
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003625void helper_fldl_FT0(CPUX86State *env, uint64_t val)
Jun Nakajima86797932011-01-29 14:24:24 -08003626{
3627 union {
3628 float64 f;
3629 uint64_t i;
3630 } u;
3631 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003632 FT0 = float64_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003633}
3634
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003635void helper_fildl_FT0(CPUX86State *env, int32_t val)
Jun Nakajima86797932011-01-29 14:24:24 -08003636{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003637 FT0 = int32_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003638}
3639
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003640void helper_flds_ST0(CPUX86State *env, uint32_t val)
Jun Nakajima86797932011-01-29 14:24:24 -08003641{
3642 int new_fpstt;
3643 union {
3644 float32 f;
3645 uint32_t i;
3646 } u;
3647 new_fpstt = (env->fpstt - 1) & 7;
3648 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003649 env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003650 env->fpstt = new_fpstt;
3651 env->fptags[new_fpstt] = 0; /* validate stack entry */
3652}
3653
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003654void helper_fldl_ST0(CPUX86State *env, uint64_t val)
Jun Nakajima86797932011-01-29 14:24:24 -08003655{
3656 int new_fpstt;
3657 union {
3658 float64 f;
3659 uint64_t i;
3660 } u;
3661 new_fpstt = (env->fpstt - 1) & 7;
3662 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003663 env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003664 env->fpstt = new_fpstt;
3665 env->fptags[new_fpstt] = 0; /* validate stack entry */
3666}
3667
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003668void helper_fildl_ST0(CPUX86State *env, int32_t val)
Jun Nakajima86797932011-01-29 14:24:24 -08003669{
3670 int new_fpstt;
3671 new_fpstt = (env->fpstt - 1) & 7;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003672 env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003673 env->fpstt = new_fpstt;
3674 env->fptags[new_fpstt] = 0; /* validate stack entry */
3675}
3676
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003677void helper_fildll_ST0(CPUX86State *env, int64_t val)
Jun Nakajima86797932011-01-29 14:24:24 -08003678{
3679 int new_fpstt;
3680 new_fpstt = (env->fpstt - 1) & 7;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003681 env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003682 env->fpstt = new_fpstt;
3683 env->fptags[new_fpstt] = 0; /* validate stack entry */
3684}
3685
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003686uint32_t helper_fsts_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003687{
3688 union {
3689 float32 f;
3690 uint32_t i;
3691 } u;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003692 u.f = floatx80_to_float32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003693 return u.i;
3694}
3695
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003696uint64_t helper_fstl_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003697{
3698 union {
3699 float64 f;
3700 uint64_t i;
3701 } u;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003702 u.f = floatx80_to_float64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003703 return u.i;
3704}
3705
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003706int32_t helper_fist_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003707{
3708 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003709 val = floatx80_to_int32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003710 if (val != (int16_t)val)
3711 val = -32768;
3712 return val;
3713}
3714
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003715int32_t helper_fistl_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003716{
3717 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003718 val = floatx80_to_int32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003719 return val;
3720}
3721
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003722int64_t helper_fistll_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003723{
3724 int64_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003725 val = floatx80_to_int64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003726 return val;
3727}
3728
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003729int32_t helper_fistt_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003730{
3731 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003732 val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003733 if (val != (int16_t)val)
3734 val = -32768;
3735 return val;
3736}
3737
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003738int32_t helper_fisttl_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003739{
3740 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003741 val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003742 return val;
3743}
3744
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003745int64_t helper_fisttll_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003746{
3747 int64_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003748 val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003749 return val;
3750}
3751
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003752void helper_fldt_ST0(CPUX86State *env, target_ulong ptr)
Jun Nakajima86797932011-01-29 14:24:24 -08003753{
3754 int new_fpstt;
3755 new_fpstt = (env->fpstt - 1) & 7;
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003756 env->fpregs[new_fpstt].d = helper_fldt(env, ptr);
Jun Nakajima86797932011-01-29 14:24:24 -08003757 env->fpstt = new_fpstt;
3758 env->fptags[new_fpstt] = 0; /* validate stack entry */
3759}
3760
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003761void helper_fstt_ST0(CPUX86State *env, target_ulong ptr)
Jun Nakajima86797932011-01-29 14:24:24 -08003762{
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003763 helper_fstt(env, ST0, ptr);
Jun Nakajima86797932011-01-29 14:24:24 -08003764}
3765
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003766void helper_fpush(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003767{
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003768 fpush(env);
Jun Nakajima86797932011-01-29 14:24:24 -08003769}
3770
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003771void helper_fpop(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003772{
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003773 fpop(env);
Jun Nakajima86797932011-01-29 14:24:24 -08003774}
3775
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003776void helper_fdecstp(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003777{
3778 env->fpstt = (env->fpstt - 1) & 7;
3779 env->fpus &= (~0x4700);
3780}
3781
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003782void helper_fincstp(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003783{
3784 env->fpstt = (env->fpstt + 1) & 7;
3785 env->fpus &= (~0x4700);
3786}
3787
3788/* FPU move */
3789
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003790void helper_ffree_STN(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003791{
3792 env->fptags[(env->fpstt + st_index) & 7] = 1;
3793}
3794
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003795void helper_fmov_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003796{
3797 ST0 = FT0;
3798}
3799
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003800void helper_fmov_FT0_STN(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003801{
3802 FT0 = ST(st_index);
3803}
3804
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003805void helper_fmov_ST0_STN(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003806{
3807 ST0 = ST(st_index);
3808}
3809
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003810void helper_fmov_STN_ST0(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003811{
3812 ST(st_index) = ST0;
3813}
3814
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003815void helper_fxchg_ST0_STN(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003816{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003817 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08003818 tmp = ST(st_index);
3819 ST(st_index) = ST0;
3820 ST0 = tmp;
3821}
3822
3823/* FPU operations */
3824
3825static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500};
3826
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003827void helper_fcom_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003828{
3829 int ret;
3830
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003831 ret = floatx80_compare(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003832 env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
3833}
3834
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003835void helper_fucom_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003836{
3837 int ret;
3838
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003839 ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003840 env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1];
3841}
3842
3843static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
3844
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003845void helper_fcomi_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003846{
3847 int eflags;
3848 int ret;
3849
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003850 ret = floatx80_compare(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003851 eflags = helper_cc_compute_all(CC_OP);
3852 eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
3853 CC_SRC = eflags;
3854}
3855
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003856void helper_fucomi_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003857{
3858 int eflags;
3859 int ret;
3860
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003861 ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003862 eflags = helper_cc_compute_all(CC_OP);
3863 eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
3864 CC_SRC = eflags;
3865}
3866
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003867void helper_fadd_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003868{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003869 ST0 = floatx80_add(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003870}
3871
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003872void helper_fmul_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003873{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003874 ST0 = floatx80_mul(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003875}
3876
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003877void helper_fsub_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003878{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003879 ST0 = floatx80_sub(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003880}
3881
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003882void helper_fsubr_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003883{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003884 ST0 = floatx80_sub(FT0, ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003885}
3886
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003887void helper_fdiv_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003888{
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003889 ST0 = helper_fdiv(env, ST0, FT0);
Jun Nakajima86797932011-01-29 14:24:24 -08003890}
3891
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003892void helper_fdivr_ST0_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003893{
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003894 ST0 = helper_fdiv(env, FT0, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003895}
3896
3897/* fp operations between STN and ST0 */
3898
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003899void helper_fadd_STN_ST0(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003900{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003901 ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003902}
3903
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003904void helper_fmul_STN_ST0(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003905{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003906 ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003907}
3908
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003909void helper_fsub_STN_ST0(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003910{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003911 ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003912}
3913
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003914void helper_fsubr_STN_ST0(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003915{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003916 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003917 p = &ST(st_index);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003918 *p = floatx80_sub(ST0, *p, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003919}
3920
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003921void helper_fdiv_STN_ST0(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003922{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003923 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003924 p = &ST(st_index);
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003925 *p = helper_fdiv(env, *p, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003926}
3927
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003928void helper_fdivr_STN_ST0(CPUX86State *env, int st_index)
Jun Nakajima86797932011-01-29 14:24:24 -08003929{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003930 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003931 p = &ST(st_index);
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003932 *p = helper_fdiv(env, ST0, *p);
Jun Nakajima86797932011-01-29 14:24:24 -08003933}
3934
3935/* misc FPU operations */
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003936void helper_fchs_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003937{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003938 ST0 = floatx80_chs(ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003939}
3940
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003941void helper_fabs_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003942{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003943 ST0 = floatx80_abs(ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003944}
3945
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003946void helper_fld1_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003947{
3948 ST0 = f15rk[1];
3949}
3950
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003951void helper_fldl2t_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003952{
3953 ST0 = f15rk[6];
3954}
3955
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003956void helper_fldl2e_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003957{
3958 ST0 = f15rk[5];
3959}
3960
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003961void helper_fldpi_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003962{
3963 ST0 = f15rk[2];
3964}
3965
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003966void helper_fldlg2_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003967{
3968 ST0 = f15rk[3];
3969}
3970
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003971void helper_fldln2_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003972{
3973 ST0 = f15rk[4];
3974}
3975
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003976void helper_fldz_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003977{
3978 ST0 = f15rk[0];
3979}
3980
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003981void helper_fldz_FT0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003982{
3983 FT0 = f15rk[0];
3984}
3985
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003986uint32_t helper_fnstsw(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003987{
3988 return (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
3989}
3990
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003991uint32_t helper_fnstcw(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003992{
3993 return env->fpuc;
3994}
3995
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02003996static void update_fp_status(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08003997{
3998 int rnd_type;
3999
4000 /* set rounding mode */
4001 switch(env->fpuc & RC_MASK) {
4002 default:
4003 case RC_NEAR:
4004 rnd_type = float_round_nearest_even;
4005 break;
4006 case RC_DOWN:
4007 rnd_type = float_round_down;
4008 break;
4009 case RC_UP:
4010 rnd_type = float_round_up;
4011 break;
4012 case RC_CHOP:
4013 rnd_type = float_round_to_zero;
4014 break;
4015 }
4016 set_float_rounding_mode(rnd_type, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004017 switch((env->fpuc >> 8) & 3) {
4018 case 0:
4019 rnd_type = 32;
4020 break;
4021 case 2:
4022 rnd_type = 64;
4023 break;
4024 case 3:
4025 default:
4026 rnd_type = 80;
4027 break;
4028 }
4029 set_floatx80_rounding_precision(rnd_type, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004030}
4031
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004032void helper_fldcw(CPUX86State *env, uint32_t val)
Jun Nakajima86797932011-01-29 14:24:24 -08004033{
4034 env->fpuc = val;
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004035 update_fp_status(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004036}
4037
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004038void helper_fclex(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004039{
4040 env->fpus &= 0x7f00;
4041}
4042
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004043void helper_fwait(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004044{
4045 if (env->fpus & FPUS_SE)
4046 fpu_raise_exception();
4047}
4048
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004049void helper_fninit(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004050{
4051 env->fpus = 0;
4052 env->fpstt = 0;
4053 env->fpuc = 0x37f;
4054 env->fptags[0] = 1;
4055 env->fptags[1] = 1;
4056 env->fptags[2] = 1;
4057 env->fptags[3] = 1;
4058 env->fptags[4] = 1;
4059 env->fptags[5] = 1;
4060 env->fptags[6] = 1;
4061 env->fptags[7] = 1;
4062}
4063
4064/* BCD ops */
4065
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004066void helper_fbld_ST0(CPUX86State *env, target_ulong ptr)
Jun Nakajima86797932011-01-29 14:24:24 -08004067{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004068 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004069 uint64_t val;
4070 unsigned int v;
4071 int i;
4072
4073 val = 0;
4074 for(i = 8; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004075 v = cpu_ldub_data(env, ptr + i);
Jun Nakajima86797932011-01-29 14:24:24 -08004076 val = (val * 100) + ((v >> 4) * 10) + (v & 0xf);
4077 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004078 tmp = int64_to_floatx80(val, &env->fp_status);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004079 if (cpu_ldub_data(env, ptr + 9) & 0x80) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004080 floatx80_chs(tmp);
4081 }
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004082 fpush(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004083 ST0 = tmp;
4084}
4085
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004086void helper_fbst_ST0(CPUX86State *env, target_ulong ptr)
Jun Nakajima86797932011-01-29 14:24:24 -08004087{
4088 int v;
4089 target_ulong mem_ref, mem_end;
4090 int64_t val;
4091
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004092 val = floatx80_to_int64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004093 mem_ref = ptr;
4094 mem_end = mem_ref + 9;
4095 if (val < 0) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004096 cpu_stb_data(env, mem_end, 0x80);
Jun Nakajima86797932011-01-29 14:24:24 -08004097 val = -val;
4098 } else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004099 cpu_stb_data(env, mem_end, 0x00);
Jun Nakajima86797932011-01-29 14:24:24 -08004100 }
4101 while (mem_ref < mem_end) {
4102 if (val == 0)
4103 break;
4104 v = val % 100;
4105 val = val / 100;
4106 v = ((v / 10) << 4) | (v % 10);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004107 cpu_stb_data(env, mem_ref++, v);
Jun Nakajima86797932011-01-29 14:24:24 -08004108 }
4109 while (mem_ref < mem_end) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004110 cpu_stb_data(env, mem_ref++, 0);
Jun Nakajima86797932011-01-29 14:24:24 -08004111 }
4112}
4113
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004114void helper_f2xm1(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004115{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004116 double val = floatx80_to_double(env, ST0);
4117 val = pow(2.0, val) - 1.0;
4118 ST0 = double_to_floatx80(env, val);
Jun Nakajima86797932011-01-29 14:24:24 -08004119}
4120
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004121void helper_fyl2x(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004122{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004123 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004124
Jun Nakajima86797932011-01-29 14:24:24 -08004125 if (fptemp>0.0){
4126 fptemp = log(fptemp)/log(2.0); /* log2(ST) */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004127 fptemp *= floatx80_to_double(env, ST1);
4128 ST1 = double_to_floatx80(env, fptemp);
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004129 fpop(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004130 } else {
4131 env->fpus &= (~0x4700);
4132 env->fpus |= 0x400;
4133 }
4134}
4135
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004136void helper_fptan(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004137{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004138 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004139
Jun Nakajima86797932011-01-29 14:24:24 -08004140 if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4141 env->fpus |= 0x400;
4142 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004143 fptemp = tan(fptemp);
4144 ST0 = double_to_floatx80(env, fptemp);
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004145 fpush(env);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004146 ST0 = floatx80_one;
Jun Nakajima86797932011-01-29 14:24:24 -08004147 env->fpus &= (~0x400); /* C2 <-- 0 */
4148 /* the above code is for |arg| < 2**52 only */
4149 }
4150}
4151
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004152void helper_fpatan(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004153{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004154 double fptemp, fpsrcop;
Jun Nakajima86797932011-01-29 14:24:24 -08004155
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004156 fpsrcop = floatx80_to_double(env, ST1);
4157 fptemp = floatx80_to_double(env, ST0);
4158 ST1 = double_to_floatx80(env, atan2(fpsrcop,fptemp));
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004159 fpop(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004160}
4161
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004162void helper_fxtract(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004163{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004164 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004165 unsigned int expdif;
4166
4167 temp.d = ST0;
4168 expdif = EXPD(temp) - EXPBIAS;
4169 /*DP exponent bias*/
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004170 ST0 = int32_to_floatx80(expdif, &env->fp_status);
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004171 fpush(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004172 BIASEXPONENT(temp);
4173 ST0 = temp.d;
4174}
4175
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004176void helper_fprem1(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004177{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004178 double st0, st1, dblq, fpsrcop, fptemp;
4179 CPU_LDoubleU fpsrcop1, fptemp1;
Jun Nakajima86797932011-01-29 14:24:24 -08004180 int expdif;
4181 signed long long int q;
4182
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004183 st0 = floatx80_to_double(env, ST0);
4184 st1 = floatx80_to_double(env, ST1);
4185
4186 if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
4187 ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */
Jun Nakajima86797932011-01-29 14:24:24 -08004188 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4189 return;
4190 }
4191
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004192 fpsrcop = st0;
4193 fptemp = st1;
4194 fpsrcop1.d = ST0;
4195 fptemp1.d = ST1;
Jun Nakajima86797932011-01-29 14:24:24 -08004196 expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
4197
4198 if (expdif < 0) {
4199 /* optimisation? taken from the AMD docs */
4200 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4201 /* ST0 is unchanged */
4202 return;
4203 }
4204
4205 if (expdif < 53) {
4206 dblq = fpsrcop / fptemp;
4207 /* round dblq towards nearest integer */
4208 dblq = rint(dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004209 st0 = fpsrcop - fptemp * dblq;
Jun Nakajima86797932011-01-29 14:24:24 -08004210
4211 /* convert dblq to q by truncating towards zero */
4212 if (dblq < 0.0)
4213 q = (signed long long int)(-dblq);
4214 else
4215 q = (signed long long int)dblq;
4216
4217 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4218 /* (C0,C3,C1) <-- (q2,q1,q0) */
4219 env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
4220 env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
4221 env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
4222 } else {
4223 env->fpus |= 0x400; /* C2 <-- 1 */
4224 fptemp = pow(2.0, expdif - 50);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004225 fpsrcop = (st0 / st1) / fptemp;
Jun Nakajima86797932011-01-29 14:24:24 -08004226 /* fpsrcop = integer obtained by chopping */
4227 fpsrcop = (fpsrcop < 0.0) ?
4228 -(floor(fabs(fpsrcop))) : floor(fpsrcop);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004229 st0 -= (st1 * fpsrcop * fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004230 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004231 ST0 = double_to_floatx80(env, st0);
Jun Nakajima86797932011-01-29 14:24:24 -08004232}
4233
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004234void helper_fprem(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004235{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004236 double st0, st1, dblq, fpsrcop, fptemp;
4237 CPU_LDoubleU fpsrcop1, fptemp1;
Jun Nakajima86797932011-01-29 14:24:24 -08004238 int expdif;
4239 signed long long int q;
4240
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004241 st0 = floatx80_to_double(env, ST0);
4242 st1 = floatx80_to_double(env, ST1);
4243
4244 if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
4245 ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */
Jun Nakajima86797932011-01-29 14:24:24 -08004246 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4247 return;
4248 }
4249
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004250 fpsrcop = st0;
4251 fptemp = st1;
4252 fpsrcop1.d = ST0;
4253 fptemp1.d = ST1;
Jun Nakajima86797932011-01-29 14:24:24 -08004254 expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
4255
4256 if (expdif < 0) {
4257 /* optimisation? taken from the AMD docs */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004258 env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */
Jun Nakajima86797932011-01-29 14:24:24 -08004259 /* ST0 is unchanged */
4260 return;
4261 }
4262
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004263 if (expdif < 53) {
4264 dblq = fpsrcop / fptemp; /* ST0 / ST1*/;
Jun Nakajima86797932011-01-29 14:24:24 -08004265 /* round dblq towards zero */
4266 dblq = (dblq < 0.0) ? ceil(dblq) : floor(dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004267 st0 = fpsrcop - fptemp * dblq; /* fpsrcop is ST0 */
Jun Nakajima86797932011-01-29 14:24:24 -08004268
4269 /* convert dblq to q by truncating towards zero */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004270 if (dblq < 0.0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004271 q = (signed long long int)(-dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004272 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004273 q = (signed long long int)dblq;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004274 }
Jun Nakajima86797932011-01-29 14:24:24 -08004275
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004276 env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */
4277 /* (C0,C3,C1) <-- (q2,q1,q0) */
Jun Nakajima86797932011-01-29 14:24:24 -08004278 env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
4279 env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
4280 env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
4281 } else {
4282 int N = 32 + (expdif % 32); /* as per AMD docs */
4283 env->fpus |= 0x400; /* C2 <-- 1 */
4284 fptemp = pow(2.0, (double)(expdif - N));
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004285 fpsrcop = (st0 / st1) / fptemp;
Jun Nakajima86797932011-01-29 14:24:24 -08004286 /* fpsrcop = integer obtained by chopping */
4287 fpsrcop = (fpsrcop < 0.0) ?
4288 -(floor(fabs(fpsrcop))) : floor(fpsrcop);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004289 st0 -= (st1 * fpsrcop * fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004290 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004291 ST0 = double_to_floatx80(env, st0);
Jun Nakajima86797932011-01-29 14:24:24 -08004292}
4293
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004294void helper_fyl2xp1(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004295{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004296 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004297
Jun Nakajima86797932011-01-29 14:24:24 -08004298 if ((fptemp+1.0)>0.0) {
4299 fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004300 fptemp *= floatx80_to_double(env, ST1);
4301 ST1 = double_to_floatx80(env, fptemp);
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004302 fpop(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004303 } else {
4304 env->fpus &= (~0x4700);
4305 env->fpus |= 0x400;
4306 }
4307}
4308
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004309void helper_fsqrt(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004310{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004311 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004312
Jun Nakajima86797932011-01-29 14:24:24 -08004313 if (fptemp<0.0) {
4314 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4315 env->fpus |= 0x400;
4316 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004317 ST0 = floatx80_sqrt(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004318}
4319
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004320void helper_fsincos(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004321{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004322 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004323
Jun Nakajima86797932011-01-29 14:24:24 -08004324 if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4325 env->fpus |= 0x400;
4326 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004327 ST0 = double_to_floatx80(env, sin(fptemp));
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004328 fpush(env);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004329 ST0 = double_to_floatx80(env, cos(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004330 env->fpus &= (~0x400); /* C2 <-- 0 */
4331 /* the above code is for |arg| < 2**63 only */
4332 }
4333}
4334
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004335void helper_frndint(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004336{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004337 ST0 = floatx80_round_to_int(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004338}
4339
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004340void helper_fscale(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004341{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004342 double st0 = floatx80_to_double(env, ST0);
4343 double st1 = floatx80_to_double(env, ST1);
4344 double val = ldexp(st0, (int)st1);
4345 ST0 = double_to_floatx80(env, val);
Jun Nakajima86797932011-01-29 14:24:24 -08004346}
4347
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004348void helper_fsin(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004349{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004350 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004351
Jun Nakajima86797932011-01-29 14:24:24 -08004352 if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4353 env->fpus |= 0x400;
4354 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004355 ST0 = double_to_floatx80(env, sin(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004356 env->fpus &= (~0x400); /* C2 <-- 0 */
4357 /* the above code is for |arg| < 2**53 only */
4358 }
4359}
4360
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004361void helper_fcos(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004362{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004363 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004364
Jun Nakajima86797932011-01-29 14:24:24 -08004365 if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4366 env->fpus |= 0x400;
4367 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004368 ST0 = double_to_floatx80(env, cos(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004369 env->fpus &= (~0x400); /* C2 <-- 0 */
4370 /* the above code is for |arg5 < 2**63 only */
4371 }
4372}
4373
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004374void helper_fxam_ST0(CPUX86State *env)
Jun Nakajima86797932011-01-29 14:24:24 -08004375{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004376 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004377 int expdif;
4378
4379 temp.d = ST0;
4380
4381 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4382 if (SIGND(temp))
4383 env->fpus |= 0x200; /* C1 <-- 1 */
4384
4385 /* XXX: test fptags too */
4386 expdif = EXPD(temp);
4387 if (expdif == MAXEXPD) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004388 if (MANTD(temp) == 0x8000000000000000ULL) {
Jun Nakajima86797932011-01-29 14:24:24 -08004389 env->fpus |= 0x500 /*Infinity*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004390 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004391 env->fpus |= 0x100 /*NaN*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004392 }
Jun Nakajima86797932011-01-29 14:24:24 -08004393 } else if (expdif == 0) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004394 if (MANTD(temp) == 0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004395 env->fpus |= 0x4000 /*Zero*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004396 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004397 env->fpus |= 0x4400 /*Denormal*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004398 }
Jun Nakajima86797932011-01-29 14:24:24 -08004399 } else {
4400 env->fpus |= 0x400;
4401 }
4402}
4403
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004404void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32)
Jun Nakajima86797932011-01-29 14:24:24 -08004405{
4406 int fpus, fptag, exp, i;
4407 uint64_t mant;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004408 CPU_LDoubleU tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004409
4410 fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
4411 fptag = 0;
4412 for (i=7; i>=0; i--) {
4413 fptag <<= 2;
4414 if (env->fptags[i]) {
4415 fptag |= 3;
4416 } else {
4417 tmp.d = env->fpregs[i].d;
4418 exp = EXPD(tmp);
4419 mant = MANTD(tmp);
4420 if (exp == 0 && mant == 0) {
4421 /* zero */
4422 fptag |= 1;
4423 } else if (exp == 0 || exp == MAXEXPD
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004424 || (mant & (1LL << 63)) == 0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004425 /* NaNs, infinity, denormal */
4426 fptag |= 2;
4427 }
4428 }
4429 }
4430 if (data32) {
4431 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004432 cpu_stl_data(env, ptr, env->fpuc);
4433 cpu_stl_data(env, ptr + 4, fpus);
4434 cpu_stl_data(env, ptr + 8, fptag);
4435 cpu_stl_data(env, ptr + 12, 0); /* fpip */
4436 cpu_stl_data(env, ptr + 16, 0); /* fpcs */
4437 cpu_stl_data(env, ptr + 20, 0); /* fpoo */
4438 cpu_stl_data(env, ptr + 24, 0); /* fpos */
Jun Nakajima86797932011-01-29 14:24:24 -08004439 } else {
4440 /* 16 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004441 cpu_stw_data(env, ptr, env->fpuc);
4442 cpu_stw_data(env, ptr + 2, fpus);
4443 cpu_stw_data(env, ptr + 4, fptag);
4444 cpu_stw_data(env, ptr + 6, 0);
4445 cpu_stw_data(env, ptr + 8, 0);
4446 cpu_stw_data(env, ptr + 10, 0);
4447 cpu_stw_data(env, ptr + 12, 0);
Jun Nakajima86797932011-01-29 14:24:24 -08004448 }
4449}
4450
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004451void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32)
Jun Nakajima86797932011-01-29 14:24:24 -08004452{
4453 int i, fpus, fptag;
4454
4455 if (data32) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004456 env->fpuc = cpu_lduw_data(env, ptr);
4457 fpus = cpu_lduw_data(env, ptr + 4);
4458 fptag = cpu_lduw_data(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08004459 }
4460 else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004461 env->fpuc = cpu_lduw_data(env, ptr);
4462 fpus = cpu_lduw_data(env, ptr + 2);
4463 fptag = cpu_lduw_data(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004464 }
4465 env->fpstt = (fpus >> 11) & 7;
4466 env->fpus = fpus & ~0x3800;
4467 for(i = 0;i < 8; i++) {
4468 env->fptags[i] = ((fptag & 3) == 3);
4469 fptag >>= 2;
4470 }
4471}
4472
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004473void helper_fsave(CPUX86State *env, target_ulong ptr, int data32)
Jun Nakajima86797932011-01-29 14:24:24 -08004474{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004475 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004476 int i;
4477
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004478 helper_fstenv(env, ptr, data32);
Jun Nakajima86797932011-01-29 14:24:24 -08004479
4480 ptr += (14 << data32);
4481 for(i = 0;i < 8; i++) {
4482 tmp = ST(i);
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004483 helper_fstt(env, tmp, ptr);
Jun Nakajima86797932011-01-29 14:24:24 -08004484 ptr += 10;
4485 }
4486
4487 /* fninit */
4488 env->fpus = 0;
4489 env->fpstt = 0;
4490 env->fpuc = 0x37f;
4491 env->fptags[0] = 1;
4492 env->fptags[1] = 1;
4493 env->fptags[2] = 1;
4494 env->fptags[3] = 1;
4495 env->fptags[4] = 1;
4496 env->fptags[5] = 1;
4497 env->fptags[6] = 1;
4498 env->fptags[7] = 1;
4499}
4500
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004501void helper_frstor(CPUX86State *env, target_ulong ptr, int data32)
Jun Nakajima86797932011-01-29 14:24:24 -08004502{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004503 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004504 int i;
4505
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004506 helper_fldenv(env, ptr, data32);
Jun Nakajima86797932011-01-29 14:24:24 -08004507 ptr += (14 << data32);
4508
4509 for(i = 0;i < 8; i++) {
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004510 tmp = helper_fldt(env, ptr);
Jun Nakajima86797932011-01-29 14:24:24 -08004511 ST(i) = tmp;
4512 ptr += 10;
4513 }
4514}
4515
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004516void helper_fxsave(CPUX86State *env, target_ulong ptr, int data64)
Jun Nakajima86797932011-01-29 14:24:24 -08004517{
4518 int fpus, fptag, i, nb_xmm_regs;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004519 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004520 target_ulong addr;
4521
4522 fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
4523 fptag = 0;
4524 for(i = 0; i < 8; i++) {
4525 fptag |= (env->fptags[i] << i);
4526 }
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004527 cpu_stw_data(env, ptr, env->fpuc);
4528 cpu_stw_data(env, ptr + 2, fpus);
4529 cpu_stw_data(env, ptr + 4, fptag ^ 0xff);
Jun Nakajima86797932011-01-29 14:24:24 -08004530#ifdef TARGET_X86_64
4531 if (data64) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004532 cpu_stq_data(env, ptr + 0x08, 0); /* rip */
4533 cpu_stq_data(env, ptr + 0x10, 0); /* rdp */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004534 } else
Jun Nakajima86797932011-01-29 14:24:24 -08004535#endif
4536 {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004537 cpu_stl_data(env, ptr + 0x08, 0); /* eip */
4538 cpu_stl_data(env, ptr + 0x0c, 0); /* sel */
4539 cpu_stl_data(env, ptr + 0x10, 0); /* dp */
4540 cpu_stl_data(env, ptr + 0x14, 0); /* sel */
Jun Nakajima86797932011-01-29 14:24:24 -08004541 }
4542
4543 addr = ptr + 0x20;
4544 for(i = 0;i < 8; i++) {
4545 tmp = ST(i);
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004546 helper_fstt(env, tmp, addr);
Jun Nakajima86797932011-01-29 14:24:24 -08004547 addr += 16;
4548 }
4549
4550 if (env->cr[4] & CR4_OSFXSR_MASK) {
4551 /* XXX: finish it */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004552 cpu_stl_data(env, ptr + 0x18, env->mxcsr); /* mxcsr */
4553 cpu_stl_data(env, ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */
Jun Nakajima86797932011-01-29 14:24:24 -08004554 if (env->hflags & HF_CS64_MASK)
4555 nb_xmm_regs = 16;
4556 else
4557 nb_xmm_regs = 8;
4558 addr = ptr + 0xa0;
4559 /* Fast FXSAVE leaves out the XMM registers */
4560 if (!(env->efer & MSR_EFER_FFXSR)
4561 || (env->hflags & HF_CPL_MASK)
4562 || !(env->hflags & HF_LMA_MASK)) {
4563 for(i = 0; i < nb_xmm_regs; i++) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004564 cpu_stq_data(env, addr, env->xmm_regs[i].XMM_Q(0));
4565 cpu_stq_data(env, addr + 8, env->xmm_regs[i].XMM_Q(1));
Jun Nakajima86797932011-01-29 14:24:24 -08004566 addr += 16;
4567 }
4568 }
4569 }
4570}
4571
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004572void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64)
Jun Nakajima86797932011-01-29 14:24:24 -08004573{
4574 int i, fpus, fptag, nb_xmm_regs;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004575 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004576 target_ulong addr;
4577
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004578 env->fpuc = cpu_lduw_data(env, ptr);
4579 fpus = cpu_lduw_data(env, ptr + 2);
4580 fptag = cpu_lduw_data(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004581 env->fpstt = (fpus >> 11) & 7;
4582 env->fpus = fpus & ~0x3800;
4583 fptag ^= 0xff;
4584 for(i = 0;i < 8; i++) {
4585 env->fptags[i] = ((fptag >> i) & 1);
4586 }
4587
4588 addr = ptr + 0x20;
4589 for(i = 0;i < 8; i++) {
David 'Digit' Turnerdfb40c62014-04-04 04:55:48 +02004590 tmp = helper_fldt(env, addr);
Jun Nakajima86797932011-01-29 14:24:24 -08004591 ST(i) = tmp;
4592 addr += 16;
4593 }
4594
4595 if (env->cr[4] & CR4_OSFXSR_MASK) {
4596 /* XXX: finish it */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004597 env->mxcsr = cpu_ldl_data(env, ptr + 0x18);
Jun Nakajima86797932011-01-29 14:24:24 -08004598 //ldl(ptr + 0x1c);
4599 if (env->hflags & HF_CS64_MASK)
4600 nb_xmm_regs = 16;
4601 else
4602 nb_xmm_regs = 8;
4603 addr = ptr + 0xa0;
4604 /* Fast FXRESTORE leaves out the XMM registers */
4605 if (!(env->efer & MSR_EFER_FFXSR)
4606 || (env->hflags & HF_CPL_MASK)
4607 || !(env->hflags & HF_LMA_MASK)) {
4608 for(i = 0; i < nb_xmm_regs; i++) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004609 env->xmm_regs[i].XMM_Q(0) = cpu_ldq_data(env, addr);
4610 env->xmm_regs[i].XMM_Q(1) = cpu_ldq_data(env, addr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08004611 addr += 16;
4612 }
4613 }
4614 }
4615}
4616
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004617void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f)
Jun Nakajima86797932011-01-29 14:24:24 -08004618{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004619 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004620
4621 temp.d = f;
4622 *pmant = temp.l.lower;
4623 *pexp = temp.l.upper;
4624}
4625
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004626floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper)
Jun Nakajima86797932011-01-29 14:24:24 -08004627{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004628 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004629
4630 temp.l.upper = upper;
4631 temp.l.lower = mant;
4632 return temp.d;
4633}
Jun Nakajima86797932011-01-29 14:24:24 -08004634
4635#ifdef TARGET_X86_64
4636
4637//#define DEBUG_MULDIV
4638
4639static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
4640{
4641 *plow += a;
4642 /* carry test */
4643 if (*plow < a)
4644 (*phigh)++;
4645 *phigh += b;
4646}
4647
4648static void neg128(uint64_t *plow, uint64_t *phigh)
4649{
4650 *plow = ~ *plow;
4651 *phigh = ~ *phigh;
4652 add128(plow, phigh, 1, 0);
4653}
4654
4655/* return TRUE if overflow */
4656static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
4657{
4658 uint64_t q, r, a1, a0;
4659 int i, qb, ab;
4660
4661 a0 = *plow;
4662 a1 = *phigh;
4663 if (a1 == 0) {
4664 q = a0 / b;
4665 r = a0 % b;
4666 *plow = q;
4667 *phigh = r;
4668 } else {
4669 if (a1 >= b)
4670 return 1;
4671 /* XXX: use a better algorithm */
4672 for(i = 0; i < 64; i++) {
4673 ab = a1 >> 63;
4674 a1 = (a1 << 1) | (a0 >> 63);
4675 if (ab || a1 >= b) {
4676 a1 -= b;
4677 qb = 1;
4678 } else {
4679 qb = 0;
4680 }
4681 a0 = (a0 << 1) | qb;
4682 }
4683#if defined(DEBUG_MULDIV)
4684 printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n",
4685 *phigh, *plow, b, a0, a1);
4686#endif
4687 *plow = a0;
4688 *phigh = a1;
4689 }
4690 return 0;
4691}
4692
4693/* return TRUE if overflow */
4694static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
4695{
4696 int sa, sb;
4697 sa = ((int64_t)*phigh < 0);
4698 if (sa)
4699 neg128(plow, phigh);
4700 sb = (b < 0);
4701 if (sb)
4702 b = -b;
4703 if (div64(plow, phigh, b) != 0)
4704 return 1;
4705 if (sa ^ sb) {
4706 if (*plow > (1ULL << 63))
4707 return 1;
4708 *plow = - *plow;
4709 } else {
4710 if (*plow >= (1ULL << 63))
4711 return 1;
4712 }
4713 if (sa)
4714 *phigh = - *phigh;
4715 return 0;
4716}
4717
David 'Digit' Turner90edd952014-04-03 17:40:22 +02004718void helper_mulq_EAX_T0(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08004719{
4720 uint64_t r0, r1;
4721
4722 mulu64(&r0, &r1, EAX, t0);
4723 EAX = r0;
4724 EDX = r1;
4725 CC_DST = r0;
4726 CC_SRC = r1;
4727}
4728
David 'Digit' Turner90edd952014-04-03 17:40:22 +02004729void helper_imulq_EAX_T0(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08004730{
4731 uint64_t r0, r1;
4732
4733 muls64(&r0, &r1, EAX, t0);
4734 EAX = r0;
4735 EDX = r1;
4736 CC_DST = r0;
4737 CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
4738}
4739
David 'Digit' Turner90edd952014-04-03 17:40:22 +02004740target_ulong helper_imulq_T0_T1(CPUX86State *env, target_ulong t0, target_ulong t1)
Jun Nakajima86797932011-01-29 14:24:24 -08004741{
4742 uint64_t r0, r1;
4743
4744 muls64(&r0, &r1, t0, t1);
4745 CC_DST = r0;
4746 CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
4747 return r0;
4748}
4749
David 'Digit' Turner90edd952014-04-03 17:40:22 +02004750void helper_divq_EAX(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08004751{
4752 uint64_t r0, r1;
4753 if (t0 == 0) {
4754 raise_exception(EXCP00_DIVZ);
4755 }
4756 r0 = EAX;
4757 r1 = EDX;
4758 if (div64(&r0, &r1, t0))
4759 raise_exception(EXCP00_DIVZ);
4760 EAX = r0;
4761 EDX = r1;
4762}
4763
David 'Digit' Turner90edd952014-04-03 17:40:22 +02004764void helper_idivq_EAX(CPUX86State *env, target_ulong t0)
Jun Nakajima86797932011-01-29 14:24:24 -08004765{
4766 uint64_t r0, r1;
4767 if (t0 == 0) {
4768 raise_exception(EXCP00_DIVZ);
4769 }
4770 r0 = EAX;
4771 r1 = EDX;
4772 if (idiv64(&r0, &r1, t0))
4773 raise_exception(EXCP00_DIVZ);
4774 EAX = r0;
4775 EDX = r1;
4776}
4777#endif
4778
4779static void do_hlt(void)
4780{
4781 env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */
4782 env->halted = 1;
4783 env->exception_index = EXCP_HLT;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01004784 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004785}
4786
4787void helper_hlt(int next_eip_addend)
4788{
4789 helper_svm_check_intercept_param(SVM_EXIT_HLT, 0);
4790 EIP += next_eip_addend;
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004791
Jun Nakajima86797932011-01-29 14:24:24 -08004792 do_hlt();
4793}
4794
4795void helper_monitor(target_ulong ptr)
4796{
4797 if ((uint32_t)ECX != 0)
4798 raise_exception(EXCP0D_GPF);
4799 /* XXX: store address ? */
4800 helper_svm_check_intercept_param(SVM_EXIT_MONITOR, 0);
4801}
4802
4803void helper_mwait(int next_eip_addend)
4804{
4805 if ((uint32_t)ECX != 0)
4806 raise_exception(EXCP0D_GPF);
4807 helper_svm_check_intercept_param(SVM_EXIT_MWAIT, 0);
4808 EIP += next_eip_addend;
4809
4810 /* XXX: not complete but not completely erroneous */
4811 if (env->cpu_index != 0 || env->next_cpu != NULL) {
4812 /* more than one CPU: do not sleep because another CPU may
4813 wake this one */
4814 } else {
4815 do_hlt();
4816 }
4817}
4818
4819void helper_debug(void)
4820{
4821 env->exception_index = EXCP_DEBUG;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01004822 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004823}
4824
4825void helper_reset_rf(void)
4826{
4827 env->eflags &= ~RF_MASK;
4828}
4829
4830void helper_raise_interrupt(int intno, int next_eip_addend)
4831{
4832 raise_interrupt(intno, 1, 0, next_eip_addend);
4833}
4834
4835void helper_raise_exception(int exception_index)
4836{
4837 raise_exception(exception_index);
4838}
4839
4840void helper_cli(void)
4841{
4842 env->eflags &= ~IF_MASK;
4843}
4844
4845void helper_sti(void)
4846{
4847 env->eflags |= IF_MASK;
4848}
4849
4850#if 0
4851/* vm86plus instructions */
4852void helper_cli_vm(void)
4853{
4854 env->eflags &= ~VIF_MASK;
4855}
4856
4857void helper_sti_vm(void)
4858{
4859 env->eflags |= VIF_MASK;
4860 if (env->eflags & VIP_MASK) {
4861 raise_exception(EXCP0D_GPF);
4862 }
4863}
4864#endif
4865
4866void helper_set_inhibit_irq(void)
4867{
4868 env->hflags |= HF_INHIBIT_IRQ_MASK;
4869}
4870
4871void helper_reset_inhibit_irq(void)
4872{
4873 env->hflags &= ~HF_INHIBIT_IRQ_MASK;
4874}
4875
4876void helper_boundw(target_ulong a0, int v)
4877{
4878 int low, high;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004879 low = cpu_ldsw_data(env, a0);
4880 high = cpu_ldsw_data(env, a0 + 2);
Jun Nakajima86797932011-01-29 14:24:24 -08004881 v = (int16_t)v;
4882 if (v < low || v > high) {
4883 raise_exception(EXCP05_BOUND);
4884 }
4885}
4886
4887void helper_boundl(target_ulong a0, int v)
4888{
4889 int low, high;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004890 low = cpu_ldl_data(env, a0);
4891 high = cpu_ldl_data(env, a0 + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004892 if (v < low || v > high) {
4893 raise_exception(EXCP05_BOUND);
4894 }
4895}
4896
4897static float approx_rsqrt(float a)
4898{
4899 return 1.0 / sqrt(a);
4900}
4901
4902static float approx_rcp(float a)
4903{
4904 return 1.0 / a;
4905}
4906
4907#if !defined(CONFIG_USER_ONLY)
4908
4909#define MMUSUFFIX _mmu
4910
4911#define SHIFT 0
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004912#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004913
4914#define SHIFT 1
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004915#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004916
4917#define SHIFT 2
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004918#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004919
4920#define SHIFT 3
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004921#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004922
4923#endif
4924
4925#if !defined(CONFIG_USER_ONLY)
4926/* try to fill the TLB and return an exception if error. If retaddr is
4927 NULL, it means that the function was called in C code (i.e. not
4928 from generated code or from helper.c) */
4929/* XXX: fix it to restore all registers */
David 'Digit' Turnereb3bc462014-03-21 11:09:04 +01004930void tlb_fill(CPUX86State* env1, target_ulong addr, int is_write, int mmu_idx,
4931 uintptr_t retaddr)
Jun Nakajima86797932011-01-29 14:24:24 -08004932{
4933 TranslationBlock *tb;
4934 int ret;
Jun Nakajima86797932011-01-29 14:24:24 -08004935 CPUX86State *saved_env;
4936
4937 /* XXX: hack to restore env in all cases, even if not called from
4938 generated code */
4939 saved_env = env;
David 'Digit' Turner6d1afd32014-03-14 10:51:57 +01004940 env = env1;
David 'Digit' Turner0d8b2352014-03-20 17:13:13 +01004941 ret = cpu_x86_handle_mmu_fault(env, addr, is_write, mmu_idx);
Jun Nakajima86797932011-01-29 14:24:24 -08004942 if (ret) {
4943 if (retaddr) {
4944 /* now we have a real cpu fault */
David 'Digit' Turnereb3bc462014-03-21 11:09:04 +01004945 tb = tb_find_pc(retaddr);
Jun Nakajima86797932011-01-29 14:24:24 -08004946 if (tb) {
4947 /* the PC is inside the translated code. It means that we have
4948 a virtual CPU fault */
David 'Digit' Turnereb3bc462014-03-21 11:09:04 +01004949 cpu_restore_state(env, retaddr);
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}