helper.c revision 178d85b8274f9ac82fb553c80760bbbb4044401c
/*
* i386 helpers (without register variable usage)
*
* Copyright (c) 2003 Fabrice Bellard
*
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
* other than GPL or LGPL is available it will apply instead, Oracle elects to use only
* the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
* a choice of LGPL license versions is made available with the language indicating
* that LGPLv2 or any later version may be used, or where a choice of which version
* of the LGPL is applied is otherwise unspecified.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef VBOX
# include <inttypes.h>
# include <signal.h>
# include <assert.h>
#endif
#include "cpu.h"
#include "exec-all.h"
#include "svm.h"
#include "qemu-common.h"
//#define DEBUG_MMU
#ifndef VBOX
{
int i;
/* feature flags taken from "Intel Processor Identification and the CPUID
* Instruction" and AMD's "CPUID Specification". In cases of disagreement
* about feature names, the Linux name is used. */
static const char *feature_name[] = {
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
"pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */, NULL, "ds" /* Intel dts */, "acpi", "mmx",
"fxsr", "sse", "sse2", "ss", "ht" /* Intel htt */, "tm", "ia64", "pbe",
};
static const char *ext_feature_name[] = {
};
static const char *ext2_feature_name[] = {
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
"fxsr", "fxsr_opt" /* AMD ffxsr */, "pdpe1gb" /* AMD Page1GB */, "rdtscp", NULL, "lm" /* Intel 64 */, "3dnowext", "3dnow",
};
static const char *ext3_feature_name[] = {
"lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, "cr8legacy" /* AMD AltMovCr8 */, "abm", "sse4a", "misalignsse",
};
for ( i = 0 ; i < 32 ; i++ )
*features |= 1 << i;
return;
}
for ( i = 0 ; i < 32 ; i++ )
*ext_features |= 1 << i;
return;
}
for ( i = 0 ; i < 32 ; i++ )
*ext2_features |= 1 << i;
return;
}
for ( i = 0 ; i < 32 ; i++ )
*ext3_features |= 1 << i;
return;
}
}
#endif /* !VBOX */
#ifndef VBOX
#else
#endif
{
#ifndef VBOX
#endif
static int inited;
#ifndef VBOX
if (!env)
return NULL;
#endif
/* init various static tables */
if (!inited) {
inited = 1;
}
return NULL;
}
#ifdef USE_KQEMU
#endif
return env;
}
typedef struct x86_def_t {
const char *name;
int family;
int model;
int stepping;
char model_id[48];
} x86_def_t;
#ifndef VBOX
#ifdef TARGET_X86_64
{
.name = "qemu64",
.level = 2,
.family = 6,
.model = 2,
.stepping = 3,
.features = PPRO_FEATURES |
/* these features are needed for Win64 and aren't fully implemented */
/* this feature is needed for Solaris and isn't fully implemented */
.xlevel = 0x8000000A,
},
{
.name = "core2duo",
.level = 10,
.family = 6,
.model = 15,
.stepping = 11,
/* The original CPU also implements these features:
CPUID_VME, CPUID_DTS, CPUID_ACPI, CPUID_SS, CPUID_HT,
CPUID_TM, CPUID_PBE */
.features = PPRO_FEATURES |
/* The original CPU also implements these ext features:
CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_EST,
CPUID_EXT_TM2, CPUID_EXT_CX16, CPUID_EXT_XTPR, CPUID_EXT_PDCM */
/* Missing: .ext3_features = CPUID_EXT3_LAHF_LM */
.xlevel = 0x80000008,
.model_id = "Intel(R) Core(TM)2 Duo CPU T7700 @ 2.40GHz",
},
#endif
{
.name = "qemu32",
.level = 2,
.family = 6,
.model = 3,
.stepping = 3,
.xlevel = 0,
},
{
.name = "coreduo",
.level = 10,
.family = 6,
.model = 14,
.stepping = 8,
/* The original CPU also implements these features:
CPUID_DTS, CPUID_ACPI, CPUID_SS, CPUID_HT,
CPUID_TM, CPUID_PBE */
/* The original CPU also implements these ext features:
CPUID_EXT_VMX, CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_XTPR,
CPUID_EXT_PDCM */
.xlevel = 0x80000008,
.model_id = "Genuine Intel(R) CPU T2600 @ 2.16GHz",
},
{
.name = "486",
.level = 0,
.family = 4,
.model = 0,
.stepping = 0,
.xlevel = 0,
},
{
.name = "pentium",
.level = 1,
.family = 5,
.model = 4,
.stepping = 3,
.xlevel = 0,
},
{
.name = "pentium2",
.level = 2,
.family = 6,
.model = 5,
.stepping = 2,
.xlevel = 0,
},
{
.name = "pentium3",
.level = 2,
.family = 6,
.model = 7,
.stepping = 3,
.xlevel = 0,
},
{
.name = "athlon",
.level = 2,
.family = 6,
.model = 2,
.stepping = 3,
.ext2_features = (PPRO_FEATURES & 0x0183F3FF) | CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT,
.xlevel = 0x80000008,
/* XXX: put another string ? */
},
{
.name = "n270",
/* original is on level 10 */
.level = 5,
.family = 6,
.model = 28,
.stepping = 2,
.features = PPRO_FEATURES |
/* Missing: CPUID_DTS | CPUID_ACPI | CPUID_SS |
* CPUID_HT | CPUID_TM | CPUID_PBE */
/* Some CPUs got no CPUID_SEP */
/* Missing: CPUID_EXT_DSCPL | CPUID_EXT_EST |
* CPUID_EXT_TM2 | CPUID_EXT_XTPR */
/* Missing: .ext3_features = CPUID_EXT3_LAHF_LM */
.xlevel = 0x8000000A,
.model_id = "Intel(R) Atom(TM) CPU N270 @ 1.60GHz",
},
};
{
unsigned int i;
uint32_t minus_features = 0, minus_ext_features = 0, minus_ext2_features = 0, minus_ext3_features = 0;
break;
}
}
if (!def)
goto error;
while (featurestr) {
char *val;
if (featurestr[0] == '+') {
add_flagname_to_bitmaps(featurestr + 1, &plus_features, &plus_ext_features, &plus_ext2_features, &plus_ext3_features);
} else if (featurestr[0] == '-') {
add_flagname_to_bitmaps(featurestr + 1, &minus_features, &minus_ext_features, &minus_ext2_features, &minus_ext3_features);
char *err;
goto error;
}
char *err;
goto error;
}
char *err;
goto error;
}
goto error;
}
x86_cpu_def->vendor1 = 0;
x86_cpu_def->vendor2 = 0;
x86_cpu_def->vendor3 = 0;
for(i = 0; i < 4; i++) {
}
val);
} else {
goto error;
}
} else {
goto error;
}
}
free(s);
return 0;
free(s);
return -1;
}
{
unsigned int i;
}
#endif /* !VBOX */
{
#ifndef VBOX
return -1;
} else {
}
{
int c, len, i;
if (!model_id)
model_id = "";
for(i = 0; i < 48; i++) {
if (i >= len)
c = '\0';
else
}
}
#endif /* !VBOX */
return 0;
}
/* NOTE: must be called outside the CPU execute loop */
{
int i;
/* init to reset state */
#ifdef CONFIG_SOFTMMU
#endif
#ifndef VBOX
#else
/** @todo: is it right? */
#endif
/* FPU init */
for(i = 0;i < 8; i++)
}
{
#ifndef VBOX
#endif
}
/***********************************************************/
/* x86 debug */
static const char *cc_op_str[] = {
"DYNAMIC",
"EFLAGS",
"MULB",
"MULW",
"MULL",
"MULQ",
"ADDB",
"ADDW",
"ADDL",
"ADDQ",
"ADCB",
"ADCW",
"ADCL",
"ADCQ",
"SUBB",
"SUBW",
"SUBL",
"SUBQ",
"SBBB",
"SBBW",
"SBBL",
"SBBQ",
"LOGICB",
"LOGICW",
"LOGICL",
"LOGICQ",
"INCB",
"INCW",
"INCL",
"INCQ",
"DECB",
"DECW",
"DECL",
"DECQ",
"SHLB",
"SHLW",
"SHLL",
"SHLQ",
"SARB",
"SARW",
"SARL",
"SARQ",
};
int flags)
{
char cc_op_name[32];
#ifdef TARGET_X86_64
cpu_fprintf(f,
} else
#endif
{
cpu_fprintf(f, "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
"EIP=%08x EFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d SMM=%d HLT=%d\n",
}
#ifdef TARGET_X86_64
for(i = 0; i < 6; i++) {
seg_name[i],
}
} else
#endif
{
for(i = 0; i < 6; i++) {
cpu_fprintf(f, "%s =%04x %08x %08x %08x\n",
seg_name[i],
}
cpu_fprintf(f, "LDT=%04x %08x %08x %08x\n",
cpu_fprintf(f, "TR =%04x %08x %08x %08x\n",
cpu_fprintf(f, "GDT= %08x %08x\n",
cpu_fprintf(f, "IDT= %08x %08x\n",
cpu_fprintf(f, "CR0=%08x CR2=%08x CR3=%08x CR4=%08x\n",
}
if (flags & X86_DUMP_CCOP) {
else
#ifdef TARGET_X86_64
} else
#endif
{
cpu_fprintf(f, "CCS=%08x CCD=%08x CCO=%-8s\n",
}
}
if (flags & X86_DUMP_FPU) {
int fptag;
fptag = 0;
for(i = 0; i < 8; i++) {
}
cpu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n",
for(i=0;i<8;i++) {
#if defined(USE_X86LDOUBLE)
union {
long double d;
struct {
} l;
} tmp;
#else
#endif
if ((i & 1) == 1)
cpu_fprintf(f, "\n");
else
cpu_fprintf(f, " ");
}
nb = 16;
else
nb = 8;
for(i=0;i<nb;i++) {
cpu_fprintf(f, "XMM%02d=%08x%08x%08x%08x",
i,
if ((i & 1) == 1)
cpu_fprintf(f, "\n");
else
cpu_fprintf(f, " ");
}
}
}
/***********************************************************/
/* x86 mmu */
/* XXX: add PGE support */
{
#if defined(DEBUG_MMU)
#endif
/* if the cpu is currently executing code, we must unlink it and
all the potentially executing TB */
/* when a20 is changed, all the MMU mappings are invalid, so
we must flush everything */
}
}
{
int pe_state;
#if defined(DEBUG_MMU)
#endif
}
#ifdef TARGET_X86_64
/* enter in long mode */
/* XXX: generate an exception */
return;
/* exit long mode */
}
#endif
/* update PE flag in hidden flags */
/* ensure that ADDSEG is always set in real mode */
/* update FPU flags */
#ifdef VBOX
#endif
}
/* XXX: in legacy PAE mode, generate a GPF if reserved bits are set in
the PDPT */
{
#if defined(DEBUG_MMU)
#endif
}
}
{
#if defined(DEBUG_MMU)
#endif
}
/* SSE handling */
new_cr4 &= ~CR4_OSFXSR_MASK;
if (new_cr4 & CR4_OSFXSR_MASK)
else
#ifdef VBOX
#endif
}
/* XXX: also flush 4MB pages */
{
}
#if defined(CONFIG_USER_ONLY)
{
/* user mode only emulation */
is_write &= 1;
return 1;
}
{
return addr;
}
#else
/* XXX: This value should match the one returned by CPUID
* and in exec.c */
#if defined(USE_KQEMU)
#define PHYS_ADDR_MASK 0xfffff000LL
#else
# if defined(TARGET_X86_64)
# define PHYS_ADDR_MASK 0xfffffff000LL
# else
# define PHYS_ADDR_MASK 0xffffff000LL
# endif
#endif
/* return value:
-1 = cannot handle fault
0 = nothing more to do
1 = generate PF fault
2 = soft MMU activation required for this block
*/
{
#if defined(DEBUG_MMU)
#endif
page_size = 4096;
goto do_mapping;
}
#ifdef TARGET_X86_64
/* test virtual address sign extension */
env->error_code = 0;
return 1;
}
if (!(pml4e & PG_PRESENT_MASK)) {
error_code = 0;
goto do_fault;
}
goto do_fault;
}
if (!(pml4e & PG_ACCESSED_MASK)) {
}
if (!(pdpe & PG_PRESENT_MASK)) {
error_code = 0;
goto do_fault;
}
goto do_fault;
}
if (!(pdpe & PG_ACCESSED_MASK)) {
pdpe |= PG_ACCESSED_MASK;
}
} else
#endif
{
/* XXX: load them when cr3 is loaded ? */
if (!(pdpe & PG_PRESENT_MASK)) {
error_code = 0;
goto do_fault;
}
}
if (!(pde & PG_PRESENT_MASK)) {
error_code = 0;
goto do_fault;
}
goto do_fault;
}
if (pde & PG_PSE_MASK) {
/* 2 MB page */
ptep ^= PG_NX_MASK;
goto do_fault_protect;
if (is_user) {
if (!(ptep & PG_USER_MASK))
goto do_fault_protect;
goto do_fault_protect;
} else {
goto do_fault_protect;
}
pde |= PG_ACCESSED_MASK;
if (is_dirty)
pde |= PG_DIRTY_MASK;
}
/* align to page_size */
} else {
/* 4 KB page */
if (!(pde & PG_ACCESSED_MASK)) {
pde |= PG_ACCESSED_MASK;
}
if (!(pte & PG_PRESENT_MASK)) {
error_code = 0;
goto do_fault;
}
goto do_fault;
}
/* combine pde and pte nx, user and rw protections */
ptep ^= PG_NX_MASK;
goto do_fault_protect;
if (is_user) {
if (!(ptep & PG_USER_MASK))
goto do_fault_protect;
goto do_fault_protect;
} else {
goto do_fault_protect;
}
pte |= PG_ACCESSED_MASK;
if (is_dirty)
pte |= PG_DIRTY_MASK;
}
page_size = 4096;
}
} else {
/* page directory entry */
if (!(pde & PG_PRESENT_MASK)) {
error_code = 0;
goto do_fault;
}
/* if PSE bit is set, then we use a 4MB page */
if (is_user) {
if (!(pde & PG_USER_MASK))
goto do_fault_protect;
goto do_fault_protect;
} else {
goto do_fault_protect;
}
pde |= PG_ACCESSED_MASK;
if (is_dirty)
pde |= PG_DIRTY_MASK;
}
} else {
if (!(pde & PG_ACCESSED_MASK)) {
pde |= PG_ACCESSED_MASK;
}
/* page directory entry */
if (!(pte & PG_PRESENT_MASK)) {
error_code = 0;
goto do_fault;
}
/* combine pde and pte user and rw protections */
if (is_user) {
if (!(ptep & PG_USER_MASK))
goto do_fault_protect;
goto do_fault_protect;
} else {
goto do_fault_protect;
}
pte |= PG_ACCESSED_MASK;
if (is_dirty)
pte |= PG_DIRTY_MASK;
}
page_size = 4096;
}
}
/* the page can be put in the TLB */
if (!(ptep & PG_NX_MASK))
if (pte & PG_DIRTY_MASK) {
/* only set write access if already dirty... otherwise wait
for dirty access */
if (is_user) {
if (ptep & PG_RW_MASK)
prot |= PAGE_WRITE;
} else {
(ptep & PG_RW_MASK))
prot |= PAGE_WRITE;
}
}
/* Even if 4MB pages, we map only one 4KB page in the cache to
avoid filling it too fast */
return ret;
if (is_user)
if (is_write1 == 2 &&
/* cr2 is not modified in case of exceptions */
addr);
} else {
}
return 1;
}
{
int page_size;
#ifdef TARGET_X86_64
/* test virtual address sign extension */
return -1;
if (!(pml4e & PG_PRESENT_MASK))
return -1;
if (!(pdpe & PG_PRESENT_MASK))
return -1;
} else
#endif
{
if (!(pdpe & PG_PRESENT_MASK))
return -1;
}
if (!(pde & PG_PRESENT_MASK)) {
return -1;
}
if (pde & PG_PSE_MASK) {
/* 2 MB page */
} else {
/* 4 KB page */
page_size = 4096;
}
if (!(pte & PG_PRESENT_MASK))
return -1;
} else {
page_size = 4096;
} else {
/* page directory entry */
if (!(pde & PG_PRESENT_MASK))
return -1;
} else {
/* page directory entry */
if (!(pte & PG_PRESENT_MASK))
return -1;
page_size = 4096;
}
}
}
return paddr;
}
#endif /* !CONFIG_USER_ONLY */