From: Andrew Cooper Date: Fri, 1 Jun 2018 13:11:35 +0000 (+0100) Subject: debug-regs: Detect the PV IO shadow handling bugs X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=5870a695a03af605b5bde54934ad465cb030440f;p=people%2Fandrewcoop%2Fxen-test-framework.git debug-regs: Detect the PV IO shadow handling bugs Also fix up some poor choice of constant names. Signed-off-by: Andrew Cooper --- diff --git a/arch/x86/include/arch/x86-dbg-reg.h b/arch/x86/include/arch/x86-dbg-reg.h index a982e39..da9bd9d 100644 --- a/arch/x86/include/arch/x86-dbg-reg.h +++ b/arch/x86/include/arch/x86-dbg-reg.h @@ -19,9 +19,9 @@ #define X86_DR6_BD (1u << 13) /* Debug register accessed */ #define X86_DR6_BS (1u << 14) /* Single step */ #define X86_DR6_BT (1u << 15) /* Task switch */ -#define X86_DR6_NOT_RTM (1u << 16) /* #DB/#BP in RTM region */ +#define X86_DR6_RTM (1u << 16) /* #DB/#BP in RTM region */ -#define X86_DR6_RESET 0xffff0ff0u +#define X86_DR6_DEFAULT 0xffff0ff0 /* * DR7 unique control bits. @@ -31,6 +31,8 @@ #define X86_DR7_RTM (1u << 11) /* Debugging in RTM regions */ #define X86_DR7_GD (1u << 13) /* General Detect */ +#define X86_DR7_DEFAULT 0x00000400 + /* * DR7 common control bits. Intended for use with the DR7_SYM() helper. */ diff --git a/tests/debug-regs/main.c b/tests/debug-regs/main.c index 789a899..2835f21 100644 --- a/tests/debug-regs/main.c +++ b/tests/debug-regs/main.c @@ -4,7 +4,7 @@ * * @page test-debug-regs Debug register and control tests * - * The following misc tests are implemented: + * The following PV tests are implemented: * * 1. Xen, between * [65e3554908](http://xenbits.xen.org/gitweb/?p=xen.git;a=commitdiff;h=65e355490817ac1783c9ef06c13cf980edf05b5b) @@ -17,6 +17,17 @@ * context switch of @%dr7 fails to take effect until the next full vcpu * reschedule. * + * 2. Xen, before + * [f539ae2706](http://xenbits.xen.org/gitweb/?p=xen.git;a=commitdiff;h=f539ae27061c6811fd5e80e0755bf0514e22b977) + * (Xen 4.11) had a bug whereby a write which cleared @%dr7.L/G would + * leave stale IO shadow state visible in later reads of @%dr7. + * + * Unfortunately, that changset introduced a second bug, fixed by + * [237c31b5a1](http://xenbits.xen.org/gitweb/?p=xen.git;a=commitdiff;h=237c31b5a1d5aa88cdb59b8c31b1b62eb13e82d1) + * (Xen 4.11), which caused an attempt to set up an IO breakpoint with + * @%cr4.DE clear to clobber an already configured state, despite the + * update failing. + * * @see tests/debug-regs/main.c */ #include @@ -75,18 +86,70 @@ static void test_pv_dr7_latch(void) } } - if ( i == 10 ) - printk(" All ok - %%dr7 seems to work fine\n"); - /* Reset other state. */ write_dr0(0); - write_dr6(X86_DR6_RESET); + write_dr6(X86_DR6_DEFAULT); +} + +/* + * Detect both bugs with shadow IO breakpoint state handling. + */ +static void test_pv_dr7_io_breakpoints(void) +{ + unsigned long io0, io1, dr7, cr4 = read_cr4(); + + printk("Test PV %%dr7 IO breakpoints\n"); + + if ( !(cr4 & X86_CR4_DE) ) + write_cr4(cr4 |= X86_CR4_DE); + + /* Active IO breakpoint in %dr0. */ + io0 = DR7_SYM(0, G, IO, 32) | X86_DR7_GE | X86_DR7_DEFAULT; + write_dr7(io0); + + if ( (dr7 = read_dr7()) != io0 ) + xtf_failure(" Fail: dr7 %#lx != io0 %#lx\n", + dr7, io0); + + /* Inactive IO breakpoint in %dr1. */ + io1 = DR7_SYM(1, G, IO, 32) | X86_DR7_DEFAULT; + write_dr7(io1); + + /* Bug 1. Old %dr0 configuration still visible in %dr7. */ + if ( (dr7 = read_dr7()) != io1 ) + xtf_failure(" Fail: dr7 %#lx != io1 %#lx\n", + dr7, io1); + + /* Reload active configuration. */ + write_dr7(io0); + + /* Clear %cr4.de, after which IO breakpoints are invalid. */ + write_cr4(cr4 &= ~X86_CR4_DE); + + /* Attempt to reload an IO breakpoint in %dr0, which should fail ... */ + exinfo_t fault = 0; + asm volatile ("1: mov %[val], %%dr7; 2:" + _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_eax) + : "+a" (fault) + : [val] "r" (io0), + "X" (ex_record_fault_eax)); + + if ( fault != EXINFO_SYM(GP, 0) ) + xtf_error("Error: Unexpected fault %pe\n", _p(fault)); + + /* Bug 2. ... but may drop the existing %dr7 configuration. */ + if ( (dr7 = read_dr7()) != io0 ) + xtf_failure(" Fail: dr7 %#lx != io0 %#lx\n", + dr7, io0); } void test_main(void) { if ( IS_DEFINED(CONFIG_PV) ) + { test_pv_dr7_latch(); + test_pv_dr7_io_breakpoints(); + } xtf_success(NULL); }