Patchwork [v8,09/10] qtest: add set_irq_{in, out} infrastructure for testing interrupt controllers

login
register
mail settings
Submitter Matthew Ogilvie
Date Dec. 16, 2012, 11:56 p.m.
Message ID <1355702189-6994-10-git-send-email-mmogilvi_qemu@miniinfo.net>
Download mbox | patch
Permalink /patch/206761/
State New
Headers show

Comments

Matthew Ogilvie - Dec. 16, 2012, 11:56 p.m.
Signed-off-by: Matthew Ogilvie <mmogilvi_qemu@miniinfo.net>
---
 hw/i8259.c       |  6 ++++++
 qtest.c          | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/libqtest.c | 12 ++++++++++++
 tests/libqtest.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 117 insertions(+)

Patch

diff --git a/hw/i8259.c b/hw/i8259.c
index 9b2ec40..5f09f2f 100644
--- a/hw/i8259.c
+++ b/hw/i8259.c
@@ -453,6 +453,9 @@  qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
     }
 
     isa_pic = &dev->qdev;
+    object_property_add_link(OBJECT(bus),
+                             "i8259-master", TYPE_DEVICE,
+                             (Object **)&isa_pic, NULL);
 
     dev = i8259_init_chip("isa-i8259", bus, false);
 
@@ -462,6 +465,9 @@  qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
     }
 
     slave_pic = DO_UPCAST(PICCommonState, dev, dev);
+    object_property_add_link(OBJECT(bus),
+                             "i8259-slave", TYPE_DEVICE,
+                             (Object **)&slave_pic, NULL);
 
     return irq_set;
 }
diff --git a/qtest.c b/qtest.c
index 6965910..d4c2dd7 100644
--- a/qtest.c
+++ b/qtest.c
@@ -117,6 +117,21 @@  static bool qtest_opened;
  * where NUM is an IRQ number.  For the PC, interrupts can be intercepted
  * simply with "irq_intercept_in ioapic" (note that IRQ0 comes out with
  * NUM=0 even though it is remapped to GSI 2).
+ *
+ * Testing interrupt handler chips like the i8259:
+ *
+ *  > set_irq_in QOM-PATH IRQ LEVEL
+ *  < OK
+ *
+ *  > set_irq_out QOM-PATH IRQ LEVEL
+ *  < OK
+ *
+ * Forcibly set the given interrupt pin to the given level (from the
+ * consumer's point of view).
+ *
+ * FUTURE: Some abstracted mechanism to initiate delivery of an
+ *   interrupt to the CPU, returning the interrupt vector number
+ *   that would be delivered to that CPU.
  */
 
 static int hex2nib(char ch)
@@ -239,7 +254,43 @@  static void qtest_process_command(CharDriverState *chr, gchar **words)
         irq_intercept_dev = dev;
         qtest_send_prefix(chr);
         qtest_send(chr, "OK\n");
+    } else if (strcmp(words[0], "set_irq_in") == 0 ||
+               strcmp(words[0], "set_irq_out") == 0) {
+        DeviceState *dev;
+        qemu_irq *irqs;
+        int num_irqs;
+        int num;
+        int level;
+
+        g_assert(words[1] && words[2] && words[3]);
+
+        dev = DEVICE(object_resolve_path(words[1], NULL));
+        if (!dev) {
+            qtest_send_prefix(chr);
+            qtest_send(chr, "FAIL Unknown device\n");
+            return;
+        }
 
+        if (words[0][8] == 'o') {
+            irqs = dev->gpio_out;
+            num_irqs = dev->num_gpio_out;
+        } else {
+            irqs = dev->gpio_in;
+            num_irqs = dev->num_gpio_in;
+        }
+
+        num = strtol(words[2], NULL, 0);
+        if (num < 0 || num >= num_irqs) {
+            qtest_send_prefix(chr);
+            qtest_send(chr, "FAIL Bad IRQ number\n");
+            return;
+        }
+
+        level = strtol(words[3], NULL, 0);
+
+        qemu_set_irq(irqs[num], level != 0);
+        qtest_send_prefix(chr);
+        qtest_send(chr, "OK\n");
     } else if (strcmp(words[0], "outb") == 0 ||
                strcmp(words[0], "outw") == 0 ||
                strcmp(words[0], "outl") == 0) {
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 71b84c1..f6160ad 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -379,6 +379,18 @@  void qtest_irq_intercept_in(QTestState *s, const char *qom_path)
     qtest_rsp(s, 0);
 }
 
+void qtest_set_irq_in(QTestState *s, const char *qom_path, int num, int level)
+{
+    qtest_sendf(s, "set_irq_in %s %d %d\n", qom_path, num, level);
+    qtest_rsp(s, 0);
+}
+
+void qtest_set_irq_out(QTestState *s, const char *qom_path, int num, int level)
+{
+    qtest_sendf(s, "set_irq_out %s %d %d\n", qom_path, num, level);
+    qtest_rsp(s, 0);
+}
+
 static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value)
 {
     qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value);
diff --git a/tests/libqtest.h b/tests/libqtest.h
index c8ade85..a045fc7 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -76,6 +76,30 @@  void qtest_irq_intercept_in(QTestState *s, const char *string);
 void qtest_irq_intercept_out(QTestState *s, const char *string);
 
 /**
+ * qtest_set_irq_in:
+ * @s: QTestState instance to operate on.
+ * @string: QOM path of a device
+ * @irq: IRQ number
+ * @level: level to set it to (0 or 1)
+ *
+ * Force given device/irq GPIO-in pin to the given level.
+ * Mostly useful for testing interrupt controllers, rather than other devices.
+ */
+void qtest_set_irq_in(QTestState *s, const char *string, int irq, int level);
+
+/**
+ * qtest_set_irq_out:
+ * @s: QTestState instance to operate on.
+ * @string: QOM path of a device
+ * @irq: IRQ number
+ * @level: level to set it to (0 or 1)
+ *
+ * Force given device/irq GPIO-out pin to the given level.
+ * Mostly useful for testing interrupt controllers, rather than other devices.
+ */
+void qtest_set_irq_out(QTestState *s, const char *string, int irq, int level);
+
+/**
  * qtest_outb:
  * @s: QTestState instance to operate on.
  * @addr: I/O port to write to.
@@ -250,6 +274,30 @@  void qtest_add_func(const char *str, void (*fn));
 #define irq_intercept_out(string) qtest_irq_intercept_out(global_qtest, string)
 
 /**
+ * qtest_set_irq_in:
+ * @string: QOM path of a device
+ * @irq: IRQ number
+ * @level: level to set it to (0 or 1)
+ *
+ * Force given device/irq GPIO-in pin to the given level.
+ * Mostly useful for testing interrupt controllers, rather than other devices.
+ */
+#define set_irq_in(string, irq, level) \
+        qtest_set_irq_in(global_qtest, string, irq, level)
+
+/**
+ * qtest_set_irq_in:
+ * @string: QOM path of a device
+ * @irq: IRQ number
+ * @level: level to set it to (0 or 1)
+ *
+ * Force given device/irq GPIO-in pin to the given level.
+ * Mostly useful for testing interrupt controllers, rather than other devices.
+ */
+#define set_irq_out(string, irq, level) \
+        qtest_set_irq_out(global_qtest, string, irq, level)
+
+/**
  * outb:
  * @addr: I/O port to write to.
  * @value: Value being written.