@@ -41,6 +41,8 @@ Coveres XSA-106 and XSA-156.
@subpage test-umip - Guest User-Mode Instruction Prevention support.
+@subpage test-misaligned-io-breakpoints - Misaligned I/O port hardware breakpoint test.
+
@section index-xsa XSA Proof-of-Concept tests
new file mode 100644
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME := misaligned-io-breakpoints
+CATEGORY := utility
+TEST-ENVS := pv64
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
new file mode 100644
@@ -0,0 +1,283 @@
+/**
+ * @file tests/misaligned-io-breakpoints/main.c
+ * @ref test-misaligned-io-breakpoints - Misaligned I/O port hardware breakpoint test.
+ *
+ * @page test-misaligned-io-breakpoints Misaligned I/O Breakpoints
+ *
+ * When hardware breakpoints are configured on misaligned IO ports, the
+ * hardware will mask the addresses based on the breakpoint width during
+ * comparison.
+ *
+ * This test checks that the emulated behaviour of misaligned IO breakpoints
+ * in PV guests match that of real hardware.
+ *
+ * @see tests/io-breakpoint-bug/main.c
+ */
+#include <xtf.h>
+
+enum width { _16, _32 };
+
+const char test_title[] = "Misaligned I/O Breakpoints";
+
+static const struct test {
+ unsigned long breakpoint_port;
+ enum width breakpoint_size;
+ uint16_t io_op_port;
+ enum width io_op_len;
+ bool expect_breakpoint_trigger;
+} tests[] = {
+ /* 16-bit breakpoint, 16-bit I/O operation */
+ { 0x09, _16, 0x06, _16, false },
+ { 0x09, _16, 0x07, _16, true },
+ { 0x09, _16, 0x08, _16, true },
+ { 0x09, _16, 0x09, _16, true },
+ { 0x09, _16, 0x0a, _16, false },
+
+ { 0x0b, _16, 0x08, _16, false },
+ { 0x0b, _16, 0x09, _16, true },
+ { 0x0b, _16, 0x0a, _16, true },
+ { 0x0b, _16, 0x0b, _16, true },
+ { 0x0b, _16, 0x0c, _16, false },
+
+ /* 16-bit breakpoint, 32-bit I/O operation */
+ { 0x09, _16, 0x04, _32, false },
+ { 0x09, _16, 0x05, _32, true },
+ { 0x09, _16, 0x06, _32, true },
+ { 0x09, _16, 0x07, _32, true },
+ { 0x09, _16, 0x08, _32, true },
+ { 0x09, _16, 0x09, _32, true },
+ { 0x09, _16, 0x0a, _32, false },
+
+ { 0x0b, _16, 0x06, _32, false },
+ { 0x0b, _16, 0x07, _32, true },
+ { 0x0b, _16, 0x08, _32, true },
+ { 0x0b, _16, 0x09, _32, true },
+ { 0x0b, _16, 0x0a, _32, true },
+ { 0x0b, _16, 0x0b, _32, true },
+ { 0x0b, _16, 0x0c, _32, false },
+
+ /* 32-bit breakpoint, 16-bit I/O operation */
+ { 0x09, _32, 0x06, _16, false },
+ { 0x09, _32, 0x07, _16, true },
+ { 0x09, _32, 0x08, _16, true },
+ { 0x09, _32, 0x09, _16, true },
+ { 0x09, _32, 0x0a, _16, true },
+ { 0x09, _32, 0x0b, _16, true },
+ { 0x09, _32, 0x0c, _16, false },
+
+ { 0x0a, _32, 0x06, _16, false },
+ { 0x0a, _32, 0x07, _16, true },
+ { 0x0a, _32, 0x08, _16, true },
+ { 0x0a, _32, 0x09, _16, true },
+ { 0x0a, _32, 0x0a, _16, true },
+ { 0x0a, _32, 0x0b, _16, true },
+ { 0x0a, _32, 0x0c, _16, false },
+
+ { 0x0b, _32, 0x06, _16, false },
+ { 0x0b, _32, 0x07, _16, true },
+ { 0x0b, _32, 0x08, _16, true },
+ { 0x0b, _32, 0x09, _16, true },
+ { 0x0b, _32, 0x0a, _16, true },
+ { 0x0b, _32, 0x0b, _16, true },
+ { 0x0b, _32, 0x0c, _16, false },
+
+ { 0x0d, _32, 0x0a, _16, false },
+ { 0x0d, _32, 0x0b, _16, true },
+ { 0x0d, _32, 0x0c, _16, true },
+ { 0x0d, _32, 0x0d, _16, true },
+ { 0x0d, _32, 0x0e, _16, true },
+ { 0x0d, _32, 0x0f, _16, true },
+ { 0x0d, _32, 0x10, _16, false },
+
+ { 0x0e, _32, 0x0a, _16, false },
+ { 0x0e, _32, 0x0b, _16, true },
+ { 0x0e, _32, 0x0c, _16, true },
+ { 0x0e, _32, 0x0d, _16, true },
+ { 0x0e, _32, 0x0e, _16, true },
+ { 0x0e, _32, 0x0f, _16, true },
+ { 0x0e, _32, 0x10, _16, false },
+
+ { 0x0f, _32, 0x0a, _16, false },
+ { 0x0f, _32, 0x0b, _16, true },
+ { 0x0f, _32, 0x0c, _16, true },
+ { 0x0f, _32, 0x0d, _16, true },
+ { 0x0f, _32, 0x0e, _16, true },
+ { 0x0f, _32, 0x0f, _16, true },
+ { 0x0f, _32, 0x10, _16, false },
+
+ /* 32-bit breakpoint, 32-bit I/O operation */
+ { 0x09, _32, 0x04, _32, false },
+ { 0x09, _32, 0x05, _32, true },
+ { 0x09, _32, 0x06, _32, true },
+ { 0x09, _32, 0x07, _32, true },
+ { 0x09, _32, 0x08, _32, true },
+ { 0x09, _32, 0x09, _32, true },
+ { 0x09, _32, 0x0a, _32, true },
+ { 0x09, _32, 0x0b, _32, true },
+ { 0x09, _32, 0x0c, _32, false },
+
+ { 0x0a, _32, 0x04, _32, false },
+ { 0x0a, _32, 0x05, _32, true },
+ { 0x0a, _32, 0x06, _32, true },
+ { 0x0a, _32, 0x07, _32, true },
+ { 0x0a, _32, 0x08, _32, true },
+ { 0x0a, _32, 0x09, _32, true },
+ { 0x0a, _32, 0x0a, _32, true },
+ { 0x0a, _32, 0x0b, _32, true },
+ { 0x0a, _32, 0x0c, _32, false },
+
+ { 0x0b, _32, 0x04, _32, false },
+ { 0x0b, _32, 0x05, _32, true },
+ { 0x0b, _32, 0x06, _32, true },
+ { 0x0b, _32, 0x07, _32, true },
+ { 0x0b, _32, 0x08, _32, true },
+ { 0x0b, _32, 0x09, _32, true },
+ { 0x0b, _32, 0x0a, _32, true },
+ { 0x0b, _32, 0x0b, _32, true },
+ { 0x0b, _32, 0x0c, _32, false },
+
+ { 0x0d, _32, 0x08, _32, false },
+ { 0x0d, _32, 0x09, _32, true },
+ { 0x0d, _32, 0x0a, _32, true },
+ { 0x0d, _32, 0x0b, _32, true },
+ { 0x0d, _32, 0x0c, _32, true },
+ { 0x0d, _32, 0x0d, _32, true },
+ { 0x0d, _32, 0x0e, _32, true },
+ { 0x0d, _32, 0x0f, _32, true },
+ { 0x0d, _32, 0x10, _32, false },
+
+ { 0x0e, _32, 0x08, _32, false },
+ { 0x0e, _32, 0x09, _32, true },
+ { 0x0e, _32, 0x0a, _32, true },
+ { 0x0e, _32, 0x0b, _32, true },
+ { 0x0e, _32, 0x0c, _32, true },
+ { 0x0e, _32, 0x0d, _32, true },
+ { 0x0e, _32, 0x0e, _32, true },
+ { 0x0e, _32, 0x0f, _32, true },
+ { 0x0e, _32, 0x10, _32, false },
+
+ { 0x0f, _32, 0x08, _32, false },
+ { 0x0f, _32, 0x09, _32, true },
+ { 0x0f, _32, 0x0a, _32, true },
+ { 0x0f, _32, 0x0b, _32, true },
+ { 0x0f, _32, 0x0c, _32, true },
+ { 0x0f, _32, 0x0d, _32, true },
+ { 0x0f, _32, 0x0e, _32, true },
+ { 0x0f, _32, 0x0f, _32, true },
+ { 0x0f, _32, 0x10, _32, false },
+};
+
+static void enable_debug_extensions(void)
+{
+ unsigned long cr4 = read_cr4();
+
+ if ( !(cr4 & X86_CR4_DE) )
+ write_cr4(cr4 | X86_CR4_DE);
+}
+
+static void init_breakpoint(enum width width)
+{
+ unsigned long io0, dr7;
+
+ switch (width)
+ {
+ case _16:
+ io0 = DR7_SYM(0, G, IO, 16) | X86_DR7_GE | X86_DR7_DEFAULT;
+ break;
+ case _32:
+ io0 = DR7_SYM(0, G, IO, 32) | X86_DR7_GE | X86_DR7_DEFAULT;
+ break;
+ default:
+ xtf_failure(" Fail: Unknown width %d\n", width);
+ return;
+ }
+
+ write_dr7(io0);
+
+ if ( (dr7 = read_dr7()) != io0 )
+ xtf_failure(" Fail: dr7 %#lx != io0 %#lx\n", dr7, io0);
+}
+
+static void set_breakpoint(unsigned long io_port)
+{
+ unsigned long dr0;
+
+ write_dr0(io_port);
+
+ if ( (dr0 = read_dr0()) != io_port )
+ xtf_failure(" Fail: dr0 %#lx != %#lx\n", dr0, io_port);
+}
+
+/* Set vCPU IOPL to 1 to allow IO calls in kernel space */
+static void set_iopl(void)
+{
+ unsigned int iopl = 1;
+
+ hypercall_physdev_op(PHYSDEVOP_set_iopl, &iopl);
+}
+
+static exinfo_t io_op(uint16_t io_port, enum width io_op_len)
+{
+ exinfo_t fault = 0;
+
+ switch (io_op_len)
+ {
+ case _16:
+ asm volatile ("outw %w0, %w1; 1:"
+ _ASM_EXTABLE_HANDLER(1b, 1b, %P[rec])
+ : "+a" (fault)
+ : "Nd" (io_port),
+ [rec] "p" (ex_record_fault_eax));
+ break;
+ case _32:
+ asm volatile ("outl %k0, %w1; 1:"
+ _ASM_EXTABLE_HANDLER(1b, 1b, %P[rec])
+ : "+a" (fault)
+ : "Nd" (io_port),
+ [rec] "p" (ex_record_fault_eax));
+ break;
+ default:
+ xtf_failure(" Fail: Unknown width %d\n", io_op_len);
+ break;
+ }
+
+ return fault;
+}
+
+void test_main(void)
+{
+ unsigned int i;
+
+ enable_debug_extensions();
+ set_iopl();
+
+ for ( i = 0; i < ARRAY_SIZE(tests); ++i )
+ {
+ const struct test *t = &tests[i];
+
+ /* arrange */
+ init_breakpoint(t->breakpoint_size);
+ set_breakpoint(t->breakpoint_port);
+
+ /* act */
+ exinfo_t ex = io_op(t->io_op_port, t->io_op_len);
+
+ /* assert */
+ if ( t->expect_breakpoint_trigger && ex != EXINFO_SYM(DB, 0) )
+ xtf_failure("Fail: Expected trigger with breakpoint port 0x%02lx and io port 0x%02x, but got no trigger.\n", t->breakpoint_port, t->io_op_port);
+ if ( !t->expect_breakpoint_trigger && ex == EXINFO_SYM(DB, 0) )
+ xtf_failure("Fail: Expected no trigger with breakpoint port 0x%02lx and io port 0x%02x, but got trigger.\n", t->breakpoint_port, t->io_op_port);
+ }
+
+ xtf_success(NULL);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
This xtf test tests a bug to hardware IO port breakpoints fixed by 08aacc392d86 ("x86/emul: Fix misaligned IO breakpoint behaviour in PV guests") Signed-off-by: Matthew Barnes <matthew.barnes@cloud.com> --- docs/all-tests.dox | 2 + tests/misaligned-io-breakpoints/Makefile | 9 + tests/misaligned-io-breakpoints/main.c | 283 +++++++++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 tests/misaligned-io-breakpoints/Makefile create mode 100644 tests/misaligned-io-breakpoints/main.c