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