mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-07 09:43:56 -06:00
target/i386: emulate 64-bit ring 0 for linux-user if LM feature is set
32-bit binaries can run on a long mode processor even if the kernel is 64-bit, of course, and this can have slightly different behavior; for example, SYSCALL is allowed on Intel processors. Allow reporting LM to programs running under user mode emulation, so that "-cpu" can be used with named CPU models even for qemu-i386 and even without disabling LM by hand. Fortunately, most of the runtime code in QEMU has to depend on HF_LMA_MASK or on HF_CS64_MASK (which is anyway false for qemu-i386's 32-bit code segment) rather than TARGET_X86_64, therefore all that is needed is an update of linux-user's ring 0 setup. Fixes: https://gitlab.com/qemu-project/qemu/-/issues/1534 Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
d903259dd2
commit
40a205da41
3 changed files with 44 additions and 34 deletions
|
@ -47,7 +47,7 @@ static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
|||
}
|
||||
|
||||
static uint64_t *idt_table;
|
||||
#ifdef TARGET_X86_64
|
||||
|
||||
static void set_gate64(void *ptr, unsigned int type, unsigned int dpl,
|
||||
uint64_t addr, unsigned int sel)
|
||||
{
|
||||
|
@ -60,8 +60,10 @@ static void set_gate64(void *ptr, unsigned int type, unsigned int dpl,
|
|||
p[2] = tswap32(addr >> 32);
|
||||
p[3] = 0;
|
||||
}
|
||||
|
||||
#ifdef TARGET_X86_64
|
||||
/* only dpl matters as we do only user space emulation */
|
||||
static void set_idt(int n, unsigned int dpl)
|
||||
static void set_idt(int n, unsigned int dpl, bool is64)
|
||||
{
|
||||
set_gate64(idt_table + n * 2, 0, dpl, 0, 0);
|
||||
}
|
||||
|
@ -78,9 +80,13 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
|
|||
}
|
||||
|
||||
/* only dpl matters as we do only user space emulation */
|
||||
static void set_idt(int n, unsigned int dpl)
|
||||
static void set_idt(int n, unsigned int dpl, bool is64)
|
||||
{
|
||||
set_gate(idt_table + n, 0, dpl, 0, 0);
|
||||
if (is64) {
|
||||
set_gate64(idt_table + n * 2, 0, dpl, 0, 0);
|
||||
} else {
|
||||
set_gate(idt_table + n, 0, dpl, 0, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -325,6 +331,9 @@ static void target_cpu_free(void *obj)
|
|||
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
|
||||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
bool is64 = (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) != 0;
|
||||
int i;
|
||||
|
||||
OBJECT(cpu)->free = target_cpu_free;
|
||||
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
|
||||
env->hflags |= HF_PE_MASK | HF_CPL_MASK;
|
||||
|
@ -332,15 +341,18 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
|
|||
env->cr[4] |= CR4_OSFXSR_MASK;
|
||||
env->hflags |= HF_OSFXSR_MASK;
|
||||
}
|
||||
#ifndef TARGET_ABI32
|
||||
|
||||
/* enable 64 bit mode if possible */
|
||||
if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) {
|
||||
if (is64) {
|
||||
env->cr[4] |= CR4_PAE_MASK;
|
||||
env->efer |= MSR_EFER_LMA | MSR_EFER_LME;
|
||||
env->hflags |= HF_LMA_MASK;
|
||||
}
|
||||
#ifndef TARGET_ABI32
|
||||
else {
|
||||
fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
env->cr[4] |= CR4_PAE_MASK;
|
||||
env->efer |= MSR_EFER_LMA | MSR_EFER_LME;
|
||||
env->hflags |= HF_LMA_MASK;
|
||||
#endif
|
||||
|
||||
/* flags setup : we activate the IRQs by default as in user mode */
|
||||
|
@ -379,27 +391,12 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
|
|||
PROT_READ|PROT_WRITE,
|
||||
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
idt_table = g2h_untagged(env->idt.base);
|
||||
set_idt(0, 0);
|
||||
set_idt(1, 0);
|
||||
set_idt(2, 0);
|
||||
set_idt(3, 3);
|
||||
set_idt(4, 3);
|
||||
set_idt(5, 0);
|
||||
set_idt(6, 0);
|
||||
set_idt(7, 0);
|
||||
set_idt(8, 0);
|
||||
set_idt(9, 0);
|
||||
set_idt(10, 0);
|
||||
set_idt(11, 0);
|
||||
set_idt(12, 0);
|
||||
set_idt(13, 0);
|
||||
set_idt(14, 0);
|
||||
set_idt(15, 0);
|
||||
set_idt(16, 0);
|
||||
set_idt(17, 0);
|
||||
set_idt(18, 0);
|
||||
set_idt(19, 0);
|
||||
set_idt(0x80, 3);
|
||||
for (i = 0; i < 20; i++) {
|
||||
set_idt(i, 0, is64);
|
||||
}
|
||||
set_idt(3, 3, is64);
|
||||
set_idt(4, 3, is64);
|
||||
set_idt(0x80, 3, is64);
|
||||
|
||||
/* linux segment setup */
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue