#include <xen/sched.h>
#include <xen/dmi.h>
-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[] = {
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. */
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 */
+ /* <I/O stub> */
+ /* 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);
+ {
+ quirk_bytes = ioemul_handle_quirk(opcode, p, ctxt->ctxt.regs);
+ p += quirk_bytes;
+ }
- if ( !use_quirk_stub )
+ /* Default I/O stub. */
+ if ( likely(!quirk_bytes) )
{
- /* data16 or nop */
- ctxt->io_emul_stub[5] = (bytes != 2) ? 0x90 : 0x66;
- /* <io-access opcode> */
- 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;
+ *p++ = (bytes != 2) ? 0x90 : 0x66; /* data16 or nop */
+ *p++ = opcode; /* <opcode> */
+ *p++ = !(opcode & 8) ? port : 0x90; /* imm8 or nop */
}
- BUILD_BUG_ON(STUB_BUF_SIZE / 2 < MAX(9, /* Default emul stub */
- 5 + IOEMUL_QUIRK_STUB_BYTES));
+ 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
}
#include <asm/asm_defns.h>
-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
*/
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);
__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
#define __weak __attribute__((__weak__))
+#define nocall __attribute__((error("Nonstandard ABI")))
+
#if (!defined(__clang__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 5))
#define unreachable() do {} while (1)
#else