blob: 27fa615a71715ecba866d70046c35762fc47b08a [file] [log] [blame]
Jun Nakajima86797932011-01-29 14:24:24 -08001/*
2 * i386 helpers
3 *
4 * Copyright (c) 2003 Fabrice Bellard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
19 */
David 'Digit' Turnere2288402014-01-09 18:35:14 +010020#include <math.h>
21
Jun Nakajima86797932011-01-29 14:24:24 -080022#define CPU_NO_GLOBAL_REGS
23#include "exec.h"
David 'Digit' Turnere90d6652013-12-14 14:55:12 +010024#include "qemu/host-utils.h"
Jun Nakajima86797932011-01-29 14:24:24 -080025
26//#define DEBUG_PCALL
27
28
29#ifdef DEBUG_PCALL
30# define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__)
31# define LOG_PCALL_STATE(env) \
32 log_cpu_state_mask(CPU_LOG_PCALL, (env), X86_DUMP_CCOP)
33#else
34# define LOG_PCALL(...) do { } while (0)
35# define LOG_PCALL_STATE(env) do { } while (0)
36#endif
37
38
39#if 0
40#define raise_exception_err(a, b)\
41do {\
42 qemu_log("raise_exception line=%d\n", __LINE__);\
43 (raise_exception_err)(a, b);\
44} while (0)
45#endif
46
47static const uint8_t parity_table[256] = {
48 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
49 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
50 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
51 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
52 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
53 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
54 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
55 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
56 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
57 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
58 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
59 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
60 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
61 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
62 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
63 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
64 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
65 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
66 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
67 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
68 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
69 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
70 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
71 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
72 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
73 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
74 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
75 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
76 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
77 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
78 CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
79 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
80};
81
82/* modulo 17 table */
83static const uint8_t rclw_table[32] = {
84 0, 1, 2, 3, 4, 5, 6, 7,
85 8, 9,10,11,12,13,14,15,
86 16, 0, 1, 2, 3, 4, 5, 6,
87 7, 8, 9,10,11,12,13,14,
88};
89
90/* modulo 9 table */
91static const uint8_t rclb_table[32] = {
92 0, 1, 2, 3, 4, 5, 6, 7,
93 8, 0, 1, 2, 3, 4, 5, 6,
94 7, 8, 0, 1, 2, 3, 4, 5,
95 6, 7, 8, 0, 1, 2, 3, 4,
96};
97
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +010098#define floatx80_lg2 make_floatx80( 0x3ffd, 0x9a209a84fbcff799LL )
99#define floatx80_l2e make_floatx80( 0x3fff, 0xb8aa3b295c17f0bcLL )
100#define floatx80_l2t make_floatx80( 0x4000, 0xd49a784bcd1b8afeLL )
101
102static const floatx80 f15rk[7] =
Jun Nakajima86797932011-01-29 14:24:24 -0800103{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +0100104 floatx80_zero,
105 floatx80_one,
106 floatx80_pi,
107 floatx80_lg2,
108 floatx80_ln2,
109 floatx80_l2e,
110 floatx80_l2t,
Jun Nakajima86797932011-01-29 14:24:24 -0800111};
112
113/* broken thread support */
114
115static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
116
117void helper_lock(void)
118{
119 spin_lock(&global_cpu_lock);
120}
121
122void helper_unlock(void)
123{
124 spin_unlock(&global_cpu_lock);
125}
126
127void helper_write_eflags(target_ulong t0, uint32_t update_mask)
128{
129 load_eflags(t0, update_mask);
130}
131
132target_ulong helper_read_eflags(void)
133{
134 uint32_t eflags;
135 eflags = helper_cc_compute_all(CC_OP);
136 eflags |= (DF & DF_MASK);
137 eflags |= env->eflags & ~(VM_MASK | RF_MASK);
138 return eflags;
139}
140
141/* return non zero if error */
142static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr,
143 int selector)
144{
145 SegmentCache *dt;
146 int index;
147 target_ulong ptr;
148
149 if (selector & 0x4)
150 dt = &env->ldt;
151 else
152 dt = &env->gdt;
153 index = selector & ~7;
154 if ((index + 7) > dt->limit)
155 return -1;
156 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100157 *e1_ptr = cpu_ldl_kernel(env, ptr);
158 *e2_ptr = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800159 return 0;
160}
161
162static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
163{
164 unsigned int limit;
165 limit = (e1 & 0xffff) | (e2 & 0x000f0000);
166 if (e2 & DESC_G_MASK)
167 limit = (limit << 12) | 0xfff;
168 return limit;
169}
170
171static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
172{
173 return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
174}
175
176static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2)
177{
178 sc->base = get_seg_base(e1, e2);
179 sc->limit = get_seg_limit(e1, e2);
180 sc->flags = e2;
181}
182
183/* init the segment cache in vm86 mode. */
184static inline void load_seg_vm(int seg, int selector)
185{
186 selector &= 0xffff;
187 cpu_x86_load_seg_cache(env, seg, selector,
188 (selector << 4), 0xffff, 0);
189}
190
191static inline void get_ss_esp_from_tss(uint32_t *ss_ptr,
192 uint32_t *esp_ptr, int dpl)
193{
194 int type, index, shift;
195
196#if 0
197 {
198 int i;
199 printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
200 for(i=0;i<env->tr.limit;i++) {
201 printf("%02x ", env->tr.base[i]);
202 if ((i & 7) == 7) printf("\n");
203 }
204 printf("\n");
205 }
206#endif
207
208 if (!(env->tr.flags & DESC_P_MASK))
209 cpu_abort(env, "invalid tss");
210 type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
211 if ((type & 7) != 1)
212 cpu_abort(env, "invalid tss type");
213 shift = type >> 3;
214 index = (dpl * 4 + 2) << shift;
215 if (index + (4 << shift) - 1 > env->tr.limit)
216 raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
217 if (shift == 0) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100218 *esp_ptr = cpu_lduw_kernel(env, env->tr.base + index);
219 *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 2);
Jun Nakajima86797932011-01-29 14:24:24 -0800220 } else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100221 *esp_ptr = cpu_ldl_kernel(env, env->tr.base + index);
222 *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800223 }
224}
225
226/* XXX: merge with load_seg() */
227static void tss_load_seg(int seg_reg, int selector)
228{
229 uint32_t e1, e2;
230 int rpl, dpl, cpl;
231
232 if ((selector & 0xfffc) != 0) {
233 if (load_segment(&e1, &e2, selector) != 0)
234 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
235 if (!(e2 & DESC_S_MASK))
236 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
237 rpl = selector & 3;
238 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
239 cpl = env->hflags & HF_CPL_MASK;
240 if (seg_reg == R_CS) {
241 if (!(e2 & DESC_CS_MASK))
242 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
243 /* XXX: is it correct ? */
244 if (dpl != rpl)
245 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
246 if ((e2 & DESC_C_MASK) && dpl > rpl)
247 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
248 } else if (seg_reg == R_SS) {
249 /* SS must be writable data */
250 if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
251 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
252 if (dpl != cpl || dpl != rpl)
253 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
254 } else {
255 /* not readable code */
256 if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK))
257 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
258 /* if data or non conforming code, checks the rights */
259 if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) {
260 if (dpl < cpl || dpl < rpl)
261 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
262 }
263 }
264 if (!(e2 & DESC_P_MASK))
265 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
266 cpu_x86_load_seg_cache(env, seg_reg, selector,
267 get_seg_base(e1, e2),
268 get_seg_limit(e1, e2),
269 e2);
270 } else {
271 if (seg_reg == R_SS || seg_reg == R_CS)
272 raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
273 }
274}
275
276#define SWITCH_TSS_JMP 0
277#define SWITCH_TSS_IRET 1
278#define SWITCH_TSS_CALL 2
279
280/* XXX: restore CPU state in registers (PowerPC case) */
281static void switch_tss(int tss_selector,
282 uint32_t e1, uint32_t e2, int source,
283 uint32_t next_eip)
284{
285 int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
286 target_ulong tss_base;
287 uint32_t new_regs[8], new_segs[6];
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +0100288 uint32_t new_eflags, new_eip, new_cr3, new_ldt;
Jun Nakajima86797932011-01-29 14:24:24 -0800289 uint32_t old_eflags, eflags_mask;
290 SegmentCache *dt;
291 int index;
292 target_ulong ptr;
293
294 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
295 LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source);
296
297 /* if task gate, we read the TSS segment and we load it */
298 if (type == 5) {
299 if (!(e2 & DESC_P_MASK))
300 raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
301 tss_selector = e1 >> 16;
302 if (tss_selector & 4)
303 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
304 if (load_segment(&e1, &e2, tss_selector) != 0)
305 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
306 if (e2 & DESC_S_MASK)
307 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
308 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
309 if ((type & 7) != 1)
310 raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
311 }
312
313 if (!(e2 & DESC_P_MASK))
314 raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
315
316 if (type & 8)
317 tss_limit_max = 103;
318 else
319 tss_limit_max = 43;
320 tss_limit = get_seg_limit(e1, e2);
321 tss_base = get_seg_base(e1, e2);
322 if ((tss_selector & 4) != 0 ||
323 tss_limit < tss_limit_max)
324 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
325 old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
326 if (old_type & 8)
327 old_tss_limit_max = 103;
328 else
329 old_tss_limit_max = 43;
330
331 /* read all the registers from the new TSS */
332 if (type & 8) {
333 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100334 new_cr3 = cpu_ldl_kernel(env, tss_base + 0x1c);
335 new_eip = cpu_ldl_kernel(env, tss_base + 0x20);
336 new_eflags = cpu_ldl_kernel(env, tss_base + 0x24);
Jun Nakajima86797932011-01-29 14:24:24 -0800337 for(i = 0; i < 8; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100338 new_regs[i] = cpu_ldl_kernel(env, tss_base + (0x28 + i * 4));
Jun Nakajima86797932011-01-29 14:24:24 -0800339 for(i = 0; i < 6; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100340 new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x48 + i * 4));
341 new_ldt = cpu_lduw_kernel(env, tss_base + 0x60);
342 cpu_ldl_kernel(env, tss_base + 0x64);
Jun Nakajima86797932011-01-29 14:24:24 -0800343 } else {
344 /* 16 bit */
345 new_cr3 = 0;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100346 new_eip = cpu_lduw_kernel(env, tss_base + 0x0e);
347 new_eflags = cpu_lduw_kernel(env, tss_base + 0x10);
Jun Nakajima86797932011-01-29 14:24:24 -0800348 for(i = 0; i < 8; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100349 new_regs[i] = cpu_lduw_kernel(env, tss_base + (0x12 + i * 2)) | 0xffff0000;
Jun Nakajima86797932011-01-29 14:24:24 -0800350 for(i = 0; i < 4; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100351 new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x22 + i * 4));
352 new_ldt = cpu_lduw_kernel(env, tss_base + 0x2a);
Jun Nakajima86797932011-01-29 14:24:24 -0800353 new_segs[R_FS] = 0;
354 new_segs[R_GS] = 0;
Jun Nakajima86797932011-01-29 14:24:24 -0800355 }
356
357 /* NOTE: we must avoid memory exceptions during the task switch,
358 so we make dummy accesses before */
359 /* XXX: it can still fail in some cases, so a bigger hack is
360 necessary to valid the TLB after having done the accesses */
361
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100362 v1 = cpu_ldub_kernel(env, env->tr.base);
363 v2 = cpu_ldub_kernel(env, env->tr.base + old_tss_limit_max);
364 cpu_stb_kernel(env, env->tr.base, v1);
365 cpu_stb_kernel(env, env->tr.base + old_tss_limit_max, v2);
Jun Nakajima86797932011-01-29 14:24:24 -0800366
367 /* clear busy bit (it is restartable) */
368 if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
369 target_ulong ptr;
370 uint32_t e2;
371 ptr = env->gdt.base + (env->tr.selector & ~7);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100372 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800373 e2 &= ~DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100374 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -0800375 }
376 old_eflags = compute_eflags();
377 if (source == SWITCH_TSS_IRET)
378 old_eflags &= ~NT_MASK;
379
380 /* save the current state in the old TSS */
381 if (type & 8) {
382 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100383 cpu_stl_kernel(env, env->tr.base + 0x20, next_eip);
384 cpu_stl_kernel(env, env->tr.base + 0x24, old_eflags);
385 cpu_stl_kernel(env, env->tr.base + (0x28 + 0 * 4), EAX);
386 cpu_stl_kernel(env, env->tr.base + (0x28 + 1 * 4), ECX);
387 cpu_stl_kernel(env, env->tr.base + (0x28 + 2 * 4), EDX);
388 cpu_stl_kernel(env, env->tr.base + (0x28 + 3 * 4), EBX);
389 cpu_stl_kernel(env, env->tr.base + (0x28 + 4 * 4), ESP);
390 cpu_stl_kernel(env, env->tr.base + (0x28 + 5 * 4), EBP);
391 cpu_stl_kernel(env, env->tr.base + (0x28 + 6 * 4), ESI);
392 cpu_stl_kernel(env, env->tr.base + (0x28 + 7 * 4), EDI);
Jun Nakajima86797932011-01-29 14:24:24 -0800393 for(i = 0; i < 6; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100394 cpu_stw_kernel(env, env->tr.base + (0x48 + i * 4), env->segs[i].selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800395 } else {
396 /* 16 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100397 cpu_stw_kernel(env, env->tr.base + 0x0e, next_eip);
398 cpu_stw_kernel(env, env->tr.base + 0x10, old_eflags);
399 cpu_stw_kernel(env, env->tr.base + (0x12 + 0 * 2), EAX);
400 cpu_stw_kernel(env, env->tr.base + (0x12 + 1 * 2), ECX);
401 cpu_stw_kernel(env, env->tr.base + (0x12 + 2 * 2), EDX);
402 cpu_stw_kernel(env, env->tr.base + (0x12 + 3 * 2), EBX);
403 cpu_stw_kernel(env, env->tr.base + (0x12 + 4 * 2), ESP);
404 cpu_stw_kernel(env, env->tr.base + (0x12 + 5 * 2), EBP);
405 cpu_stw_kernel(env, env->tr.base + (0x12 + 6 * 2), ESI);
406 cpu_stw_kernel(env, env->tr.base + (0x12 + 7 * 2), EDI);
Jun Nakajima86797932011-01-29 14:24:24 -0800407 for(i = 0; i < 4; i++)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100408 cpu_stw_kernel(env, env->tr.base + (0x22 + i * 4), env->segs[i].selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800409 }
410
411 /* now if an exception occurs, it will occurs in the next task
412 context */
413
414 if (source == SWITCH_TSS_CALL) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100415 cpu_stw_kernel(env, tss_base, env->tr.selector);
Jun Nakajima86797932011-01-29 14:24:24 -0800416 new_eflags |= NT_MASK;
417 }
418
419 /* set busy bit */
420 if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
421 target_ulong ptr;
422 uint32_t e2;
423 ptr = env->gdt.base + (tss_selector & ~7);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100424 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800425 e2 |= DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100426 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -0800427 }
428
429 /* set the new CPU state */
430 /* from this point, any exception which occurs can give problems */
431 env->cr[0] |= CR0_TS_MASK;
432 env->hflags |= HF_TS_MASK;
433 env->tr.selector = tss_selector;
434 env->tr.base = tss_base;
435 env->tr.limit = tss_limit;
436 env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
437
438 if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
439 cpu_x86_update_cr3(env, new_cr3);
440 }
441
442 /* load all registers without an exception, then reload them with
443 possible exception */
444 env->eip = new_eip;
445 eflags_mask = TF_MASK | AC_MASK | ID_MASK |
446 IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
447 if (!(type & 8))
448 eflags_mask &= 0xffff;
449 load_eflags(new_eflags, eflags_mask);
450 /* XXX: what to do in 16 bit case ? */
451 EAX = new_regs[0];
452 ECX = new_regs[1];
453 EDX = new_regs[2];
454 EBX = new_regs[3];
455 ESP = new_regs[4];
456 EBP = new_regs[5];
457 ESI = new_regs[6];
458 EDI = new_regs[7];
459 if (new_eflags & VM_MASK) {
460 for(i = 0; i < 6; i++)
461 load_seg_vm(i, new_segs[i]);
462 /* in vm86, CPL is always 3 */
463 cpu_x86_set_cpl(env, 3);
464 } else {
465 /* CPL is set the RPL of CS */
466 cpu_x86_set_cpl(env, new_segs[R_CS] & 3);
467 /* first just selectors as the rest may trigger exceptions */
468 for(i = 0; i < 6; i++)
469 cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
470 }
471
472 env->ldt.selector = new_ldt & ~4;
473 env->ldt.base = 0;
474 env->ldt.limit = 0;
475 env->ldt.flags = 0;
476
477 /* load the LDT */
478 if (new_ldt & 4)
479 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
480
481 if ((new_ldt & 0xfffc) != 0) {
482 dt = &env->gdt;
483 index = new_ldt & ~7;
484 if ((index + 7) > dt->limit)
485 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
486 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100487 e1 = cpu_ldl_kernel(env, ptr);
488 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800489 if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
490 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
491 if (!(e2 & DESC_P_MASK))
492 raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
493 load_seg_cache_raw_dt(&env->ldt, e1, e2);
494 }
495
496 /* load the segments */
497 if (!(new_eflags & VM_MASK)) {
498 tss_load_seg(R_CS, new_segs[R_CS]);
499 tss_load_seg(R_SS, new_segs[R_SS]);
500 tss_load_seg(R_ES, new_segs[R_ES]);
501 tss_load_seg(R_DS, new_segs[R_DS]);
502 tss_load_seg(R_FS, new_segs[R_FS]);
503 tss_load_seg(R_GS, new_segs[R_GS]);
504 }
505
506 /* check that EIP is in the CS segment limits */
507 if (new_eip > env->segs[R_CS].limit) {
508 /* XXX: different exception if CALL ? */
509 raise_exception_err(EXCP0D_GPF, 0);
510 }
511
512#ifndef CONFIG_USER_ONLY
513 /* reset local breakpoints */
514 if (env->dr[7] & 0x55) {
515 for (i = 0; i < 4; i++) {
516 if (hw_breakpoint_enabled(env->dr[7], i) == 0x1)
517 hw_breakpoint_remove(env, i);
518 }
519 env->dr[7] &= ~0x55;
520 }
521#endif
522}
523
524/* check if Port I/O is allowed in TSS */
525static inline void check_io(int addr, int size)
526{
527 int io_offset, val, mask;
528
529 /* TSS must be a valid 32 bit one */
530 if (!(env->tr.flags & DESC_P_MASK) ||
531 ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
532 env->tr.limit < 103)
533 goto fail;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100534 io_offset = cpu_lduw_kernel(env, env->tr.base + 0x66);
Jun Nakajima86797932011-01-29 14:24:24 -0800535 io_offset += (addr >> 3);
536 /* Note: the check needs two bytes */
537 if ((io_offset + 1) > env->tr.limit)
538 goto fail;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100539 val = cpu_lduw_kernel(env, env->tr.base + io_offset);
Jun Nakajima86797932011-01-29 14:24:24 -0800540 val >>= (addr & 7);
541 mask = (1 << size) - 1;
542 /* all bits must be zero to allow the I/O */
543 if ((val & mask) != 0) {
544 fail:
545 raise_exception_err(EXCP0D_GPF, 0);
546 }
547}
548
549void helper_check_iob(uint32_t t0)
550{
551 check_io(t0, 1);
552}
553
554void helper_check_iow(uint32_t t0)
555{
556 check_io(t0, 2);
557}
558
559void helper_check_iol(uint32_t t0)
560{
561 check_io(t0, 4);
562}
563
564void helper_outb(uint32_t port, uint32_t data)
565{
566 cpu_outb(port, data & 0xff);
567}
568
569target_ulong helper_inb(uint32_t port)
570{
571 return cpu_inb(port);
572}
573
574void helper_outw(uint32_t port, uint32_t data)
575{
576 cpu_outw(port, data & 0xffff);
577}
578
579target_ulong helper_inw(uint32_t port)
580{
581 return cpu_inw(port);
582}
583
584void helper_outl(uint32_t port, uint32_t data)
585{
586 cpu_outl(port, data);
587}
588
589target_ulong helper_inl(uint32_t port)
590{
591 return cpu_inl(port);
592}
593
594static inline unsigned int get_sp_mask(unsigned int e2)
595{
596 if (e2 & DESC_B_MASK)
597 return 0xffffffff;
598 else
599 return 0xffff;
600}
601
602static int exeption_has_error_code(int intno)
603{
604 switch(intno) {
605 case 8:
606 case 10:
607 case 11:
608 case 12:
609 case 13:
610 case 14:
611 case 17:
612 return 1;
613 }
614 return 0;
615}
616
617#ifdef TARGET_X86_64
618#define SET_ESP(val, sp_mask)\
619do {\
620 if ((sp_mask) == 0xffff)\
621 ESP = (ESP & ~0xffff) | ((val) & 0xffff);\
622 else if ((sp_mask) == 0xffffffffLL)\
623 ESP = (uint32_t)(val);\
624 else\
625 ESP = (val);\
626} while (0)
627#else
628#define SET_ESP(val, sp_mask) ESP = (ESP & ~(sp_mask)) | ((val) & (sp_mask))
629#endif
630
631/* in 64-bit machines, this can overflow. So this segment addition macro
632 * can be used to trim the value to 32-bit whenever needed */
633#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask))))
634
635/* XXX: add a is_user flag to have proper security support */
636#define PUSHW(ssp, sp, sp_mask, val)\
637{\
638 sp -= 2;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100639 cpu_stw_kernel(env, (ssp) + (sp & (sp_mask)), (val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800640}
641
642#define PUSHL(ssp, sp, sp_mask, val)\
643{\
644 sp -= 4;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100645 cpu_stl_kernel(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800646}
647
648#define POPW(ssp, sp, sp_mask, val)\
649{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100650 val = cpu_lduw_kernel(env, (ssp) + (sp & (sp_mask)));\
Jun Nakajima86797932011-01-29 14:24:24 -0800651 sp += 2;\
652}
653
654#define POPL(ssp, sp, sp_mask, val)\
655{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100656 val = (uint32_t)cpu_ldl_kernel(env, SEG_ADDL(ssp, sp, sp_mask));\
Jun Nakajima86797932011-01-29 14:24:24 -0800657 sp += 4;\
658}
659
660/* protected mode interrupt */
661static void do_interrupt_protected(int intno, int is_int, int error_code,
662 unsigned int next_eip, int is_hw)
663{
664 SegmentCache *dt;
665 target_ulong ptr, ssp;
666 int type, dpl, selector, ss_dpl, cpl;
667 int has_error_code, new_stack, shift;
668 uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
669 uint32_t old_eip, sp_mask;
670
671 has_error_code = 0;
672 if (!is_int && !is_hw)
673 has_error_code = exeption_has_error_code(intno);
674 if (is_int)
675 old_eip = next_eip;
676 else
677 old_eip = env->eip;
678
679 dt = &env->idt;
680 if (intno * 8 + 7 > dt->limit)
681 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
682 ptr = dt->base + intno * 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100683 e1 = cpu_ldl_kernel(env, ptr);
684 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -0800685 /* check gate type */
686 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
687 switch(type) {
688 case 5: /* task gate */
689 /* must do that check here to return the correct error code */
690 if (!(e2 & DESC_P_MASK))
691 raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
692 switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
693 if (has_error_code) {
694 int type;
695 uint32_t mask;
696 /* push the error code */
697 type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
698 shift = type >> 3;
699 if (env->segs[R_SS].flags & DESC_B_MASK)
700 mask = 0xffffffff;
701 else
702 mask = 0xffff;
703 esp = (ESP - (2 << shift)) & mask;
704 ssp = env->segs[R_SS].base + esp;
705 if (shift)
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100706 cpu_stl_kernel(env, ssp, error_code);
Jun Nakajima86797932011-01-29 14:24:24 -0800707 else
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100708 cpu_stw_kernel(env, ssp, error_code);
Jun Nakajima86797932011-01-29 14:24:24 -0800709 SET_ESP(esp, mask);
710 }
711 return;
712 case 6: /* 286 interrupt gate */
713 case 7: /* 286 trap gate */
714 case 14: /* 386 interrupt gate */
715 case 15: /* 386 trap gate */
716 break;
717 default:
718 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
719 break;
720 }
721 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
722 cpl = env->hflags & HF_CPL_MASK;
723 /* check privilege if software int */
724 if (is_int && dpl < cpl)
725 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
726 /* check valid bit */
727 if (!(e2 & DESC_P_MASK))
728 raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
729 selector = e1 >> 16;
730 offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
731 if ((selector & 0xfffc) == 0)
732 raise_exception_err(EXCP0D_GPF, 0);
733
734 if (load_segment(&e1, &e2, selector) != 0)
735 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
736 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
737 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
738 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
739 if (dpl > cpl)
740 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
741 if (!(e2 & DESC_P_MASK))
742 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
743 if (!(e2 & DESC_C_MASK) && dpl < cpl) {
744 /* to inner privilege */
745 get_ss_esp_from_tss(&ss, &esp, dpl);
746 if ((ss & 0xfffc) == 0)
747 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
748 if ((ss & 3) != dpl)
749 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
750 if (load_segment(&ss_e1, &ss_e2, ss) != 0)
751 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
752 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
753 if (ss_dpl != dpl)
754 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
755 if (!(ss_e2 & DESC_S_MASK) ||
756 (ss_e2 & DESC_CS_MASK) ||
757 !(ss_e2 & DESC_W_MASK))
758 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
759 if (!(ss_e2 & DESC_P_MASK))
760 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
761 new_stack = 1;
762 sp_mask = get_sp_mask(ss_e2);
763 ssp = get_seg_base(ss_e1, ss_e2);
764 } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
765 /* to same privilege */
766 if (env->eflags & VM_MASK)
767 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
768 new_stack = 0;
769 sp_mask = get_sp_mask(env->segs[R_SS].flags);
770 ssp = env->segs[R_SS].base;
771 esp = ESP;
772 dpl = cpl;
773 } else {
774 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
775 new_stack = 0; /* avoid warning */
776 sp_mask = 0; /* avoid warning */
777 ssp = 0; /* avoid warning */
778 esp = 0; /* avoid warning */
779 }
780
781 shift = type >> 3;
782
783#if 0
784 /* XXX: check that enough room is available */
785 push_size = 6 + (new_stack << 2) + (has_error_code << 1);
786 if (env->eflags & VM_MASK)
787 push_size += 8;
788 push_size <<= shift;
789#endif
790 if (shift == 1) {
791 if (new_stack) {
792 if (env->eflags & VM_MASK) {
793 PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
794 PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
795 PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
796 PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
797 }
798 PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
799 PUSHL(ssp, esp, sp_mask, ESP);
800 }
801 PUSHL(ssp, esp, sp_mask, compute_eflags());
802 PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
803 PUSHL(ssp, esp, sp_mask, old_eip);
804 if (has_error_code) {
805 PUSHL(ssp, esp, sp_mask, error_code);
806 }
807 } else {
808 if (new_stack) {
809 if (env->eflags & VM_MASK) {
810 PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
811 PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
812 PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
813 PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector);
814 }
815 PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
816 PUSHW(ssp, esp, sp_mask, ESP);
817 }
818 PUSHW(ssp, esp, sp_mask, compute_eflags());
819 PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
820 PUSHW(ssp, esp, sp_mask, old_eip);
821 if (has_error_code) {
822 PUSHW(ssp, esp, sp_mask, error_code);
823 }
824 }
825
826 if (new_stack) {
827 if (env->eflags & VM_MASK) {
828 cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
829 cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
830 cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
831 cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
832 }
833 ss = (ss & ~3) | dpl;
834 cpu_x86_load_seg_cache(env, R_SS, ss,
835 ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
836 }
837 SET_ESP(esp, sp_mask);
838
839 selector = (selector & ~3) | dpl;
840 cpu_x86_load_seg_cache(env, R_CS, selector,
841 get_seg_base(e1, e2),
842 get_seg_limit(e1, e2),
843 e2);
844 cpu_x86_set_cpl(env, dpl);
845 env->eip = offset;
846
847 /* interrupt gate clear IF mask */
848 if ((type & 1) == 0) {
849 env->eflags &= ~IF_MASK;
850 }
851 env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
852}
853
854#ifdef TARGET_X86_64
855
856#define PUSHQ(sp, val)\
857{\
858 sp -= 8;\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100859 cpu_stq_kernel(env, sp, (val));\
Jun Nakajima86797932011-01-29 14:24:24 -0800860}
861
862#define POPQ(sp, val)\
863{\
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100864 val = cpu_ldq_kernel(env, sp);\
Jun Nakajima86797932011-01-29 14:24:24 -0800865 sp += 8;\
866}
867
868static inline target_ulong get_rsp_from_tss(int level)
869{
870 int index;
871
872#if 0
873 printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
874 env->tr.base, env->tr.limit);
875#endif
876
877 if (!(env->tr.flags & DESC_P_MASK))
878 cpu_abort(env, "invalid tss");
879 index = 8 * level + 4;
880 if ((index + 7) > env->tr.limit)
881 raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100882 return cpu_ldq_kernel(env, env->tr.base + index);
Jun Nakajima86797932011-01-29 14:24:24 -0800883}
884
885/* 64 bit interrupt */
886static void do_interrupt64(int intno, int is_int, int error_code,
887 target_ulong next_eip, int is_hw)
888{
889 SegmentCache *dt;
890 target_ulong ptr;
891 int type, dpl, selector, cpl, ist;
892 int has_error_code, new_stack;
893 uint32_t e1, e2, e3, ss;
894 target_ulong old_eip, esp, offset;
895
896 has_error_code = 0;
897 if (!is_int && !is_hw)
898 has_error_code = exeption_has_error_code(intno);
899 if (is_int)
900 old_eip = next_eip;
901 else
902 old_eip = env->eip;
903
904 dt = &env->idt;
905 if (intno * 16 + 15 > dt->limit)
906 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
907 ptr = dt->base + intno * 16;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +0100908 e1 = cpu_ldl_kernel(env, ptr);
909 e2 = cpu_ldl_kernel(env, ptr + 4);
910 e3 = cpu_ldl_kernel(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -0800911 /* check gate type */
912 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
913 switch(type) {
914 case 14: /* 386 interrupt gate */
915 case 15: /* 386 trap gate */
916 break;
917 default:
918 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
919 break;
920 }
921 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
922 cpl = env->hflags & HF_CPL_MASK;
923 /* check privilege if software int */
924 if (is_int && dpl < cpl)
925 raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
926 /* check valid bit */
927 if (!(e2 & DESC_P_MASK))
928 raise_exception_err(EXCP0B_NOSEG, intno * 16 + 2);
929 selector = e1 >> 16;
930 offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
931 ist = e2 & 7;
932 if ((selector & 0xfffc) == 0)
933 raise_exception_err(EXCP0D_GPF, 0);
934
935 if (load_segment(&e1, &e2, selector) != 0)
936 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
937 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
938 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
939 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
940 if (dpl > cpl)
941 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
942 if (!(e2 & DESC_P_MASK))
943 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
944 if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK))
945 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
946 if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {
947 /* to inner privilege */
948 if (ist != 0)
949 esp = get_rsp_from_tss(ist + 3);
950 else
951 esp = get_rsp_from_tss(dpl);
952 esp &= ~0xfLL; /* align stack */
953 ss = 0;
954 new_stack = 1;
955 } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
956 /* to same privilege */
957 if (env->eflags & VM_MASK)
958 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
959 new_stack = 0;
960 if (ist != 0)
961 esp = get_rsp_from_tss(ist + 3);
962 else
963 esp = ESP;
964 esp &= ~0xfLL; /* align stack */
965 dpl = cpl;
966 } else {
967 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
968 new_stack = 0; /* avoid warning */
969 esp = 0; /* avoid warning */
970 }
971
972 PUSHQ(esp, env->segs[R_SS].selector);
973 PUSHQ(esp, ESP);
974 PUSHQ(esp, compute_eflags());
975 PUSHQ(esp, env->segs[R_CS].selector);
976 PUSHQ(esp, old_eip);
977 if (has_error_code) {
978 PUSHQ(esp, error_code);
979 }
980
981 if (new_stack) {
982 ss = 0 | dpl;
983 cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
984 }
985 ESP = esp;
986
987 selector = (selector & ~3) | dpl;
988 cpu_x86_load_seg_cache(env, R_CS, selector,
989 get_seg_base(e1, e2),
990 get_seg_limit(e1, e2),
991 e2);
992 cpu_x86_set_cpl(env, dpl);
993 env->eip = offset;
994
995 /* interrupt gate clear IF mask */
996 if ((type & 1) == 0) {
997 env->eflags &= ~IF_MASK;
998 }
999 env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
1000}
1001#endif
1002
1003#ifdef TARGET_X86_64
1004#if defined(CONFIG_USER_ONLY)
1005void helper_syscall(int next_eip_addend)
1006{
1007 env->exception_index = EXCP_SYSCALL;
1008 env->exception_next_eip = env->eip + next_eip_addend;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01001009 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08001010}
1011#else
1012void helper_syscall(int next_eip_addend)
1013{
1014 int selector;
1015
1016 if (!(env->efer & MSR_EFER_SCE)) {
1017 raise_exception_err(EXCP06_ILLOP, 0);
1018 }
1019 selector = (env->star >> 32) & 0xffff;
1020 if (env->hflags & HF_LMA_MASK) {
1021 int code64;
1022
1023 ECX = env->eip + next_eip_addend;
1024 env->regs[11] = compute_eflags();
1025
1026 code64 = env->hflags & HF_CS64_MASK;
1027
1028 cpu_x86_set_cpl(env, 0);
1029 cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1030 0, 0xffffffff,
1031 DESC_G_MASK | DESC_P_MASK |
1032 DESC_S_MASK |
1033 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
1034 cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1035 0, 0xffffffff,
1036 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1037 DESC_S_MASK |
1038 DESC_W_MASK | DESC_A_MASK);
1039 env->eflags &= ~env->fmask;
1040 load_eflags(env->eflags, 0);
1041 if (code64)
1042 env->eip = env->lstar;
1043 else
1044 env->eip = env->cstar;
1045 } else {
1046 ECX = (uint32_t)(env->eip + next_eip_addend);
1047
1048 cpu_x86_set_cpl(env, 0);
1049 cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1050 0, 0xffffffff,
1051 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1052 DESC_S_MASK |
1053 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1054 cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1055 0, 0xffffffff,
1056 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1057 DESC_S_MASK |
1058 DESC_W_MASK | DESC_A_MASK);
1059 env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
1060 env->eip = (uint32_t)env->star;
1061 }
1062}
1063#endif
1064#endif
1065
1066#ifdef TARGET_X86_64
1067void helper_sysret(int dflag)
1068{
1069 int cpl, selector;
1070
1071 if (!(env->efer & MSR_EFER_SCE)) {
1072 raise_exception_err(EXCP06_ILLOP, 0);
1073 }
1074 cpl = env->hflags & HF_CPL_MASK;
1075 if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
1076 raise_exception_err(EXCP0D_GPF, 0);
1077 }
1078 selector = (env->star >> 48) & 0xffff;
1079 if (env->hflags & HF_LMA_MASK) {
1080 if (dflag == 2) {
1081 cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
1082 0, 0xffffffff,
1083 DESC_G_MASK | DESC_P_MASK |
1084 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1085 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
1086 DESC_L_MASK);
1087 env->eip = ECX;
1088 } else {
1089 cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1090 0, 0xffffffff,
1091 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1092 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1093 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1094 env->eip = (uint32_t)ECX;
1095 }
1096 cpu_x86_load_seg_cache(env, R_SS, selector + 8,
1097 0, 0xffffffff,
1098 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1099 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1100 DESC_W_MASK | DESC_A_MASK);
1101 load_eflags((uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK |
1102 IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK);
1103 cpu_x86_set_cpl(env, 3);
1104 } else {
1105 cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1106 0, 0xffffffff,
1107 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1108 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1109 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1110 env->eip = (uint32_t)ECX;
1111 cpu_x86_load_seg_cache(env, R_SS, selector + 8,
1112 0, 0xffffffff,
1113 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1114 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1115 DESC_W_MASK | DESC_A_MASK);
1116 env->eflags |= IF_MASK;
1117 cpu_x86_set_cpl(env, 3);
1118 }
Jun Nakajima86797932011-01-29 14:24:24 -08001119}
1120#endif
1121
1122/* real mode interrupt */
1123static void do_interrupt_real(int intno, int is_int, int error_code,
1124 unsigned int next_eip)
1125{
1126 SegmentCache *dt;
1127 target_ulong ptr, ssp;
1128 int selector;
1129 uint32_t offset, esp;
1130 uint32_t old_cs, old_eip;
1131
1132 /* real mode (simpler !) */
1133 dt = &env->idt;
1134 if (intno * 4 + 3 > dt->limit)
1135 raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
1136 ptr = dt->base + intno * 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001137 offset = cpu_lduw_kernel(env, ptr);
1138 selector = cpu_lduw_kernel(env, ptr + 2);
Jun Nakajima86797932011-01-29 14:24:24 -08001139 esp = ESP;
1140 ssp = env->segs[R_SS].base;
1141 if (is_int)
1142 old_eip = next_eip;
1143 else
1144 old_eip = env->eip;
1145 old_cs = env->segs[R_CS].selector;
1146 /* XXX: use SS segment size ? */
1147 PUSHW(ssp, esp, 0xffff, compute_eflags());
1148 PUSHW(ssp, esp, 0xffff, old_cs);
1149 PUSHW(ssp, esp, 0xffff, old_eip);
1150
1151 /* update processor state */
1152 ESP = (ESP & ~0xffff) | (esp & 0xffff);
1153 env->eip = offset;
1154 env->segs[R_CS].selector = selector;
1155 env->segs[R_CS].base = (selector << 4);
1156 env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
1157}
1158
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001159#if defined(CONFIG_USER_ONLY)
Jun Nakajima86797932011-01-29 14:24:24 -08001160/* fake user mode interrupt */
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001161static void do_interrupt_user(int intno, int is_int, int error_code,
1162 target_ulong next_eip)
Jun Nakajima86797932011-01-29 14:24:24 -08001163{
1164 SegmentCache *dt;
1165 target_ulong ptr;
1166 int dpl, cpl, shift;
1167 uint32_t e2;
1168
1169 dt = &env->idt;
1170 if (env->hflags & HF_LMA_MASK) {
1171 shift = 4;
1172 } else {
1173 shift = 3;
1174 }
1175 ptr = dt->base + (intno << shift);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001176 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08001177
1178 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1179 cpl = env->hflags & HF_CPL_MASK;
1180 /* check privilege if software int */
1181 if (is_int && dpl < cpl)
1182 raise_exception_err(EXCP0D_GPF, (intno << shift) + 2);
1183
1184 /* Since we emulate only user space, we cannot do more than
1185 exiting the emulation with the suitable exception and error
1186 code */
1187 if (is_int)
1188 EIP = next_eip;
1189}
1190
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001191#else
1192
Jun Nakajima86797932011-01-29 14:24:24 -08001193static void handle_even_inj(int intno, int is_int, int error_code,
1194 int is_hw, int rm)
1195{
1196 uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
1197 if (!(event_inj & SVM_EVTINJ_VALID)) {
1198 int type;
1199 if (is_int)
1200 type = SVM_EVTINJ_TYPE_SOFT;
1201 else
1202 type = SVM_EVTINJ_TYPE_EXEPT;
1203 event_inj = intno | type | SVM_EVTINJ_VALID;
1204 if (!rm && exeption_has_error_code(intno)) {
1205 event_inj |= SVM_EVTINJ_VALID_ERR;
1206 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err), error_code);
1207 }
1208 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj);
1209 }
1210}
1211#endif
1212
1213/*
1214 * Begin execution of an interruption. is_int is TRUE if coming from
1215 * the int instruction. next_eip is the EIP value AFTER the interrupt
1216 * instruction. It is only relevant if is_int is TRUE.
1217 */
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001218static void do_interrupt_all(int intno, int is_int, int error_code,
1219 target_ulong next_eip, int is_hw)
Jun Nakajima86797932011-01-29 14:24:24 -08001220{
1221 if (qemu_loglevel_mask(CPU_LOG_INT)) {
1222 if ((env->cr[0] & CR0_PE_MASK)) {
1223 static int count;
1224 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,
1225 count, intno, error_code, is_int,
1226 env->hflags & HF_CPL_MASK,
1227 env->segs[R_CS].selector, EIP,
1228 (int)env->segs[R_CS].base + EIP,
1229 env->segs[R_SS].selector, ESP);
1230 if (intno == 0x0e) {
1231 qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]);
1232 } else {
1233 qemu_log(" EAX=" TARGET_FMT_lx, EAX);
1234 }
1235 qemu_log("\n");
1236 log_cpu_state(env, X86_DUMP_CCOP);
1237#if 0
1238 {
1239 int i;
1240 uint8_t *ptr;
1241 qemu_log(" code=");
1242 ptr = env->segs[R_CS].base + env->eip;
1243 for(i = 0; i < 16; i++) {
1244 qemu_log(" %02x", ldub(ptr + i));
1245 }
1246 qemu_log("\n");
1247 }
1248#endif
1249 count++;
1250 }
1251 }
1252 if (env->cr[0] & CR0_PE_MASK) {
1253#if !defined(CONFIG_USER_ONLY)
1254 if (env->hflags & HF_SVMI_MASK)
1255 handle_even_inj(intno, is_int, error_code, is_hw, 0);
1256#endif
1257#ifdef TARGET_X86_64
1258 if (env->hflags & HF_LMA_MASK) {
1259 do_interrupt64(intno, is_int, error_code, next_eip, is_hw);
1260 } else
1261#endif
1262 {
1263 do_interrupt_protected(intno, is_int, error_code, next_eip, is_hw);
1264 }
1265 } else {
1266#if !defined(CONFIG_USER_ONLY)
1267 if (env->hflags & HF_SVMI_MASK)
1268 handle_even_inj(intno, is_int, error_code, is_hw, 1);
1269#endif
1270 do_interrupt_real(intno, is_int, error_code, next_eip);
1271 }
1272
1273#if !defined(CONFIG_USER_ONLY)
1274 if (env->hflags & HF_SVMI_MASK) {
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001275 uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
1276 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj & ~SVM_EVTINJ_VALID);
Jun Nakajima86797932011-01-29 14:24:24 -08001277 }
1278#endif
1279}
1280
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001281void do_interrupt(CPUArchState *env1)
1282{
1283 CPUArchState *saved_env;
1284
1285 saved_env = env;
1286 env = env1;
1287#if defined(CONFIG_USER_ONLY)
1288 /* if user mode only, we simulate a fake exception
1289 which will be handled outside the cpu execution
1290 loop */
1291 do_interrupt_user(env->exception_index,
1292 env->exception_is_int,
1293 env->error_code,
1294 env->exception_next_eip);
1295 /* successfully delivered */
1296 env->old_exception = -1;
1297#else
1298 /* simulate a real cpu exception. On i386, it can
1299 trigger new exceptions, but we do not handle
1300 double or triple faults yet. */
1301 do_interrupt_all(env->exception_index,
1302 env->exception_is_int,
1303 env->error_code,
1304 env->exception_next_eip, 0);
1305 /* successfully delivered */
1306 env->old_exception = -1;
1307#endif
1308 env = saved_env;
1309}
1310
1311void do_interrupt_x86_hardirq(CPUArchState *env1, int intno, int is_hw)
1312{
1313 CPUArchState *saved_env;
1314
1315 saved_env = env;
1316 env = env1;
1317 do_interrupt_all(intno, 0, 0, 0, is_hw);
1318 env = saved_env;
1319}
1320
Jun Nakajima86797932011-01-29 14:24:24 -08001321/* This should come from sysemu.h - if we could include it here... */
1322void qemu_system_reset_request(void);
1323
1324/*
1325 * Check nested exceptions and change to double or triple fault if
1326 * needed. It should only be called, if this is not an interrupt.
1327 * Returns the new exception number.
1328 */
1329static int check_exception(int intno, int *error_code)
1330{
1331 int first_contributory = env->old_exception == 0 ||
1332 (env->old_exception >= 10 &&
1333 env->old_exception <= 13);
1334 int second_contributory = intno == 0 ||
1335 (intno >= 10 && intno <= 13);
1336
1337 qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n",
1338 env->old_exception, intno);
1339
1340#if !defined(CONFIG_USER_ONLY)
1341 if (env->old_exception == EXCP08_DBLE) {
1342 if (env->hflags & HF_SVMI_MASK)
1343 helper_vmexit(SVM_EXIT_SHUTDOWN, 0); /* does not return */
1344
1345 qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
1346
1347 qemu_system_reset_request();
1348 return EXCP_HLT;
1349 }
1350#endif
1351
1352 if ((first_contributory && second_contributory)
1353 || (env->old_exception == EXCP0E_PAGE &&
1354 (second_contributory || (intno == EXCP0E_PAGE)))) {
1355 intno = EXCP08_DBLE;
1356 *error_code = 0;
1357 }
1358
1359 if (second_contributory || (intno == EXCP0E_PAGE) ||
1360 (intno == EXCP08_DBLE))
1361 env->old_exception = intno;
1362
1363 return intno;
1364}
1365
1366/*
1367 * Signal an interruption. It is executed in the main CPU loop.
1368 * is_int is TRUE if coming from the int instruction. next_eip is the
1369 * EIP value AFTER the interrupt instruction. It is only relevant if
1370 * is_int is TRUE.
1371 */
1372static void QEMU_NORETURN raise_interrupt(int intno, int is_int, int error_code,
1373 int next_eip_addend)
1374{
1375 if (!is_int) {
1376 helper_svm_check_intercept_param(SVM_EXIT_EXCP_BASE + intno, error_code);
1377 intno = check_exception(intno, &error_code);
1378 } else {
1379 helper_svm_check_intercept_param(SVM_EXIT_SWINT, 0);
1380 }
1381
1382 env->exception_index = intno;
1383 env->error_code = error_code;
1384 env->exception_is_int = is_int;
1385 env->exception_next_eip = env->eip + next_eip_addend;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01001386 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08001387}
1388
1389/* shortcuts to generate exceptions */
1390
1391void raise_exception_err(int exception_index, int error_code)
1392{
1393 raise_interrupt(exception_index, 0, error_code, 0);
1394}
1395
1396void raise_exception(int exception_index)
1397{
1398 raise_interrupt(exception_index, 0, 0, 0);
1399}
1400
1401/* SMM support */
1402
1403#if defined(CONFIG_USER_ONLY)
1404
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001405void do_smm_enter(CPUArchState *env1)
Jun Nakajima86797932011-01-29 14:24:24 -08001406{
1407}
1408
1409void helper_rsm(void)
1410{
1411}
1412
1413#else
1414
1415#ifdef TARGET_X86_64
1416#define SMM_REVISION_ID 0x00020064
1417#else
1418#define SMM_REVISION_ID 0x00020000
1419#endif
1420
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001421void do_smm_enter(CPUArchState *env1)
Jun Nakajima86797932011-01-29 14:24:24 -08001422{
1423 target_ulong sm_state;
1424 SegmentCache *dt;
1425 int i, offset;
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001426 CPUArchState *saved_env;
1427
1428 saved_env = env;
1429 env = env1;
Jun Nakajima86797932011-01-29 14:24:24 -08001430
1431 qemu_log_mask(CPU_LOG_INT, "SMM: enter\n");
1432 log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
1433
1434 env->hflags |= HF_SMM_MASK;
1435 cpu_smm_update(env);
1436
1437 sm_state = env->smbase + 0x8000;
1438
1439#ifdef TARGET_X86_64
1440 for(i = 0; i < 6; i++) {
1441 dt = &env->segs[i];
1442 offset = 0x7e00 + i * 16;
1443 stw_phys(sm_state + offset, dt->selector);
1444 stw_phys(sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff);
1445 stl_phys(sm_state + offset + 4, dt->limit);
1446 stq_phys(sm_state + offset + 8, dt->base);
1447 }
1448
1449 stq_phys(sm_state + 0x7e68, env->gdt.base);
1450 stl_phys(sm_state + 0x7e64, env->gdt.limit);
1451
1452 stw_phys(sm_state + 0x7e70, env->ldt.selector);
1453 stq_phys(sm_state + 0x7e78, env->ldt.base);
1454 stl_phys(sm_state + 0x7e74, env->ldt.limit);
1455 stw_phys(sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff);
1456
1457 stq_phys(sm_state + 0x7e88, env->idt.base);
1458 stl_phys(sm_state + 0x7e84, env->idt.limit);
1459
1460 stw_phys(sm_state + 0x7e90, env->tr.selector);
1461 stq_phys(sm_state + 0x7e98, env->tr.base);
1462 stl_phys(sm_state + 0x7e94, env->tr.limit);
1463 stw_phys(sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff);
1464
1465 stq_phys(sm_state + 0x7ed0, env->efer);
1466
1467 stq_phys(sm_state + 0x7ff8, EAX);
1468 stq_phys(sm_state + 0x7ff0, ECX);
1469 stq_phys(sm_state + 0x7fe8, EDX);
1470 stq_phys(sm_state + 0x7fe0, EBX);
1471 stq_phys(sm_state + 0x7fd8, ESP);
1472 stq_phys(sm_state + 0x7fd0, EBP);
1473 stq_phys(sm_state + 0x7fc8, ESI);
1474 stq_phys(sm_state + 0x7fc0, EDI);
1475 for(i = 8; i < 16; i++)
1476 stq_phys(sm_state + 0x7ff8 - i * 8, env->regs[i]);
1477 stq_phys(sm_state + 0x7f78, env->eip);
1478 stl_phys(sm_state + 0x7f70, compute_eflags());
1479 stl_phys(sm_state + 0x7f68, env->dr[6]);
1480 stl_phys(sm_state + 0x7f60, env->dr[7]);
1481
1482 stl_phys(sm_state + 0x7f48, env->cr[4]);
1483 stl_phys(sm_state + 0x7f50, env->cr[3]);
1484 stl_phys(sm_state + 0x7f58, env->cr[0]);
1485
1486 stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
1487 stl_phys(sm_state + 0x7f00, env->smbase);
1488#else
1489 stl_phys(sm_state + 0x7ffc, env->cr[0]);
1490 stl_phys(sm_state + 0x7ff8, env->cr[3]);
1491 stl_phys(sm_state + 0x7ff4, compute_eflags());
1492 stl_phys(sm_state + 0x7ff0, env->eip);
1493 stl_phys(sm_state + 0x7fec, EDI);
1494 stl_phys(sm_state + 0x7fe8, ESI);
1495 stl_phys(sm_state + 0x7fe4, EBP);
1496 stl_phys(sm_state + 0x7fe0, ESP);
1497 stl_phys(sm_state + 0x7fdc, EBX);
1498 stl_phys(sm_state + 0x7fd8, EDX);
1499 stl_phys(sm_state + 0x7fd4, ECX);
1500 stl_phys(sm_state + 0x7fd0, EAX);
1501 stl_phys(sm_state + 0x7fcc, env->dr[6]);
1502 stl_phys(sm_state + 0x7fc8, env->dr[7]);
1503
1504 stl_phys(sm_state + 0x7fc4, env->tr.selector);
1505 stl_phys(sm_state + 0x7f64, env->tr.base);
1506 stl_phys(sm_state + 0x7f60, env->tr.limit);
1507 stl_phys(sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff);
1508
1509 stl_phys(sm_state + 0x7fc0, env->ldt.selector);
1510 stl_phys(sm_state + 0x7f80, env->ldt.base);
1511 stl_phys(sm_state + 0x7f7c, env->ldt.limit);
1512 stl_phys(sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff);
1513
1514 stl_phys(sm_state + 0x7f74, env->gdt.base);
1515 stl_phys(sm_state + 0x7f70, env->gdt.limit);
1516
1517 stl_phys(sm_state + 0x7f58, env->idt.base);
1518 stl_phys(sm_state + 0x7f54, env->idt.limit);
1519
1520 for(i = 0; i < 6; i++) {
1521 dt = &env->segs[i];
1522 if (i < 3)
1523 offset = 0x7f84 + i * 12;
1524 else
1525 offset = 0x7f2c + (i - 3) * 12;
1526 stl_phys(sm_state + 0x7fa8 + i * 4, dt->selector);
1527 stl_phys(sm_state + offset + 8, dt->base);
1528 stl_phys(sm_state + offset + 4, dt->limit);
1529 stl_phys(sm_state + offset, (dt->flags >> 8) & 0xf0ff);
1530 }
1531 stl_phys(sm_state + 0x7f14, env->cr[4]);
1532
1533 stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
1534 stl_phys(sm_state + 0x7ef8, env->smbase);
1535#endif
1536 /* init SMM cpu state */
1537
1538#ifdef TARGET_X86_64
1539 cpu_load_efer(env, 0);
1540#endif
1541 load_eflags(0, ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1542 env->eip = 0x00008000;
1543 cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase,
1544 0xffffffff, 0);
1545 cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, 0);
1546 cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, 0);
1547 cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, 0);
1548 cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, 0);
1549 cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, 0);
1550
1551 cpu_x86_update_cr0(env,
1552 env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK | CR0_PG_MASK));
1553 cpu_x86_update_cr4(env, 0);
1554 env->dr[7] = 0x00000400;
1555 CC_OP = CC_OP_EFLAGS;
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01001556 env = saved_env;
Jun Nakajima86797932011-01-29 14:24:24 -08001557}
1558
1559void helper_rsm(void)
1560{
1561 target_ulong sm_state;
1562 int i, offset;
1563 uint32_t val;
1564
1565 sm_state = env->smbase + 0x8000;
1566#ifdef TARGET_X86_64
1567 cpu_load_efer(env, ldq_phys(sm_state + 0x7ed0));
1568
1569 for(i = 0; i < 6; i++) {
1570 offset = 0x7e00 + i * 16;
1571 cpu_x86_load_seg_cache(env, i,
1572 lduw_phys(sm_state + offset),
1573 ldq_phys(sm_state + offset + 8),
1574 ldl_phys(sm_state + offset + 4),
1575 (lduw_phys(sm_state + offset + 2) & 0xf0ff) << 8);
1576 }
1577
1578 env->gdt.base = ldq_phys(sm_state + 0x7e68);
1579 env->gdt.limit = ldl_phys(sm_state + 0x7e64);
1580
1581 env->ldt.selector = lduw_phys(sm_state + 0x7e70);
1582 env->ldt.base = ldq_phys(sm_state + 0x7e78);
1583 env->ldt.limit = ldl_phys(sm_state + 0x7e74);
1584 env->ldt.flags = (lduw_phys(sm_state + 0x7e72) & 0xf0ff) << 8;
1585
1586 env->idt.base = ldq_phys(sm_state + 0x7e88);
1587 env->idt.limit = ldl_phys(sm_state + 0x7e84);
1588
1589 env->tr.selector = lduw_phys(sm_state + 0x7e90);
1590 env->tr.base = ldq_phys(sm_state + 0x7e98);
1591 env->tr.limit = ldl_phys(sm_state + 0x7e94);
1592 env->tr.flags = (lduw_phys(sm_state + 0x7e92) & 0xf0ff) << 8;
1593
1594 EAX = ldq_phys(sm_state + 0x7ff8);
1595 ECX = ldq_phys(sm_state + 0x7ff0);
1596 EDX = ldq_phys(sm_state + 0x7fe8);
1597 EBX = ldq_phys(sm_state + 0x7fe0);
1598 ESP = ldq_phys(sm_state + 0x7fd8);
1599 EBP = ldq_phys(sm_state + 0x7fd0);
1600 ESI = ldq_phys(sm_state + 0x7fc8);
1601 EDI = ldq_phys(sm_state + 0x7fc0);
1602 for(i = 8; i < 16; i++)
1603 env->regs[i] = ldq_phys(sm_state + 0x7ff8 - i * 8);
1604 env->eip = ldq_phys(sm_state + 0x7f78);
1605 load_eflags(ldl_phys(sm_state + 0x7f70),
1606 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1607 env->dr[6] = ldl_phys(sm_state + 0x7f68);
1608 env->dr[7] = ldl_phys(sm_state + 0x7f60);
1609
1610 cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f48));
1611 cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7f50));
1612 cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7f58));
1613
1614 val = ldl_phys(sm_state + 0x7efc); /* revision ID */
1615 if (val & 0x20000) {
1616 env->smbase = ldl_phys(sm_state + 0x7f00) & ~0x7fff;
1617 }
1618#else
1619 cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7ffc));
1620 cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7ff8));
1621 load_eflags(ldl_phys(sm_state + 0x7ff4),
1622 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
1623 env->eip = ldl_phys(sm_state + 0x7ff0);
1624 EDI = ldl_phys(sm_state + 0x7fec);
1625 ESI = ldl_phys(sm_state + 0x7fe8);
1626 EBP = ldl_phys(sm_state + 0x7fe4);
1627 ESP = ldl_phys(sm_state + 0x7fe0);
1628 EBX = ldl_phys(sm_state + 0x7fdc);
1629 EDX = ldl_phys(sm_state + 0x7fd8);
1630 ECX = ldl_phys(sm_state + 0x7fd4);
1631 EAX = ldl_phys(sm_state + 0x7fd0);
1632 env->dr[6] = ldl_phys(sm_state + 0x7fcc);
1633 env->dr[7] = ldl_phys(sm_state + 0x7fc8);
1634
1635 env->tr.selector = ldl_phys(sm_state + 0x7fc4) & 0xffff;
1636 env->tr.base = ldl_phys(sm_state + 0x7f64);
1637 env->tr.limit = ldl_phys(sm_state + 0x7f60);
1638 env->tr.flags = (ldl_phys(sm_state + 0x7f5c) & 0xf0ff) << 8;
1639
1640 env->ldt.selector = ldl_phys(sm_state + 0x7fc0) & 0xffff;
1641 env->ldt.base = ldl_phys(sm_state + 0x7f80);
1642 env->ldt.limit = ldl_phys(sm_state + 0x7f7c);
1643 env->ldt.flags = (ldl_phys(sm_state + 0x7f78) & 0xf0ff) << 8;
1644
1645 env->gdt.base = ldl_phys(sm_state + 0x7f74);
1646 env->gdt.limit = ldl_phys(sm_state + 0x7f70);
1647
1648 env->idt.base = ldl_phys(sm_state + 0x7f58);
1649 env->idt.limit = ldl_phys(sm_state + 0x7f54);
1650
1651 for(i = 0; i < 6; i++) {
1652 if (i < 3)
1653 offset = 0x7f84 + i * 12;
1654 else
1655 offset = 0x7f2c + (i - 3) * 12;
1656 cpu_x86_load_seg_cache(env, i,
1657 ldl_phys(sm_state + 0x7fa8 + i * 4) & 0xffff,
1658 ldl_phys(sm_state + offset + 8),
1659 ldl_phys(sm_state + offset + 4),
1660 (ldl_phys(sm_state + offset) & 0xf0ff) << 8);
1661 }
1662 cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f14));
1663
1664 val = ldl_phys(sm_state + 0x7efc); /* revision ID */
1665 if (val & 0x20000) {
1666 env->smbase = ldl_phys(sm_state + 0x7ef8) & ~0x7fff;
1667 }
1668#endif
1669 CC_OP = CC_OP_EFLAGS;
1670 env->hflags &= ~HF_SMM_MASK;
1671 cpu_smm_update(env);
1672
1673 qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
1674 log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
1675}
1676
1677#endif /* !CONFIG_USER_ONLY */
1678
1679
1680/* division, flags are undefined */
1681
1682void helper_divb_AL(target_ulong t0)
1683{
1684 unsigned int num, den, q, r;
1685
1686 num = (EAX & 0xffff);
1687 den = (t0 & 0xff);
1688 if (den == 0) {
1689 raise_exception(EXCP00_DIVZ);
1690 }
1691 q = (num / den);
1692 if (q > 0xff)
1693 raise_exception(EXCP00_DIVZ);
1694 q &= 0xff;
1695 r = (num % den) & 0xff;
1696 EAX = (EAX & ~0xffff) | (r << 8) | q;
1697}
1698
1699void helper_idivb_AL(target_ulong t0)
1700{
1701 int num, den, q, r;
1702
1703 num = (int16_t)EAX;
1704 den = (int8_t)t0;
1705 if (den == 0) {
1706 raise_exception(EXCP00_DIVZ);
1707 }
1708 q = (num / den);
1709 if (q != (int8_t)q)
1710 raise_exception(EXCP00_DIVZ);
1711 q &= 0xff;
1712 r = (num % den) & 0xff;
1713 EAX = (EAX & ~0xffff) | (r << 8) | q;
1714}
1715
1716void helper_divw_AX(target_ulong t0)
1717{
1718 unsigned int num, den, q, r;
1719
1720 num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
1721 den = (t0 & 0xffff);
1722 if (den == 0) {
1723 raise_exception(EXCP00_DIVZ);
1724 }
1725 q = (num / den);
1726 if (q > 0xffff)
1727 raise_exception(EXCP00_DIVZ);
1728 q &= 0xffff;
1729 r = (num % den) & 0xffff;
1730 EAX = (EAX & ~0xffff) | q;
1731 EDX = (EDX & ~0xffff) | r;
1732}
1733
1734void helper_idivw_AX(target_ulong t0)
1735{
1736 int num, den, q, r;
1737
1738 num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
1739 den = (int16_t)t0;
1740 if (den == 0) {
1741 raise_exception(EXCP00_DIVZ);
1742 }
1743 q = (num / den);
1744 if (q != (int16_t)q)
1745 raise_exception(EXCP00_DIVZ);
1746 q &= 0xffff;
1747 r = (num % den) & 0xffff;
1748 EAX = (EAX & ~0xffff) | q;
1749 EDX = (EDX & ~0xffff) | r;
1750}
1751
1752void helper_divl_EAX(target_ulong t0)
1753{
1754 unsigned int den, r;
1755 uint64_t num, q;
1756
1757 num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
1758 den = t0;
1759 if (den == 0) {
1760 raise_exception(EXCP00_DIVZ);
1761 }
1762 q = (num / den);
1763 r = (num % den);
1764 if (q > 0xffffffff)
1765 raise_exception(EXCP00_DIVZ);
1766 EAX = (uint32_t)q;
1767 EDX = (uint32_t)r;
1768}
1769
1770void helper_idivl_EAX(target_ulong t0)
1771{
1772 int den, r;
1773 int64_t num, q;
1774
1775 num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
1776 den = t0;
1777 if (den == 0) {
1778 raise_exception(EXCP00_DIVZ);
1779 }
1780 q = (num / den);
1781 r = (num % den);
1782 if (q != (int32_t)q)
1783 raise_exception(EXCP00_DIVZ);
1784 EAX = (uint32_t)q;
1785 EDX = (uint32_t)r;
1786}
1787
1788/* bcd */
1789
1790/* XXX: exception */
1791void helper_aam(int base)
1792{
1793 int al, ah;
1794 al = EAX & 0xff;
1795 ah = al / base;
1796 al = al % base;
1797 EAX = (EAX & ~0xffff) | al | (ah << 8);
1798 CC_DST = al;
1799}
1800
1801void helper_aad(int base)
1802{
1803 int al, ah;
1804 al = EAX & 0xff;
1805 ah = (EAX >> 8) & 0xff;
1806 al = ((ah * base) + al) & 0xff;
1807 EAX = (EAX & ~0xffff) | al;
1808 CC_DST = al;
1809}
1810
1811void helper_aaa(void)
1812{
1813 int icarry;
1814 int al, ah, af;
1815 int eflags;
1816
1817 eflags = helper_cc_compute_all(CC_OP);
1818 af = eflags & CC_A;
1819 al = EAX & 0xff;
1820 ah = (EAX >> 8) & 0xff;
1821
1822 icarry = (al > 0xf9);
1823 if (((al & 0x0f) > 9 ) || af) {
1824 al = (al + 6) & 0x0f;
1825 ah = (ah + 1 + icarry) & 0xff;
1826 eflags |= CC_C | CC_A;
1827 } else {
1828 eflags &= ~(CC_C | CC_A);
1829 al &= 0x0f;
1830 }
1831 EAX = (EAX & ~0xffff) | al | (ah << 8);
1832 CC_SRC = eflags;
1833}
1834
1835void helper_aas(void)
1836{
1837 int icarry;
1838 int al, ah, af;
1839 int eflags;
1840
1841 eflags = helper_cc_compute_all(CC_OP);
1842 af = eflags & CC_A;
1843 al = EAX & 0xff;
1844 ah = (EAX >> 8) & 0xff;
1845
1846 icarry = (al < 6);
1847 if (((al & 0x0f) > 9 ) || af) {
1848 al = (al - 6) & 0x0f;
1849 ah = (ah - 1 - icarry) & 0xff;
1850 eflags |= CC_C | CC_A;
1851 } else {
1852 eflags &= ~(CC_C | CC_A);
1853 al &= 0x0f;
1854 }
1855 EAX = (EAX & ~0xffff) | al | (ah << 8);
1856 CC_SRC = eflags;
1857}
1858
1859void helper_daa(void)
1860{
1861 int al, af, cf;
1862 int eflags;
1863
1864 eflags = helper_cc_compute_all(CC_OP);
1865 cf = eflags & CC_C;
1866 af = eflags & CC_A;
1867 al = EAX & 0xff;
1868
1869 eflags = 0;
1870 if (((al & 0x0f) > 9 ) || af) {
1871 al = (al + 6) & 0xff;
1872 eflags |= CC_A;
1873 }
1874 if ((al > 0x9f) || cf) {
1875 al = (al + 0x60) & 0xff;
1876 eflags |= CC_C;
1877 }
1878 EAX = (EAX & ~0xff) | al;
1879 /* well, speed is not an issue here, so we compute the flags by hand */
1880 eflags |= (al == 0) << 6; /* zf */
1881 eflags |= parity_table[al]; /* pf */
1882 eflags |= (al & 0x80); /* sf */
1883 CC_SRC = eflags;
1884}
1885
1886void helper_das(void)
1887{
1888 int al, al1, af, cf;
1889 int eflags;
1890
1891 eflags = helper_cc_compute_all(CC_OP);
1892 cf = eflags & CC_C;
1893 af = eflags & CC_A;
1894 al = EAX & 0xff;
1895
1896 eflags = 0;
1897 al1 = al;
1898 if (((al & 0x0f) > 9 ) || af) {
1899 eflags |= CC_A;
1900 if (al < 6 || cf)
1901 eflags |= CC_C;
1902 al = (al - 6) & 0xff;
1903 }
1904 if ((al1 > 0x99) || cf) {
1905 al = (al - 0x60) & 0xff;
1906 eflags |= CC_C;
1907 }
1908 EAX = (EAX & ~0xff) | al;
1909 /* well, speed is not an issue here, so we compute the flags by hand */
1910 eflags |= (al == 0) << 6; /* zf */
1911 eflags |= parity_table[al]; /* pf */
1912 eflags |= (al & 0x80); /* sf */
1913 CC_SRC = eflags;
1914}
1915
1916void helper_into(int next_eip_addend)
1917{
1918 int eflags;
1919 eflags = helper_cc_compute_all(CC_OP);
1920 if (eflags & CC_O) {
1921 raise_interrupt(EXCP04_INTO, 1, 0, next_eip_addend);
1922 }
1923}
1924
1925void helper_cmpxchg8b(target_ulong a0)
1926{
1927 uint64_t d;
1928 int eflags;
1929
1930 eflags = helper_cc_compute_all(CC_OP);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001931 d = cpu_ldq_data(env, a0);
Jun Nakajima86797932011-01-29 14:24:24 -08001932 if (d == (((uint64_t)EDX << 32) | (uint32_t)EAX)) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001933 cpu_stq_data(env, a0, ((uint64_t)ECX << 32) | (uint32_t)EBX);
Jun Nakajima86797932011-01-29 14:24:24 -08001934 eflags |= CC_Z;
1935 } else {
1936 /* always do the store */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001937 cpu_stq_data(env, a0, d);
Jun Nakajima86797932011-01-29 14:24:24 -08001938 EDX = (uint32_t)(d >> 32);
1939 EAX = (uint32_t)d;
1940 eflags &= ~CC_Z;
1941 }
1942 CC_SRC = eflags;
1943}
1944
1945#ifdef TARGET_X86_64
1946void helper_cmpxchg16b(target_ulong a0)
1947{
1948 uint64_t d0, d1;
1949 int eflags;
1950
1951 if ((a0 & 0xf) != 0)
1952 raise_exception(EXCP0D_GPF);
1953 eflags = helper_cc_compute_all(CC_OP);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001954 d0 = cpu_ldq_data(env, a0);
1955 d1 = cpu_ldq_data(env, a0 + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08001956 if (d0 == EAX && d1 == EDX) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001957 cpu_stq_data(env, a0, EBX);
1958 cpu_stq_data(env, a0 + 8, ECX);
Jun Nakajima86797932011-01-29 14:24:24 -08001959 eflags |= CC_Z;
1960 } else {
1961 /* always do the store */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01001962 cpu_stq_data(env, a0, d0);
1963 cpu_stq_data(env, a0 + 8, d1);
Jun Nakajima86797932011-01-29 14:24:24 -08001964 EDX = d1;
1965 EAX = d0;
1966 eflags &= ~CC_Z;
1967 }
1968 CC_SRC = eflags;
1969}
1970#endif
1971
1972void helper_single_step(void)
1973{
1974#ifndef CONFIG_USER_ONLY
1975 check_hw_breakpoints(env, 1);
1976 env->dr[6] |= DR6_BS;
1977#endif
1978 raise_exception(EXCP01_DB);
1979}
1980
1981void helper_cpuid(void)
1982{
1983 uint32_t eax, ebx, ecx, edx;
1984
1985 helper_svm_check_intercept_param(SVM_EXIT_CPUID, 0);
1986
1987 cpu_x86_cpuid(env, (uint32_t)EAX, (uint32_t)ECX, &eax, &ebx, &ecx, &edx);
1988 EAX = eax;
1989 EBX = ebx;
1990 ECX = ecx;
1991 EDX = edx;
1992}
1993
1994void helper_enter_level(int level, int data32, target_ulong t1)
1995{
1996 target_ulong ssp;
1997 uint32_t esp_mask, esp, ebp;
1998
1999 esp_mask = get_sp_mask(env->segs[R_SS].flags);
2000 ssp = env->segs[R_SS].base;
2001 ebp = EBP;
2002 esp = ESP;
2003 if (data32) {
2004 /* 32 bit */
2005 esp -= 4;
2006 while (--level) {
2007 esp -= 4;
2008 ebp -= 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002009 cpu_stl_data(env, ssp + (esp & esp_mask),
2010 cpu_ldl_data(env, ssp + (ebp & esp_mask)));
Jun Nakajima86797932011-01-29 14:24:24 -08002011 }
2012 esp -= 4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002013 cpu_stl_data(env, ssp + (esp & esp_mask), t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002014 } else {
2015 /* 16 bit */
2016 esp -= 2;
2017 while (--level) {
2018 esp -= 2;
2019 ebp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002020 cpu_stw_data(env, ssp + (esp & esp_mask),
2021 cpu_lduw_data(env, ssp + (ebp & esp_mask)));
Jun Nakajima86797932011-01-29 14:24:24 -08002022 }
2023 esp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002024 cpu_stw_data(env, ssp + (esp & esp_mask), t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002025 }
2026}
2027
2028#ifdef TARGET_X86_64
2029void helper_enter64_level(int level, int data64, target_ulong t1)
2030{
2031 target_ulong esp, ebp;
2032 ebp = EBP;
2033 esp = ESP;
2034
2035 if (data64) {
2036 /* 64 bit */
2037 esp -= 8;
2038 while (--level) {
2039 esp -= 8;
2040 ebp -= 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002041 cpu_stq_data(env, esp, cpu_ldq_data(env, ebp));
Jun Nakajima86797932011-01-29 14:24:24 -08002042 }
2043 esp -= 8;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002044 cpu_stq_data(env, esp, t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002045 } else {
2046 /* 16 bit */
2047 esp -= 2;
2048 while (--level) {
2049 esp -= 2;
2050 ebp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002051 cpu_stw_data(env, esp, cpu_lduw_data(env, ebp));
Jun Nakajima86797932011-01-29 14:24:24 -08002052 }
2053 esp -= 2;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002054 cpu_stw_data(env, esp, t1);
Jun Nakajima86797932011-01-29 14:24:24 -08002055 }
2056}
2057#endif
2058
2059void helper_lldt(int selector)
2060{
2061 SegmentCache *dt;
2062 uint32_t e1, e2;
2063 int index, entry_limit;
2064 target_ulong ptr;
2065
2066 selector &= 0xffff;
2067 if ((selector & 0xfffc) == 0) {
2068 /* XXX: NULL selector case: invalid LDT */
2069 env->ldt.base = 0;
2070 env->ldt.limit = 0;
2071 } else {
2072 if (selector & 0x4)
2073 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2074 dt = &env->gdt;
2075 index = selector & ~7;
2076#ifdef TARGET_X86_64
2077 if (env->hflags & HF_LMA_MASK)
2078 entry_limit = 15;
2079 else
2080#endif
2081 entry_limit = 7;
2082 if ((index + entry_limit) > dt->limit)
2083 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2084 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002085 e1 = cpu_ldl_kernel(env, ptr);
2086 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002087 if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
2088 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2089 if (!(e2 & DESC_P_MASK))
2090 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2091#ifdef TARGET_X86_64
2092 if (env->hflags & HF_LMA_MASK) {
2093 uint32_t e3;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002094 e3 = cpu_ldl_kernel(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08002095 load_seg_cache_raw_dt(&env->ldt, e1, e2);
2096 env->ldt.base |= (target_ulong)e3 << 32;
2097 } else
2098#endif
2099 {
2100 load_seg_cache_raw_dt(&env->ldt, e1, e2);
2101 }
2102 }
2103 env->ldt.selector = selector;
2104}
2105
2106void helper_ltr(int selector)
2107{
2108 SegmentCache *dt;
2109 uint32_t e1, e2;
2110 int index, type, entry_limit;
2111 target_ulong ptr;
2112
2113 selector &= 0xffff;
2114 if ((selector & 0xfffc) == 0) {
2115 /* NULL selector case: invalid TR */
2116 env->tr.base = 0;
2117 env->tr.limit = 0;
2118 env->tr.flags = 0;
2119 } else {
2120 if (selector & 0x4)
2121 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2122 dt = &env->gdt;
2123 index = selector & ~7;
2124#ifdef TARGET_X86_64
2125 if (env->hflags & HF_LMA_MASK)
2126 entry_limit = 15;
2127 else
2128#endif
2129 entry_limit = 7;
2130 if ((index + entry_limit) > dt->limit)
2131 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2132 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002133 e1 = cpu_ldl_kernel(env, ptr);
2134 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002135 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2136 if ((e2 & DESC_S_MASK) ||
2137 (type != 1 && type != 9))
2138 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2139 if (!(e2 & DESC_P_MASK))
2140 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2141#ifdef TARGET_X86_64
2142 if (env->hflags & HF_LMA_MASK) {
2143 uint32_t e3, e4;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002144 e3 = cpu_ldl_kernel(env, ptr + 8);
2145 e4 = cpu_ldl_kernel(env, ptr + 12);
Jun Nakajima86797932011-01-29 14:24:24 -08002146 if ((e4 >> DESC_TYPE_SHIFT) & 0xf)
2147 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2148 load_seg_cache_raw_dt(&env->tr, e1, e2);
2149 env->tr.base |= (target_ulong)e3 << 32;
2150 } else
2151#endif
2152 {
2153 load_seg_cache_raw_dt(&env->tr, e1, e2);
2154 }
2155 e2 |= DESC_TSS_BUSY_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002156 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -08002157 }
2158 env->tr.selector = selector;
2159}
2160
2161/* only works if protected mode and not VM86. seg_reg must be != R_CS */
2162void helper_load_seg(int seg_reg, int selector)
2163{
2164 uint32_t e1, e2;
2165 int cpl, dpl, rpl;
2166 SegmentCache *dt;
2167 int index;
2168 target_ulong ptr;
2169
2170 selector &= 0xffff;
2171 cpl = env->hflags & HF_CPL_MASK;
2172 if ((selector & 0xfffc) == 0) {
2173 /* null selector case */
2174 if (seg_reg == R_SS
2175#ifdef TARGET_X86_64
2176 && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
2177#endif
2178 )
2179 raise_exception_err(EXCP0D_GPF, 0);
2180 cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
2181 } else {
2182
2183 if (selector & 0x4)
2184 dt = &env->ldt;
2185 else
2186 dt = &env->gdt;
2187 index = selector & ~7;
2188 if ((index + 7) > dt->limit)
2189 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2190 ptr = dt->base + index;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002191 e1 = cpu_ldl_kernel(env, ptr);
2192 e2 = cpu_ldl_kernel(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08002193
2194 if (!(e2 & DESC_S_MASK))
2195 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2196 rpl = selector & 3;
2197 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2198 if (seg_reg == R_SS) {
2199 /* must be writable segment */
2200 if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
2201 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2202 if (rpl != cpl || dpl != cpl)
2203 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2204 } else {
2205 /* must be readable segment */
2206 if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK)
2207 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2208
2209 if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2210 /* if not conforming code, test rights */
2211 if (dpl < cpl || dpl < rpl)
2212 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2213 }
2214 }
2215
2216 if (!(e2 & DESC_P_MASK)) {
2217 if (seg_reg == R_SS)
2218 raise_exception_err(EXCP0C_STACK, selector & 0xfffc);
2219 else
2220 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2221 }
2222
2223 /* set the access bit if not already set */
2224 if (!(e2 & DESC_A_MASK)) {
2225 e2 |= DESC_A_MASK;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002226 cpu_stl_kernel(env, ptr + 4, e2);
Jun Nakajima86797932011-01-29 14:24:24 -08002227 }
2228
2229 cpu_x86_load_seg_cache(env, seg_reg, selector,
2230 get_seg_base(e1, e2),
2231 get_seg_limit(e1, e2),
2232 e2);
2233#if 0
2234 qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
2235 selector, (unsigned long)sc->base, sc->limit, sc->flags);
2236#endif
2237 }
2238}
2239
2240/* protected mode jump */
2241void helper_ljmp_protected(int new_cs, target_ulong new_eip,
2242 int next_eip_addend)
2243{
2244 int gate_cs, type;
2245 uint32_t e1, e2, cpl, dpl, rpl, limit;
2246 target_ulong next_eip;
2247
2248 if ((new_cs & 0xfffc) == 0)
2249 raise_exception_err(EXCP0D_GPF, 0);
2250 if (load_segment(&e1, &e2, new_cs) != 0)
2251 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2252 cpl = env->hflags & HF_CPL_MASK;
2253 if (e2 & DESC_S_MASK) {
2254 if (!(e2 & DESC_CS_MASK))
2255 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2256 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2257 if (e2 & DESC_C_MASK) {
2258 /* conforming code segment */
2259 if (dpl > cpl)
2260 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2261 } else {
2262 /* non conforming code segment */
2263 rpl = new_cs & 3;
2264 if (rpl > cpl)
2265 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2266 if (dpl != cpl)
2267 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2268 }
2269 if (!(e2 & DESC_P_MASK))
2270 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2271 limit = get_seg_limit(e1, e2);
2272 if (new_eip > limit &&
2273 !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK))
2274 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2275 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2276 get_seg_base(e1, e2), limit, e2);
2277 EIP = new_eip;
2278 } else {
2279 /* jump to call or task gate */
2280 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2281 rpl = new_cs & 3;
2282 cpl = env->hflags & HF_CPL_MASK;
2283 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2284 switch(type) {
2285 case 1: /* 286 TSS */
2286 case 9: /* 386 TSS */
2287 case 5: /* task gate */
2288 if (dpl < cpl || dpl < rpl)
2289 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2290 next_eip = env->eip + next_eip_addend;
2291 switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
2292 CC_OP = CC_OP_EFLAGS;
2293 break;
2294 case 4: /* 286 call gate */
2295 case 12: /* 386 call gate */
2296 if ((dpl < cpl) || (dpl < rpl))
2297 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2298 if (!(e2 & DESC_P_MASK))
2299 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2300 gate_cs = e1 >> 16;
2301 new_eip = (e1 & 0xffff);
2302 if (type == 12)
2303 new_eip |= (e2 & 0xffff0000);
2304 if (load_segment(&e1, &e2, gate_cs) != 0)
2305 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2306 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2307 /* must be code segment */
2308 if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
2309 (DESC_S_MASK | DESC_CS_MASK)))
2310 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2311 if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
2312 (!(e2 & DESC_C_MASK) && (dpl != cpl)))
2313 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2314 if (!(e2 & DESC_P_MASK))
2315 raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
2316 limit = get_seg_limit(e1, e2);
2317 if (new_eip > limit)
2318 raise_exception_err(EXCP0D_GPF, 0);
2319 cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl,
2320 get_seg_base(e1, e2), limit, e2);
2321 EIP = new_eip;
2322 break;
2323 default:
2324 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2325 break;
2326 }
2327 }
2328}
2329
2330/* real mode call */
2331void helper_lcall_real(int new_cs, target_ulong new_eip1,
2332 int shift, int next_eip)
2333{
2334 int new_eip;
2335 uint32_t esp, esp_mask;
2336 target_ulong ssp;
2337
2338 new_eip = new_eip1;
2339 esp = ESP;
2340 esp_mask = get_sp_mask(env->segs[R_SS].flags);
2341 ssp = env->segs[R_SS].base;
2342 if (shift) {
2343 PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector);
2344 PUSHL(ssp, esp, esp_mask, next_eip);
2345 } else {
2346 PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector);
2347 PUSHW(ssp, esp, esp_mask, next_eip);
2348 }
2349
2350 SET_ESP(esp, esp_mask);
2351 env->eip = new_eip;
2352 env->segs[R_CS].selector = new_cs;
2353 env->segs[R_CS].base = (new_cs << 4);
2354}
2355
2356/* protected mode call */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02002357void helper_lcall_protected(int new_cs, target_ulong new_eip,
Jun Nakajima86797932011-01-29 14:24:24 -08002358 int shift, int next_eip_addend)
2359{
2360 int new_stack, i;
2361 uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
2362 uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask;
2363 uint32_t val, limit, old_sp_mask;
2364 target_ulong ssp, old_ssp, next_eip;
2365
2366 next_eip = env->eip + next_eip_addend;
2367 LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift);
2368 LOG_PCALL_STATE(env);
2369 if ((new_cs & 0xfffc) == 0)
2370 raise_exception_err(EXCP0D_GPF, 0);
2371 if (load_segment(&e1, &e2, new_cs) != 0)
2372 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2373 cpl = env->hflags & HF_CPL_MASK;
2374 LOG_PCALL("desc=%08x:%08x\n", e1, e2);
2375 if (e2 & DESC_S_MASK) {
2376 if (!(e2 & DESC_CS_MASK))
2377 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2378 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2379 if (e2 & DESC_C_MASK) {
2380 /* conforming code segment */
2381 if (dpl > cpl)
2382 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2383 } else {
2384 /* non conforming code segment */
2385 rpl = new_cs & 3;
2386 if (rpl > cpl)
2387 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2388 if (dpl != cpl)
2389 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2390 }
2391 if (!(e2 & DESC_P_MASK))
2392 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2393
2394#ifdef TARGET_X86_64
2395 /* XXX: check 16/32 bit cases in long mode */
2396 if (shift == 2) {
2397 target_ulong rsp;
2398 /* 64 bit case */
2399 rsp = ESP;
2400 PUSHQ(rsp, env->segs[R_CS].selector);
2401 PUSHQ(rsp, next_eip);
2402 /* from this point, not restartable */
2403 ESP = rsp;
2404 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2405 get_seg_base(e1, e2),
2406 get_seg_limit(e1, e2), e2);
2407 EIP = new_eip;
2408 } else
2409#endif
2410 {
2411 sp = ESP;
2412 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2413 ssp = env->segs[R_SS].base;
2414 if (shift) {
2415 PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
2416 PUSHL(ssp, sp, sp_mask, next_eip);
2417 } else {
2418 PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
2419 PUSHW(ssp, sp, sp_mask, next_eip);
2420 }
2421
2422 limit = get_seg_limit(e1, e2);
2423 if (new_eip > limit)
2424 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2425 /* from this point, not restartable */
2426 SET_ESP(sp, sp_mask);
2427 cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
2428 get_seg_base(e1, e2), limit, e2);
2429 EIP = new_eip;
2430 }
2431 } else {
2432 /* check gate type */
2433 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
2434 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2435 rpl = new_cs & 3;
2436 switch(type) {
2437 case 1: /* available 286 TSS */
2438 case 9: /* available 386 TSS */
2439 case 5: /* task gate */
2440 if (dpl < cpl || dpl < rpl)
2441 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2442 switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
2443 CC_OP = CC_OP_EFLAGS;
2444 return;
2445 case 4: /* 286 call gate */
2446 case 12: /* 386 call gate */
2447 break;
2448 default:
2449 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2450 break;
2451 }
2452 shift = type >> 3;
2453
2454 if (dpl < cpl || dpl < rpl)
2455 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2456 /* check valid bit */
2457 if (!(e2 & DESC_P_MASK))
2458 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2459 selector = e1 >> 16;
2460 offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
2461 param_count = e2 & 0x1f;
2462 if ((selector & 0xfffc) == 0)
2463 raise_exception_err(EXCP0D_GPF, 0);
2464
2465 if (load_segment(&e1, &e2, selector) != 0)
2466 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2467 if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
2468 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2469 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2470 if (dpl > cpl)
2471 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
2472 if (!(e2 & DESC_P_MASK))
2473 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
2474
2475 if (!(e2 & DESC_C_MASK) && dpl < cpl) {
2476 /* to inner privilege */
2477 get_ss_esp_from_tss(&ss, &sp, dpl);
2478 LOG_PCALL("new ss:esp=%04x:%08x param_count=%d ESP=" TARGET_FMT_lx "\n",
2479 ss, sp, param_count, ESP);
2480 if ((ss & 0xfffc) == 0)
2481 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2482 if ((ss & 3) != dpl)
2483 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2484 if (load_segment(&ss_e1, &ss_e2, ss) != 0)
2485 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2486 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
2487 if (ss_dpl != dpl)
2488 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2489 if (!(ss_e2 & DESC_S_MASK) ||
2490 (ss_e2 & DESC_CS_MASK) ||
2491 !(ss_e2 & DESC_W_MASK))
2492 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2493 if (!(ss_e2 & DESC_P_MASK))
2494 raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
2495
2496 // push_size = ((param_count * 2) + 8) << shift;
2497
2498 old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
2499 old_ssp = env->segs[R_SS].base;
2500
2501 sp_mask = get_sp_mask(ss_e2);
2502 ssp = get_seg_base(ss_e1, ss_e2);
2503 if (shift) {
2504 PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector);
2505 PUSHL(ssp, sp, sp_mask, ESP);
2506 for(i = param_count - 1; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002507 val = cpu_ldl_kernel(env, old_ssp + ((ESP + i * 4) & old_sp_mask));
Jun Nakajima86797932011-01-29 14:24:24 -08002508 PUSHL(ssp, sp, sp_mask, val);
2509 }
2510 } else {
2511 PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector);
2512 PUSHW(ssp, sp, sp_mask, ESP);
2513 for(i = param_count - 1; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002514 val = cpu_lduw_kernel(env, old_ssp + ((ESP + i * 2) & old_sp_mask));
Jun Nakajima86797932011-01-29 14:24:24 -08002515 PUSHW(ssp, sp, sp_mask, val);
2516 }
2517 }
2518 new_stack = 1;
2519 } else {
2520 /* to same privilege */
2521 sp = ESP;
2522 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2523 ssp = env->segs[R_SS].base;
2524 // push_size = (4 << shift);
2525 new_stack = 0;
2526 }
2527
2528 if (shift) {
2529 PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
2530 PUSHL(ssp, sp, sp_mask, next_eip);
2531 } else {
2532 PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
2533 PUSHW(ssp, sp, sp_mask, next_eip);
2534 }
2535
2536 /* from this point, not restartable */
2537
2538 if (new_stack) {
2539 ss = (ss & ~3) | dpl;
2540 cpu_x86_load_seg_cache(env, R_SS, ss,
2541 ssp,
2542 get_seg_limit(ss_e1, ss_e2),
2543 ss_e2);
2544 }
2545
2546 selector = (selector & ~3) | dpl;
2547 cpu_x86_load_seg_cache(env, R_CS, selector,
2548 get_seg_base(e1, e2),
2549 get_seg_limit(e1, e2),
2550 e2);
2551 cpu_x86_set_cpl(env, dpl);
2552 SET_ESP(sp, sp_mask);
2553 EIP = offset;
2554 }
Jun Nakajima86797932011-01-29 14:24:24 -08002555}
2556
2557/* real and vm86 mode iret */
2558void helper_iret_real(int shift)
2559{
2560 uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
2561 target_ulong ssp;
2562 int eflags_mask;
2563
2564 sp_mask = 0xffff; /* XXXX: use SS segment size ? */
2565 sp = ESP;
2566 ssp = env->segs[R_SS].base;
2567 if (shift == 1) {
2568 /* 32 bits */
2569 POPL(ssp, sp, sp_mask, new_eip);
2570 POPL(ssp, sp, sp_mask, new_cs);
2571 new_cs &= 0xffff;
2572 POPL(ssp, sp, sp_mask, new_eflags);
2573 } else {
2574 /* 16 bits */
2575 POPW(ssp, sp, sp_mask, new_eip);
2576 POPW(ssp, sp, sp_mask, new_cs);
2577 POPW(ssp, sp, sp_mask, new_eflags);
2578 }
2579 ESP = (ESP & ~sp_mask) | (sp & sp_mask);
2580 env->segs[R_CS].selector = new_cs;
2581 env->segs[R_CS].base = (new_cs << 4);
2582 env->eip = new_eip;
2583 if (env->eflags & VM_MASK)
2584 eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK;
2585 else
2586 eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK | NT_MASK;
2587 if (shift == 0)
2588 eflags_mask &= 0xffff;
2589 load_eflags(new_eflags, eflags_mask);
2590 env->hflags2 &= ~HF2_NMI_MASK;
2591}
2592
2593static inline void validate_seg(int seg_reg, int cpl)
2594{
2595 int dpl;
2596 uint32_t e2;
2597
2598 /* XXX: on x86_64, we do not want to nullify FS and GS because
2599 they may still contain a valid base. I would be interested to
2600 know how a real x86_64 CPU behaves */
2601 if ((seg_reg == R_FS || seg_reg == R_GS) &&
2602 (env->segs[seg_reg].selector & 0xfffc) == 0)
2603 return;
2604
2605 e2 = env->segs[seg_reg].flags;
2606 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2607 if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2608 /* data or non conforming code segment */
2609 if (dpl < cpl) {
2610 cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0);
2611 }
2612 }
2613}
2614
2615/* protected mode iret */
2616static inline void helper_ret_protected(int shift, int is_iret, int addend)
2617{
2618 uint32_t new_cs, new_eflags, new_ss;
2619 uint32_t new_es, new_ds, new_fs, new_gs;
2620 uint32_t e1, e2, ss_e1, ss_e2;
2621 int cpl, dpl, rpl, eflags_mask, iopl;
2622 target_ulong ssp, sp, new_eip, new_esp, sp_mask;
2623
2624#ifdef TARGET_X86_64
2625 if (shift == 2)
2626 sp_mask = -1;
2627 else
2628#endif
2629 sp_mask = get_sp_mask(env->segs[R_SS].flags);
2630 sp = ESP;
2631 ssp = env->segs[R_SS].base;
2632 new_eflags = 0; /* avoid warning */
2633#ifdef TARGET_X86_64
2634 if (shift == 2) {
2635 POPQ(sp, new_eip);
2636 POPQ(sp, new_cs);
2637 new_cs &= 0xffff;
2638 if (is_iret) {
2639 POPQ(sp, new_eflags);
2640 }
2641 } else
2642#endif
2643 if (shift == 1) {
2644 /* 32 bits */
2645 POPL(ssp, sp, sp_mask, new_eip);
2646 POPL(ssp, sp, sp_mask, new_cs);
2647 new_cs &= 0xffff;
2648 if (is_iret) {
2649 POPL(ssp, sp, sp_mask, new_eflags);
2650 if (new_eflags & VM_MASK)
2651 goto return_to_vm86;
2652 }
2653 } else {
2654 /* 16 bits */
2655 POPW(ssp, sp, sp_mask, new_eip);
2656 POPW(ssp, sp, sp_mask, new_cs);
2657 if (is_iret)
2658 POPW(ssp, sp, sp_mask, new_eflags);
2659 }
2660 LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
2661 new_cs, new_eip, shift, addend);
2662 LOG_PCALL_STATE(env);
2663 if ((new_cs & 0xfffc) == 0)
2664 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2665 if (load_segment(&e1, &e2, new_cs) != 0)
2666 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2667 if (!(e2 & DESC_S_MASK) ||
2668 !(e2 & DESC_CS_MASK))
2669 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2670 cpl = env->hflags & HF_CPL_MASK;
2671 rpl = new_cs & 3;
2672 if (rpl < cpl)
2673 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2674 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2675 if (e2 & DESC_C_MASK) {
2676 if (dpl > rpl)
2677 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2678 } else {
2679 if (dpl != rpl)
2680 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
2681 }
2682 if (!(e2 & DESC_P_MASK))
2683 raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
2684
2685 sp += addend;
2686 if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
2687 ((env->hflags & HF_CS64_MASK) && !is_iret))) {
2688 /* return to same privilege level */
2689 cpu_x86_load_seg_cache(env, R_CS, new_cs,
2690 get_seg_base(e1, e2),
2691 get_seg_limit(e1, e2),
2692 e2);
2693 } else {
2694 /* return to different privilege level */
2695#ifdef TARGET_X86_64
2696 if (shift == 2) {
2697 POPQ(sp, new_esp);
2698 POPQ(sp, new_ss);
2699 new_ss &= 0xffff;
2700 } else
2701#endif
2702 if (shift == 1) {
2703 /* 32 bits */
2704 POPL(ssp, sp, sp_mask, new_esp);
2705 POPL(ssp, sp, sp_mask, new_ss);
2706 new_ss &= 0xffff;
2707 } else {
2708 /* 16 bits */
2709 POPW(ssp, sp, sp_mask, new_esp);
2710 POPW(ssp, sp, sp_mask, new_ss);
2711 }
2712 LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n",
2713 new_ss, new_esp);
2714 if ((new_ss & 0xfffc) == 0) {
2715#ifdef TARGET_X86_64
2716 /* NULL ss is allowed in long mode if cpl != 3*/
2717 /* XXX: test CS64 ? */
2718 if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
2719 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2720 0, 0xffffffff,
2721 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2722 DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
2723 DESC_W_MASK | DESC_A_MASK);
2724 ss_e2 = DESC_B_MASK; /* XXX: should not be needed ? */
2725 } else
2726#endif
2727 {
2728 raise_exception_err(EXCP0D_GPF, 0);
2729 }
2730 } else {
2731 if ((new_ss & 3) != rpl)
2732 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2733 if (load_segment(&ss_e1, &ss_e2, new_ss) != 0)
2734 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2735 if (!(ss_e2 & DESC_S_MASK) ||
2736 (ss_e2 & DESC_CS_MASK) ||
2737 !(ss_e2 & DESC_W_MASK))
2738 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2739 dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
2740 if (dpl != rpl)
2741 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
2742 if (!(ss_e2 & DESC_P_MASK))
2743 raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
2744 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2745 get_seg_base(ss_e1, ss_e2),
2746 get_seg_limit(ss_e1, ss_e2),
2747 ss_e2);
2748 }
2749
2750 cpu_x86_load_seg_cache(env, R_CS, new_cs,
2751 get_seg_base(e1, e2),
2752 get_seg_limit(e1, e2),
2753 e2);
2754 cpu_x86_set_cpl(env, rpl);
2755 sp = new_esp;
2756#ifdef TARGET_X86_64
2757 if (env->hflags & HF_CS64_MASK)
2758 sp_mask = -1;
2759 else
2760#endif
2761 sp_mask = get_sp_mask(ss_e2);
2762
2763 /* validate data segments */
2764 validate_seg(R_ES, rpl);
2765 validate_seg(R_DS, rpl);
2766 validate_seg(R_FS, rpl);
2767 validate_seg(R_GS, rpl);
2768
2769 sp += addend;
2770 }
2771 SET_ESP(sp, sp_mask);
2772 env->eip = new_eip;
2773 if (is_iret) {
2774 /* NOTE: 'cpl' is the _old_ CPL */
2775 eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
2776 if (cpl == 0)
2777 eflags_mask |= IOPL_MASK;
2778 iopl = (env->eflags >> IOPL_SHIFT) & 3;
2779 if (cpl <= iopl)
2780 eflags_mask |= IF_MASK;
2781 if (shift == 0)
2782 eflags_mask &= 0xffff;
2783 load_eflags(new_eflags, eflags_mask);
2784 }
2785 return;
2786
2787 return_to_vm86:
2788 POPL(ssp, sp, sp_mask, new_esp);
2789 POPL(ssp, sp, sp_mask, new_ss);
2790 POPL(ssp, sp, sp_mask, new_es);
2791 POPL(ssp, sp, sp_mask, new_ds);
2792 POPL(ssp, sp, sp_mask, new_fs);
2793 POPL(ssp, sp, sp_mask, new_gs);
2794
2795 /* modify processor state */
2796 load_eflags(new_eflags, TF_MASK | AC_MASK | ID_MASK |
2797 IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | VIP_MASK);
2798 load_seg_vm(R_CS, new_cs & 0xffff);
2799 cpu_x86_set_cpl(env, 3);
2800 load_seg_vm(R_SS, new_ss & 0xffff);
2801 load_seg_vm(R_ES, new_es & 0xffff);
2802 load_seg_vm(R_DS, new_ds & 0xffff);
2803 load_seg_vm(R_FS, new_fs & 0xffff);
2804 load_seg_vm(R_GS, new_gs & 0xffff);
2805
2806 env->eip = new_eip & 0xffff;
2807 ESP = new_esp;
2808}
2809
2810void helper_iret_protected(int shift, int next_eip)
2811{
2812 int tss_selector, type;
2813 uint32_t e1, e2;
2814
2815 /* specific case for TSS */
2816 if (env->eflags & NT_MASK) {
2817#ifdef TARGET_X86_64
2818 if (env->hflags & HF_LMA_MASK)
2819 raise_exception_err(EXCP0D_GPF, 0);
2820#endif
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01002821 tss_selector = cpu_lduw_kernel(env, env->tr.base + 0);
Jun Nakajima86797932011-01-29 14:24:24 -08002822 if (tss_selector & 4)
2823 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2824 if (load_segment(&e1, &e2, tss_selector) != 0)
2825 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2826 type = (e2 >> DESC_TYPE_SHIFT) & 0x17;
2827 /* NOTE: we check both segment and busy TSS */
2828 if (type != 3)
2829 raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
2830 switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip);
2831 } else {
2832 helper_ret_protected(shift, 1, 0);
2833 }
2834 env->hflags2 &= ~HF2_NMI_MASK;
Jun Nakajima86797932011-01-29 14:24:24 -08002835}
2836
2837void helper_lret_protected(int shift, int addend)
2838{
2839 helper_ret_protected(shift, 0, addend);
Jun Nakajima86797932011-01-29 14:24:24 -08002840}
2841
2842void helper_sysenter(void)
2843{
2844 if (env->sysenter_cs == 0) {
2845 raise_exception_err(EXCP0D_GPF, 0);
2846 }
2847 env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
2848 cpu_x86_set_cpl(env, 0);
2849
2850#ifdef TARGET_X86_64
2851 if (env->hflags & HF_LMA_MASK) {
2852 cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2853 0, 0xffffffff,
2854 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2855 DESC_S_MASK |
2856 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
2857 } else
2858#endif
2859 {
2860 cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2861 0, 0xffffffff,
2862 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2863 DESC_S_MASK |
2864 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
2865 }
2866 cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
2867 0, 0xffffffff,
2868 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2869 DESC_S_MASK |
2870 DESC_W_MASK | DESC_A_MASK);
2871 ESP = env->sysenter_esp;
2872 EIP = env->sysenter_eip;
2873}
2874
2875void helper_sysexit(int dflag)
2876{
2877 int cpl;
2878
2879 cpl = env->hflags & HF_CPL_MASK;
2880 if (env->sysenter_cs == 0 || cpl != 0) {
2881 raise_exception_err(EXCP0D_GPF, 0);
2882 }
2883 cpu_x86_set_cpl(env, 3);
2884#ifdef TARGET_X86_64
2885 if (dflag == 2) {
2886 cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | 3,
2887 0, 0xffffffff,
2888 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2889 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2890 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
2891 cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | 3,
2892 0, 0xffffffff,
2893 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2894 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2895 DESC_W_MASK | DESC_A_MASK);
2896 } else
2897#endif
2898 {
2899 cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3,
2900 0, 0xffffffff,
2901 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2902 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2903 DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
2904 cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3,
2905 0, 0xffffffff,
2906 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2907 DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2908 DESC_W_MASK | DESC_A_MASK);
2909 }
2910 ESP = ECX;
2911 EIP = EDX;
Jun Nakajima86797932011-01-29 14:24:24 -08002912}
2913
2914#if defined(CONFIG_USER_ONLY)
2915target_ulong helper_read_crN(int reg)
2916{
2917 return 0;
2918}
2919
2920void helper_write_crN(int reg, target_ulong t0)
2921{
2922}
2923
2924void helper_movl_drN_T0(int reg, target_ulong t0)
2925{
2926}
2927#else
2928target_ulong helper_read_crN(int reg)
2929{
2930 target_ulong val;
2931
2932 helper_svm_check_intercept_param(SVM_EXIT_READ_CR0 + reg, 0);
2933 switch(reg) {
2934 default:
2935 val = env->cr[reg];
2936 break;
2937 case 8:
2938 if (!(env->hflags2 & HF2_VINTR_MASK)) {
2939 val = cpu_get_apic_tpr(env);
2940 } else {
2941 val = env->v_tpr;
2942 }
2943 break;
2944 }
2945 return val;
2946}
2947
2948void helper_write_crN(int reg, target_ulong t0)
2949{
2950 helper_svm_check_intercept_param(SVM_EXIT_WRITE_CR0 + reg, 0);
2951 switch(reg) {
2952 case 0:
2953 cpu_x86_update_cr0(env, t0);
2954 break;
2955 case 3:
2956 cpu_x86_update_cr3(env, t0);
2957 break;
2958 case 4:
2959 cpu_x86_update_cr4(env, t0);
2960 break;
2961 case 8:
2962 if (!(env->hflags2 & HF2_VINTR_MASK)) {
2963 cpu_set_apic_tpr(env, t0);
2964 }
2965 env->v_tpr = t0 & 0x0f;
2966 break;
2967 default:
2968 env->cr[reg] = t0;
2969 break;
2970 }
2971}
2972
2973void helper_movl_drN_T0(int reg, target_ulong t0)
2974{
2975 int i;
2976
2977 if (reg < 4) {
2978 hw_breakpoint_remove(env, reg);
2979 env->dr[reg] = t0;
2980 hw_breakpoint_insert(env, reg);
2981 } else if (reg == 7) {
2982 for (i = 0; i < 4; i++)
2983 hw_breakpoint_remove(env, i);
2984 env->dr[7] = t0;
2985 for (i = 0; i < 4; i++)
2986 hw_breakpoint_insert(env, i);
2987 } else
2988 env->dr[reg] = t0;
2989}
2990#endif
2991
2992void helper_lmsw(target_ulong t0)
2993{
2994 /* only 4 lower bits of CR0 are modified. PE cannot be set to zero
2995 if already set to one. */
2996 t0 = (env->cr[0] & ~0xe) | (t0 & 0xf);
2997 helper_write_crN(0, t0);
2998}
2999
3000void helper_clts(void)
3001{
3002 env->cr[0] &= ~CR0_TS_MASK;
3003 env->hflags &= ~HF_TS_MASK;
3004}
3005
3006void helper_invlpg(target_ulong addr)
3007{
3008 helper_svm_check_intercept_param(SVM_EXIT_INVLPG, 0);
3009 tlb_flush_page(env, addr);
3010}
3011
3012void helper_rdtsc(void)
3013{
3014 uint64_t val;
3015
3016 if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
3017 raise_exception(EXCP0D_GPF);
3018 }
3019 helper_svm_check_intercept_param(SVM_EXIT_RDTSC, 0);
3020
3021 val = cpu_get_tsc(env) + env->tsc_offset;
3022 EAX = (uint32_t)(val);
3023 EDX = (uint32_t)(val >> 32);
3024}
3025
3026void helper_rdpmc(void)
3027{
3028 if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
3029 raise_exception(EXCP0D_GPF);
3030 }
3031 helper_svm_check_intercept_param(SVM_EXIT_RDPMC, 0);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02003032
Jun Nakajima86797932011-01-29 14:24:24 -08003033 /* currently unimplemented */
3034 raise_exception_err(EXCP06_ILLOP, 0);
3035}
3036
3037#if defined(CONFIG_USER_ONLY)
3038void helper_wrmsr(void)
3039{
3040}
3041
3042void helper_rdmsr(void)
3043{
3044}
3045#else
3046void helper_wrmsr(void)
3047{
3048 uint64_t val;
3049
3050 helper_svm_check_intercept_param(SVM_EXIT_MSR, 1);
3051
3052 val = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
3053
3054 switch((uint32_t)ECX) {
3055 case MSR_IA32_SYSENTER_CS:
3056 env->sysenter_cs = val & 0xffff;
3057 break;
3058 case MSR_IA32_SYSENTER_ESP:
3059 env->sysenter_esp = val;
3060 break;
3061 case MSR_IA32_SYSENTER_EIP:
3062 env->sysenter_eip = val;
3063 break;
3064 case MSR_IA32_APICBASE:
3065 cpu_set_apic_base(env, val);
3066 break;
3067 case MSR_EFER:
3068 {
3069 uint64_t update_mask;
3070 update_mask = 0;
3071 if (env->cpuid_ext2_features & CPUID_EXT2_SYSCALL)
3072 update_mask |= MSR_EFER_SCE;
3073 if (env->cpuid_ext2_features & CPUID_EXT2_LM)
3074 update_mask |= MSR_EFER_LME;
3075 if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
3076 update_mask |= MSR_EFER_FFXSR;
3077 if (env->cpuid_ext2_features & CPUID_EXT2_NX)
3078 update_mask |= MSR_EFER_NXE;
3079 if (env->cpuid_ext3_features & CPUID_EXT3_SVM)
3080 update_mask |= MSR_EFER_SVME;
3081 if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
3082 update_mask |= MSR_EFER_FFXSR;
3083 cpu_load_efer(env, (env->efer & ~update_mask) |
3084 (val & update_mask));
3085 }
3086 break;
3087 case MSR_STAR:
3088 env->star = val;
3089 break;
3090 case MSR_PAT:
3091 env->pat = val;
3092 break;
3093 case MSR_VM_HSAVE_PA:
3094 env->vm_hsave = val;
3095 break;
3096#ifdef TARGET_X86_64
3097 case MSR_LSTAR:
3098 env->lstar = val;
3099 break;
3100 case MSR_CSTAR:
3101 env->cstar = val;
3102 break;
3103 case MSR_FMASK:
3104 env->fmask = val;
3105 break;
3106 case MSR_FSBASE:
3107 env->segs[R_FS].base = val;
3108 break;
3109 case MSR_GSBASE:
3110 env->segs[R_GS].base = val;
3111 break;
3112 case MSR_KERNELGSBASE:
3113 env->kernelgsbase = val;
3114 break;
3115#endif
3116 case MSR_MTRRphysBase(0):
3117 case MSR_MTRRphysBase(1):
3118 case MSR_MTRRphysBase(2):
3119 case MSR_MTRRphysBase(3):
3120 case MSR_MTRRphysBase(4):
3121 case MSR_MTRRphysBase(5):
3122 case MSR_MTRRphysBase(6):
3123 case MSR_MTRRphysBase(7):
3124 env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base = val;
3125 break;
3126 case MSR_MTRRphysMask(0):
3127 case MSR_MTRRphysMask(1):
3128 case MSR_MTRRphysMask(2):
3129 case MSR_MTRRphysMask(3):
3130 case MSR_MTRRphysMask(4):
3131 case MSR_MTRRphysMask(5):
3132 case MSR_MTRRphysMask(6):
3133 case MSR_MTRRphysMask(7):
3134 env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask = val;
3135 break;
3136 case MSR_MTRRfix64K_00000:
3137 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix64K_00000] = val;
3138 break;
3139 case MSR_MTRRfix16K_80000:
3140 case MSR_MTRRfix16K_A0000:
3141 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1] = val;
3142 break;
3143 case MSR_MTRRfix4K_C0000:
3144 case MSR_MTRRfix4K_C8000:
3145 case MSR_MTRRfix4K_D0000:
3146 case MSR_MTRRfix4K_D8000:
3147 case MSR_MTRRfix4K_E0000:
3148 case MSR_MTRRfix4K_E8000:
3149 case MSR_MTRRfix4K_F0000:
3150 case MSR_MTRRfix4K_F8000:
3151 env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3] = val;
3152 break;
3153 case MSR_MTRRdefType:
3154 env->mtrr_deftype = val;
3155 break;
3156 case MSR_MCG_STATUS:
3157 env->mcg_status = val;
3158 break;
3159 case MSR_MCG_CTL:
3160 if ((env->mcg_cap & MCG_CTL_P)
3161 && (val == 0 || val == ~(uint64_t)0))
3162 env->mcg_ctl = val;
3163 break;
3164 default:
3165 if ((uint32_t)ECX >= MSR_MC0_CTL
3166 && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
3167 uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
3168 if ((offset & 0x3) != 0
3169 || (val == 0 || val == ~(uint64_t)0))
3170 env->mce_banks[offset] = val;
3171 break;
3172 }
3173 /* XXX: exception ? */
3174 break;
3175 }
3176}
3177
3178void helper_rdmsr(void)
3179{
3180 uint64_t val;
3181
3182 helper_svm_check_intercept_param(SVM_EXIT_MSR, 0);
3183
3184 switch((uint32_t)ECX) {
3185 case MSR_IA32_SYSENTER_CS:
3186 val = env->sysenter_cs;
3187 break;
3188 case MSR_IA32_SYSENTER_ESP:
3189 val = env->sysenter_esp;
3190 break;
3191 case MSR_IA32_SYSENTER_EIP:
3192 val = env->sysenter_eip;
3193 break;
3194 case MSR_IA32_APICBASE:
3195 val = cpu_get_apic_base(env);
3196 break;
3197 case MSR_EFER:
3198 val = env->efer;
3199 break;
3200 case MSR_STAR:
3201 val = env->star;
3202 break;
3203 case MSR_PAT:
3204 val = env->pat;
3205 break;
3206 case MSR_VM_HSAVE_PA:
3207 val = env->vm_hsave;
3208 break;
3209 case MSR_IA32_PERF_STATUS:
3210 /* tsc_increment_by_tick */
3211 val = 1000ULL;
3212 /* CPU multiplier */
3213 val |= (((uint64_t)4ULL) << 40);
3214 break;
3215#ifdef TARGET_X86_64
3216 case MSR_LSTAR:
3217 val = env->lstar;
3218 break;
3219 case MSR_CSTAR:
3220 val = env->cstar;
3221 break;
3222 case MSR_FMASK:
3223 val = env->fmask;
3224 break;
3225 case MSR_FSBASE:
3226 val = env->segs[R_FS].base;
3227 break;
3228 case MSR_GSBASE:
3229 val = env->segs[R_GS].base;
3230 break;
3231 case MSR_KERNELGSBASE:
3232 val = env->kernelgsbase;
3233 break;
3234#endif
Jun Nakajima86797932011-01-29 14:24:24 -08003235 case MSR_MTRRphysBase(0):
3236 case MSR_MTRRphysBase(1):
3237 case MSR_MTRRphysBase(2):
3238 case MSR_MTRRphysBase(3):
3239 case MSR_MTRRphysBase(4):
3240 case MSR_MTRRphysBase(5):
3241 case MSR_MTRRphysBase(6):
3242 case MSR_MTRRphysBase(7):
3243 val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base;
3244 break;
3245 case MSR_MTRRphysMask(0):
3246 case MSR_MTRRphysMask(1):
3247 case MSR_MTRRphysMask(2):
3248 case MSR_MTRRphysMask(3):
3249 case MSR_MTRRphysMask(4):
3250 case MSR_MTRRphysMask(5):
3251 case MSR_MTRRphysMask(6):
3252 case MSR_MTRRphysMask(7):
3253 val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask;
3254 break;
3255 case MSR_MTRRfix64K_00000:
3256 val = env->mtrr_fixed[0];
3257 break;
3258 case MSR_MTRRfix16K_80000:
3259 case MSR_MTRRfix16K_A0000:
3260 val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1];
3261 break;
3262 case MSR_MTRRfix4K_C0000:
3263 case MSR_MTRRfix4K_C8000:
3264 case MSR_MTRRfix4K_D0000:
3265 case MSR_MTRRfix4K_D8000:
3266 case MSR_MTRRfix4K_E0000:
3267 case MSR_MTRRfix4K_E8000:
3268 case MSR_MTRRfix4K_F0000:
3269 case MSR_MTRRfix4K_F8000:
3270 val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3];
3271 break;
3272 case MSR_MTRRdefType:
3273 val = env->mtrr_deftype;
3274 break;
3275 case MSR_MTRRcap:
3276 if (env->cpuid_features & CPUID_MTRR)
3277 val = MSR_MTRRcap_VCNT | MSR_MTRRcap_FIXRANGE_SUPPORT | MSR_MTRRcap_WC_SUPPORTED;
3278 else
3279 /* XXX: exception ? */
3280 val = 0;
3281 break;
3282 case MSR_MCG_CAP:
3283 val = env->mcg_cap;
3284 break;
3285 case MSR_MCG_CTL:
3286 if (env->mcg_cap & MCG_CTL_P)
3287 val = env->mcg_ctl;
3288 else
3289 val = 0;
3290 break;
3291 case MSR_MCG_STATUS:
3292 val = env->mcg_status;
3293 break;
3294 default:
3295 if ((uint32_t)ECX >= MSR_MC0_CTL
3296 && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
3297 uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
3298 val = env->mce_banks[offset];
3299 break;
3300 }
3301 /* XXX: exception ? */
3302 val = 0;
3303 break;
3304 }
3305 EAX = (uint32_t)(val);
3306 EDX = (uint32_t)(val >> 32);
3307}
3308#endif
3309
3310target_ulong helper_lsl(target_ulong selector1)
3311{
3312 unsigned int limit;
3313 uint32_t e1, e2, eflags, selector;
3314 int rpl, dpl, cpl, type;
3315
3316 selector = selector1 & 0xffff;
3317 eflags = helper_cc_compute_all(CC_OP);
3318 if ((selector & 0xfffc) == 0)
3319 goto fail;
3320 if (load_segment(&e1, &e2, selector) != 0)
3321 goto fail;
3322 rpl = selector & 3;
3323 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3324 cpl = env->hflags & HF_CPL_MASK;
3325 if (e2 & DESC_S_MASK) {
3326 if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
3327 /* conforming */
3328 } else {
3329 if (dpl < cpl || dpl < rpl)
3330 goto fail;
3331 }
3332 } else {
3333 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
3334 switch(type) {
3335 case 1:
3336 case 2:
3337 case 3:
3338 case 9:
3339 case 11:
3340 break;
3341 default:
3342 goto fail;
3343 }
3344 if (dpl < cpl || dpl < rpl) {
3345 fail:
3346 CC_SRC = eflags & ~CC_Z;
3347 return 0;
3348 }
3349 }
3350 limit = get_seg_limit(e1, e2);
3351 CC_SRC = eflags | CC_Z;
3352 return limit;
3353}
3354
3355target_ulong helper_lar(target_ulong selector1)
3356{
3357 uint32_t e1, e2, eflags, selector;
3358 int rpl, dpl, cpl, type;
3359
3360 selector = selector1 & 0xffff;
3361 eflags = helper_cc_compute_all(CC_OP);
3362 if ((selector & 0xfffc) == 0)
3363 goto fail;
3364 if (load_segment(&e1, &e2, selector) != 0)
3365 goto fail;
3366 rpl = selector & 3;
3367 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3368 cpl = env->hflags & HF_CPL_MASK;
3369 if (e2 & DESC_S_MASK) {
3370 if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
3371 /* conforming */
3372 } else {
3373 if (dpl < cpl || dpl < rpl)
3374 goto fail;
3375 }
3376 } else {
3377 type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
3378 switch(type) {
3379 case 1:
3380 case 2:
3381 case 3:
3382 case 4:
3383 case 5:
3384 case 9:
3385 case 11:
3386 case 12:
3387 break;
3388 default:
3389 goto fail;
3390 }
3391 if (dpl < cpl || dpl < rpl) {
3392 fail:
3393 CC_SRC = eflags & ~CC_Z;
3394 return 0;
3395 }
3396 }
3397 CC_SRC = eflags | CC_Z;
3398 return e2 & 0x00f0ff00;
3399}
3400
3401void helper_verr(target_ulong selector1)
3402{
3403 uint32_t e1, e2, eflags, selector;
3404 int rpl, dpl, cpl;
3405
3406 selector = selector1 & 0xffff;
3407 eflags = helper_cc_compute_all(CC_OP);
3408 if ((selector & 0xfffc) == 0)
3409 goto fail;
3410 if (load_segment(&e1, &e2, selector) != 0)
3411 goto fail;
3412 if (!(e2 & DESC_S_MASK))
3413 goto fail;
3414 rpl = selector & 3;
3415 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3416 cpl = env->hflags & HF_CPL_MASK;
3417 if (e2 & DESC_CS_MASK) {
3418 if (!(e2 & DESC_R_MASK))
3419 goto fail;
3420 if (!(e2 & DESC_C_MASK)) {
3421 if (dpl < cpl || dpl < rpl)
3422 goto fail;
3423 }
3424 } else {
3425 if (dpl < cpl || dpl < rpl) {
3426 fail:
3427 CC_SRC = eflags & ~CC_Z;
3428 return;
3429 }
3430 }
3431 CC_SRC = eflags | CC_Z;
3432}
3433
3434void helper_verw(target_ulong selector1)
3435{
3436 uint32_t e1, e2, eflags, selector;
3437 int rpl, dpl, cpl;
3438
3439 selector = selector1 & 0xffff;
3440 eflags = helper_cc_compute_all(CC_OP);
3441 if ((selector & 0xfffc) == 0)
3442 goto fail;
3443 if (load_segment(&e1, &e2, selector) != 0)
3444 goto fail;
3445 if (!(e2 & DESC_S_MASK))
3446 goto fail;
3447 rpl = selector & 3;
3448 dpl = (e2 >> DESC_DPL_SHIFT) & 3;
3449 cpl = env->hflags & HF_CPL_MASK;
3450 if (e2 & DESC_CS_MASK) {
3451 goto fail;
3452 } else {
3453 if (dpl < cpl || dpl < rpl)
3454 goto fail;
3455 if (!(e2 & DESC_W_MASK)) {
3456 fail:
3457 CC_SRC = eflags & ~CC_Z;
3458 return;
3459 }
3460 }
3461 CC_SRC = eflags | CC_Z;
3462}
3463
3464/* x87 FPU helpers */
3465
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003466static inline double floatx80_to_double(CPUX86State *env, floatx80 a)
3467{
3468 union {
3469 float64 f64;
3470 double d;
3471 } u;
3472
3473 u.f64 = floatx80_to_float64(a, &env->fp_status);
3474 return u.d;
3475}
3476
3477static inline floatx80 double_to_floatx80(CPUX86State *env, double a)
3478{
3479 union {
3480 float64 f64;
3481 double d;
3482 } u;
3483
3484 u.d = a;
3485 return float64_to_floatx80(u.f64, &env->fp_status);
3486}
3487
Jun Nakajima86797932011-01-29 14:24:24 -08003488static void fpu_set_exception(int mask)
3489{
3490 env->fpus |= mask;
3491 if (env->fpus & (~env->fpuc & FPUC_EM))
3492 env->fpus |= FPUS_SE | FPUS_B;
3493}
3494
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003495static inline floatx80 helper_fdiv(floatx80 a, floatx80 b)
Jun Nakajima86797932011-01-29 14:24:24 -08003496{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003497 if (floatx80_is_zero(b)) {
Jun Nakajima86797932011-01-29 14:24:24 -08003498 fpu_set_exception(FPUS_ZE);
David 'Digit' Turner763b5972014-03-26 17:10:52 +01003499 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003500 return floatx80_div(a, b, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003501}
3502
3503static void fpu_raise_exception(void)
3504{
3505 if (env->cr[0] & CR0_NE_MASK) {
3506 raise_exception(EXCP10_COPR);
3507 }
3508#if !defined(CONFIG_USER_ONLY)
3509 else {
3510 cpu_set_ferr(env);
3511 }
3512#endif
3513}
3514
3515void helper_flds_FT0(uint32_t val)
3516{
3517 union {
3518 float32 f;
3519 uint32_t i;
3520 } u;
3521 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003522 FT0 = float32_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003523}
3524
3525void helper_fldl_FT0(uint64_t val)
3526{
3527 union {
3528 float64 f;
3529 uint64_t i;
3530 } u;
3531 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003532 FT0 = float64_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003533}
3534
3535void helper_fildl_FT0(int32_t val)
3536{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003537 FT0 = int32_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003538}
3539
3540void helper_flds_ST0(uint32_t val)
3541{
3542 int new_fpstt;
3543 union {
3544 float32 f;
3545 uint32_t i;
3546 } u;
3547 new_fpstt = (env->fpstt - 1) & 7;
3548 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003549 env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003550 env->fpstt = new_fpstt;
3551 env->fptags[new_fpstt] = 0; /* validate stack entry */
3552}
3553
3554void helper_fldl_ST0(uint64_t val)
3555{
3556 int new_fpstt;
3557 union {
3558 float64 f;
3559 uint64_t i;
3560 } u;
3561 new_fpstt = (env->fpstt - 1) & 7;
3562 u.i = val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003563 env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003564 env->fpstt = new_fpstt;
3565 env->fptags[new_fpstt] = 0; /* validate stack entry */
3566}
3567
3568void helper_fildl_ST0(int32_t val)
3569{
3570 int new_fpstt;
3571 new_fpstt = (env->fpstt - 1) & 7;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003572 env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003573 env->fpstt = new_fpstt;
3574 env->fptags[new_fpstt] = 0; /* validate stack entry */
3575}
3576
3577void helper_fildll_ST0(int64_t val)
3578{
3579 int new_fpstt;
3580 new_fpstt = (env->fpstt - 1) & 7;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003581 env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003582 env->fpstt = new_fpstt;
3583 env->fptags[new_fpstt] = 0; /* validate stack entry */
3584}
3585
3586uint32_t helper_fsts_ST0(void)
3587{
3588 union {
3589 float32 f;
3590 uint32_t i;
3591 } u;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003592 u.f = floatx80_to_float32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003593 return u.i;
3594}
3595
3596uint64_t helper_fstl_ST0(void)
3597{
3598 union {
3599 float64 f;
3600 uint64_t i;
3601 } u;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003602 u.f = floatx80_to_float64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003603 return u.i;
3604}
3605
3606int32_t helper_fist_ST0(void)
3607{
3608 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003609 val = floatx80_to_int32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003610 if (val != (int16_t)val)
3611 val = -32768;
3612 return val;
3613}
3614
3615int32_t helper_fistl_ST0(void)
3616{
3617 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003618 val = floatx80_to_int32(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003619 return val;
3620}
3621
3622int64_t helper_fistll_ST0(void)
3623{
3624 int64_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003625 val = floatx80_to_int64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003626 return val;
3627}
3628
3629int32_t helper_fistt_ST0(void)
3630{
3631 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003632 val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003633 if (val != (int16_t)val)
3634 val = -32768;
3635 return val;
3636}
3637
3638int32_t helper_fisttl_ST0(void)
3639{
3640 int32_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003641 val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003642 return val;
3643}
3644
3645int64_t helper_fisttll_ST0(void)
3646{
3647 int64_t val;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003648 val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003649 return val;
3650}
3651
3652void helper_fldt_ST0(target_ulong ptr)
3653{
3654 int new_fpstt;
3655 new_fpstt = (env->fpstt - 1) & 7;
3656 env->fpregs[new_fpstt].d = helper_fldt(ptr);
3657 env->fpstt = new_fpstt;
3658 env->fptags[new_fpstt] = 0; /* validate stack entry */
3659}
3660
3661void helper_fstt_ST0(target_ulong ptr)
3662{
3663 helper_fstt(ST0, ptr);
3664}
3665
3666void helper_fpush(void)
3667{
3668 fpush();
3669}
3670
3671void helper_fpop(void)
3672{
3673 fpop();
3674}
3675
3676void helper_fdecstp(void)
3677{
3678 env->fpstt = (env->fpstt - 1) & 7;
3679 env->fpus &= (~0x4700);
3680}
3681
3682void helper_fincstp(void)
3683{
3684 env->fpstt = (env->fpstt + 1) & 7;
3685 env->fpus &= (~0x4700);
3686}
3687
3688/* FPU move */
3689
3690void helper_ffree_STN(int st_index)
3691{
3692 env->fptags[(env->fpstt + st_index) & 7] = 1;
3693}
3694
3695void helper_fmov_ST0_FT0(void)
3696{
3697 ST0 = FT0;
3698}
3699
3700void helper_fmov_FT0_STN(int st_index)
3701{
3702 FT0 = ST(st_index);
3703}
3704
3705void helper_fmov_ST0_STN(int st_index)
3706{
3707 ST0 = ST(st_index);
3708}
3709
3710void helper_fmov_STN_ST0(int st_index)
3711{
3712 ST(st_index) = ST0;
3713}
3714
3715void helper_fxchg_ST0_STN(int st_index)
3716{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003717 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08003718 tmp = ST(st_index);
3719 ST(st_index) = ST0;
3720 ST0 = tmp;
3721}
3722
3723/* FPU operations */
3724
3725static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500};
3726
3727void helper_fcom_ST0_FT0(void)
3728{
3729 int ret;
3730
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003731 ret = floatx80_compare(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003732 env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
3733}
3734
3735void helper_fucom_ST0_FT0(void)
3736{
3737 int ret;
3738
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003739 ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003740 env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1];
3741}
3742
3743static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
3744
3745void helper_fcomi_ST0_FT0(void)
3746{
3747 int eflags;
3748 int ret;
3749
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003750 ret = floatx80_compare(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003751 eflags = helper_cc_compute_all(CC_OP);
3752 eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
3753 CC_SRC = eflags;
3754}
3755
3756void helper_fucomi_ST0_FT0(void)
3757{
3758 int eflags;
3759 int ret;
3760
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003761 ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003762 eflags = helper_cc_compute_all(CC_OP);
3763 eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
3764 CC_SRC = eflags;
3765}
3766
3767void helper_fadd_ST0_FT0(void)
3768{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003769 ST0 = floatx80_add(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003770}
3771
3772void helper_fmul_ST0_FT0(void)
3773{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003774 ST0 = floatx80_mul(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003775}
3776
3777void helper_fsub_ST0_FT0(void)
3778{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003779 ST0 = floatx80_sub(ST0, FT0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003780}
3781
3782void helper_fsubr_ST0_FT0(void)
3783{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003784 ST0 = floatx80_sub(FT0, ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003785}
3786
3787void helper_fdiv_ST0_FT0(void)
3788{
3789 ST0 = helper_fdiv(ST0, FT0);
3790}
3791
3792void helper_fdivr_ST0_FT0(void)
3793{
3794 ST0 = helper_fdiv(FT0, ST0);
3795}
3796
3797/* fp operations between STN and ST0 */
3798
3799void helper_fadd_STN_ST0(int st_index)
3800{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003801 ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003802}
3803
3804void helper_fmul_STN_ST0(int st_index)
3805{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003806 ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003807}
3808
3809void helper_fsub_STN_ST0(int st_index)
3810{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003811 ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003812}
3813
3814void helper_fsubr_STN_ST0(int st_index)
3815{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003816 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003817 p = &ST(st_index);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003818 *p = floatx80_sub(ST0, *p, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003819}
3820
3821void helper_fdiv_STN_ST0(int st_index)
3822{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003823 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003824 p = &ST(st_index);
3825 *p = helper_fdiv(*p, ST0);
3826}
3827
3828void helper_fdivr_STN_ST0(int st_index)
3829{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003830 floatx80 *p;
Jun Nakajima86797932011-01-29 14:24:24 -08003831 p = &ST(st_index);
3832 *p = helper_fdiv(ST0, *p);
3833}
3834
3835/* misc FPU operations */
3836void helper_fchs_ST0(void)
3837{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003838 ST0 = floatx80_chs(ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003839}
3840
3841void helper_fabs_ST0(void)
3842{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003843 ST0 = floatx80_abs(ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08003844}
3845
3846void helper_fld1_ST0(void)
3847{
3848 ST0 = f15rk[1];
3849}
3850
3851void helper_fldl2t_ST0(void)
3852{
3853 ST0 = f15rk[6];
3854}
3855
3856void helper_fldl2e_ST0(void)
3857{
3858 ST0 = f15rk[5];
3859}
3860
3861void helper_fldpi_ST0(void)
3862{
3863 ST0 = f15rk[2];
3864}
3865
3866void helper_fldlg2_ST0(void)
3867{
3868 ST0 = f15rk[3];
3869}
3870
3871void helper_fldln2_ST0(void)
3872{
3873 ST0 = f15rk[4];
3874}
3875
3876void helper_fldz_ST0(void)
3877{
3878 ST0 = f15rk[0];
3879}
3880
3881void helper_fldz_FT0(void)
3882{
3883 FT0 = f15rk[0];
3884}
3885
3886uint32_t helper_fnstsw(void)
3887{
3888 return (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
3889}
3890
3891uint32_t helper_fnstcw(void)
3892{
3893 return env->fpuc;
3894}
3895
3896static void update_fp_status(void)
3897{
3898 int rnd_type;
3899
3900 /* set rounding mode */
3901 switch(env->fpuc & RC_MASK) {
3902 default:
3903 case RC_NEAR:
3904 rnd_type = float_round_nearest_even;
3905 break;
3906 case RC_DOWN:
3907 rnd_type = float_round_down;
3908 break;
3909 case RC_UP:
3910 rnd_type = float_round_up;
3911 break;
3912 case RC_CHOP:
3913 rnd_type = float_round_to_zero;
3914 break;
3915 }
3916 set_float_rounding_mode(rnd_type, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003917 switch((env->fpuc >> 8) & 3) {
3918 case 0:
3919 rnd_type = 32;
3920 break;
3921 case 2:
3922 rnd_type = 64;
3923 break;
3924 case 3:
3925 default:
3926 rnd_type = 80;
3927 break;
3928 }
3929 set_floatx80_rounding_precision(rnd_type, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003930}
3931
3932void helper_fldcw(uint32_t val)
3933{
3934 env->fpuc = val;
3935 update_fp_status();
3936}
3937
3938void helper_fclex(void)
3939{
3940 env->fpus &= 0x7f00;
3941}
3942
3943void helper_fwait(void)
3944{
3945 if (env->fpus & FPUS_SE)
3946 fpu_raise_exception();
3947}
3948
3949void helper_fninit(void)
3950{
3951 env->fpus = 0;
3952 env->fpstt = 0;
3953 env->fpuc = 0x37f;
3954 env->fptags[0] = 1;
3955 env->fptags[1] = 1;
3956 env->fptags[2] = 1;
3957 env->fptags[3] = 1;
3958 env->fptags[4] = 1;
3959 env->fptags[5] = 1;
3960 env->fptags[6] = 1;
3961 env->fptags[7] = 1;
3962}
3963
3964/* BCD ops */
3965
3966void helper_fbld_ST0(target_ulong ptr)
3967{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003968 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08003969 uint64_t val;
3970 unsigned int v;
3971 int i;
3972
3973 val = 0;
3974 for(i = 8; i >= 0; i--) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003975 v = cpu_ldub_data(env, ptr + i);
Jun Nakajima86797932011-01-29 14:24:24 -08003976 val = (val * 100) + ((v >> 4) * 10) + (v & 0xf);
3977 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003978 tmp = int64_to_floatx80(val, &env->fp_status);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003979 if (cpu_ldub_data(env, ptr + 9) & 0x80) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003980 floatx80_chs(tmp);
3981 }
Jun Nakajima86797932011-01-29 14:24:24 -08003982 fpush();
3983 ST0 = tmp;
3984}
3985
3986void helper_fbst_ST0(target_ulong ptr)
3987{
3988 int v;
3989 target_ulong mem_ref, mem_end;
3990 int64_t val;
3991
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01003992 val = floatx80_to_int64(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08003993 mem_ref = ptr;
3994 mem_end = mem_ref + 9;
3995 if (val < 0) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003996 cpu_stb_data(env, mem_end, 0x80);
Jun Nakajima86797932011-01-29 14:24:24 -08003997 val = -val;
3998 } else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01003999 cpu_stb_data(env, mem_end, 0x00);
Jun Nakajima86797932011-01-29 14:24:24 -08004000 }
4001 while (mem_ref < mem_end) {
4002 if (val == 0)
4003 break;
4004 v = val % 100;
4005 val = val / 100;
4006 v = ((v / 10) << 4) | (v % 10);
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004007 cpu_stb_data(env, mem_ref++, v);
Jun Nakajima86797932011-01-29 14:24:24 -08004008 }
4009 while (mem_ref < mem_end) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004010 cpu_stb_data(env, mem_ref++, 0);
Jun Nakajima86797932011-01-29 14:24:24 -08004011 }
4012}
4013
4014void helper_f2xm1(void)
4015{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004016 double val = floatx80_to_double(env, ST0);
4017 val = pow(2.0, val) - 1.0;
4018 ST0 = double_to_floatx80(env, val);
Jun Nakajima86797932011-01-29 14:24:24 -08004019}
4020
4021void helper_fyl2x(void)
4022{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004023 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004024
Jun Nakajima86797932011-01-29 14:24:24 -08004025 if (fptemp>0.0){
4026 fptemp = log(fptemp)/log(2.0); /* log2(ST) */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004027 fptemp *= floatx80_to_double(env, ST1);
4028 ST1 = double_to_floatx80(env, fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004029 fpop();
4030 } else {
4031 env->fpus &= (~0x4700);
4032 env->fpus |= 0x400;
4033 }
4034}
4035
4036void helper_fptan(void)
4037{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004038 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004039
Jun Nakajima86797932011-01-29 14:24:24 -08004040 if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4041 env->fpus |= 0x400;
4042 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004043 fptemp = tan(fptemp);
4044 ST0 = double_to_floatx80(env, fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004045 fpush();
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004046 ST0 = floatx80_one;
Jun Nakajima86797932011-01-29 14:24:24 -08004047 env->fpus &= (~0x400); /* C2 <-- 0 */
4048 /* the above code is for |arg| < 2**52 only */
4049 }
4050}
4051
4052void helper_fpatan(void)
4053{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004054 double fptemp, fpsrcop;
Jun Nakajima86797932011-01-29 14:24:24 -08004055
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004056 fpsrcop = floatx80_to_double(env, ST1);
4057 fptemp = floatx80_to_double(env, ST0);
4058 ST1 = double_to_floatx80(env, atan2(fpsrcop,fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004059 fpop();
4060}
4061
4062void helper_fxtract(void)
4063{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004064 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004065 unsigned int expdif;
4066
4067 temp.d = ST0;
4068 expdif = EXPD(temp) - EXPBIAS;
4069 /*DP exponent bias*/
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004070 ST0 = int32_to_floatx80(expdif, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004071 fpush();
4072 BIASEXPONENT(temp);
4073 ST0 = temp.d;
4074}
4075
4076void helper_fprem1(void)
4077{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004078 double st0, st1, dblq, fpsrcop, fptemp;
4079 CPU_LDoubleU fpsrcop1, fptemp1;
Jun Nakajima86797932011-01-29 14:24:24 -08004080 int expdif;
4081 signed long long int q;
4082
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004083 st0 = floatx80_to_double(env, ST0);
4084 st1 = floatx80_to_double(env, ST1);
4085
4086 if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
4087 ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */
Jun Nakajima86797932011-01-29 14:24:24 -08004088 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4089 return;
4090 }
4091
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004092 fpsrcop = st0;
4093 fptemp = st1;
4094 fpsrcop1.d = ST0;
4095 fptemp1.d = ST1;
Jun Nakajima86797932011-01-29 14:24:24 -08004096 expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
4097
4098 if (expdif < 0) {
4099 /* optimisation? taken from the AMD docs */
4100 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4101 /* ST0 is unchanged */
4102 return;
4103 }
4104
4105 if (expdif < 53) {
4106 dblq = fpsrcop / fptemp;
4107 /* round dblq towards nearest integer */
4108 dblq = rint(dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004109 st0 = fpsrcop - fptemp * dblq;
Jun Nakajima86797932011-01-29 14:24:24 -08004110
4111 /* convert dblq to q by truncating towards zero */
4112 if (dblq < 0.0)
4113 q = (signed long long int)(-dblq);
4114 else
4115 q = (signed long long int)dblq;
4116
4117 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4118 /* (C0,C3,C1) <-- (q2,q1,q0) */
4119 env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
4120 env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
4121 env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
4122 } else {
4123 env->fpus |= 0x400; /* C2 <-- 1 */
4124 fptemp = pow(2.0, expdif - 50);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004125 fpsrcop = (st0 / st1) / fptemp;
Jun Nakajima86797932011-01-29 14:24:24 -08004126 /* fpsrcop = integer obtained by chopping */
4127 fpsrcop = (fpsrcop < 0.0) ?
4128 -(floor(fabs(fpsrcop))) : floor(fpsrcop);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004129 st0 -= (st1 * fpsrcop * fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004130 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004131 ST0 = double_to_floatx80(env, st0);
Jun Nakajima86797932011-01-29 14:24:24 -08004132}
4133
4134void helper_fprem(void)
4135{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004136 double st0, st1, dblq, fpsrcop, fptemp;
4137 CPU_LDoubleU fpsrcop1, fptemp1;
Jun Nakajima86797932011-01-29 14:24:24 -08004138 int expdif;
4139 signed long long int q;
4140
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004141 st0 = floatx80_to_double(env, ST0);
4142 st1 = floatx80_to_double(env, ST1);
4143
4144 if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
4145 ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */
Jun Nakajima86797932011-01-29 14:24:24 -08004146 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4147 return;
4148 }
4149
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004150 fpsrcop = st0;
4151 fptemp = st1;
4152 fpsrcop1.d = ST0;
4153 fptemp1.d = ST1;
Jun Nakajima86797932011-01-29 14:24:24 -08004154 expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
4155
4156 if (expdif < 0) {
4157 /* optimisation? taken from the AMD docs */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004158 env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */
Jun Nakajima86797932011-01-29 14:24:24 -08004159 /* ST0 is unchanged */
4160 return;
4161 }
4162
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004163 if (expdif < 53) {
4164 dblq = fpsrcop / fptemp; /* ST0 / ST1*/;
Jun Nakajima86797932011-01-29 14:24:24 -08004165 /* round dblq towards zero */
4166 dblq = (dblq < 0.0) ? ceil(dblq) : floor(dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004167 st0 = fpsrcop - fptemp * dblq; /* fpsrcop is ST0 */
Jun Nakajima86797932011-01-29 14:24:24 -08004168
4169 /* convert dblq to q by truncating towards zero */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004170 if (dblq < 0.0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004171 q = (signed long long int)(-dblq);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004172 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004173 q = (signed long long int)dblq;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004174 }
Jun Nakajima86797932011-01-29 14:24:24 -08004175
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004176 env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */
4177 /* (C0,C3,C1) <-- (q2,q1,q0) */
Jun Nakajima86797932011-01-29 14:24:24 -08004178 env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
4179 env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
4180 env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
4181 } else {
4182 int N = 32 + (expdif % 32); /* as per AMD docs */
4183 env->fpus |= 0x400; /* C2 <-- 1 */
4184 fptemp = pow(2.0, (double)(expdif - N));
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004185 fpsrcop = (st0 / st1) / fptemp;
Jun Nakajima86797932011-01-29 14:24:24 -08004186 /* fpsrcop = integer obtained by chopping */
4187 fpsrcop = (fpsrcop < 0.0) ?
4188 -(floor(fabs(fpsrcop))) : floor(fpsrcop);
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004189 st0 -= (st1 * fpsrcop * fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004190 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004191 ST0 = double_to_floatx80(env, st0);
Jun Nakajima86797932011-01-29 14:24:24 -08004192}
4193
4194void helper_fyl2xp1(void)
4195{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004196 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004197
Jun Nakajima86797932011-01-29 14:24:24 -08004198 if ((fptemp+1.0)>0.0) {
4199 fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004200 fptemp *= floatx80_to_double(env, ST1);
4201 ST1 = double_to_floatx80(env, fptemp);
Jun Nakajima86797932011-01-29 14:24:24 -08004202 fpop();
4203 } else {
4204 env->fpus &= (~0x4700);
4205 env->fpus |= 0x400;
4206 }
4207}
4208
4209void helper_fsqrt(void)
4210{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004211 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004212
Jun Nakajima86797932011-01-29 14:24:24 -08004213 if (fptemp<0.0) {
4214 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4215 env->fpus |= 0x400;
4216 }
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004217 ST0 = floatx80_sqrt(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004218}
4219
4220void helper_fsincos(void)
4221{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004222 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004223
Jun Nakajima86797932011-01-29 14:24:24 -08004224 if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4225 env->fpus |= 0x400;
4226 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004227 ST0 = double_to_floatx80(env, sin(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004228 fpush();
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004229 ST0 = double_to_floatx80(env, cos(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004230 env->fpus &= (~0x400); /* C2 <-- 0 */
4231 /* the above code is for |arg| < 2**63 only */
4232 }
4233}
4234
4235void helper_frndint(void)
4236{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004237 ST0 = floatx80_round_to_int(ST0, &env->fp_status);
Jun Nakajima86797932011-01-29 14:24:24 -08004238}
4239
4240void helper_fscale(void)
4241{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004242 double st0 = floatx80_to_double(env, ST0);
4243 double st1 = floatx80_to_double(env, ST1);
4244 double val = ldexp(st0, (int)st1);
4245 ST0 = double_to_floatx80(env, val);
Jun Nakajima86797932011-01-29 14:24:24 -08004246}
4247
4248void helper_fsin(void)
4249{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004250 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004251
Jun Nakajima86797932011-01-29 14:24:24 -08004252 if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4253 env->fpus |= 0x400;
4254 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004255 ST0 = double_to_floatx80(env, sin(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004256 env->fpus &= (~0x400); /* C2 <-- 0 */
4257 /* the above code is for |arg| < 2**53 only */
4258 }
4259}
4260
4261void helper_fcos(void)
4262{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004263 double fptemp = floatx80_to_double(env, ST0);
Jun Nakajima86797932011-01-29 14:24:24 -08004264
Jun Nakajima86797932011-01-29 14:24:24 -08004265 if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
4266 env->fpus |= 0x400;
4267 } else {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004268 ST0 = double_to_floatx80(env, cos(fptemp));
Jun Nakajima86797932011-01-29 14:24:24 -08004269 env->fpus &= (~0x400); /* C2 <-- 0 */
4270 /* the above code is for |arg5 < 2**63 only */
4271 }
4272}
4273
4274void helper_fxam_ST0(void)
4275{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004276 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004277 int expdif;
4278
4279 temp.d = ST0;
4280
4281 env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
4282 if (SIGND(temp))
4283 env->fpus |= 0x200; /* C1 <-- 1 */
4284
4285 /* XXX: test fptags too */
4286 expdif = EXPD(temp);
4287 if (expdif == MAXEXPD) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004288 if (MANTD(temp) == 0x8000000000000000ULL) {
Jun Nakajima86797932011-01-29 14:24:24 -08004289 env->fpus |= 0x500 /*Infinity*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004290 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004291 env->fpus |= 0x100 /*NaN*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004292 }
Jun Nakajima86797932011-01-29 14:24:24 -08004293 } else if (expdif == 0) {
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004294 if (MANTD(temp) == 0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004295 env->fpus |= 0x4000 /*Zero*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004296 } else {
Jun Nakajima86797932011-01-29 14:24:24 -08004297 env->fpus |= 0x4400 /*Denormal*/;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004298 }
Jun Nakajima86797932011-01-29 14:24:24 -08004299 } else {
4300 env->fpus |= 0x400;
4301 }
4302}
4303
4304void helper_fstenv(target_ulong ptr, int data32)
4305{
4306 int fpus, fptag, exp, i;
4307 uint64_t mant;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004308 CPU_LDoubleU tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004309
4310 fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
4311 fptag = 0;
4312 for (i=7; i>=0; i--) {
4313 fptag <<= 2;
4314 if (env->fptags[i]) {
4315 fptag |= 3;
4316 } else {
4317 tmp.d = env->fpregs[i].d;
4318 exp = EXPD(tmp);
4319 mant = MANTD(tmp);
4320 if (exp == 0 && mant == 0) {
4321 /* zero */
4322 fptag |= 1;
4323 } else if (exp == 0 || exp == MAXEXPD
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004324 || (mant & (1LL << 63)) == 0) {
Jun Nakajima86797932011-01-29 14:24:24 -08004325 /* NaNs, infinity, denormal */
4326 fptag |= 2;
4327 }
4328 }
4329 }
4330 if (data32) {
4331 /* 32 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004332 cpu_stl_data(env, ptr, env->fpuc);
4333 cpu_stl_data(env, ptr + 4, fpus);
4334 cpu_stl_data(env, ptr + 8, fptag);
4335 cpu_stl_data(env, ptr + 12, 0); /* fpip */
4336 cpu_stl_data(env, ptr + 16, 0); /* fpcs */
4337 cpu_stl_data(env, ptr + 20, 0); /* fpoo */
4338 cpu_stl_data(env, ptr + 24, 0); /* fpos */
Jun Nakajima86797932011-01-29 14:24:24 -08004339 } else {
4340 /* 16 bit */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004341 cpu_stw_data(env, ptr, env->fpuc);
4342 cpu_stw_data(env, ptr + 2, fpus);
4343 cpu_stw_data(env, ptr + 4, fptag);
4344 cpu_stw_data(env, ptr + 6, 0);
4345 cpu_stw_data(env, ptr + 8, 0);
4346 cpu_stw_data(env, ptr + 10, 0);
4347 cpu_stw_data(env, ptr + 12, 0);
Jun Nakajima86797932011-01-29 14:24:24 -08004348 }
4349}
4350
4351void helper_fldenv(target_ulong ptr, int data32)
4352{
4353 int i, fpus, fptag;
4354
4355 if (data32) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004356 env->fpuc = cpu_lduw_data(env, ptr);
4357 fpus = cpu_lduw_data(env, ptr + 4);
4358 fptag = cpu_lduw_data(env, ptr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08004359 }
4360 else {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004361 env->fpuc = cpu_lduw_data(env, ptr);
4362 fpus = cpu_lduw_data(env, ptr + 2);
4363 fptag = cpu_lduw_data(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004364 }
4365 env->fpstt = (fpus >> 11) & 7;
4366 env->fpus = fpus & ~0x3800;
4367 for(i = 0;i < 8; i++) {
4368 env->fptags[i] = ((fptag & 3) == 3);
4369 fptag >>= 2;
4370 }
4371}
4372
4373void helper_fsave(target_ulong ptr, int data32)
4374{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004375 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004376 int i;
4377
4378 helper_fstenv(ptr, data32);
4379
4380 ptr += (14 << data32);
4381 for(i = 0;i < 8; i++) {
4382 tmp = ST(i);
4383 helper_fstt(tmp, ptr);
4384 ptr += 10;
4385 }
4386
4387 /* fninit */
4388 env->fpus = 0;
4389 env->fpstt = 0;
4390 env->fpuc = 0x37f;
4391 env->fptags[0] = 1;
4392 env->fptags[1] = 1;
4393 env->fptags[2] = 1;
4394 env->fptags[3] = 1;
4395 env->fptags[4] = 1;
4396 env->fptags[5] = 1;
4397 env->fptags[6] = 1;
4398 env->fptags[7] = 1;
4399}
4400
4401void helper_frstor(target_ulong ptr, int data32)
4402{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004403 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004404 int i;
4405
4406 helper_fldenv(ptr, data32);
4407 ptr += (14 << data32);
4408
4409 for(i = 0;i < 8; i++) {
4410 tmp = helper_fldt(ptr);
4411 ST(i) = tmp;
4412 ptr += 10;
4413 }
4414}
4415
4416void helper_fxsave(target_ulong ptr, int data64)
4417{
4418 int fpus, fptag, i, nb_xmm_regs;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004419 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004420 target_ulong addr;
4421
4422 fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
4423 fptag = 0;
4424 for(i = 0; i < 8; i++) {
4425 fptag |= (env->fptags[i] << i);
4426 }
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004427 cpu_stw_data(env, ptr, env->fpuc);
4428 cpu_stw_data(env, ptr + 2, fpus);
4429 cpu_stw_data(env, ptr + 4, fptag ^ 0xff);
Jun Nakajima86797932011-01-29 14:24:24 -08004430#ifdef TARGET_X86_64
4431 if (data64) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004432 cpu_stq_data(env, ptr + 0x08, 0); /* rip */
4433 cpu_stq_data(env, ptr + 0x10, 0); /* rdp */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004434 } else
Jun Nakajima86797932011-01-29 14:24:24 -08004435#endif
4436 {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004437 cpu_stl_data(env, ptr + 0x08, 0); /* eip */
4438 cpu_stl_data(env, ptr + 0x0c, 0); /* sel */
4439 cpu_stl_data(env, ptr + 0x10, 0); /* dp */
4440 cpu_stl_data(env, ptr + 0x14, 0); /* sel */
Jun Nakajima86797932011-01-29 14:24:24 -08004441 }
4442
4443 addr = ptr + 0x20;
4444 for(i = 0;i < 8; i++) {
4445 tmp = ST(i);
4446 helper_fstt(tmp, addr);
4447 addr += 16;
4448 }
4449
4450 if (env->cr[4] & CR4_OSFXSR_MASK) {
4451 /* XXX: finish it */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004452 cpu_stl_data(env, ptr + 0x18, env->mxcsr); /* mxcsr */
4453 cpu_stl_data(env, ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */
Jun Nakajima86797932011-01-29 14:24:24 -08004454 if (env->hflags & HF_CS64_MASK)
4455 nb_xmm_regs = 16;
4456 else
4457 nb_xmm_regs = 8;
4458 addr = ptr + 0xa0;
4459 /* Fast FXSAVE leaves out the XMM registers */
4460 if (!(env->efer & MSR_EFER_FFXSR)
4461 || (env->hflags & HF_CPL_MASK)
4462 || !(env->hflags & HF_LMA_MASK)) {
4463 for(i = 0; i < nb_xmm_regs; i++) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004464 cpu_stq_data(env, addr, env->xmm_regs[i].XMM_Q(0));
4465 cpu_stq_data(env, addr + 8, env->xmm_regs[i].XMM_Q(1));
Jun Nakajima86797932011-01-29 14:24:24 -08004466 addr += 16;
4467 }
4468 }
4469 }
4470}
4471
4472void helper_fxrstor(target_ulong ptr, int data64)
4473{
4474 int i, fpus, fptag, nb_xmm_regs;
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004475 floatx80 tmp;
Jun Nakajima86797932011-01-29 14:24:24 -08004476 target_ulong addr;
4477
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004478 env->fpuc = cpu_lduw_data(env, ptr);
4479 fpus = cpu_lduw_data(env, ptr + 2);
4480 fptag = cpu_lduw_data(env, ptr + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004481 env->fpstt = (fpus >> 11) & 7;
4482 env->fpus = fpus & ~0x3800;
4483 fptag ^= 0xff;
4484 for(i = 0;i < 8; i++) {
4485 env->fptags[i] = ((fptag >> i) & 1);
4486 }
4487
4488 addr = ptr + 0x20;
4489 for(i = 0;i < 8; i++) {
4490 tmp = helper_fldt(addr);
4491 ST(i) = tmp;
4492 addr += 16;
4493 }
4494
4495 if (env->cr[4] & CR4_OSFXSR_MASK) {
4496 /* XXX: finish it */
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004497 env->mxcsr = cpu_ldl_data(env, ptr + 0x18);
Jun Nakajima86797932011-01-29 14:24:24 -08004498 //ldl(ptr + 0x1c);
4499 if (env->hflags & HF_CS64_MASK)
4500 nb_xmm_regs = 16;
4501 else
4502 nb_xmm_regs = 8;
4503 addr = ptr + 0xa0;
4504 /* Fast FXRESTORE leaves out the XMM registers */
4505 if (!(env->efer & MSR_EFER_FFXSR)
4506 || (env->hflags & HF_CPL_MASK)
4507 || !(env->hflags & HF_LMA_MASK)) {
4508 for(i = 0; i < nb_xmm_regs; i++) {
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004509 env->xmm_regs[i].XMM_Q(0) = cpu_ldq_data(env, addr);
4510 env->xmm_regs[i].XMM_Q(1) = cpu_ldq_data(env, addr + 8);
Jun Nakajima86797932011-01-29 14:24:24 -08004511 addr += 16;
4512 }
4513 }
4514 }
4515}
4516
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004517void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f)
Jun Nakajima86797932011-01-29 14:24:24 -08004518{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004519 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004520
4521 temp.d = f;
4522 *pmant = temp.l.lower;
4523 *pexp = temp.l.upper;
4524}
4525
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004526floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper)
Jun Nakajima86797932011-01-29 14:24:24 -08004527{
David 'Digit' Turnera6aabef2014-03-28 15:11:59 +01004528 CPU_LDoubleU temp;
Jun Nakajima86797932011-01-29 14:24:24 -08004529
4530 temp.l.upper = upper;
4531 temp.l.lower = mant;
4532 return temp.d;
4533}
Jun Nakajima86797932011-01-29 14:24:24 -08004534
4535#ifdef TARGET_X86_64
4536
4537//#define DEBUG_MULDIV
4538
4539static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
4540{
4541 *plow += a;
4542 /* carry test */
4543 if (*plow < a)
4544 (*phigh)++;
4545 *phigh += b;
4546}
4547
4548static void neg128(uint64_t *plow, uint64_t *phigh)
4549{
4550 *plow = ~ *plow;
4551 *phigh = ~ *phigh;
4552 add128(plow, phigh, 1, 0);
4553}
4554
4555/* return TRUE if overflow */
4556static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
4557{
4558 uint64_t q, r, a1, a0;
4559 int i, qb, ab;
4560
4561 a0 = *plow;
4562 a1 = *phigh;
4563 if (a1 == 0) {
4564 q = a0 / b;
4565 r = a0 % b;
4566 *plow = q;
4567 *phigh = r;
4568 } else {
4569 if (a1 >= b)
4570 return 1;
4571 /* XXX: use a better algorithm */
4572 for(i = 0; i < 64; i++) {
4573 ab = a1 >> 63;
4574 a1 = (a1 << 1) | (a0 >> 63);
4575 if (ab || a1 >= b) {
4576 a1 -= b;
4577 qb = 1;
4578 } else {
4579 qb = 0;
4580 }
4581 a0 = (a0 << 1) | qb;
4582 }
4583#if defined(DEBUG_MULDIV)
4584 printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n",
4585 *phigh, *plow, b, a0, a1);
4586#endif
4587 *plow = a0;
4588 *phigh = a1;
4589 }
4590 return 0;
4591}
4592
4593/* return TRUE if overflow */
4594static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
4595{
4596 int sa, sb;
4597 sa = ((int64_t)*phigh < 0);
4598 if (sa)
4599 neg128(plow, phigh);
4600 sb = (b < 0);
4601 if (sb)
4602 b = -b;
4603 if (div64(plow, phigh, b) != 0)
4604 return 1;
4605 if (sa ^ sb) {
4606 if (*plow > (1ULL << 63))
4607 return 1;
4608 *plow = - *plow;
4609 } else {
4610 if (*plow >= (1ULL << 63))
4611 return 1;
4612 }
4613 if (sa)
4614 *phigh = - *phigh;
4615 return 0;
4616}
4617
4618void helper_mulq_EAX_T0(target_ulong t0)
4619{
4620 uint64_t r0, r1;
4621
4622 mulu64(&r0, &r1, EAX, t0);
4623 EAX = r0;
4624 EDX = r1;
4625 CC_DST = r0;
4626 CC_SRC = r1;
4627}
4628
4629void helper_imulq_EAX_T0(target_ulong t0)
4630{
4631 uint64_t r0, r1;
4632
4633 muls64(&r0, &r1, EAX, t0);
4634 EAX = r0;
4635 EDX = r1;
4636 CC_DST = r0;
4637 CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
4638}
4639
4640target_ulong helper_imulq_T0_T1(target_ulong t0, target_ulong t1)
4641{
4642 uint64_t r0, r1;
4643
4644 muls64(&r0, &r1, t0, t1);
4645 CC_DST = r0;
4646 CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
4647 return r0;
4648}
4649
4650void helper_divq_EAX(target_ulong t0)
4651{
4652 uint64_t r0, r1;
4653 if (t0 == 0) {
4654 raise_exception(EXCP00_DIVZ);
4655 }
4656 r0 = EAX;
4657 r1 = EDX;
4658 if (div64(&r0, &r1, t0))
4659 raise_exception(EXCP00_DIVZ);
4660 EAX = r0;
4661 EDX = r1;
4662}
4663
4664void helper_idivq_EAX(target_ulong t0)
4665{
4666 uint64_t r0, r1;
4667 if (t0 == 0) {
4668 raise_exception(EXCP00_DIVZ);
4669 }
4670 r0 = EAX;
4671 r1 = EDX;
4672 if (idiv64(&r0, &r1, t0))
4673 raise_exception(EXCP00_DIVZ);
4674 EAX = r0;
4675 EDX = r1;
4676}
4677#endif
4678
4679static void do_hlt(void)
4680{
4681 env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */
4682 env->halted = 1;
4683 env->exception_index = EXCP_HLT;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01004684 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004685}
4686
4687void helper_hlt(int next_eip_addend)
4688{
4689 helper_svm_check_intercept_param(SVM_EXIT_HLT, 0);
4690 EIP += next_eip_addend;
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004691
Jun Nakajima86797932011-01-29 14:24:24 -08004692 do_hlt();
4693}
4694
4695void helper_monitor(target_ulong ptr)
4696{
4697 if ((uint32_t)ECX != 0)
4698 raise_exception(EXCP0D_GPF);
4699 /* XXX: store address ? */
4700 helper_svm_check_intercept_param(SVM_EXIT_MONITOR, 0);
4701}
4702
4703void helper_mwait(int next_eip_addend)
4704{
4705 if ((uint32_t)ECX != 0)
4706 raise_exception(EXCP0D_GPF);
4707 helper_svm_check_intercept_param(SVM_EXIT_MWAIT, 0);
4708 EIP += next_eip_addend;
4709
4710 /* XXX: not complete but not completely erroneous */
4711 if (env->cpu_index != 0 || env->next_cpu != NULL) {
4712 /* more than one CPU: do not sleep because another CPU may
4713 wake this one */
4714 } else {
4715 do_hlt();
4716 }
4717}
4718
4719void helper_debug(void)
4720{
4721 env->exception_index = EXCP_DEBUG;
David 'Digit' Turner85c62202014-02-16 20:53:40 +01004722 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08004723}
4724
4725void helper_reset_rf(void)
4726{
4727 env->eflags &= ~RF_MASK;
4728}
4729
4730void helper_raise_interrupt(int intno, int next_eip_addend)
4731{
4732 raise_interrupt(intno, 1, 0, next_eip_addend);
4733}
4734
4735void helper_raise_exception(int exception_index)
4736{
4737 raise_exception(exception_index);
4738}
4739
4740void helper_cli(void)
4741{
4742 env->eflags &= ~IF_MASK;
4743}
4744
4745void helper_sti(void)
4746{
4747 env->eflags |= IF_MASK;
4748}
4749
4750#if 0
4751/* vm86plus instructions */
4752void helper_cli_vm(void)
4753{
4754 env->eflags &= ~VIF_MASK;
4755}
4756
4757void helper_sti_vm(void)
4758{
4759 env->eflags |= VIF_MASK;
4760 if (env->eflags & VIP_MASK) {
4761 raise_exception(EXCP0D_GPF);
4762 }
4763}
4764#endif
4765
4766void helper_set_inhibit_irq(void)
4767{
4768 env->hflags |= HF_INHIBIT_IRQ_MASK;
4769}
4770
4771void helper_reset_inhibit_irq(void)
4772{
4773 env->hflags &= ~HF_INHIBIT_IRQ_MASK;
4774}
4775
4776void helper_boundw(target_ulong a0, int v)
4777{
4778 int low, high;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004779 low = cpu_ldsw_data(env, a0);
4780 high = cpu_ldsw_data(env, a0 + 2);
Jun Nakajima86797932011-01-29 14:24:24 -08004781 v = (int16_t)v;
4782 if (v < low || v > high) {
4783 raise_exception(EXCP05_BOUND);
4784 }
4785}
4786
4787void helper_boundl(target_ulong a0, int v)
4788{
4789 int low, high;
David 'Digit' Turnereca7bc22014-03-14 23:58:39 +01004790 low = cpu_ldl_data(env, a0);
4791 high = cpu_ldl_data(env, a0 + 4);
Jun Nakajima86797932011-01-29 14:24:24 -08004792 if (v < low || v > high) {
4793 raise_exception(EXCP05_BOUND);
4794 }
4795}
4796
4797static float approx_rsqrt(float a)
4798{
4799 return 1.0 / sqrt(a);
4800}
4801
4802static float approx_rcp(float a)
4803{
4804 return 1.0 / a;
4805}
4806
4807#if !defined(CONFIG_USER_ONLY)
4808
4809#define MMUSUFFIX _mmu
4810
4811#define SHIFT 0
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004812#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004813
4814#define SHIFT 1
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004815#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004816
4817#define SHIFT 2
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004818#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004819
4820#define SHIFT 3
David 'Digit' Turner852088c2013-12-14 23:04:12 +01004821#include "exec/softmmu_template.h"
Jun Nakajima86797932011-01-29 14:24:24 -08004822
4823#endif
4824
4825#if !defined(CONFIG_USER_ONLY)
4826/* try to fill the TLB and return an exception if error. If retaddr is
4827 NULL, it means that the function was called in C code (i.e. not
4828 from generated code or from helper.c) */
4829/* XXX: fix it to restore all registers */
David 'Digit' Turner6d1afd32014-03-14 10:51:57 +01004830void tlb_fill(CPUX86State* env1, target_ulong addr, int is_write, int mmu_idx, void *retaddr)
Jun Nakajima86797932011-01-29 14:24:24 -08004831{
4832 TranslationBlock *tb;
4833 int ret;
4834 unsigned long pc;
4835 CPUX86State *saved_env;
4836
4837 /* XXX: hack to restore env in all cases, even if not called from
4838 generated code */
4839 saved_env = env;
David 'Digit' Turner6d1afd32014-03-14 10:51:57 +01004840 env = env1;
Jun Nakajima86797932011-01-29 14:24:24 -08004841 ret = cpu_x86_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
4842 if (ret) {
4843 if (retaddr) {
4844 /* now we have a real cpu fault */
4845 pc = (unsigned long)retaddr;
4846 tb = tb_find_pc(pc);
4847 if (tb) {
4848 /* the PC is inside the translated code. It means that we have
4849 a virtual CPU fault */
David 'Digit' Turner3e0677d2014-03-07 15:01:06 +01004850 cpu_restore_state(env, pc);
Jun Nakajima86797932011-01-29 14:24:24 -08004851 }
4852 }
4853 raise_exception_err(env->exception_index, env->error_code);
4854 }
4855 env = saved_env;
4856}
4857#endif
4858
4859/* Secure Virtual Machine helpers */
4860
4861#if defined(CONFIG_USER_ONLY)
4862
4863void helper_vmrun(int aflag, int next_eip_addend)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004864{
Jun Nakajima86797932011-01-29 14:24:24 -08004865}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004866void helper_vmmcall(void)
4867{
Jun Nakajima86797932011-01-29 14:24:24 -08004868}
4869void helper_vmload(int aflag)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004870{
Jun Nakajima86797932011-01-29 14:24:24 -08004871}
4872void helper_vmsave(int aflag)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004873{
Jun Nakajima86797932011-01-29 14:24:24 -08004874}
4875void helper_stgi(void)
4876{
4877}
4878void helper_clgi(void)
4879{
4880}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004881void helper_skinit(void)
4882{
Jun Nakajima86797932011-01-29 14:24:24 -08004883}
4884void helper_invlpga(int aflag)
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004885{
Jun Nakajima86797932011-01-29 14:24:24 -08004886}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004887void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1)
4888{
Jun Nakajima86797932011-01-29 14:24:24 -08004889}
4890void helper_svm_check_intercept_param(uint32_t type, uint64_t param)
4891{
4892}
4893
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01004894void svm_check_intercept(CPUArchState *env1, uint32_t type)
4895{
4896}
4897
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004898void helper_svm_check_io(uint32_t port, uint32_t param,
Jun Nakajima86797932011-01-29 14:24:24 -08004899 uint32_t next_eip_addend)
4900{
4901}
4902#else
4903
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +01004904static inline void svm_save_seg(hwaddr addr,
Jun Nakajima86797932011-01-29 14:24:24 -08004905 const SegmentCache *sc)
4906{
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004907 stw_phys(addr + offsetof(struct vmcb_seg, selector),
Jun Nakajima86797932011-01-29 14:24:24 -08004908 sc->selector);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004909 stq_phys(addr + offsetof(struct vmcb_seg, base),
Jun Nakajima86797932011-01-29 14:24:24 -08004910 sc->base);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004911 stl_phys(addr + offsetof(struct vmcb_seg, limit),
Jun Nakajima86797932011-01-29 14:24:24 -08004912 sc->limit);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004913 stw_phys(addr + offsetof(struct vmcb_seg, attrib),
Jun Nakajima86797932011-01-29 14:24:24 -08004914 ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00));
4915}
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004916
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +01004917static inline void svm_load_seg(hwaddr addr, SegmentCache *sc)
Jun Nakajima86797932011-01-29 14:24:24 -08004918{
4919 unsigned int flags;
4920
4921 sc->selector = lduw_phys(addr + offsetof(struct vmcb_seg, selector));
4922 sc->base = ldq_phys(addr + offsetof(struct vmcb_seg, base));
4923 sc->limit = ldl_phys(addr + offsetof(struct vmcb_seg, limit));
4924 flags = lduw_phys(addr + offsetof(struct vmcb_seg, attrib));
4925 sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12);
4926}
4927
David 'Digit' Turnerbcde1092014-01-09 23:19:19 +01004928static inline void svm_load_seg_cache(hwaddr addr,
David 'Digit' Turnere2678e12014-01-16 15:56:43 +01004929 CPUX86State *env, int seg_reg)
Jun Nakajima86797932011-01-29 14:24:24 -08004930{
4931 SegmentCache sc1, *sc = &sc1;
4932 svm_load_seg(addr, sc);
4933 cpu_x86_load_seg_cache(env, seg_reg, sc->selector,
4934 sc->base, sc->limit, sc->flags);
4935}
4936
4937void helper_vmrun(int aflag, int next_eip_addend)
4938{
4939 target_ulong addr;
4940 uint32_t event_inj;
4941 uint32_t int_ctl;
4942
4943 helper_svm_check_intercept_param(SVM_EXIT_VMRUN, 0);
4944
4945 if (aflag == 2)
4946 addr = EAX;
4947 else
4948 addr = (uint32_t)EAX;
4949
4950 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n", addr);
4951
4952 env->vm_vmcb = addr;
4953
4954 /* save the current CPU state in the hsave page */
4955 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), env->gdt.base);
4956 stl_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit);
4957
4958 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.base), env->idt.base);
4959 stl_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), env->idt.limit);
4960
4961 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]);
4962 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]);
4963 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]);
4964 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]);
4965 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]);
4966 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]);
4967
4968 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer);
4969 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags), compute_eflags());
4970
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004971 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.es),
Jun Nakajima86797932011-01-29 14:24:24 -08004972 &env->segs[R_ES]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004973 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.cs),
Jun Nakajima86797932011-01-29 14:24:24 -08004974 &env->segs[R_CS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004975 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ss),
Jun Nakajima86797932011-01-29 14:24:24 -08004976 &env->segs[R_SS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02004977 svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ds),
Jun Nakajima86797932011-01-29 14:24:24 -08004978 &env->segs[R_DS]);
4979
4980 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rip),
4981 EIP + next_eip_addend);
4982 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rsp), ESP);
4983 stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rax), EAX);
4984
4985 /* load the interception bitmaps so we do not need to access the
4986 vmcb in svm mode */
4987 env->intercept = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept));
4988 env->intercept_cr_read = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_read));
4989 env->intercept_cr_write = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_write));
4990 env->intercept_dr_read = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_read));
4991 env->intercept_dr_write = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_write));
4992 env->intercept_exceptions = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_exceptions));
4993
4994 /* enable intercepts */
4995 env->hflags |= HF_SVMI_MASK;
4996
4997 env->tsc_offset = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.tsc_offset));
4998
4999 env->gdt.base = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base));
5000 env->gdt.limit = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit));
5001
5002 env->idt.base = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.base));
5003 env->idt.limit = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit));
5004
5005 /* clear exit_info_2 so we behave like the real hardware */
5006 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0);
5007
5008 cpu_x86_update_cr0(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr0)));
5009 cpu_x86_update_cr4(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr4)));
5010 cpu_x86_update_cr3(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr3)));
5011 env->cr[2] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr2));
5012 int_ctl = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
5013 env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
5014 if (int_ctl & V_INTR_MASKING_MASK) {
5015 env->v_tpr = int_ctl & V_TPR_MASK;
5016 env->hflags2 |= HF2_VINTR_MASK;
5017 if (env->eflags & IF_MASK)
5018 env->hflags2 |= HF2_HIF_MASK;
5019 }
5020
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005021 cpu_load_efer(env,
Jun Nakajima86797932011-01-29 14:24:24 -08005022 ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.efer)));
5023 env->eflags = 0;
5024 load_eflags(ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rflags)),
5025 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
5026 CC_OP = CC_OP_EFLAGS;
5027
5028 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.es),
5029 env, R_ES);
5030 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.cs),
5031 env, R_CS);
5032 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.ss),
5033 env, R_SS);
5034 svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.ds),
5035 env, R_DS);
5036
5037 EIP = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rip));
5038 env->eip = EIP;
5039 ESP = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rsp));
5040 EAX = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rax));
5041 env->dr[7] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr7));
5042 env->dr[6] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr6));
5043 cpu_x86_set_cpl(env, ldub_phys(env->vm_vmcb + offsetof(struct vmcb, save.cpl)));
5044
5045 /* FIXME: guest state consistency checks */
5046
5047 switch(ldub_phys(env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) {
5048 case TLB_CONTROL_DO_NOTHING:
5049 break;
5050 case TLB_CONTROL_FLUSH_ALL_ASID:
5051 /* FIXME: this is not 100% correct but should work for now */
5052 tlb_flush(env, 1);
5053 break;
5054 }
5055
5056 env->hflags2 |= HF2_GIF_MASK;
5057
5058 if (int_ctl & V_IRQ_MASK) {
5059 env->interrupt_request |= CPU_INTERRUPT_VIRQ;
5060 }
5061
5062 /* maybe we need to inject an event */
5063 event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
5064 if (event_inj & SVM_EVTINJ_VALID) {
5065 uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK;
5066 uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR;
5067 uint32_t event_inj_err = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err));
5068
5069 qemu_log_mask(CPU_LOG_TB_IN_ASM, "Injecting(%#hx): ", valid_err);
5070 /* FIXME: need to implement valid_err */
5071 switch (event_inj & SVM_EVTINJ_TYPE_MASK) {
5072 case SVM_EVTINJ_TYPE_INTR:
5073 env->exception_index = vector;
5074 env->error_code = event_inj_err;
5075 env->exception_is_int = 0;
5076 env->exception_next_eip = -1;
5077 qemu_log_mask(CPU_LOG_TB_IN_ASM, "INTR");
5078 /* XXX: is it always correct ? */
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01005079 do_interrupt_all(vector, 0, 0, 0, 1);
Jun Nakajima86797932011-01-29 14:24:24 -08005080 break;
5081 case SVM_EVTINJ_TYPE_NMI:
5082 env->exception_index = EXCP02_NMI;
5083 env->error_code = event_inj_err;
5084 env->exception_is_int = 0;
5085 env->exception_next_eip = EIP;
5086 qemu_log_mask(CPU_LOG_TB_IN_ASM, "NMI");
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005087 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005088 break;
5089 case SVM_EVTINJ_TYPE_EXEPT:
5090 env->exception_index = vector;
5091 env->error_code = event_inj_err;
5092 env->exception_is_int = 0;
5093 env->exception_next_eip = -1;
5094 qemu_log_mask(CPU_LOG_TB_IN_ASM, "EXEPT");
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005095 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005096 break;
5097 case SVM_EVTINJ_TYPE_SOFT:
5098 env->exception_index = vector;
5099 env->error_code = event_inj_err;
5100 env->exception_is_int = 1;
5101 env->exception_next_eip = EIP;
5102 qemu_log_mask(CPU_LOG_TB_IN_ASM, "SOFT");
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005103 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005104 break;
5105 }
5106 qemu_log_mask(CPU_LOG_TB_IN_ASM, " %#x %#x\n", env->exception_index, env->error_code);
5107 }
5108}
5109
5110void helper_vmmcall(void)
5111{
5112 helper_svm_check_intercept_param(SVM_EXIT_VMMCALL, 0);
5113 raise_exception(EXCP06_ILLOP);
5114}
5115
5116void helper_vmload(int aflag)
5117{
5118 target_ulong addr;
5119 helper_svm_check_intercept_param(SVM_EXIT_VMLOAD, 0);
5120
5121 if (aflag == 2)
5122 addr = EAX;
5123 else
5124 addr = (uint32_t)EAX;
5125
5126 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
5127 addr, ldq_phys(addr + offsetof(struct vmcb, save.fs.base)),
5128 env->segs[R_FS].base);
5129
5130 svm_load_seg_cache(addr + offsetof(struct vmcb, save.fs),
5131 env, R_FS);
5132 svm_load_seg_cache(addr + offsetof(struct vmcb, save.gs),
5133 env, R_GS);
5134 svm_load_seg(addr + offsetof(struct vmcb, save.tr),
5135 &env->tr);
5136 svm_load_seg(addr + offsetof(struct vmcb, save.ldtr),
5137 &env->ldt);
5138
5139#ifdef TARGET_X86_64
5140 env->kernelgsbase = ldq_phys(addr + offsetof(struct vmcb, save.kernel_gs_base));
5141 env->lstar = ldq_phys(addr + offsetof(struct vmcb, save.lstar));
5142 env->cstar = ldq_phys(addr + offsetof(struct vmcb, save.cstar));
5143 env->fmask = ldq_phys(addr + offsetof(struct vmcb, save.sfmask));
5144#endif
5145 env->star = ldq_phys(addr + offsetof(struct vmcb, save.star));
5146 env->sysenter_cs = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_cs));
5147 env->sysenter_esp = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_esp));
5148 env->sysenter_eip = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_eip));
5149}
5150
5151void helper_vmsave(int aflag)
5152{
5153 target_ulong addr;
5154 helper_svm_check_intercept_param(SVM_EXIT_VMSAVE, 0);
5155
5156 if (aflag == 2)
5157 addr = EAX;
5158 else
5159 addr = (uint32_t)EAX;
5160
5161 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
5162 addr, ldq_phys(addr + offsetof(struct vmcb, save.fs.base)),
5163 env->segs[R_FS].base);
5164
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005165 svm_save_seg(addr + offsetof(struct vmcb, save.fs),
Jun Nakajima86797932011-01-29 14:24:24 -08005166 &env->segs[R_FS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005167 svm_save_seg(addr + offsetof(struct vmcb, save.gs),
Jun Nakajima86797932011-01-29 14:24:24 -08005168 &env->segs[R_GS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005169 svm_save_seg(addr + offsetof(struct vmcb, save.tr),
Jun Nakajima86797932011-01-29 14:24:24 -08005170 &env->tr);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005171 svm_save_seg(addr + offsetof(struct vmcb, save.ldtr),
Jun Nakajima86797932011-01-29 14:24:24 -08005172 &env->ldt);
5173
5174#ifdef TARGET_X86_64
5175 stq_phys(addr + offsetof(struct vmcb, save.kernel_gs_base), env->kernelgsbase);
5176 stq_phys(addr + offsetof(struct vmcb, save.lstar), env->lstar);
5177 stq_phys(addr + offsetof(struct vmcb, save.cstar), env->cstar);
5178 stq_phys(addr + offsetof(struct vmcb, save.sfmask), env->fmask);
5179#endif
5180 stq_phys(addr + offsetof(struct vmcb, save.star), env->star);
5181 stq_phys(addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs);
5182 stq_phys(addr + offsetof(struct vmcb, save.sysenter_esp), env->sysenter_esp);
5183 stq_phys(addr + offsetof(struct vmcb, save.sysenter_eip), env->sysenter_eip);
5184}
5185
5186void helper_stgi(void)
5187{
5188 helper_svm_check_intercept_param(SVM_EXIT_STGI, 0);
5189 env->hflags2 |= HF2_GIF_MASK;
5190}
5191
5192void helper_clgi(void)
5193{
5194 helper_svm_check_intercept_param(SVM_EXIT_CLGI, 0);
5195 env->hflags2 &= ~HF2_GIF_MASK;
5196}
5197
5198void helper_skinit(void)
5199{
5200 helper_svm_check_intercept_param(SVM_EXIT_SKINIT, 0);
5201 /* XXX: not implemented */
5202 raise_exception(EXCP06_ILLOP);
5203}
5204
5205void helper_invlpga(int aflag)
5206{
5207 target_ulong addr;
5208 helper_svm_check_intercept_param(SVM_EXIT_INVLPGA, 0);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005209
Jun Nakajima86797932011-01-29 14:24:24 -08005210 if (aflag == 2)
5211 addr = EAX;
5212 else
5213 addr = (uint32_t)EAX;
5214
5215 /* XXX: could use the ASID to see if it is needed to do the
5216 flush */
5217 tlb_flush_page(env, addr);
5218}
5219
5220void helper_svm_check_intercept_param(uint32_t type, uint64_t param)
5221{
5222 if (likely(!(env->hflags & HF_SVMI_MASK)))
5223 return;
5224 switch(type) {
5225 case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8:
5226 if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) {
5227 helper_vmexit(type, param);
5228 }
5229 break;
5230 case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8:
5231 if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) {
5232 helper_vmexit(type, param);
5233 }
5234 break;
5235 case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7:
5236 if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) {
5237 helper_vmexit(type, param);
5238 }
5239 break;
5240 case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7:
5241 if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) {
5242 helper_vmexit(type, param);
5243 }
5244 break;
5245 case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31:
5246 if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) {
5247 helper_vmexit(type, param);
5248 }
5249 break;
5250 case SVM_EXIT_MSR:
5251 if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) {
5252 /* FIXME: this should be read in at vmrun (faster this way?) */
5253 uint64_t addr = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.msrpm_base_pa));
5254 uint32_t t0, t1;
5255 switch((uint32_t)ECX) {
5256 case 0 ... 0x1fff:
5257 t0 = (ECX * 2) % 8;
5258 t1 = ECX / 8;
5259 break;
5260 case 0xc0000000 ... 0xc0001fff:
5261 t0 = (8192 + ECX - 0xc0000000) * 2;
5262 t1 = (t0 / 8);
5263 t0 %= 8;
5264 break;
5265 case 0xc0010000 ... 0xc0011fff:
5266 t0 = (16384 + ECX - 0xc0010000) * 2;
5267 t1 = (t0 / 8);
5268 t0 %= 8;
5269 break;
5270 default:
5271 helper_vmexit(type, param);
5272 t0 = 0;
5273 t1 = 0;
5274 break;
5275 }
5276 if (ldub_phys(addr + t1) & ((1 << param) << t0))
5277 helper_vmexit(type, param);
5278 }
5279 break;
5280 default:
5281 if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) {
5282 helper_vmexit(type, param);
5283 }
5284 break;
5285 }
5286}
5287
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01005288void svm_check_intercept(CPUArchState *env1, uint32_t type)
5289{
5290 CPUArchState *saved_env;
5291
5292 saved_env = env;
5293 env = env1;
5294 helper_svm_check_intercept_param(type, 0);
5295 env = saved_env;
5296}
5297
5298
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005299void helper_svm_check_io(uint32_t port, uint32_t param,
Jun Nakajima86797932011-01-29 14:24:24 -08005300 uint32_t next_eip_addend)
5301{
5302 if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) {
5303 /* FIXME: this should be read in at vmrun (faster this way?) */
5304 uint64_t addr = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.iopm_base_pa));
5305 uint16_t mask = (1 << ((param >> 4) & 7)) - 1;
5306 if(lduw_phys(addr + port / 8) & (mask << (port & 7))) {
5307 /* next EIP */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005308 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2),
Jun Nakajima86797932011-01-29 14:24:24 -08005309 env->eip + next_eip_addend);
5310 helper_vmexit(SVM_EXIT_IOIO, param | (port << 16));
5311 }
5312 }
5313}
5314
5315/* Note: currently only 32 bits of exit_code are used */
5316void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1)
5317{
5318 uint32_t int_ctl;
5319
5320 qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" PRIx64 ", " TARGET_FMT_lx ")!\n",
5321 exit_code, exit_info_1,
5322 ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2)),
5323 EIP);
5324
5325 if(env->hflags & HF_INHIBIT_IRQ_MASK) {
5326 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_state), SVM_INTERRUPT_SHADOW_MASK);
5327 env->hflags &= ~HF_INHIBIT_IRQ_MASK;
5328 } else {
5329 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0);
5330 }
5331
5332 /* Save the VM state in the vmcb */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005333 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.es),
Jun Nakajima86797932011-01-29 14:24:24 -08005334 &env->segs[R_ES]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005335 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.cs),
Jun Nakajima86797932011-01-29 14:24:24 -08005336 &env->segs[R_CS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005337 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ss),
Jun Nakajima86797932011-01-29 14:24:24 -08005338 &env->segs[R_SS]);
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005339 svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ds),
Jun Nakajima86797932011-01-29 14:24:24 -08005340 &env->segs[R_DS]);
5341
5342 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), env->gdt.base);
5343 stl_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit);
5344
5345 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), env->idt.base);
5346 stl_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), env->idt.limit);
5347
5348 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer);
5349 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]);
5350 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]);
5351 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]);
5352 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]);
5353
5354 int_ctl = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
5355 int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK);
5356 int_ctl |= env->v_tpr & V_TPR_MASK;
5357 if (env->interrupt_request & CPU_INTERRUPT_VIRQ)
5358 int_ctl |= V_IRQ_MASK;
5359 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl);
5360
5361 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rflags), compute_eflags());
5362 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rip), env->eip);
5363 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rsp), ESP);
5364 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rax), EAX);
5365 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]);
5366 stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]);
5367 stb_phys(env->vm_vmcb + offsetof(struct vmcb, save.cpl), env->hflags & HF_CPL_MASK);
5368
5369 /* Reload the host state from vm_hsave */
5370 env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
5371 env->hflags &= ~HF_SVMI_MASK;
5372 env->intercept = 0;
5373 env->intercept_exceptions = 0;
5374 env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
5375 env->tsc_offset = 0;
5376
5377 env->gdt.base = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.base));
5378 env->gdt.limit = ldl_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit));
5379
5380 env->idt.base = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.base));
5381 env->idt.limit = ldl_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.limit));
5382
5383 cpu_x86_update_cr0(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr0)) | CR0_PE_MASK);
5384 cpu_x86_update_cr4(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr4)));
5385 cpu_x86_update_cr3(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr3)));
5386 /* we need to set the efer after the crs so the hidden flags get
5387 set properly */
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005388 cpu_load_efer(env,
Jun Nakajima86797932011-01-29 14:24:24 -08005389 ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer)));
5390 env->eflags = 0;
5391 load_eflags(ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags)),
5392 ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
5393 CC_OP = CC_OP_EFLAGS;
5394
5395 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.es),
5396 env, R_ES);
5397 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.cs),
5398 env, R_CS);
5399 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.ss),
5400 env, R_SS);
5401 svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.ds),
5402 env, R_DS);
5403
5404 EIP = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rip));
5405 ESP = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rsp));
5406 EAX = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rax));
5407
5408 env->dr[6] = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr6));
5409 env->dr[7] = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr7));
5410
5411 /* other setups */
5412 cpu_x86_set_cpl(env, 0);
5413 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_code), exit_code);
5414 stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), exit_info_1);
5415
5416 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info),
5417 ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj)));
5418 stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err),
5419 ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err)));
5420
5421 env->hflags2 &= ~HF2_GIF_MASK;
5422 /* FIXME: Resets the current ASID register to zero (host ASID). */
5423
5424 /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */
5425
5426 /* Clears the TSC_OFFSET inside the processor. */
5427
5428 /* If the host is in PAE mode, the processor reloads the host's PDPEs
5429 from the page table indicated the host's CR3. If the PDPEs contain
5430 illegal state, the processor causes a shutdown. */
5431
5432 /* Forces CR0.PE = 1, RFLAGS.VM = 0. */
5433 env->cr[0] |= CR0_PE_MASK;
5434 env->eflags &= ~VM_MASK;
5435
5436 /* Disables all breakpoints in the host DR7 register. */
5437
5438 /* Checks the reloaded host state for consistency. */
5439
5440 /* If the host's rIP reloaded by #VMEXIT is outside the limit of the
5441 host's code segment or non-canonical (in the case of long mode), a
5442 #GP fault is delivered inside the host.) */
5443
5444 /* remove any pending exception */
5445 env->exception_index = -1;
5446 env->error_code = 0;
5447 env->old_exception = -1;
5448
David 'Digit' Turner85c62202014-02-16 20:53:40 +01005449 cpu_loop_exit(env);
Jun Nakajima86797932011-01-29 14:24:24 -08005450}
5451
5452#endif
5453
5454/* MMX/SSE */
5455/* XXX: optimize by storing fptt and fptags in the static cpu state */
5456void helper_enter_mmx(void)
5457{
5458 env->fpstt = 0;
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +01005459 memset(env->fptags, 0, sizeof(env->fptags));
Jun Nakajima86797932011-01-29 14:24:24 -08005460}
5461
5462void helper_emms(void)
5463{
5464 /* set to empty state */
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +01005465 memset(env->fptags, 1, sizeof(env->fptags));
Jun Nakajima86797932011-01-29 14:24:24 -08005466}
5467
5468/* XXX: suppress */
5469void helper_movq(void *d, void *s)
5470{
5471 *(uint64_t *)d = *(uint64_t *)s;
5472}
5473
5474#define SHIFT 0
5475#include "ops_sse.h"
5476
5477#define SHIFT 1
5478#include "ops_sse.h"
5479
5480#define SHIFT 0
5481#include "helper_template.h"
5482#undef SHIFT
5483
5484#define SHIFT 1
5485#include "helper_template.h"
5486#undef SHIFT
5487
5488#define SHIFT 2
5489#include "helper_template.h"
5490#undef SHIFT
5491
5492#ifdef TARGET_X86_64
5493
5494#define SHIFT 3
5495#include "helper_template.h"
5496#undef SHIFT
5497
5498#endif
5499
5500/* bit operations */
5501target_ulong helper_bsf(target_ulong t0)
5502{
5503 int count;
5504 target_ulong res;
5505
5506 res = t0;
5507 count = 0;
5508 while ((res & 1) == 0) {
5509 count++;
5510 res >>= 1;
5511 }
5512 return count;
5513}
5514
5515target_ulong helper_bsr(target_ulong t0)
5516{
5517 int count;
5518 target_ulong res, mask;
David 'Digit' Turnerf645f7d2011-05-11 00:44:05 +02005519
Jun Nakajima86797932011-01-29 14:24:24 -08005520 res = t0;
5521 count = TARGET_LONG_BITS - 1;
5522 mask = (target_ulong)1 << (TARGET_LONG_BITS - 1);
5523 while ((res & mask) == 0) {
5524 count--;
5525 res <<= 1;
5526 }
5527 return count;
5528}
5529
5530
5531static int compute_all_eflags(void)
5532{
5533 return CC_SRC;
5534}
5535
5536static int compute_c_eflags(void)
5537{
5538 return CC_SRC & CC_C;
5539}
5540
5541uint32_t helper_cc_compute_all(int op)
5542{
5543 switch (op) {
5544 default: /* should never happen */ return 0;
5545
5546 case CC_OP_EFLAGS: return compute_all_eflags();
5547
5548 case CC_OP_MULB: return compute_all_mulb();
5549 case CC_OP_MULW: return compute_all_mulw();
5550 case CC_OP_MULL: return compute_all_mull();
5551
5552 case CC_OP_ADDB: return compute_all_addb();
5553 case CC_OP_ADDW: return compute_all_addw();
5554 case CC_OP_ADDL: return compute_all_addl();
5555
5556 case CC_OP_ADCB: return compute_all_adcb();
5557 case CC_OP_ADCW: return compute_all_adcw();
5558 case CC_OP_ADCL: return compute_all_adcl();
5559
5560 case CC_OP_SUBB: return compute_all_subb();
5561 case CC_OP_SUBW: return compute_all_subw();
5562 case CC_OP_SUBL: return compute_all_subl();
5563
5564 case CC_OP_SBBB: return compute_all_sbbb();
5565 case CC_OP_SBBW: return compute_all_sbbw();
5566 case CC_OP_SBBL: return compute_all_sbbl();
5567
5568 case CC_OP_LOGICB: return compute_all_logicb();
5569 case CC_OP_LOGICW: return compute_all_logicw();
5570 case CC_OP_LOGICL: return compute_all_logicl();
5571
5572 case CC_OP_INCB: return compute_all_incb();
5573 case CC_OP_INCW: return compute_all_incw();
5574 case CC_OP_INCL: return compute_all_incl();
5575
5576 case CC_OP_DECB: return compute_all_decb();
5577 case CC_OP_DECW: return compute_all_decw();
5578 case CC_OP_DECL: return compute_all_decl();
5579
5580 case CC_OP_SHLB: return compute_all_shlb();
5581 case CC_OP_SHLW: return compute_all_shlw();
5582 case CC_OP_SHLL: return compute_all_shll();
5583
5584 case CC_OP_SARB: return compute_all_sarb();
5585 case CC_OP_SARW: return compute_all_sarw();
5586 case CC_OP_SARL: return compute_all_sarl();
5587
5588#ifdef TARGET_X86_64
5589 case CC_OP_MULQ: return compute_all_mulq();
5590
5591 case CC_OP_ADDQ: return compute_all_addq();
5592
5593 case CC_OP_ADCQ: return compute_all_adcq();
5594
5595 case CC_OP_SUBQ: return compute_all_subq();
5596
5597 case CC_OP_SBBQ: return compute_all_sbbq();
5598
5599 case CC_OP_LOGICQ: return compute_all_logicq();
5600
5601 case CC_OP_INCQ: return compute_all_incq();
5602
5603 case CC_OP_DECQ: return compute_all_decq();
5604
5605 case CC_OP_SHLQ: return compute_all_shlq();
5606
5607 case CC_OP_SARQ: return compute_all_sarq();
5608#endif
5609 }
5610}
5611
David 'Digit' Turnerbf2340f2014-03-18 14:41:25 +01005612uint32_t cpu_cc_compute_all(CPUArchState *env1, int op)
5613{
5614 CPUArchState *saved_env;
5615 uint32_t ret;
5616
5617 saved_env = env;
5618 env = env1;
5619 ret = helper_cc_compute_all(op);
5620 env = saved_env;
5621 return ret;
5622}
5623
Jun Nakajima86797932011-01-29 14:24:24 -08005624uint32_t helper_cc_compute_c(int op)
5625{
5626 switch (op) {
5627 default: /* should never happen */ return 0;
5628
5629 case CC_OP_EFLAGS: return compute_c_eflags();
5630
5631 case CC_OP_MULB: return compute_c_mull();
5632 case CC_OP_MULW: return compute_c_mull();
5633 case CC_OP_MULL: return compute_c_mull();
5634
5635 case CC_OP_ADDB: return compute_c_addb();
5636 case CC_OP_ADDW: return compute_c_addw();
5637 case CC_OP_ADDL: return compute_c_addl();
5638
5639 case CC_OP_ADCB: return compute_c_adcb();
5640 case CC_OP_ADCW: return compute_c_adcw();
5641 case CC_OP_ADCL: return compute_c_adcl();
5642
5643 case CC_OP_SUBB: return compute_c_subb();
5644 case CC_OP_SUBW: return compute_c_subw();
5645 case CC_OP_SUBL: return compute_c_subl();
5646
5647 case CC_OP_SBBB: return compute_c_sbbb();
5648 case CC_OP_SBBW: return compute_c_sbbw();
5649 case CC_OP_SBBL: return compute_c_sbbl();
5650
5651 case CC_OP_LOGICB: return compute_c_logicb();
5652 case CC_OP_LOGICW: return compute_c_logicw();
5653 case CC_OP_LOGICL: return compute_c_logicl();
5654
5655 case CC_OP_INCB: return compute_c_incl();
5656 case CC_OP_INCW: return compute_c_incl();
5657 case CC_OP_INCL: return compute_c_incl();
5658
5659 case CC_OP_DECB: return compute_c_incl();
5660 case CC_OP_DECW: return compute_c_incl();
5661 case CC_OP_DECL: return compute_c_incl();
5662
5663 case CC_OP_SHLB: return compute_c_shlb();
5664 case CC_OP_SHLW: return compute_c_shlw();
5665 case CC_OP_SHLL: return compute_c_shll();
5666
5667 case CC_OP_SARB: return compute_c_sarl();
5668 case CC_OP_SARW: return compute_c_sarl();
5669 case CC_OP_SARL: return compute_c_sarl();
5670
5671#ifdef TARGET_X86_64
5672 case CC_OP_MULQ: return compute_c_mull();
5673
5674 case CC_OP_ADDQ: return compute_c_addq();
5675
5676 case CC_OP_ADCQ: return compute_c_adcq();
5677
5678 case CC_OP_SUBQ: return compute_c_subq();
5679
5680 case CC_OP_SBBQ: return compute_c_sbbq();
5681
5682 case CC_OP_LOGICQ: return compute_c_logicq();
5683
5684 case CC_OP_INCQ: return compute_c_incl();
5685
5686 case CC_OP_DECQ: return compute_c_incl();
5687
5688 case CC_OP_SHLQ: return compute_c_shlq();
5689
5690 case CC_OP_SARQ: return compute_c_sarl();
5691#endif
5692 }
5693}