186 lines
4.8 KiB
C
186 lines
4.8 KiB
C
/*
|
|
* Copyright (C) 2016 Veertu Inc,
|
|
* Copyright (C) 2017 Google Inc,
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* 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 program 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 program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
|
|
#include "cpu.h"
|
|
#include "qemu-common.h"
|
|
#include "x86_decode.h"
|
|
#include "x86_emu.h"
|
|
#include "vmcs.h"
|
|
#include "vmx.h"
|
|
#include "x86_mmu.h"
|
|
#include "x86_descr.h"
|
|
|
|
/* static uint32_t x86_segment_access_rights(struct x86_segment_descriptor *var)
|
|
{
|
|
uint32_t ar;
|
|
|
|
if (!var->p) {
|
|
ar = 1 << 16;
|
|
return ar;
|
|
}
|
|
|
|
ar = var->type & 15;
|
|
ar |= (var->s & 1) << 4;
|
|
ar |= (var->dpl & 3) << 5;
|
|
ar |= (var->p & 1) << 7;
|
|
ar |= (var->avl & 1) << 12;
|
|
ar |= (var->l & 1) << 13;
|
|
ar |= (var->db & 1) << 14;
|
|
ar |= (var->g & 1) << 15;
|
|
return ar;
|
|
}*/
|
|
|
|
bool x86_read_segment_descriptor(struct CPUState *cpu,
|
|
struct x86_segment_descriptor *desc,
|
|
x68_segment_selector sel)
|
|
{
|
|
target_ulong base;
|
|
uint32_t limit;
|
|
|
|
memset(desc, 0, sizeof(*desc));
|
|
|
|
/* valid gdt descriptors start from index 1 */
|
|
if (!sel.index && GDT_SEL == sel.ti) {
|
|
return false;
|
|
}
|
|
|
|
if (GDT_SEL == sel.ti) {
|
|
base = rvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_BASE);
|
|
limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_LIMIT);
|
|
} else {
|
|
base = rvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_BASE);
|
|
limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_LIMIT);
|
|
}
|
|
|
|
if (sel.index * 8 >= limit) {
|
|
return false;
|
|
}
|
|
|
|
vmx_read_mem(cpu, desc, base + sel.index * 8, sizeof(*desc));
|
|
return true;
|
|
}
|
|
|
|
bool x86_write_segment_descriptor(struct CPUState *cpu,
|
|
struct x86_segment_descriptor *desc,
|
|
x68_segment_selector sel)
|
|
{
|
|
target_ulong base;
|
|
uint32_t limit;
|
|
|
|
if (GDT_SEL == sel.ti) {
|
|
base = rvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_BASE);
|
|
limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_LIMIT);
|
|
} else {
|
|
base = rvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_BASE);
|
|
limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_LIMIT);
|
|
}
|
|
|
|
if (sel.index * 8 >= limit) {
|
|
printf("%s: gdt limit\n", __func__);
|
|
return false;
|
|
}
|
|
vmx_write_mem(cpu, base + sel.index * 8, desc, sizeof(*desc));
|
|
return true;
|
|
}
|
|
|
|
bool x86_read_call_gate(struct CPUState *cpu, struct x86_call_gate *idt_desc,
|
|
int gate)
|
|
{
|
|
target_ulong base = rvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_BASE);
|
|
uint32_t limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_LIMIT);
|
|
|
|
memset(idt_desc, 0, sizeof(*idt_desc));
|
|
if (gate * 8 >= limit) {
|
|
printf("%s: idt limit\n", __func__);
|
|
return false;
|
|
}
|
|
|
|
vmx_read_mem(cpu, idt_desc, base + gate * 8, sizeof(*idt_desc));
|
|
return true;
|
|
}
|
|
|
|
bool x86_is_protected(struct CPUState *cpu)
|
|
{
|
|
uint64_t cr0 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0);
|
|
return cr0 & CR0_PE;
|
|
}
|
|
|
|
bool x86_is_real(struct CPUState *cpu)
|
|
{
|
|
return !x86_is_protected(cpu);
|
|
}
|
|
|
|
bool x86_is_v8086(struct CPUState *cpu)
|
|
{
|
|
X86CPU *x86_cpu = X86_CPU(cpu);
|
|
CPUX86State *env = &x86_cpu->env;
|
|
return x86_is_protected(cpu) && (env->eflags & VM_MASK);
|
|
}
|
|
|
|
bool x86_is_long_mode(struct CPUState *cpu)
|
|
{
|
|
return rvmcs(cpu->hvf_fd, VMCS_GUEST_IA32_EFER) & MSR_EFER_LMA;
|
|
}
|
|
|
|
bool x86_is_long64_mode(struct CPUState *cpu)
|
|
{
|
|
struct vmx_segment desc;
|
|
vmx_read_segment_descriptor(cpu, &desc, R_CS);
|
|
|
|
return x86_is_long_mode(cpu) && ((desc.ar >> 13) & 1);
|
|
}
|
|
|
|
bool x86_is_paging_mode(struct CPUState *cpu)
|
|
{
|
|
uint64_t cr0 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0);
|
|
return cr0 & CR0_PG;
|
|
}
|
|
|
|
bool x86_is_pae_enabled(struct CPUState *cpu)
|
|
{
|
|
uint64_t cr4 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR4);
|
|
return cr4 & CR4_PAE;
|
|
}
|
|
|
|
target_ulong linear_addr(struct CPUState *cpu, target_ulong addr, X86Seg seg)
|
|
{
|
|
return vmx_read_segment_base(cpu, seg) + addr;
|
|
}
|
|
|
|
target_ulong linear_addr_size(struct CPUState *cpu, target_ulong addr, int size,
|
|
X86Seg seg)
|
|
{
|
|
switch (size) {
|
|
case 2:
|
|
addr = (uint16_t)addr;
|
|
break;
|
|
case 4:
|
|
addr = (uint32_t)addr;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return linear_addr(cpu, addr, seg);
|
|
}
|
|
|
|
target_ulong linear_rip(struct CPUState *cpu, target_ulong rip)
|
|
{
|
|
return linear_addr(cpu, rip, R_CS);
|
|
}
|