/* * 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 . */ #include "qemu/osdep.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) { addr_t base; uint32_t limit; ZERO_INIT(*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) { addr_t 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) { addr_t base = rvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_BASE); uint32_t limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_LIMIT); ZERO_INIT(*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) && (RFLAGS(env) & RFLAGS_VM); } 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; } addr_t linear_addr(struct CPUState *cpu, addr_t addr, X86Seg seg) { return vmx_read_segment_base(cpu, seg) + addr; } addr_t linear_addr_size(struct CPUState *cpu, addr_t 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); } addr_t linear_rip(struct CPUState *cpu, addr_t rip) { return linear_addr(cpu, rip, R_CS); }