Microport UNIX System V/386 v2.1 (ca. 1987) does something similar
to the slave_imr() test, and doesn't run if the "canceling" behavior
of IRQ2 doesn't work.

I don't know anything that depends on the canceling behavior of
other interrupts (master_basic() or slave_basic() tests), but
it seems best to fix the issue generically if possible.

Signed-off-by: Matthew Ogilvie <mmogilvi_q...@miniinfo.net>
---
 tests/Makefile   |   2 +
 tests/pic-test.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 129 insertions(+)
 create mode 100644 tests/pic-test.c

diff --git a/tests/Makefile b/tests/Makefile
index b60f0fb..d21b0d5 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -25,6 +25,7 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 check-qtest-i386-y = tests/fdc-test$(EXESUF)
 check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
 check-qtest-i386-y += tests/rtc-test$(EXESUF)
+check-qtest-i386-y += tests/pic-test$(EXESUF)
 check-qtest-x86_64-y = $(check-qtest-i386-y)
 check-qtest-sparc-y = tests/m48t59-test$(EXESUF)
 check-qtest-sparc64-y = tests/m48t59-test$(EXESUF)
@@ -75,6 +76,7 @@ tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o 
tests/test-qmp-marsh
 tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o 
$(test-qapi-obj-y)
 
 tests/rtc-test$(EXESUF): tests/rtc-test.o $(trace-obj-y)
+tests/pic-test$(EXESUF): tests/pic-test.o $(trace-obj-y)
 tests/m48t59-test$(EXESUF): tests/m48t59-test.o $(trace-obj-y)
 tests/fdc-test$(EXESUF): tests/fdc-test.o tests/libqtest.o $(trace-obj-y)
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/libqtest.o $(trace-obj-y)
diff --git a/tests/pic-test.c b/tests/pic-test.c
new file mode 100644
index 0000000..57b8bee
--- /dev/null
+++ b/tests/pic-test.c
@@ -0,0 +1,127 @@
+/*
+ * QTest testcase for the 8259 interrupt controller
+ *
+ * Copyright (c) 2012 Matthew Ogilvie
+ *
+ * Authors:
+ *  Matthew Ogilvie <mmogilvi_q...@miniinfo.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include "libqtest.h"
+
+#include <glib.h>
+
+static void init_i8259(void)
+{
+    /* master: */
+    outb(0x20,0x13); /* ICW1: edge-triggered, multiple 8259s, ICW4 needed */
+    outb(0x21,0x08); /* ICW2: map to int08-int0f */
+    outb(0x21,0x04); /* ICW3: IR2 connected to slave */
+    outb(0x21,0x01); /* ICW4: not-SFNM, not-buffered, manual EOI, 8086 mode */
+
+    outb(0x21,0xfa); /* OCW1/IMR: mask off all except IRQ2 and IRQ0 */
+
+    /* slave: */
+    outb(0xa0,0x13); /* ICW1: edge-triggered, multiple 8259s, ICW4 needed */
+    outb(0xa1,0x70); /* ICW2: map to int70-int77 */
+    outb(0xa1,0x02); /* ICW3: connected to master IR2 */
+    outb(0xa1,0x01); /* ICW4: not-SFNM, not-buffered, manual EOI, 8086 mode */
+
+    outb(0xa1,0x3f); /* OCW1/IMR: mask off all except IRQ14 and IRQ15 */
+}
+
+static void set_irq(int num, int level)
+{
+    if(num > 8) {
+        set_irq_in("i8259-slave", num - 8, level);
+    } else {
+        set_irq_in("i8259-master", num, level);
+    }
+}
+
+static void slave_imr(void)
+{
+    init_i8259();
+
+    g_assert_cmpint(get_irq(0), ==, 0);
+    set_irq(14, 1);
+    g_assert_cmpint(get_irq(0), ==, 1);
+
+    outb(0xa1,0xff); /* OCW1/IMR: mask off all slave IRQs */
+    g_assert_cmpint(get_irq(0), ==, 0);
+
+    outb(0xa0,0x0a); /* slave OCW3: read IRR */
+    g_assert_cmpint(inb(0xa0)&0x40, ==, 0x40); /* IRR still has IRQ14 */
+    outb(0x20,0x0a); /* master OCW3: read IRR */
+    g_assert_cmpint(inb(0x20)&0x04, ==, 0x00); /* IRR does not have IRQ2 */
+
+    outb(0xa1,0x3f); /* OCW1/IMR: unmask IRQ14 and IRQ15 */
+    g_assert_cmpint(get_irq(0), ==, 1);
+
+    outb(0xa0,0x0a); /* slave OCW3: read IRR */
+    g_assert_cmpint(inb(0xa0)&0x40, ==, 0x40); /* IRR has IRQ14 */
+    outb(0x20,0x0a); /* master OCW3: read IRR */
+    g_assert_cmpint(inb(0x20)&0x04, ==, 0x04); /* IRR has IRQ2 */
+}
+
+static void master_cancel(void)
+{
+    init_i8259();
+
+    g_assert_cmpint(get_irq(0), ==, 0);
+    set_irq(0, 1);
+    g_assert_cmpint(get_irq(0), ==, 1);
+    set_irq(0, 0);
+    g_assert_cmpint(get_irq(0), ==, 0);
+}
+
+static void slave_cancel(void)
+{
+    init_i8259();
+
+    g_assert_cmpint(get_irq(0), ==, 0);
+    set_irq(14, 1);
+    g_assert_cmpint(get_irq(0), ==, 1);
+    set_irq(14, 0);
+    g_assert_cmpint(get_irq(0), ==, 0);
+}
+
+int main(int argc, char **argv)
+{
+    QTestState *s = NULL;
+    int ret;
+
+    g_test_init(&argc, &argv, NULL);
+
+    s = qtest_start(/*"-qtest-log /dev/tty "*/ "-display none");
+    qtest_irq_intercept_out(s, "i8259-master");
+
+    /* FUTURE: Before testing obscure cases, first test basic
+     *  interrupt delivery to the CPU and EOI functionality.
+     *  But that requires some kind of abstracted hook into
+     *  cpu_get_pic_interrupt() vs other architecture equivalents,
+     *  and the abstraction doesn't currently exist.
+     */
+
+    /* I know of at least one (admittedly obscure) guest that depends
+     * slave_imr() working.  (Microport UNIX System V/386 v2.1 (ca. 1987))
+     */
+    qtest_add_func("/pic/slave-imr", slave_imr);
+
+    /* I don't know any guest that depends on these, but a generic fix
+     * for slave_imr() issue also fixes these:
+     */
+    qtest_add_func("/pic/master-cancel", master_cancel);
+    qtest_add_func("/pic/slave-cancel", slave_cancel);
+
+    ret = g_test_run();
+
+    if (s) {
+        qtest_quit(s);
+    }
+
+    return ret;
+}
-- 
1.7.10.2.484.gcd07cc5


Reply via email to