# HG changeset patch # User Mike Garrett # Date 1196108221 21600 # Node ID 32be7dd9fab00cf4fb9014ab235497a39593ffb4 # Parent ba69fe2dce91cd7ef633b4b21706344f6be1a6de Adds support to allow host-platform-specific handling of I/O port traps. Specifically adds support to handle an HP ProLiant I/O port in a special way. Signed-off-by: Mike Garrett diff -r ba69fe2dce91 -r 32be7dd9fab0 xen/arch/x86/Makefile --- a/xen/arch/x86/Makefile Tue Nov 13 20:13:50 2007 +0000 +++ b/xen/arch/x86/Makefile Mon Nov 26 14:17:01 2007 -0600 @@ -31,6 +31,7 @@ obj-y += nmi.o obj-y += nmi.o obj-y += numa.o obj-y += physdev.o +obj-y += hp_proliant.o obj-y += rwlock.o obj-y += setup.o obj-y += shutdown.o diff -r ba69fe2dce91 -r 32be7dd9fab0 xen/arch/x86/hp_proliant.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/hp_proliant.c Mon Nov 26 14:17:01 2007 -0600 @@ -0,0 +1,87 @@ +/* HP ProLiant specific code */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Declare access to a function pointer used to handle platform specific I/O port emulation */ +extern u8 (*emulate_platform_specific_io_port)(u8 opcode, u16 port, u8 value, char* io_emul_stub, struct cpu_user_regs *regs); + +/* Do special handling of HP ProLiant port */ +static u8 emulate_proliant_specific_io_port(u8 opcode, u16 port, u8 value, char* io_emul_stub, struct cpu_user_regs *regs) +{ + /* if opcode = I/O write, and port = 0CD4 and we are writing the high bit to 1 */ + if (opcode == 0xee && port == 0x0cd4 && (value & 0x80)) + { + void (*io_emul)(struct cpu_user_regs *) __attribute__((__regparm__(1))); + + /* Handy function-typed pointer to the stub. */ + io_emul = (void *) io_emul_stub; + + /* + * important offsets (from original code) + * +0 -> +4/+11: call host_to_guest_gpr_switch (32 bit and 64 bit) + * ret is rigged to return to guest_to_host_gpr_switch via a stack stunt + */ + + io_emul_stub[12] = 0x66; // + io_emul_stub[13] = 0x9c; // pushf + io_emul_stub[14] = 0xfa; // cli + io_emul_stub[15] = 0xee; // out dx, al + // loop: + io_emul_stub[16] = 0xec; // in al, dx + io_emul_stub[17] = 0xa8; // + io_emul_stub[18] = 0x80; // test al, 80h + io_emul_stub[19] = 0x75; // jnz short loop + io_emul_stub[20] = 0xfb; // + io_emul_stub[21] = 0x66; // + io_emul_stub[22] = 0x9d; // popf + io_emul_stub[23] = 0xc3; // ret + + io_emul(regs); + + return 1; // we handled it, no need for generic handling + } + return 0; // we didn't handle it, do generic handling +} + +int __init set_proliant_io_emul(struct dmi_system_id *d) +{ + /* emulate_platform_specific_io_port should be NULL on entry */ + emulate_platform_specific_io_port = emulate_proliant_specific_io_port; + return 0; +} diff -r ba69fe2dce91 -r 32be7dd9fab0 xen/arch/x86/setup.c --- a/xen/arch/x86/setup.c Tue Nov 13 20:13:50 2007 +0000 +++ b/xen/arch/x86/setup.c Mon Nov 26 14:17:01 2007 -0600 @@ -49,6 +49,7 @@ extern void generic_apic_probe(void); extern void numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn); +extern int __init io_emul_init(void); extern u16 boot_edid_caps; extern u8 boot_edid_info[128]; @@ -868,6 +869,8 @@ void __init __start_xen(unsigned long mb acpi_boot_init(); + io_emul_init(); + init_cpu_to_node(); if ( smp_found_config ) diff -r ba69fe2dce91 -r 32be7dd9fab0 xen/arch/x86/traps.c --- a/xen/arch/x86/traps.c Tue Nov 13 20:13:50 2007 +0000 +++ b/xen/arch/x86/traps.c Mon Nov 26 14:17:01 2007 -0600 @@ -62,6 +62,7 @@ #include #include #include +#include /* * opt_nmi: one of 'ignore', 'dom0', or 'fatal'. @@ -110,6 +111,7 @@ DECLARE_TRAP_HANDLER(spurious_interrupt_ long do_set_debugreg(int reg, unsigned long value); unsigned long do_get_debugreg(int reg); +u8 (*emulate_platform_specific_io_port)(u8 opcode, u16 port, u8 value, char* io_emul_stub, struct cpu_user_regs *regs) = NULL; static int debug_stack_lines = 20; integer_param("debug_stack_lines", debug_stack_lines); @@ -1331,7 +1333,7 @@ static int emulate_privileged_op(struct ? (*(u32 *)®s->reg = (val)) \ : (*(u16 *)®s->reg = (val))) unsigned long code_base, code_limit; - char io_emul_stub[16]; + char io_emul_stub[32]; /* size increased from 16 to 32 bytes to handle platform-specific port emulation */ void (*io_emul)(struct cpu_user_regs *) __attribute__((__regparm__(1))); u32 l, h, eax, edx; @@ -1642,7 +1644,11 @@ static int emulate_privileged_op(struct switch ( op_bytes ) { case 1: - if ( guest_outb_okay(port, v, regs) ) + /* check and emulate platform-specific I/O ports */ + if (emulate_platform_specific_io_port != NULL && + emulate_platform_specific_io_port(opcode, port, (u8) regs->eax, io_emul_stub, regs)) + ; /* no more work to do, if true, the platform specific handler emulated it fully */ + else if ( guest_outb_okay(port, v, regs) ) { io_emul(regs); if ( pv_post_outb_hook ) @@ -2850,6 +2856,67 @@ unsigned long do_get_debugreg(int reg) return current->arch.guest_context.debugreg[reg]; } +/* This table is the set of system specific I/O emulation hooks */ +extern int __init set_proliant_io_emul(struct dmi_system_id *d); +static struct dmi_system_id __initdata io_emul_init_tbl[] = { + { /* Handle I/O emulation hook for certain HP ProLiant servers */ + .callback = set_proliant_io_emul, + .ident = "HP ProLiant DL3xx", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant DL3"), + }, + }, + { /* Handle I/O emulation hook for certain HP ProLiant servers */ + .callback = set_proliant_io_emul, + .ident = "HP ProLiant DL5xx", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant DL5"), + }, + }, + { /* Handle I/O emulation hook for certain HP ProLiant servers */ + .callback = set_proliant_io_emul, + .ident = "HP ProLiant ML3xx", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant ML3"), + }, + }, + { /* Handle I/O emulation hook for certain HP ProLiant servers */ + .callback = set_proliant_io_emul, + .ident = "HP ProLiant ML5xx", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant ML5"), + }, + }, + { /* Handle I/O emulation hook for certain HP ProLiant servers */ + .callback = set_proliant_io_emul, + .ident = "HP ProLiant BL4xx", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL4"), + }, + }, + { /* Handle I/O emulation hook for certain HP ProLiant servers */ + .callback = set_proliant_io_emul, + .ident = "HP ProLiant BL6xx", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant BL6"), + }, + }, + { } +}; + +/* Called during setup, allows us to hook system-specific I/O emulation hooks */ +int __init io_emul_init(void) +{ + dmi_check_system(io_emul_init_tbl); + return 0; +} + /* * Local variables: * mode: C