fixed WP semantics
[qemu] / target-i386 / helper2.c
1 /*
2  *  i386 helpers (without register variable usage)
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <stdarg.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <inttypes.h>
25 #include <signal.h>
26 #include <assert.h>
27 #include <sys/mman.h>
28
29 #include "cpu.h"
30 #include "exec-all.h"
31
32 //#define DEBUG_MMU
33
34 CPUX86State *cpu_x86_init(void)
35 {
36     CPUX86State *env;
37     int i;
38     static int inited;
39
40     cpu_exec_init();
41
42     env = malloc(sizeof(CPUX86State));
43     if (!env)
44         return NULL;
45     memset(env, 0, sizeof(CPUX86State));
46
47     /* init to reset state */
48
49     tlb_flush(env, 1);
50 #ifdef CONFIG_SOFTMMU
51     env->hflags |= HF_SOFTMMU_MASK;
52 #endif
53
54     cpu_x86_update_cr0(env, 0x60000010);
55     env->a20_mask = 0xffffffff;
56     
57     env->idt.limit = 0xffff;
58     env->gdt.limit = 0xffff;
59     env->ldt.limit = 0xffff;
60     env->ldt.flags = DESC_P_MASK;
61     env->tr.limit = 0xffff;
62     env->tr.flags = DESC_P_MASK;
63     
64     /* not correct (CS base=0xffff0000) */
65     cpu_x86_load_seg_cache(env, R_CS, 0xf000, (uint8_t *)0x000f0000, 0xffff, 0); 
66     cpu_x86_load_seg_cache(env, R_DS, 0, NULL, 0xffff, 0);
67     cpu_x86_load_seg_cache(env, R_ES, 0, NULL, 0xffff, 0);
68     cpu_x86_load_seg_cache(env, R_SS, 0, NULL, 0xffff, 0);
69     cpu_x86_load_seg_cache(env, R_FS, 0, NULL, 0xffff, 0);
70     cpu_x86_load_seg_cache(env, R_GS, 0, NULL, 0xffff, 0);
71     
72     env->eip = 0xfff0;
73     env->regs[R_EDX] = 0x600; /* indicate P6 processor */
74     
75     env->eflags = 0x2;
76     
77     /* FPU init */
78     for(i = 0;i < 8; i++)
79         env->fptags[i] = 1;
80     env->fpuc = 0x37f;
81     
82     /* init various static tables */
83     if (!inited) {
84         inited = 1;
85         optimize_flags_init();
86     }
87     return env;
88 }
89
90 void cpu_x86_close(CPUX86State *env)
91 {
92     free(env);
93 }
94
95 /***********************************************************/
96 /* x86 debug */
97
98 static const char *cc_op_str[] = {
99     "DYNAMIC",
100     "EFLAGS",
101     "MULB",
102     "MULW",
103     "MULL",
104     "ADDB",
105     "ADDW",
106     "ADDL",
107     "ADCB",
108     "ADCW",
109     "ADCL",
110     "SUBB",
111     "SUBW",
112     "SUBL",
113     "SBBB",
114     "SBBW",
115     "SBBL",
116     "LOGICB",
117     "LOGICW",
118     "LOGICL",
119     "INCB",
120     "INCW",
121     "INCL",
122     "DECB",
123     "DECW",
124     "DECL",
125     "SHLB",
126     "SHLW",
127     "SHLL",
128     "SARB",
129     "SARW",
130     "SARL",
131 };
132
133 void cpu_x86_dump_state(CPUX86State *env, FILE *f, int flags)
134 {
135     int eflags, i;
136     char cc_op_name[32];
137     static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" };
138
139     eflags = env->eflags;
140     fprintf(f, "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
141             "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
142             "EIP=%08x EFL=%08x [%c%c%c%c%c%c%c]    CPL=%d II=%d\n",
143             env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX], 
144             env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP], 
145             env->eip, eflags,
146             eflags & DF_MASK ? 'D' : '-',
147             eflags & CC_O ? 'O' : '-',
148             eflags & CC_S ? 'S' : '-',
149             eflags & CC_Z ? 'Z' : '-',
150             eflags & CC_A ? 'A' : '-',
151             eflags & CC_P ? 'P' : '-',
152             eflags & CC_C ? 'C' : '-',
153             env->hflags & HF_CPL_MASK, 
154             (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1);
155     for(i = 0; i < 6; i++) {
156         SegmentCache *sc = &env->segs[i];
157         fprintf(f, "%s =%04x %08x %08x %08x\n",
158                 seg_name[i],
159                 sc->selector,
160                 (int)sc->base,
161                 sc->limit,
162                 sc->flags);
163     }
164     fprintf(f, "LDT=%04x %08x %08x %08x\n",
165             env->ldt.selector,
166             (int)env->ldt.base,
167             env->ldt.limit,
168             env->ldt.flags);
169     fprintf(f, "TR =%04x %08x %08x %08x\n",
170             env->tr.selector,
171             (int)env->tr.base,
172             env->tr.limit,
173             env->tr.flags);
174     fprintf(f, "GDT=     %08x %08x\n",
175             (int)env->gdt.base, env->gdt.limit);
176     fprintf(f, "IDT=     %08x %08x\n",
177             (int)env->idt.base, env->idt.limit);
178     fprintf(f, "CR0=%08x CR2=%08x CR3=%08x CR4=%08x\n",
179             env->cr[0], env->cr[2], env->cr[3], env->cr[4]);
180     
181     if (flags & X86_DUMP_CCOP) {
182         if ((unsigned)env->cc_op < CC_OP_NB)
183             strcpy(cc_op_name, cc_op_str[env->cc_op]);
184         else
185             snprintf(cc_op_name, sizeof(cc_op_name), "[%d]", env->cc_op);
186         fprintf(f, "CCS=%08x CCD=%08x CCO=%-8s\n",
187                 env->cc_src, env->cc_dst, cc_op_name);
188     }
189     if (flags & X86_DUMP_FPU) {
190         fprintf(f, "ST0=%f ST1=%f ST2=%f ST3=%f\n", 
191                 (double)env->fpregs[0], 
192                 (double)env->fpregs[1], 
193                 (double)env->fpregs[2], 
194                 (double)env->fpregs[3]);
195         fprintf(f, "ST4=%f ST5=%f ST6=%f ST7=%f\n", 
196                 (double)env->fpregs[4], 
197                 (double)env->fpregs[5], 
198                 (double)env->fpregs[7], 
199                 (double)env->fpregs[8]);
200     }
201 }
202
203 /***********************************************************/
204 /* x86 mmu */
205 /* XXX: add PGE support */
206
207 void cpu_x86_set_a20(CPUX86State *env, int a20_state)
208 {
209     a20_state = (a20_state != 0);
210     if (a20_state != ((env->a20_mask >> 20) & 1)) {
211 #if defined(DEBUG_MMU)
212         printf("A20 update: a20=%d\n", a20_state);
213 #endif
214         /* if the cpu is currently executing code, we must unlink it and
215            all the potentially executing TB */
216         cpu_interrupt(env, 0);
217
218         /* when a20 is changed, all the MMU mappings are invalid, so
219            we must flush everything */
220         tlb_flush(env, 1);
221         env->a20_mask = 0xffefffff | (a20_state << 20);
222     }
223 }
224
225 void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0)
226 {
227     int pe_state;
228
229 #if defined(DEBUG_MMU)
230     printf("CR0 update: CR0=0x%08x\n", new_cr0);
231 #endif
232     if ((new_cr0 & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK)) !=
233         (env->cr[0] & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK))) {
234         tlb_flush(env, 1);
235     }
236     env->cr[0] = new_cr0;
237     
238     /* update PE flag in hidden flags */
239     pe_state = (env->cr[0] & CR0_PE_MASK);
240     env->hflags = (env->hflags & ~HF_PE_MASK) | (pe_state << HF_PE_SHIFT);
241     /* ensure that ADDSEG is always set in real mode */
242     env->hflags |= ((pe_state ^ 1) << HF_ADDSEG_SHIFT);
243 }
244
245 void cpu_x86_update_cr3(CPUX86State *env, uint32_t new_cr3)
246 {
247     env->cr[3] = new_cr3;
248     if (env->cr[0] & CR0_PG_MASK) {
249 #if defined(DEBUG_MMU)
250         printf("CR3 update: CR3=%08x\n", new_cr3);
251 #endif
252         tlb_flush(env, 0);
253     }
254 }
255
256 void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4)
257 {
258 #if defined(DEBUG_MMU)
259     printf("CR4 update: CR4=%08x\n", env->cr[4]);
260 #endif
261     if ((new_cr4 & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK)) !=
262         (env->cr[4] & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK))) {
263         tlb_flush(env, 1);
264     }
265     env->cr[4] = new_cr4;
266 }
267
268 /* XXX: also flush 4MB pages */
269 void cpu_x86_flush_tlb(CPUX86State *env, uint32_t addr)
270 {
271     tlb_flush_page(env, addr);
272 }
273
274 /* return value:
275    -1 = cannot handle fault 
276    0  = nothing more to do 
277    1  = generate PF fault
278    2  = soft MMU activation required for this block
279 */
280 int cpu_x86_handle_mmu_fault(CPUX86State *env, uint32_t addr, 
281                              int is_write, int is_user, int is_softmmu)
282 {
283     uint8_t *pde_ptr, *pte_ptr;
284     uint32_t pde, pte, virt_addr, ptep;
285     int error_code, is_dirty, prot, page_size, ret;
286     unsigned long paddr, vaddr, page_offset;
287     
288 #if defined(DEBUG_MMU)
289     printf("MMU fault: addr=0x%08x w=%d u=%d eip=%08x\n", 
290            addr, is_write, is_user, env->eip);
291 #endif
292
293     if (env->user_mode_only) {
294         /* user mode only emulation */
295         error_code = 0;
296         goto do_fault;
297     }
298
299     if (!(env->cr[0] & CR0_PG_MASK)) {
300         pte = addr;
301         virt_addr = addr & TARGET_PAGE_MASK;
302         prot = PROT_READ | PROT_WRITE;
303         page_size = 4096;
304         goto do_mapping;
305     }
306
307     /* page directory entry */
308     pde_ptr = phys_ram_base + 
309         (((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3)) & env->a20_mask);
310     pde = ldl_raw(pde_ptr);
311     if (!(pde & PG_PRESENT_MASK)) {
312         error_code = 0;
313         goto do_fault;
314     }
315     /* if PSE bit is set, then we use a 4MB page */
316     if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
317         if (is_user) {
318             if (!(pde & PG_USER_MASK))
319                 goto do_fault_protect;
320             if (is_write && !(pde & PG_RW_MASK))
321                 goto do_fault_protect;
322         } else {
323             if ((env->cr[0] & CR0_WP_MASK) && 
324                 is_write && !(pde & PG_RW_MASK)) 
325                 goto do_fault_protect;
326         }
327         is_dirty = is_write && !(pde & PG_DIRTY_MASK);
328         if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
329             pde |= PG_ACCESSED_MASK;
330             if (is_dirty)
331                 pde |= PG_DIRTY_MASK;
332             stl_raw(pde_ptr, pde);
333         }
334         
335         pte = pde & ~0x003ff000; /* align to 4MB */
336         ptep = pte;
337         page_size = 4096 * 1024;
338         virt_addr = addr & ~0x003fffff;
339     } else {
340         if (!(pde & PG_ACCESSED_MASK)) {
341             pde |= PG_ACCESSED_MASK;
342             stl_raw(pde_ptr, pde);
343         }
344
345         /* page directory entry */
346         pte_ptr = phys_ram_base + 
347             (((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask);
348         pte = ldl_raw(pte_ptr);
349         if (!(pte & PG_PRESENT_MASK)) {
350             error_code = 0;
351             goto do_fault;
352         }
353         /* combine pde and pte user and rw protections */
354         ptep = pte & pde;
355         if (is_user) {
356             if (!(ptep & PG_USER_MASK))
357                 goto do_fault_protect;
358             if (is_write && !(ptep & PG_RW_MASK))
359                 goto do_fault_protect;
360         } else {
361             if ((env->cr[0] & CR0_WP_MASK) &&
362                 is_write && !(ptep & PG_RW_MASK)) 
363                 goto do_fault_protect;
364         }
365         is_dirty = is_write && !(pte & PG_DIRTY_MASK);
366         if (!(pte & PG_ACCESSED_MASK) || is_dirty) {
367             pte |= PG_ACCESSED_MASK;
368             if (is_dirty)
369                 pte |= PG_DIRTY_MASK;
370             stl_raw(pte_ptr, pte);
371         }
372         page_size = 4096;
373         virt_addr = addr & ~0xfff;
374     }
375
376     /* the page can be put in the TLB */
377     prot = PROT_READ;
378     if (pte & PG_DIRTY_MASK) {
379         /* only set write access if already dirty... otherwise wait
380            for dirty access */
381         if (is_user) {
382             if (ptep & PG_RW_MASK)
383                 prot |= PROT_WRITE;
384         } else {
385             if (!(env->cr[0] & CR0_WP_MASK) ||
386                 (ptep & PG_RW_MASK))
387                 prot |= PROT_WRITE;
388         }
389     }
390
391  do_mapping:
392     pte = pte & env->a20_mask;
393
394     /* Even if 4MB pages, we map only one 4KB page in the cache to
395        avoid filling it too fast */
396     page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1);
397     paddr = (pte & TARGET_PAGE_MASK) + page_offset;
398     vaddr = virt_addr + page_offset;
399     
400     ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu);
401     return ret;
402  do_fault_protect:
403     error_code = PG_ERROR_P_MASK;
404  do_fault:
405     env->cr[2] = addr;
406     env->error_code = (is_write << PG_ERROR_W_BIT) | error_code;
407     if (is_user)
408         env->error_code |= PG_ERROR_U_MASK;
409     return 1;
410 }
411
412 #if defined(CONFIG_USER_ONLY) 
413 target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
414 {
415     return addr;
416 }
417 #else
418 target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
419 {
420     uint8_t *pde_ptr, *pte_ptr;
421     uint32_t pde, pte, paddr, page_offset, page_size;
422
423     if (!(env->cr[0] & CR0_PG_MASK)) {
424         pte = addr;
425         page_size = 4096;
426     } else {
427         /* page directory entry */
428         pde_ptr = phys_ram_base + 
429             (((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3)) & env->a20_mask);
430         pde = ldl_raw(pde_ptr);
431         if (!(pde & PG_PRESENT_MASK)) 
432             return -1;
433         if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
434             pte = pde & ~0x003ff000; /* align to 4MB */
435             page_size = 4096 * 1024;
436         } else {
437             /* page directory entry */
438             pte_ptr = phys_ram_base + 
439                 (((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask);
440             pte = ldl_raw(pte_ptr);
441             if (!(pte & PG_PRESENT_MASK))
442                 return -1;
443             page_size = 4096;
444         }
445     }
446     pte = pte & env->a20_mask;
447     page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1);
448     paddr = (pte & TARGET_PAGE_MASK) + page_offset;
449     return paddr;
450 }
451 #endif