@@ -6,10 +6,10 @@ CFLAGS += -mno-red-zone
tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \
$(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat \
$(TEST_DIR)/xsave.flat $(TEST_DIR)/rmap_chain.flat \
- $(TEST_DIR)/pcid.flat $(TEST_DIR)/debug.flat
+ $(TEST_DIR)/pcid.flat $(TEST_DIR)/debug.flat \
+ $(TEST_DIR)/ioapic.flat
tests += $(TEST_DIR)/svm.flat
tests += $(TEST_DIR)/vmx.flat
tests += $(TEST_DIR)/tscdeadline_latency.flat
-tests += $(TEST_DIR)/ioapic.flat
include config/config-x86-common.mak
@@ -30,21 +30,87 @@ static void toggle_irq_line(unsigned line)
set_irq_line(line, 0);
}
+static void ioapic_reg_version(void)
+{
+ u8 version_offset;
+ uint32_t data_read, data_write;
+
+ version_offset = 0x01;
+ data_read = ioapic_read_reg(version_offset);
+ data_write = data_read ^ 0xffffffff;
+
+ ioapic_write_reg(version_offset, data_write);
+ report("version register read only test",
+ data_read == ioapic_read_reg(version_offset));
+}
+
+static void ioapic_reg_id(void)
+{
+ u8 id_offset;
+ uint32_t data_read, data_write, diff;
+
+ id_offset = 0x0;
+ data_read = ioapic_read_reg(id_offset);
+ data_write = data_read ^ 0xffffffff;
+
+ ioapic_write_reg(id_offset, data_write);
+
+ diff = data_read ^ ioapic_read_reg(id_offset);
+ report("id register only bits [24:27] writable",
+ diff == 0x0f000000);
+}
+
+static void ioapic_arbitration_id(void)
+{
+ u8 id_offset, arb_offset;
+ uint32_t write;
+
+ id_offset = 0x0;
+ arb_offset = 0x2;
+ write = 0x0f000000;
+
+ ioapic_write_reg(id_offset, write);
+ report("arbitration register set by id",
+ ioapic_read_reg(arb_offset) == write);
+
+ ioapic_write_reg(arb_offset, 0x0);
+ report("arbtration register read only",
+ ioapic_read_reg(arb_offset) == write);
+}
+
+static volatile int g_isr_76;
+
+static void ioapic_isr_76(isr_regs_t *regs)
+{
+ ++g_isr_76;
+ eoi();
+}
+
+static void test_ioapic_edge_intr(void)
+{
+ handle_irq(0x76, ioapic_isr_76);
+ set_ioapic_redir(0x0e, 0x76, EDGE_TRIGGERED);
+ toggle_irq_line(0x0e);
+ asm volatile ("nop");
+ report("edge triggered intr", g_isr_76 == 1);
+}
+
static volatile int g_isr_77;
static void ioapic_isr_77(isr_regs_t *regs)
{
++g_isr_77;
+ set_irq_line(0x0e, 0);
eoi();
}
-static void test_ioapic_intr(void)
+static void test_ioapic_level_intr(void)
{
handle_irq(0x77, ioapic_isr_77);
- set_ioapic_redir(0x0e, 0x77, EDGE_TRIGGERED);
- toggle_irq_line(0x0e);
+ set_ioapic_redir(0x0e, 0x77, LEVEL_TRIGGERED);
+ set_irq_line(0x0e, 1);
asm volatile ("nop");
- report("ioapic interrupt", g_isr_77 == 1);
+ report("level triggered intr", g_isr_77 == 1);
}
static int g_78, g_66, g_66_after_78;
@@ -77,10 +143,106 @@ static void test_ioapic_simultaneous(void)
toggle_irq_line(0x0e);
irq_enable();
asm volatile ("nop");
- report("ioapic simultaneous interrupt",
- g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
+ report("ioapic simultaneous edge interrupts",
+ g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
}
+static int g_isr_98;
+
+static void ioapic_isr_98(isr_regs_t *regs)
+{
+ ++g_isr_98;
+ if (g_isr_98 == 1) {
+ set_irq_line(0x0e, 0);
+ set_irq_line(0x0e, 1);
+ }
+ set_irq_line(0x0e, 0);
+ eoi();
+}
+
+static void test_ioapic_level_coalesce(void)
+{
+ handle_irq(0x98, ioapic_isr_98);
+ set_ioapic_redir(0x0e, 0x98, LEVEL_TRIGGERED);
+ set_irq_line(0x0e, 1);
+ asm volatile ("nop");
+ report("coalesce simultaneous level interrupts", g_isr_98 == 1);
+}
+
+static int g_isr_99;
+
+static void ioapic_isr_99(isr_regs_t *regs)
+{
+ ++g_isr_99;
+ set_irq_line(0x0e, 0);
+ eoi();
+}
+
+static void test_ioapic_level_sequential(void)
+{
+ handle_irq(0x99, ioapic_isr_99);
+ set_ioapic_redir(0x0e, 0x99, LEVEL_TRIGGERED);
+ set_irq_line(0x0e, 1);
+ set_irq_line(0x0e, 1);
+ asm volatile ("nop");
+ report("sequential level interrupts", g_isr_99 == 2);
+}
+
+static volatile int g_isr_81;
+
+static void ioapic_isr_81(isr_regs_t *regs)
+{
+ ++g_isr_81;
+ set_irq_line(0x0e, 0);
+ eoi();
+}
+
+static void test_ioapic_edge_mask(void)
+{
+ handle_irq(0x81, ioapic_isr_81);
+ set_ioapic_redir(0x0e, 0x81, EDGE_TRIGGERED);
+
+ set_mask(0x0e, true);
+ set_irq_line(0x0e, 1);
+ set_irq_line(0x0e, 0);
+
+ asm volatile ("nop");
+ report("masked level interrupt", g_isr_81 == 0);
+
+ set_mask(0x0e, false);
+ set_irq_line(0x0e, 1);
+
+ asm volatile ("nop");
+ report("unmasked level interrupt", g_isr_81 == 1);
+}
+
+static volatile int g_isr_82;
+
+static void ioapic_isr_82(isr_regs_t *regs)
+{
+ ++g_isr_82;
+ set_irq_line(0x0e, 0);
+ eoi();
+}
+
+static void test_ioapic_level_mask(void)
+{
+ handle_irq(0x82, ioapic_isr_82);
+ set_ioapic_redir(0x0e, 0x82, LEVEL_TRIGGERED);
+
+ set_mask(0x0e, true);
+ set_irq_line(0x0e, 1);
+
+ asm volatile ("nop");
+ report("masked level interrupt", g_isr_82 == 0);
+
+ set_mask(0x0e, false);
+
+ asm volatile ("nop");
+ report("unmasked level interrupt", g_isr_82 == 1);
+}
+
+
int main(void)
{
setup_vm();
@@ -92,8 +254,18 @@ int main(void)
irq_enable();
- test_ioapic_intr();
+ ioapic_reg_version();
+ ioapic_reg_id();
+ ioapic_arbitration_id();
+
+ test_ioapic_edge_intr();
+ test_ioapic_level_intr();
test_ioapic_simultaneous();
+ test_ioapic_level_coalesce();
+ test_ioapic_level_sequential();
+
+ test_ioapic_edge_mask();
+ test_ioapic_level_mask();
return report_summary();
}
Add tests for fundamental behaviors of the IOAPIC: Edge & level triggered interrupts Level triggered interrupt coalescing Level triggered EOIs Interrupt masking Passes with most recent version of KVM on Intel x86. Signed-off-by: Steve Rutherford <srutherford@google.com> --- config/config-x86_64.mak | 4 +- x86/ioapic.c | 186 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 181 insertions(+), 9 deletions(-)