]>
Commit | Line | Data |
---|---|---|
cd71c089 LV |
1 | /* |
2 | * qemu user cpu loop | |
3 | * | |
4 | * Copyright (c) 2003-2008 Fabrice Bellard | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program 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 | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "qemu/osdep.h" | |
21 | #include "qemu.h" | |
22 | #include "cpu_loop-common.h" | |
23 | ||
3f8258c1 LV |
24 | /***********************************************************/ |
25 | /* CPUX86 core interface */ | |
26 | ||
27 | uint64_t cpu_get_tsc(CPUX86State *env) | |
28 | { | |
29 | return cpu_get_host_ticks(); | |
30 | } | |
31 | ||
32 | static void write_dt(void *ptr, unsigned long addr, unsigned long limit, | |
33 | int flags) | |
34 | { | |
35 | unsigned int e1, e2; | |
36 | uint32_t *p; | |
37 | e1 = (addr << 16) | (limit & 0xffff); | |
38 | e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); | |
39 | e2 |= flags; | |
40 | p = ptr; | |
41 | p[0] = tswap32(e1); | |
42 | p[1] = tswap32(e2); | |
43 | } | |
44 | ||
45 | static uint64_t *idt_table; | |
46 | #ifdef TARGET_X86_64 | |
47 | static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, | |
48 | uint64_t addr, unsigned int sel) | |
49 | { | |
50 | uint32_t *p, e1, e2; | |
51 | e1 = (addr & 0xffff) | (sel << 16); | |
52 | e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); | |
53 | p = ptr; | |
54 | p[0] = tswap32(e1); | |
55 | p[1] = tswap32(e2); | |
56 | p[2] = tswap32(addr >> 32); | |
57 | p[3] = 0; | |
58 | } | |
59 | /* only dpl matters as we do only user space emulation */ | |
60 | static void set_idt(int n, unsigned int dpl) | |
61 | { | |
62 | set_gate64(idt_table + n * 2, 0, dpl, 0, 0); | |
63 | } | |
64 | #else | |
65 | static void set_gate(void *ptr, unsigned int type, unsigned int dpl, | |
66 | uint32_t addr, unsigned int sel) | |
67 | { | |
68 | uint32_t *p, e1, e2; | |
69 | e1 = (addr & 0xffff) | (sel << 16); | |
70 | e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); | |
71 | p = ptr; | |
72 | p[0] = tswap32(e1); | |
73 | p[1] = tswap32(e2); | |
74 | } | |
75 | ||
76 | /* only dpl matters as we do only user space emulation */ | |
77 | static void set_idt(int n, unsigned int dpl) | |
78 | { | |
79 | set_gate(idt_table + n, 0, dpl, 0, 0); | |
80 | } | |
81 | #endif | |
82 | ||
83 | void cpu_loop(CPUX86State *env) | |
84 | { | |
85 | CPUState *cs = CPU(x86_env_get_cpu(env)); | |
86 | int trapnr; | |
87 | abi_ulong pc; | |
88 | abi_ulong ret; | |
89 | target_siginfo_t info; | |
90 | ||
91 | for(;;) { | |
92 | cpu_exec_start(cs); | |
93 | trapnr = cpu_exec(cs); | |
94 | cpu_exec_end(cs); | |
95 | process_queued_cpu_work(cs); | |
96 | ||
97 | switch(trapnr) { | |
98 | case 0x80: | |
99 | /* linux syscall from int $0x80 */ | |
100 | ret = do_syscall(env, | |
101 | env->regs[R_EAX], | |
102 | env->regs[R_EBX], | |
103 | env->regs[R_ECX], | |
104 | env->regs[R_EDX], | |
105 | env->regs[R_ESI], | |
106 | env->regs[R_EDI], | |
107 | env->regs[R_EBP], | |
108 | 0, 0); | |
109 | if (ret == -TARGET_ERESTARTSYS) { | |
110 | env->eip -= 2; | |
111 | } else if (ret != -TARGET_QEMU_ESIGRETURN) { | |
112 | env->regs[R_EAX] = ret; | |
113 | } | |
114 | break; | |
115 | #ifndef TARGET_ABI32 | |
116 | case EXCP_SYSCALL: | |
117 | /* linux syscall from syscall instruction */ | |
118 | ret = do_syscall(env, | |
119 | env->regs[R_EAX], | |
120 | env->regs[R_EDI], | |
121 | env->regs[R_ESI], | |
122 | env->regs[R_EDX], | |
123 | env->regs[10], | |
124 | env->regs[8], | |
125 | env->regs[9], | |
126 | 0, 0); | |
127 | if (ret == -TARGET_ERESTARTSYS) { | |
128 | env->eip -= 2; | |
129 | } else if (ret != -TARGET_QEMU_ESIGRETURN) { | |
130 | env->regs[R_EAX] = ret; | |
131 | } | |
132 | break; | |
133 | #endif | |
134 | case EXCP0B_NOSEG: | |
135 | case EXCP0C_STACK: | |
136 | info.si_signo = TARGET_SIGBUS; | |
137 | info.si_errno = 0; | |
138 | info.si_code = TARGET_SI_KERNEL; | |
139 | info._sifields._sigfault._addr = 0; | |
140 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
141 | break; | |
142 | case EXCP0D_GPF: | |
143 | /* XXX: potential problem if ABI32 */ | |
144 | #ifndef TARGET_X86_64 | |
145 | if (env->eflags & VM_MASK) { | |
146 | handle_vm86_fault(env); | |
147 | } else | |
148 | #endif | |
149 | { | |
150 | info.si_signo = TARGET_SIGSEGV; | |
151 | info.si_errno = 0; | |
152 | info.si_code = TARGET_SI_KERNEL; | |
153 | info._sifields._sigfault._addr = 0; | |
154 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
155 | } | |
156 | break; | |
157 | case EXCP0E_PAGE: | |
158 | info.si_signo = TARGET_SIGSEGV; | |
159 | info.si_errno = 0; | |
160 | if (!(env->error_code & 1)) | |
161 | info.si_code = TARGET_SEGV_MAPERR; | |
162 | else | |
163 | info.si_code = TARGET_SEGV_ACCERR; | |
164 | info._sifields._sigfault._addr = env->cr[2]; | |
165 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
166 | break; | |
167 | case EXCP00_DIVZ: | |
168 | #ifndef TARGET_X86_64 | |
169 | if (env->eflags & VM_MASK) { | |
170 | handle_vm86_trap(env, trapnr); | |
171 | } else | |
172 | #endif | |
173 | { | |
174 | /* division by zero */ | |
175 | info.si_signo = TARGET_SIGFPE; | |
176 | info.si_errno = 0; | |
177 | info.si_code = TARGET_FPE_INTDIV; | |
178 | info._sifields._sigfault._addr = env->eip; | |
179 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
180 | } | |
181 | break; | |
182 | case EXCP01_DB: | |
183 | case EXCP03_INT3: | |
184 | #ifndef TARGET_X86_64 | |
185 | if (env->eflags & VM_MASK) { | |
186 | handle_vm86_trap(env, trapnr); | |
187 | } else | |
188 | #endif | |
189 | { | |
190 | info.si_signo = TARGET_SIGTRAP; | |
191 | info.si_errno = 0; | |
192 | if (trapnr == EXCP01_DB) { | |
193 | info.si_code = TARGET_TRAP_BRKPT; | |
194 | info._sifields._sigfault._addr = env->eip; | |
195 | } else { | |
196 | info.si_code = TARGET_SI_KERNEL; | |
197 | info._sifields._sigfault._addr = 0; | |
198 | } | |
199 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
200 | } | |
201 | break; | |
202 | case EXCP04_INTO: | |
203 | case EXCP05_BOUND: | |
204 | #ifndef TARGET_X86_64 | |
205 | if (env->eflags & VM_MASK) { | |
206 | handle_vm86_trap(env, trapnr); | |
207 | } else | |
208 | #endif | |
209 | { | |
210 | info.si_signo = TARGET_SIGSEGV; | |
211 | info.si_errno = 0; | |
212 | info.si_code = TARGET_SI_KERNEL; | |
213 | info._sifields._sigfault._addr = 0; | |
214 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
215 | } | |
216 | break; | |
217 | case EXCP06_ILLOP: | |
218 | info.si_signo = TARGET_SIGILL; | |
219 | info.si_errno = 0; | |
220 | info.si_code = TARGET_ILL_ILLOPN; | |
221 | info._sifields._sigfault._addr = env->eip; | |
222 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
223 | break; | |
224 | case EXCP_INTERRUPT: | |
225 | /* just indicate that signals should be handled asap */ | |
226 | break; | |
227 | case EXCP_DEBUG: | |
228 | { | |
229 | int sig; | |
230 | ||
231 | sig = gdb_handlesig(cs, TARGET_SIGTRAP); | |
232 | if (sig) | |
233 | { | |
234 | info.si_signo = sig; | |
235 | info.si_errno = 0; | |
236 | info.si_code = TARGET_TRAP_BRKPT; | |
237 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
238 | } | |
239 | } | |
240 | break; | |
241 | case EXCP_ATOMIC: | |
242 | cpu_exec_step_atomic(cs); | |
243 | break; | |
244 | default: | |
245 | pc = env->segs[R_CS].base + env->eip; | |
246 | EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", | |
247 | (long)pc, trapnr); | |
248 | abort(); | |
249 | } | |
250 | process_pending_signals(env); | |
251 | } | |
252 | } | |
253 | ||
cd71c089 LV |
254 | void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) |
255 | { | |
3f8258c1 LV |
256 | env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; |
257 | env->hflags |= HF_PE_MASK | HF_CPL_MASK; | |
258 | if (env->features[FEAT_1_EDX] & CPUID_SSE) { | |
259 | env->cr[4] |= CR4_OSFXSR_MASK; | |
260 | env->hflags |= HF_OSFXSR_MASK; | |
261 | } | |
262 | #ifndef TARGET_ABI32 | |
263 | /* enable 64 bit mode if possible */ | |
264 | if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) { | |
265 | fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); | |
266 | exit(EXIT_FAILURE); | |
267 | } | |
268 | env->cr[4] |= CR4_PAE_MASK; | |
269 | env->efer |= MSR_EFER_LMA | MSR_EFER_LME; | |
270 | env->hflags |= HF_LMA_MASK; | |
271 | #endif | |
272 | ||
273 | /* flags setup : we activate the IRQs by default as in user mode */ | |
274 | env->eflags |= IF_MASK; | |
275 | ||
276 | /* linux register setup */ | |
277 | #ifndef TARGET_ABI32 | |
278 | env->regs[R_EAX] = regs->rax; | |
279 | env->regs[R_EBX] = regs->rbx; | |
280 | env->regs[R_ECX] = regs->rcx; | |
281 | env->regs[R_EDX] = regs->rdx; | |
282 | env->regs[R_ESI] = regs->rsi; | |
283 | env->regs[R_EDI] = regs->rdi; | |
284 | env->regs[R_EBP] = regs->rbp; | |
285 | env->regs[R_ESP] = regs->rsp; | |
286 | env->eip = regs->rip; | |
287 | #else | |
288 | env->regs[R_EAX] = regs->eax; | |
289 | env->regs[R_EBX] = regs->ebx; | |
290 | env->regs[R_ECX] = regs->ecx; | |
291 | env->regs[R_EDX] = regs->edx; | |
292 | env->regs[R_ESI] = regs->esi; | |
293 | env->regs[R_EDI] = regs->edi; | |
294 | env->regs[R_EBP] = regs->ebp; | |
295 | env->regs[R_ESP] = regs->esp; | |
296 | env->eip = regs->eip; | |
297 | #endif | |
298 | ||
299 | /* linux interrupt setup */ | |
300 | #ifndef TARGET_ABI32 | |
301 | env->idt.limit = 511; | |
302 | #else | |
303 | env->idt.limit = 255; | |
304 | #endif | |
305 | env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1), | |
306 | PROT_READ|PROT_WRITE, | |
307 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | |
308 | idt_table = g2h(env->idt.base); | |
309 | set_idt(0, 0); | |
310 | set_idt(1, 0); | |
311 | set_idt(2, 0); | |
312 | set_idt(3, 3); | |
313 | set_idt(4, 3); | |
314 | set_idt(5, 0); | |
315 | set_idt(6, 0); | |
316 | set_idt(7, 0); | |
317 | set_idt(8, 0); | |
318 | set_idt(9, 0); | |
319 | set_idt(10, 0); | |
320 | set_idt(11, 0); | |
321 | set_idt(12, 0); | |
322 | set_idt(13, 0); | |
323 | set_idt(14, 0); | |
324 | set_idt(15, 0); | |
325 | set_idt(16, 0); | |
326 | set_idt(17, 0); | |
327 | set_idt(18, 0); | |
328 | set_idt(19, 0); | |
329 | set_idt(0x80, 3); | |
330 | ||
331 | /* linux segment setup */ | |
332 | { | |
333 | uint64_t *gdt_table; | |
334 | env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES, | |
335 | PROT_READ|PROT_WRITE, | |
336 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | |
337 | env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; | |
338 | gdt_table = g2h(env->gdt.base); | |
339 | #ifdef TARGET_ABI32 | |
340 | write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, | |
341 | DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | | |
342 | (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); | |
343 | #else | |
344 | /* 64 bit code segment */ | |
345 | write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, | |
346 | DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | | |
347 | DESC_L_MASK | | |
348 | (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); | |
349 | #endif | |
350 | write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, | |
351 | DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | | |
352 | (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); | |
353 | } | |
354 | cpu_x86_load_seg(env, R_CS, __USER_CS); | |
355 | cpu_x86_load_seg(env, R_SS, __USER_DS); | |
356 | #ifdef TARGET_ABI32 | |
357 | cpu_x86_load_seg(env, R_DS, __USER_DS); | |
358 | cpu_x86_load_seg(env, R_ES, __USER_DS); | |
359 | cpu_x86_load_seg(env, R_FS, __USER_DS); | |
360 | cpu_x86_load_seg(env, R_GS, __USER_DS); | |
361 | /* This hack makes Wine work... */ | |
362 | env->segs[R_FS].selector = 0; | |
363 | #else | |
364 | cpu_x86_load_seg(env, R_DS, 0); | |
365 | cpu_x86_load_seg(env, R_ES, 0); | |
366 | cpu_x86_load_seg(env, R_FS, 0); | |
367 | cpu_x86_load_seg(env, R_GS, 0); | |
368 | #endif | |
cd71c089 | 369 | } |