mirror of https://github.com/xqemu/xqemu.git
qtest: IRQ interception infrastructure
Since /i440fx/piix3 is being removed from the composition tree, the IO-APIC is placed under /i440fx. This is wrong and should be changed as soon as the /i440fx/piix3 path is put back. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
parent
c7f0f3b1c8
commit
2028834574
17
hw/irq.c
17
hw/irq.c
|
@ -104,3 +104,20 @@ qemu_irq *qemu_irq_proxy(qemu_irq **target, int n)
|
||||||
{
|
{
|
||||||
return qemu_allocate_irqs(proxy_irq_handler, target, n);
|
return qemu_allocate_irqs(proxy_irq_handler, target, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
qemu_irq *old_irqs = qemu_allocate_irqs(NULL, NULL, n);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
*old_irqs[i] = *gpio_in[i];
|
||||||
|
gpio_in[i]->handler = handler;
|
||||||
|
gpio_in[i]->opaque = old_irqs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_irq_intercept_out(qemu_irq **gpio_out, qemu_irq_handler handler, int n)
|
||||||
|
{
|
||||||
|
qemu_irq *old_irqs = *gpio_out;
|
||||||
|
*gpio_out = qemu_allocate_irqs(handler, old_irqs, n);
|
||||||
|
}
|
||||||
|
|
5
hw/irq.h
5
hw/irq.h
|
@ -38,4 +38,9 @@ qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2);
|
||||||
*/
|
*/
|
||||||
qemu_irq *qemu_irq_proxy(qemu_irq **target, int n);
|
qemu_irq *qemu_irq_proxy(qemu_irq **target, int n);
|
||||||
|
|
||||||
|
/* For internal use in qtest. Similar to qemu_irq_split, but operating
|
||||||
|
on an existing vector of qemu_irq. */
|
||||||
|
void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n);
|
||||||
|
void qemu_irq_intercept_out(qemu_irq **gpio_out, qemu_irq_handler handler, int n);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -107,6 +107,9 @@ static void ioapic_init(GSIState *gsi_state)
|
||||||
} else {
|
} else {
|
||||||
dev = qdev_create(NULL, "ioapic");
|
dev = qdev_create(NULL, "ioapic");
|
||||||
}
|
}
|
||||||
|
/* FIXME: this should be under the piix3. */
|
||||||
|
object_property_add_child(object_resolve_path("i440fx", NULL),
|
||||||
|
"ioapic", OBJECT(dev), NULL);
|
||||||
qdev_init_nofail(dev);
|
qdev_init_nofail(dev);
|
||||||
d = sysbus_from_qdev(dev);
|
d = sysbus_from_qdev(dev);
|
||||||
sysbus_mmio_map(d, 0, 0xfec00000);
|
sysbus_mmio_map(d, 0, 0xfec00000);
|
||||||
|
|
92
qtest.c
92
qtest.c
|
@ -12,6 +12,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qtest.h"
|
#include "qtest.h"
|
||||||
|
#include "hw/qdev.h"
|
||||||
#include "qemu-char.h"
|
#include "qemu-char.h"
|
||||||
#include "ioport.h"
|
#include "ioport.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
@ -24,6 +25,7 @@ const char *qtest_chrdev;
|
||||||
const char *qtest_log;
|
const char *qtest_log;
|
||||||
int qtest_allowed = 0;
|
int qtest_allowed = 0;
|
||||||
|
|
||||||
|
static DeviceState *irq_intercept_dev;
|
||||||
static FILE *qtest_log_fp;
|
static FILE *qtest_log_fp;
|
||||||
static CharDriverState *qtest_chr;
|
static CharDriverState *qtest_chr;
|
||||||
static GString *inbuf;
|
static GString *inbuf;
|
||||||
|
@ -66,18 +68,30 @@ static bool qtest_opened;
|
||||||
* > write ADDR SIZE DATA
|
* > write ADDR SIZE DATA
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
* Valid async messages:
|
|
||||||
*
|
|
||||||
* IRQ raise NUM
|
|
||||||
* IRQ lower NUM
|
|
||||||
*
|
|
||||||
* ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0.
|
* ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0.
|
||||||
*
|
*
|
||||||
* DATA is an arbitrarily long hex number prefixed with '0x'. If it's smaller
|
* DATA is an arbitrarily long hex number prefixed with '0x'. If it's smaller
|
||||||
* than the expected size, the value will be zero filled at the end of the data
|
* than the expected size, the value will be zero filled at the end of the data
|
||||||
* sequence.
|
* sequence.
|
||||||
*
|
*
|
||||||
* NUM is an IRQ number.
|
* IRQ management:
|
||||||
|
*
|
||||||
|
* > irq_intercept_in QOM-PATH
|
||||||
|
* < OK
|
||||||
|
*
|
||||||
|
* > irq_intercept_out QOM-PATH
|
||||||
|
* < OK
|
||||||
|
*
|
||||||
|
* Attach to the gpio-in (resp. gpio-out) pins exported by the device at
|
||||||
|
* QOM-PATH. When the pin is triggered, one of the following async messages
|
||||||
|
* will be printed to the qtest stream:
|
||||||
|
*
|
||||||
|
* IRQ raise NUM
|
||||||
|
* IRQ lower NUM
|
||||||
|
*
|
||||||
|
* 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).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int hex2nib(char ch)
|
static int hex2nib(char ch)
|
||||||
|
@ -133,6 +147,20 @@ static void qtest_send(CharDriverState *chr, const char *fmt, ...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qtest_irq_handler(void *opaque, int n, int level)
|
||||||
|
{
|
||||||
|
qemu_irq *old_irqs = opaque;
|
||||||
|
qemu_set_irq(old_irqs[n], level);
|
||||||
|
|
||||||
|
if (irq_levels[n] != level) {
|
||||||
|
CharDriverState *chr = qtest_chr;
|
||||||
|
irq_levels[n] = level;
|
||||||
|
qtest_send_prefix(chr);
|
||||||
|
qtest_send(chr, "IRQ %s %d\n",
|
||||||
|
level ? "raise" : "lower", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void qtest_process_command(CharDriverState *chr, gchar **words)
|
static void qtest_process_command(CharDriverState *chr, gchar **words)
|
||||||
{
|
{
|
||||||
const gchar *command;
|
const gchar *command;
|
||||||
|
@ -155,9 +183,40 @@ static void qtest_process_command(CharDriverState *chr, gchar **words)
|
||||||
}
|
}
|
||||||
|
|
||||||
g_assert(command);
|
g_assert(command);
|
||||||
if (strcmp(words[0], "outb") == 0 ||
|
if (strcmp(words[0], "irq_intercept_out") == 0
|
||||||
strcmp(words[0], "outw") == 0 ||
|
|| strcmp(words[0], "irq_intercept_in") == 0) {
|
||||||
strcmp(words[0], "outl") == 0) {
|
DeviceState *dev;
|
||||||
|
|
||||||
|
g_assert(words[1]);
|
||||||
|
dev = DEVICE(object_resolve_path(words[1], NULL));
|
||||||
|
if (!dev) {
|
||||||
|
qtest_send_prefix(chr);
|
||||||
|
qtest_send(chr, "FAIL Unknown device\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irq_intercept_dev) {
|
||||||
|
qtest_send_prefix(chr);
|
||||||
|
if (irq_intercept_dev != dev) {
|
||||||
|
qtest_send(chr, "FAIL IRQ intercept already enabled\n");
|
||||||
|
} else {
|
||||||
|
qtest_send(chr, "OK\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (words[0][14] == 'o') {
|
||||||
|
qemu_irq_intercept_out(&dev->gpio_out, qtest_irq_handler, dev->num_gpio_out);
|
||||||
|
} else {
|
||||||
|
qemu_irq_intercept_in(dev->gpio_in, qtest_irq_handler, dev->num_gpio_in);
|
||||||
|
}
|
||||||
|
irq_intercept_dev = dev;
|
||||||
|
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) {
|
||||||
uint16_t addr;
|
uint16_t addr;
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
|
|
||||||
|
@ -312,21 +371,6 @@ static void qtest_event(void *opaque, int event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qtest_set_irq(void *opaque, int irq, int level)
|
|
||||||
{
|
|
||||||
CharDriverState *chr = qtest_chr;
|
|
||||||
bool changed;
|
|
||||||
|
|
||||||
changed = (irq_levels[irq] != level);
|
|
||||||
irq_levels[irq] = level;
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
qtest_send_prefix(chr);
|
|
||||||
qtest_send(chr, "IRQ %s %d\n",
|
|
||||||
level ? "raise" : "lower", irq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int qtest_init(void)
|
int qtest_init(void)
|
||||||
{
|
{
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
|
|
Loading…
Reference in New Issue