// Copyright 2017 The Fuchsia Authors // // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT #include "constants_priv.h" #define IA32_GS_BASE 0xc0000101 #define X86_MSR_IA32_STAR 0xc0000081 #define X86_MSR_IA32_LSTAR 0xc0000082 #define X86_MSR_IA32_SYSENTER_CS 0x00000174 #define X86_MSR_IA32_SYSENTER_ESP 0x00000175 #define X86_MSR_IA32_SYSENTER_EIP 0x00000176 #define X86_MSR_IA32_EFER 0xc0000080 #define ABSOLUTE_ADDR(base, x) (x - base + GUEST_ENTRY) #define CODE_32_SELECTOR (1<<3) #define CODE_64_SELECTOR (2<<3) #define DATA_SELECTOR (3<<3) #define USER_CODE_32_SELECTOR (4<<3) #define USER_DATA_SELECTOR (5<<3) #define USER_CODE_64_SELECTOR (6<<3) #define CODE_16_SELECTOR (8<<3) #define INVALID_HYPERCALL 0xffffffff .text // Define new function 'name'. // Local label '1:' used by other macros to compute offsets. .macro FUNCTION name .global \name .type \name, STT_FUNC \name: 1: .endm .macro init_gdt start lgdt ABSOLUTE_ADDR(\start, gdtr_for_\start) mov $GUEST_ENTRY + 0x1000, %rsp jmp end_of_init_gdt_for_\start .align 8 gdt_for_\start: // Null entry. .8byte 0 // CODE_32_SELECTOR .2byte 0xffff // Limit 15:00 .2byte 0x0000 // Base 15:00 .byte 0x00 // Base 23:16 .byte 0b10011010 // P(1) DPL(00) S(1) 1 C(0) R(1) A(0) .byte 0b11001111 // G(1) D(1) L(0) AVL(0) Limit 19:16 .byte 0x0 // Base 31:24 // CODE_64_SELECTOR .2byte 0xffff // Limit 15:00 .2byte 0x0000 // Base 15:00 .byte 0x00 // Base 23:16 .byte 0b10011010 // P(1) DPL(00) S(1) 1 C(0) R(1) A(0) .byte 0b10101111 // G(1) D(0) L(1) AVL(0) Limit 19:16 .byte 0x0 // Base 31:24 // DATA_SELECTOR .2byte 0xffff // Limit 15:00 .2byte 0x0000 // Base 15:00 .byte 0x00 // Base 23:16 .byte 0b10010010 // P(1) DPL(00) S(1) 0 E(0) W(1) A(0) .byte 0b11001111 // G(1) B(1) L(0) AVL(0) Limit 19:16 .byte 0x0 // Base 31:24 // USER_CODE_32_SELECTOR .2byte 0xffff // Limit 15:00 .2byte 0x0000 // Base 15:00 .byte 0x00 // Base 23:16 .byte 0b11111010 // P(1) DPL(11) S(1) 1 C(0) R(1) A(0) .byte 0b11001111 // G(1) D(1) L(0) AVL(0) Limit 19:16 .byte 0x0 // Base 31:24 // USER_DATA_SELECTOR .2byte 0xffff // Limit 15:00 .2byte 0x0000 // Base 15:00 .byte 0x00 // Base 23:16 .byte 0b11110010 // P(1) DPL(11) S(1) 0 E(0) W(1) A(0) .byte 0b11001111 // G(1) B(1) 0 0 limit 19:16 .byte 0x0 // Base 31:24 // USER_CODE_64_SELECTOR .2byte 0xffff // Limit 15:00 .2byte 0x0000 // Base 15:00 .byte 0x00 // Base 23:16 .byte 0b11111010 // P(1) DPL(11) S(1) 1 C(0) R(1) A(0) .byte 0b10101111 // G(1) D(0) L(1) AVL(0) Limit 19:16 .byte 0x0 // Base 31:24 // USER_DATA_SELECTOR duplicate for sysexit .2byte 0xffff // Limit 15:00 .2byte 0x0000 // Base 15:00 .byte 0x00 // Base 23:16 .byte 0b11110010 // P(1) DPL(11) S(1) 0 E(0) W(1) A(0) .byte 0b11001111 // G(1) B(1) 0 0 limit 19:16 .byte 0x0 // Base 31:24 // CODE_16_SELECTOR .2byte 0xffff // Limit 15:00 .2byte 0x0000 // Base 15:00 .byte 0x00 // Base 23:16 .byte 0b10011010 // P(1) DPL(00) S(1) 1 C(0) R(1) A(0) .byte 0b10001111 // G(1) D(0) L(0) AVL(0) Limit 19:16 .byte 0x0 // Base 31:24 gdtr_for_\start: .2byte gdtr_for_\start - gdt_for_\start - 1 .8byte ABSOLUTE_ADDR(\start, gdt_for_\start) end_of_init_gdt_for_\start: .endm .macro debug_isr start, n int_\n\()_for_\start: mov $\n, %rax popq %rbx // Error code popq %rcx // RIP movq $-1, (EXIT_TEST_ADDR) .endm .macro debug_idt start, n .2byte ABSOLUTE_ADDR(\start, \ int_\n\()_for_\start) // Offset 15:00 .2byte CODE_64_SELECTOR // Segment selector .byte 0 // IST(000) .byte 0b10001110 // P(1) DPL(00) 0 Type(1110) .2byte 0 // Offset 31:16 .4byte 0 // Offset 63:32 .4byte 0 // Reserved .endm .macro init_interrupt_handling start init_gdt \start lidt ABSOLUTE_ADDR(\start, idtr_for_\start) mov $GUEST_ENTRY + 0x1000, %rsp jmp end_of_\start .irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, \ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 debug_isr \start \n .endr int32_for_\start: movq $0, (EXIT_TEST_ADDR) .align 8 idt_for_\start: .irp n, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, \ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 debug_idt \start \n .endr .2byte ABSOLUTE_ADDR(\start, int32_for_\start) // Offset 15:00 .2byte CODE_64_SELECTOR // Segment selector .byte 0 // IST(000) .byte 0b10001110 // P(1) DPL(00) 0 Type(1110) .2byte 0 // Offset 31:16 .4byte 0 // Offset 63:32 .4byte 0 // Reserved idtr_for_\start: .2byte idtr_for_\start - idt_for_\start - 1 // Limit .8byte ABSOLUTE_ADDR(\start, idt_for_\start) // Address end_of_\start: .endm // Test vcpu_resume. FUNCTION vcpu_resume_start // Test that we do not exit on load/store of CR3. mov %cr3, %rax mov %rax, %cr3 // Test that we do not exit on store of GS_BASE. xor %eax, %eax xor %edx, %edx mov $IA32_GS_BASE, %ecx wrmsr // Test that we handle CPUID instruction correctly. xor %eax, %eax cpuid movq $0, (EXIT_TEST_ADDR) FUNCTION vcpu_resume_end // Test vcpu_interrupt. FUNCTION vcpu_interrupt_start init_interrupt_handling vcpu_interrupt_start sti jmp . FUNCTION vcpu_interrupt_end // Test guest HLT instruction handling. FUNCTION vcpu_hlt_start init_interrupt_handling vcpu_hlt_start sti hlt FUNCTION vcpu_hlt_end // Test that pause exiting works correctly. FUNCTION vcpu_pause_start pause movq $0, (EXIT_TEST_ADDR) FUNCTION vcpu_pause_end // Test that reading and writing cr0 works correctly. FUNCTION vcpu_write_cr0_start // Read cr0. mov %cr0, %rax // Write cr0 and negate the NE bit. and $~X86_CR0_NE, %rax mov %rax, %cr0 // Test cr0 masking. NE bit should be set. mov %cr0, %rax movq $0, (EXIT_TEST_ADDR) FUNCTION vcpu_write_cr0_end // Test vcpu_read_state and vcpu_write_state. FUNCTION vcpu_read_write_state_start add $1, %rax add $2, %rcx add $3, %rdx add $4, %rbx add $5, %rsp add $6, %rbp add $7, %rsi add $8, %rdi add $9, %r8 add $10, %r9 add $11, %r10 add $12, %r11 add $13, %r12 add $14, %r13 add $15, %r14 add $16, %r15 stc // Set carry flag (bit 0) stac // Set AC flag (bit 18) movq $0, (EXIT_TEST_ADDR) FUNCTION vcpu_read_write_state_end // Test 32 bit compatibility mode (long mode disabled). FUNCTION vcpu_compat_mode_start init_gdt vcpu_compat_mode_start movq $0, %rbx movq $0, %rcx // Drop into compatibility mode pushq $CODE_32_SELECTOR lea vcpu_compat_mode_code(%rip),%rax pushq %rax lretq .code32 vcpu_compat_mode_code: // Hack to check if we're x86 or x64. In x86, 0x41 is `inc %ecx` and loop // falls through. In x64, 0x41 is the REX.B prefix and %ecx is not // incremented so loop jmps to vcpu_compat_mode_exit. xor %ecx,%ecx .byte 0x41 // x86: inc %ecx; x64: rex.B prefix loop vcpu_compat_mode_exit // Write a value to ebx to check in guest.cpp mov $1,%ebx // Go back to long mode pushl $CODE_64_SELECTOR pushl $ABSOLUTE_ADDR(vcpu_compat_mode_start, vcpu_compat_mode_test16) lret .code64 vcpu_compat_mode_test16: // Drop into compatibility mode with 16 bit default operands. pushq $CODE_16_SELECTOR lea vcpu_compat_mode_code16(%rip),%rax pushq %rax lretq .code32 vcpu_compat_mode_code16: // The default operand size should be 16 bits and push should subtract 2 // from the stack pointer. mov %esp,%eax sub $2,%eax push $0 cmp %esp,%eax pop %eax jne vcpu_compat_mode_code16_done // Write a value to ebx to check in guest.cpp mov $2,%ecx // Go back to long mode vcpu_compat_mode_code16_done: pushl $CODE_64_SELECTOR pushl $ABSOLUTE_ADDR(vcpu_compat_mode_start, vcpu_compat_mode_done) lret .code64 vcpu_compat_mode_done: // Fix the data segment selectors mov $DATA_SELECTOR,%rax mov %rax,%ds mov %rax,%es mov %rax,%fs mov %rax,%gs mov %rax,%ss vcpu_compat_mode_exit: movq $0, (EXIT_TEST_ADDR) FUNCTION vcpu_compat_mode_end FUNCTION vcpu_syscall_start init_interrupt_handling vcpu_syscall_start xor %eax, %eax mov $((USER_CODE_32_SELECTOR << 16) | CODE_64_SELECTOR), %edx mov $X86_MSR_IA32_STAR, %ecx wrmsr xor %edx, %edx mov $ABSOLUTE_ADDR(vcpu_syscall_start, vcpu_syscall_done), %eax mov $X86_MSR_IA32_LSTAR, %ecx wrmsr pushfq pop %r11 mov $ABSOLUTE_ADDR(vcpu_syscall_start, vcpu_syscall_ring3), %rcx sysretq vcpu_syscall_ring3: syscall vcpu_syscall_done: movq $0, (EXIT_TEST_ADDR) FUNCTION vcpu_syscall_end FUNCTION vcpu_sysenter_start init_interrupt_handling vcpu_sysenter_start xor %edx, %edx mov $CODE_64_SELECTOR, %eax mov $X86_MSR_IA32_SYSENTER_CS, %ecx wrmsr mov $ABSOLUTE_ADDR(vcpu_sysenter_start, vcpu_sysenter_done), %eax mov $X86_MSR_IA32_SYSENTER_EIP, %ecx wrmsr mov %esp, %eax mov $X86_MSR_IA32_SYSENTER_ESP, %ecx wrmsr mov %rsp, %rdx mov $ABSOLUTE_ADDR(vcpu_sysenter_start, vcpu_sysenter_ring3), %rdx .byte 0x48 // rex.W sysexit vcpu_sysenter_ring3: sysenter vcpu_sysenter_done: movq $0, (EXIT_TEST_ADDR) FUNCTION vcpu_sysenter_end FUNCTION vcpu_sysenter_compat_start init_interrupt_handling vcpu_sysenter_compat_start xor %edx, %edx mov $CODE_64_SELECTOR, %eax mov $X86_MSR_IA32_SYSENTER_CS, %ecx wrmsr mov $ABSOLUTE_ADDR(vcpu_sysenter_compat_start, vcpu_sysenter_compat_done), %eax mov $X86_MSR_IA32_SYSENTER_EIP, %ecx wrmsr mov %esp, %eax mov $X86_MSR_IA32_SYSENTER_ESP, %ecx wrmsr mov %rsp, %rdx mov $ABSOLUTE_ADDR(vcpu_sysenter_compat_start, vcpu_sysenter_compat_ring3), %rdx sysexit .code32 vcpu_sysenter_compat_ring3: sysenter .code64 vcpu_sysenter_compat_done: movq $0, (EXIT_TEST_ADDR) FUNCTION vcpu_sysenter_compat_end // Test guest_set_trap using a memory-based trap. FUNCTION guest_set_trap_start movq $0, (TRAP_ADDR) movq $0, (EXIT_TEST_ADDR) FUNCTION guest_set_trap_end // Test guest_set_trap using an IO-based trap. FUNCTION guest_set_trap_with_io_start out %al, $TRAP_PORT movq $0, (EXIT_TEST_ADDR) FUNCTION guest_set_trap_with_io_end FUNCTION vcpu_vmcall_start movq $(INVALID_HYPERCALL), %rax vmcall movq $0, (EXIT_TEST_ADDR) FUNCTION vcpu_vmcall_end