diff --git a/xen/arch/x86/ioport_emulate.c b/xen/arch/x86/ioport_emulate.c index 499c1f6056..cf1f3f9229 100644 --- a/xen/arch/x86/ioport_emulate.c +++ b/xen/arch/x86/ioport_emulate.c @@ -8,7 +8,10 @@ #include #include -static bool ioemul_handle_proliant_quirk( +unsigned int (*__read_mostly ioemul_handle_quirk)( + uint8_t opcode, char *io_emul_stub, struct cpu_user_regs *regs); + +static unsigned int ioemul_handle_proliant_quirk( u8 opcode, char *io_emul_stub, struct cpu_user_regs *regs) { static const char stub[] = { @@ -19,18 +22,17 @@ static bool ioemul_handle_proliant_quirk( 0xa8, 0x80, /* test $0x80, %al */ 0x75, 0xfb, /* jnz 1b */ 0x9d, /* popf */ - 0xc3, /* ret */ }; uint16_t port = regs->dx; uint8_t value = regs->al; if ( (opcode != 0xee) || (port != 0xcd4) || !(value & 0x80) ) - return false; + return 0; memcpy(io_emul_stub, stub, sizeof(stub)); BUILD_BUG_ON(IOEMUL_QUIRK_STUB_BYTES < sizeof(stub)); - return true; + return sizeof(stub); } /* This table is the set of system-specific I/O emulation hooks. */ diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c index 3b705299cf..fad6c754ad 100644 --- a/xen/arch/x86/pv/emul-priv-op.c +++ b/xen/arch/x86/pv/emul-priv-op.c @@ -47,51 +47,102 @@ struct priv_op_ctxt { unsigned int bpmatch; }; -/* I/O emulation support. Helper routines for, and type of, the stack stub. */ -void host_to_guest_gpr_switch(struct cpu_user_regs *); -unsigned long guest_to_host_gpr_switch(unsigned long); +/* I/O emulation helpers. Use non-standard calling conventions. */ +void nocall load_guest_gprs(struct cpu_user_regs *); +void nocall save_guest_gprs(void); typedef void io_emul_stub_t(struct cpu_user_regs *); static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, unsigned int port, unsigned int bytes) { + /* + * Construct a stub for IN/OUT emulation. + * + * Some platform drivers communicate with the SMM handler using GPRs as a + * mailbox. Therefore, we must perform the emulation with the hardware + * domain's registers in view. + * + * We write a stub of the following form, using the guest load/save + * helpers (non-standard ABI), and one of several possible stubs + * performing the real I/O. + */ + static const char prologue[] = { + 0x53, /* push %rbx */ + 0x55, /* push %rbp */ + 0x41, 0x54, /* push %r12 */ + 0x41, 0x55, /* push %r13 */ + 0x41, 0x56, /* push %r14 */ + 0x41, 0x57, /* push %r15 */ + 0x57, /* push %rdi (param for save_guest_gprs) */ + }; /* call load_guest_gprs */ + /* */ + /* call save_guest_gprs */ + static const char epilogue[] = { + 0x5f, /* pop %rdi */ + 0x41, 0x5f, /* pop %r15 */ + 0x41, 0x5e, /* pop %r14 */ + 0x41, 0x5d, /* pop %r13 */ + 0x41, 0x5c, /* pop %r12 */ + 0x5d, /* pop %rbp */ + 0x5b, /* pop %rbx */ + 0xc3, /* ret */ + }; + struct stubs *this_stubs = &this_cpu(stubs); unsigned long stub_va = this_stubs->addr + STUB_BUF_SIZE / 2; - long disp; - bool use_quirk_stub = false; + unsigned int quirk_bytes = 0; + char *p; + + /* Helpers - Read outer scope but only modify p. */ +#define APPEND_BUFF(b) ({ memcpy(p, b, sizeof(b)); p += sizeof(b); }) +#define APPEND_CALL(f) \ + ({ \ + long disp = (long)(f) - (stub_va + p - ctxt->io_emul_stub + 5); \ + BUG_ON((int32_t)disp != disp); \ + *p++ = 0xe8; \ + *(int32_t *)p = disp; p += 4; \ + }) if ( !ctxt->io_emul_stub ) ctxt->io_emul_stub = map_domain_page(_mfn(this_stubs->mfn)) + (stub_va & ~PAGE_MASK); - /* call host_to_guest_gpr_switch */ - ctxt->io_emul_stub[0] = 0xe8; - disp = (long)host_to_guest_gpr_switch - (stub_va + 5); - BUG_ON((int32_t)disp != disp); - *(int32_t *)&ctxt->io_emul_stub[1] = disp; + p = ctxt->io_emul_stub; + APPEND_BUFF(prologue); + APPEND_CALL(load_guest_gprs); + + /* Some platforms might need to quirk the stub for specific inputs. */ if ( unlikely(ioemul_handle_quirk) ) - use_quirk_stub = ioemul_handle_quirk(opcode, &ctxt->io_emul_stub[5], - ctxt->ctxt.regs); - - if ( !use_quirk_stub ) { - /* data16 or nop */ - ctxt->io_emul_stub[5] = (bytes != 2) ? 0x90 : 0x66; - /* */ - ctxt->io_emul_stub[6] = opcode; - /* imm8 or nop */ - ctxt->io_emul_stub[7] = !(opcode & 8) ? port : 0x90; - /* ret (jumps to guest_to_host_gpr_switch) */ - ctxt->io_emul_stub[8] = 0xc3; + quirk_bytes = ioemul_handle_quirk(opcode, p, ctxt->ctxt.regs); + p += quirk_bytes; } - BUILD_BUG_ON(STUB_BUF_SIZE / 2 < MAX(9, /* Default emul stub */ - 5 + IOEMUL_QUIRK_STUB_BYTES)); + /* Default I/O stub. */ + if ( likely(!quirk_bytes) ) + { + *p++ = (bytes != 2) ? 0x90 : 0x66; /* data16 or nop */ + *p++ = opcode; /* */ + *p++ = !(opcode & 8) ? port : 0x90; /* imm8 or nop */ + } + + APPEND_CALL(save_guest_gprs); + APPEND_BUFF(epilogue); + + /* Build-time best effort attempt to catch problems. */ + BUILD_BUG_ON(STUB_BUF_SIZE / 2 < + (sizeof(prologue) + sizeof(epilogue) + 10 /* 2x call */ + + MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES))); + /* Runtime confirmation that we haven't clobbered an adjacent stub. */ + BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub)); /* Handy function-typed pointer to the stub. */ return (void *)stub_va; + +#undef APPEND_CALL +#undef APPEND_BUFF } diff --git a/xen/arch/x86/pv/gpr_switch.S b/xen/arch/x86/pv/gpr_switch.S index 6d26192c2c..e7f5bfcd2d 100644 --- a/xen/arch/x86/pv/gpr_switch.S +++ b/xen/arch/x86/pv/gpr_switch.S @@ -9,59 +9,49 @@ #include -ENTRY(host_to_guest_gpr_switch) - movq (%rsp), %rcx - movq %rdi, (%rsp) +/* Load guest GPRs. Parameter in %rdi, clobbers all registers. */ +ENTRY(load_guest_gprs) movq UREGS_rdx(%rdi), %rdx - pushq %rbx movq UREGS_rax(%rdi), %rax movq UREGS_rbx(%rdi), %rbx - pushq %rbp movq UREGS_rsi(%rdi), %rsi movq UREGS_rbp(%rdi), %rbp - pushq %r12 - movq UREGS_r8(%rdi), %r8 + movq UREGS_r8 (%rdi), %r8 movq UREGS_r12(%rdi), %r12 - pushq %r13 - movq UREGS_r9(%rdi), %r9 + movq UREGS_r9 (%rdi), %r9 movq UREGS_r13(%rdi), %r13 - pushq %r14 movq UREGS_r10(%rdi), %r10 movq UREGS_r14(%rdi), %r14 - pushq %r15 movq UREGS_r11(%rdi), %r11 movq UREGS_r15(%rdi), %r15 - pushq %rcx /* dummy push, filled by guest_to_host_gpr_switch pointer */ - pushq %rcx - leaq guest_to_host_gpr_switch(%rip),%rcx - movq %rcx,8(%rsp) movq UREGS_rcx(%rdi), %rcx movq UREGS_rdi(%rdi), %rdi ret -ENTRY(guest_to_host_gpr_switch) + .size load_guest_gprs, . - load_guest_gprs + .type load_guest_gprs, STT_FUNC + + +/* Save guest GPRs. Parameter on the stack above the return address. */ +ENTRY(save_guest_gprs) pushq %rdi - movq 7*8(%rsp), %rdi + movq 2*8(%rsp), %rdi movq %rax, UREGS_rax(%rdi) - popq UREGS_rdi(%rdi) + popq UREGS_rdi(%rdi) movq %r15, UREGS_r15(%rdi) movq %r11, UREGS_r11(%rdi) - popq %r15 movq %r14, UREGS_r14(%rdi) movq %r10, UREGS_r10(%rdi) - popq %r14 movq %r13, UREGS_r13(%rdi) - movq %r9, UREGS_r9(%rdi) - popq %r13 + movq %r9, UREGS_r9 (%rdi) movq %r12, UREGS_r12(%rdi) - movq %r8, UREGS_r8(%rdi) - popq %r12 + movq %r8, UREGS_r8 (%rdi) movq %rbp, UREGS_rbp(%rdi) movq %rsi, UREGS_rsi(%rdi) - popq %rbp movq %rbx, UREGS_rbx(%rdi) movq %rdx, UREGS_rdx(%rdi) - popq %rbx movq %rcx, UREGS_rcx(%rdi) - popq %rcx ret + + .size save_guest_gprs, . - save_guest_gprs + .type save_guest_gprs, STT_FUNC diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index e8a0877344..a8300c214d 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -118,9 +118,6 @@ idt_entry_t *idt_tables[NR_CPUS] __read_mostly; */ DEFINE_PER_CPU_PAGE_ALIGNED(struct tss_page, tss_page); -bool (*ioemul_handle_quirk)( - u8 opcode, char *io_emul_stub, struct cpu_user_regs *regs); - static int debug_stack_lines = 20; integer_param("debug_stack_lines", debug_stack_lines); diff --git a/xen/include/asm-x86/io.h b/xen/include/asm-x86/io.h index 8708b79b99..5afe68239d 100644 --- a/xen/include/asm-x86/io.h +++ b/xen/include/asm-x86/io.h @@ -49,8 +49,8 @@ __OUT(w,"w",short) __OUT(l,,int) /* Function pointer used to handle platform specific I/O port emulation. */ -#define IOEMUL_QUIRK_STUB_BYTES 10 -extern bool (*ioemul_handle_quirk)( +#define IOEMUL_QUIRK_STUB_BYTES 9 +extern unsigned int (*ioemul_handle_quirk)( u8 opcode, char *io_emul_stub, struct cpu_user_regs *regs); #endif diff --git a/xen/include/xen/compiler.h b/xen/include/xen/compiler.h index 8c846261d2..c22439b7a4 100644 --- a/xen/include/xen/compiler.h +++ b/xen/include/xen/compiler.h @@ -20,6 +20,8 @@ #define __weak __attribute__((__weak__)) +#define nocall __attribute__((error("Nonstandard ABI"))) + #if (!defined(__clang__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 5)) #define unreachable() do {} while (1) #else