Signed-off-by: Matthew Ogilvie <mmogilvi_q...@miniinfo.net> --- hw/i8259.c | 6 ++++++ qtest.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/libqtest.c | 12 ++++++++++++ tests/libqtest.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+)
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. -- 1.7.10.2.484.gcd07cc5