diff options
-rw-r--r-- | HACKING | 10 | ||||
-rw-r--r-- | Makefile.objs | 2 | ||||
-rw-r--r-- | cpu-common.h | 1 | ||||
-rw-r--r-- | cpu-defs.h | 9 | ||||
-rw-r--r-- | cpus.c | 11 | ||||
-rw-r--r-- | exec.c | 68 | ||||
-rw-r--r-- | hw/cirrus_vga.c | 8 | ||||
-rw-r--r-- | hw/container.c | 20 | ||||
-rw-r--r-- | hw/mc146818rtc.c | 27 | ||||
-rw-r--r-- | hw/pc.c | 26 | ||||
-rw-r--r-- | hw/pc.h | 14 | ||||
-rw-r--r-- | hw/pc_piix.c | 58 | ||||
-rw-r--r-- | hw/piix_pci.c | 3 | ||||
-rw-r--r-- | hw/qdev.c | 555 | ||||
-rw-r--r-- | hw/qdev.h | 281 | ||||
-rw-r--r-- | hw/usb-bus.c | 12 | ||||
-rw-r--r-- | hw/vga-pci.c | 5 | ||||
-rw-r--r-- | hw/vmware_vga.h | 6 | ||||
-rw-r--r-- | monitor.h | 4 | ||||
-rw-r--r-- | net/tap.c | 6 | ||||
-rw-r--r-- | qapi-schema.json | 107 | ||||
-rw-r--r-- | qemu-error.c | 5 | ||||
-rw-r--r-- | qemu-error.h | 1 | ||||
-rw-r--r-- | qemu-thread-win32.c | 29 | ||||
-rw-r--r-- | qemu-thread-win32.h | 3 | ||||
-rw-r--r-- | qerror.c | 4 | ||||
-rw-r--r-- | qerror.h | 3 | ||||
-rw-r--r-- | qmp-commands.hx | 18 | ||||
-rw-r--r-- | qmp.c | 93 | ||||
-rw-r--r-- | scripts/qapi-commands.py | 1 | ||||
-rw-r--r-- | scripts/qapi-types.py | 1 | ||||
-rw-r--r-- | vl.c | 2 |
32 files changed, 1333 insertions, 60 deletions
@@ -77,11 +77,13 @@ avoided. Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign APIs is not allowed in the QEMU codebase. Instead of these routines, -use the replacement g_malloc/g_malloc0/g_realloc/g_free or -qemu_vmalloc/qemu_memalign/qemu_vfree APIs. +use the GLib memory allocation routines g_malloc/g_malloc0/g_new/ +g_new0/g_realloc/g_free or QEMU's qemu_vmalloc/qemu_memalign/qemu_vfree +APIs. -Please note that NULL check for the g_malloc result is redundant and -that g_malloc() call with zero size is not allowed. +Please note that g_malloc will exit on allocation failure, so there +is no need to test for failure (as you would have to with malloc). +Calling g_malloc with a zero size is valid and will return NULL. Memory allocated by qemu_vmalloc or qemu_memalign must be freed with qemu_vfree, since breaking this will cause problems on Win32 and user diff --git a/Makefile.objs b/Makefile.objs index 281b698c3c..f753d838ff 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -278,7 +278,7 @@ hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o hw-obj-$(CONFIG_ESP) += esp.o hw-obj-y += dma-helpers.o sysbus.o isa-bus.o -hw-obj-y += qdev-addr.o +hw-obj-y += qdev-addr.o container.o # VGA hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o diff --git a/cpu-common.h b/cpu-common.h index 7c9cef85b3..48f4c01a3f 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -172,6 +172,7 @@ void cpu_physical_memory_write_rom(target_phys_addr_t addr, #define IO_MEM_ROM (1 << IO_MEM_SHIFT) /* hardcoded offset */ #define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT) #define IO_MEM_NOTDIRTY (3 << IO_MEM_SHIFT) +#define IO_MEM_SUBPAGE_RAM (4 << IO_MEM_SHIFT) /* Acts like a ROM when read and like a device when written. */ #define IO_MEM_ROMD (1) diff --git a/cpu-defs.h b/cpu-defs.h index db48a7afef..57a709b679 100644 --- a/cpu-defs.h +++ b/cpu-defs.h @@ -153,6 +153,14 @@ typedef struct CPUWatchpoint { QTAILQ_ENTRY(CPUWatchpoint) entry; } CPUWatchpoint; +#ifdef _WIN32 +#define CPU_COMMON_THREAD \ + void *hThread; + +#else +#define CPU_COMMON_THREAD +#endif + #define CPU_TEMP_BUF_NLONGS 128 #define CPU_COMMON \ struct TranslationBlock *current_tb; /* currently executing TB */ \ @@ -211,6 +219,7 @@ typedef struct CPUWatchpoint { uint32_t stop; /* Stop request */ \ uint32_t stopped; /* Artificially stopped */ \ struct QemuThread *thread; \ + CPU_COMMON_THREAD \ struct QemuCond *halt_cond; \ int thread_kicked; \ struct qemu_work_item *queued_work_first, *queued_work_last; \ @@ -793,9 +793,9 @@ static void qemu_cpu_kick_thread(CPUState *env) } #else /* _WIN32 */ if (!qemu_cpu_is_self(env)) { - SuspendThread(env->thread->thread); + SuspendThread(env->hThread); cpu_signal(0); - ResumeThread(env->thread->thread); + ResumeThread(env->hThread); } #endif } @@ -911,7 +911,10 @@ static void qemu_tcg_init_vcpu(void *_env) qemu_cond_init(env->halt_cond); tcg_halt_cond = env->halt_cond; qemu_thread_create(env->thread, qemu_tcg_cpu_thread_fn, env, - QEMU_THREAD_DETACHED); + QEMU_THREAD_JOINABLE); +#ifdef _WIN32 + env->hThread = qemu_thread_get_handle(env->thread); +#endif while (env->created == 0) { qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); } @@ -928,7 +931,7 @@ static void qemu_kvm_start_vcpu(CPUState *env) env->halt_cond = g_malloc0(sizeof(QemuCond)); qemu_cond_init(env->halt_cond); qemu_thread_create(env->thread, qemu_kvm_cpu_thread_fn, env, - QEMU_THREAD_DETACHED); + QEMU_THREAD_JOINABLE); while (env->created == 0) { qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); } @@ -418,6 +418,7 @@ static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc) pd = *lp; if (pd == NULL) { int i; + int first_index = index & ~(L2_SIZE - 1); if (!alloc) { return NULL; @@ -427,7 +428,7 @@ static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc) for (i = 0; i < L2_SIZE; i++) { pd[i].phys_offset = IO_MEM_UNASSIGNED; - pd[i].region_offset = (index + i) << TARGET_PAGE_BITS; + pd[i].region_offset = (first_index + i) << TARGET_PAGE_BITS; } } @@ -3570,6 +3571,63 @@ static CPUWriteMemoryFunc * const subpage_write[] = { &subpage_writel, }; +static uint32_t subpage_ram_readb(void *opaque, target_phys_addr_t addr) +{ + ram_addr_t raddr = addr; + void *ptr = qemu_get_ram_ptr(raddr); + return ldub_p(ptr); +} + +static void subpage_ram_writeb(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + ram_addr_t raddr = addr; + void *ptr = qemu_get_ram_ptr(raddr); + stb_p(ptr, value); +} + +static uint32_t subpage_ram_readw(void *opaque, target_phys_addr_t addr) +{ + ram_addr_t raddr = addr; + void *ptr = qemu_get_ram_ptr(raddr); + return lduw_p(ptr); +} + +static void subpage_ram_writew(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + ram_addr_t raddr = addr; + void *ptr = qemu_get_ram_ptr(raddr); + stw_p(ptr, value); +} + +static uint32_t subpage_ram_readl(void *opaque, target_phys_addr_t addr) +{ + ram_addr_t raddr = addr; + void *ptr = qemu_get_ram_ptr(raddr); + return ldl_p(ptr); +} + +static void subpage_ram_writel(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + ram_addr_t raddr = addr; + void *ptr = qemu_get_ram_ptr(raddr); + stl_p(ptr, value); +} + +static CPUReadMemoryFunc * const subpage_ram_read[] = { + &subpage_ram_readb, + &subpage_ram_readw, + &subpage_ram_readl, +}; + +static CPUWriteMemoryFunc * const subpage_ram_write[] = { + &subpage_ram_writeb, + &subpage_ram_writew, + &subpage_ram_writel, +}; + static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, ram_addr_t memory, ram_addr_t region_offset) { @@ -3583,8 +3641,9 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %ld\n", __func__, mmio, start, end, idx, eidx, memory); #endif - if ((memory & ~TARGET_PAGE_MASK) == IO_MEM_RAM) - memory = IO_MEM_UNASSIGNED; + if ((memory & ~TARGET_PAGE_MASK) == IO_MEM_RAM) { + memory = IO_MEM_SUBPAGE_RAM; + } memory = (memory >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); for (; idx <= eidx; idx++) { mmio->sub_io_index[idx] = memory; @@ -3817,6 +3876,9 @@ static void io_mem_init(void) cpu_register_io_memory_fixed(IO_MEM_NOTDIRTY, error_mem_read, notdirty_mem_write, NULL, DEVICE_NATIVE_ENDIAN); + cpu_register_io_memory_fixed(IO_MEM_SUBPAGE_RAM, subpage_ram_read, + subpage_ram_write, NULL, + DEVICE_NATIVE_ENDIAN); for (i=0; i<5; i++) io_mem_used[i] = 1; diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 1b216e8813..1388a203eb 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -2899,7 +2899,7 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci, * ***************************************/ -void isa_cirrus_vga_init(MemoryRegion *system_memory) +DeviceState *isa_cirrus_vga_init(MemoryRegion *system_memory) { CirrusVGAState *s; @@ -2913,6 +2913,8 @@ void isa_cirrus_vga_init(MemoryRegion *system_memory) vmstate_register(NULL, 0, &vmstate_cirrus_vga, s); rom_add_vga(VGABIOS_CIRRUS_FILENAME); /* XXX ISA-LFB support */ + /* FIXME not qdev yet */ + return NULL; } /*************************************** @@ -2955,9 +2957,9 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev) return 0; } -void pci_cirrus_vga_init(PCIBus *bus) +DeviceState *pci_cirrus_vga_init(PCIBus *bus) { - pci_create_simple(bus, -1, "cirrus-vga"); + return &pci_create_simple(bus, -1, "cirrus-vga")->qdev; } static PCIDeviceInfo cirrus_vga_info = { diff --git a/hw/container.c b/hw/container.c new file mode 100644 index 0000000000..9cbf3992c4 --- /dev/null +++ b/hw/container.c @@ -0,0 +1,20 @@ +#include "sysbus.h" + +static int container_initfn(SysBusDevice *dev) +{ + return 0; +} + +static SysBusDeviceInfo container_info = { + .init = container_initfn, + .qdev.name = "container", + .qdev.size = sizeof(SysBusDevice), + .qdev.no_user = 1, +}; + +static void container_init(void) +{ + sysbus_register_withprop(&container_info); +} + +device_init(container_init); diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 2aaca2ff41..0c23cb0dba 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -614,6 +614,29 @@ static const MemoryRegionOps cmos_ops = { .old_portio = cmos_portio }; +// FIXME add int32 visitor +static void visit_type_int32(Visitor *v, int *value, const char *name, Error **errp) +{ + int64_t val = *value; + visit_type_int(v, &val, name, errp); +} + +static void rtc_get_date(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + ISADevice *isa = DO_UPCAST(ISADevice, qdev, dev); + RTCState *s = DO_UPCAST(RTCState, dev, isa); + + visit_start_struct(v, NULL, "struct tm", name, 0, errp); + visit_type_int32(v, &s->current_tm.tm_year, "tm_year", errp); + visit_type_int32(v, &s->current_tm.tm_mon, "tm_mon", errp); + visit_type_int32(v, &s->current_tm.tm_mday, "tm_mday", errp); + visit_type_int32(v, &s->current_tm.tm_hour, "tm_hour", errp); + visit_type_int32(v, &s->current_tm.tm_min, "tm_min", errp); + visit_type_int32(v, &s->current_tm.tm_sec, "tm_sec", errp); + visit_end_struct(v, errp); +} + static int rtc_initfn(ISADevice *dev) { RTCState *s = DO_UPCAST(RTCState, dev, dev); @@ -647,6 +670,10 @@ static int rtc_initfn(ISADevice *dev) qdev_set_legacy_instance_id(&dev->qdev, base, 2); qemu_register_reset(rtc_reset, s); + + qdev_property_add(&s->dev.qdev, "date", "struct tm", + rtc_get_date, NULL, NULL, s, NULL); + return 0; } @@ -1069,38 +1069,44 @@ qemu_irq *pc_allocate_cpu_irq(void) return qemu_allocate_irqs(pic_irq_request, NULL, 1); } -void pc_vga_init(PCIBus *pci_bus) +DeviceState *pc_vga_init(PCIBus *pci_bus) { + DeviceState *dev = NULL; + if (cirrus_vga_enabled) { if (pci_bus) { - pci_cirrus_vga_init(pci_bus); + dev = pci_cirrus_vga_init(pci_bus); } else { - isa_cirrus_vga_init(get_system_memory()); + dev = isa_cirrus_vga_init(get_system_memory()); } } else if (vmsvga_enabled) { if (pci_bus) { - if (!pci_vmsvga_init(pci_bus)) { + dev = pci_vmsvga_init(pci_bus); + if (!dev) { fprintf(stderr, "Warning: vmware_vga not available," " using standard VGA instead\n"); - pci_vga_init(pci_bus); + dev = pci_vga_init(pci_bus); } } else { fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__); } #ifdef CONFIG_SPICE } else if (qxl_enabled) { - if (pci_bus) - pci_create_simple(pci_bus, -1, "qxl-vga"); - else + if (pci_bus) { + dev = &pci_create_simple(pci_bus, -1, "qxl-vga")->qdev; + } else { fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__); + } #endif } else if (std_vga_enabled) { if (pci_bus) { - pci_vga_init(pci_bus); + dev = pci_vga_init(pci_bus); } else { - isa_vga_init(); + dev = isa_vga_init(); } } + + return dev; } static void cpu_request_exit(void *opaque, int irq, int level) @@ -140,7 +140,7 @@ void pc_memory_init(MemoryRegion *system_memory, MemoryRegion *rom_memory, MemoryRegion **ram_memory); qemu_irq *pc_allocate_cpu_irq(void); -void pc_vga_init(PCIBus *pci_bus); +DeviceState *pc_vga_init(PCIBus *pci_bus); void pc_basic_device_init(qemu_irq *gsi, ISADevice **rtc_state, ISADevice **floppy, @@ -205,27 +205,27 @@ enum vga_retrace_method { extern enum vga_retrace_method vga_retrace_method; -static inline int isa_vga_init(void) +static inline DeviceState *isa_vga_init(void) { ISADevice *dev; dev = isa_try_create("isa-vga"); if (!dev) { fprintf(stderr, "Warning: isa-vga not available\n"); - return 0; + return NULL; } qdev_init_nofail(&dev->qdev); - return 1; + return &dev->qdev; } -int pci_vga_init(PCIBus *bus); +DeviceState *pci_vga_init(PCIBus *bus); int isa_vga_mm_init(target_phys_addr_t vram_base, target_phys_addr_t ctrl_base, int it_shift, MemoryRegion *address_space); /* cirrus_vga.c */ -void pci_cirrus_vga_init(PCIBus *bus); -void isa_cirrus_vga_init(MemoryRegion *address_space); +DeviceState *pci_cirrus_vga_init(PCIBus *bus); +DeviceState *isa_cirrus_vga_init(MemoryRegion *address_space); /* ne2000.c */ static inline bool isa_ne2000_init(int base, int irq, NICInfo *nd) diff --git a/hw/pc_piix.c b/hw/pc_piix.c index 970f43c99c..b9bb09d1e0 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -99,6 +99,7 @@ static void pc_init1(MemoryRegion *system_memory, MemoryRegion *ram_memory; MemoryRegion *pci_memory; MemoryRegion *rom_memory; + DeviceState *dev; pc_cpus_init(cpu_model); @@ -168,7 +169,10 @@ static void pc_init1(MemoryRegion *system_memory, pc_register_ferr_irq(gsi[13]); - pc_vga_init(pci_enabled? pci_bus: NULL); + dev = pc_vga_init(pci_enabled? pci_bus: NULL); + if (dev) { + qdev_property_add_child(qdev_get_root(), "vga", dev, NULL); + } if (xen_enabled()) { pci_create_simple(pci_bus, -1, "xen-platform"); @@ -205,6 +209,17 @@ static void pc_init1(MemoryRegion *system_memory, } } + /* FIXME there's some major spaghetti here. Somehow we create the devices + * on the PIIX before we actually create it. We create the PIIX3 deep in + * the recess of the i440fx creation too and then lose the DeviceState. + * + * For now, let's "fix" this by making judicious use of paths. This is not + * generally the right way to do this. + */ + + qdev_property_add_child(qdev_resolve_path("/i440fx/piix3", NULL), + "rtc", (DeviceState *)rtc_state, NULL); + audio_init(gsi, pci_enabled ? pci_bus : NULL); pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, @@ -306,6 +321,14 @@ static QEMUMachine pc_machine_v1_0 = { .is_default = 1, }; +static QEMUMachine pc_machine_v0_15 = { + .name = "pc-0.15", + .desc = "Standard PC", + .init = pc_init_pci, + .max_cpus = 255, + .is_default = 1, +}; + static QEMUMachine pc_machine_v0_14 = { .name = "pc-0.14", .desc = "Standard PC", @@ -320,6 +343,22 @@ static QEMUMachine pc_machine_v0_14 = { .driver = "qxl-vga", .property = "revision", .value = stringify(2), + },{ + .driver = "virtio-blk-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-serial-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-net-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", }, { /* end of list */ } }, @@ -360,6 +399,10 @@ static QEMUMachine pc_machine_v0_13 = { .property = "event_idx", .value = "off", },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", + },{ .driver = "AC97", .property = "use_broken_id", .value = stringify(1), @@ -407,6 +450,10 @@ static QEMUMachine pc_machine_v0_12 = { .property = "event_idx", .value = "off", },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", + },{ .driver = "AC97", .property = "use_broken_id", .value = stringify(1), @@ -462,6 +509,10 @@ static QEMUMachine pc_machine_v0_11 = { .property = "event_idx", .value = "off", },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", + },{ .driver = "AC97", .property = "use_broken_id", .value = stringify(1), @@ -529,6 +580,10 @@ static QEMUMachine pc_machine_v0_10 = { .property = "event_idx", .value = "off", },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", + },{ .driver = "AC97", .property = "use_broken_id", .value = stringify(1), @@ -557,6 +612,7 @@ static QEMUMachine xenfv_machine = { static void pc_machine_init(void) { qemu_register_machine(&pc_machine_v1_0); + qemu_register_machine(&pc_machine_v0_15); qemu_register_machine(&pc_machine_v0_14); qemu_register_machine(&pc_machine_v0_13); qemu_register_machine(&pc_machine_v0_12); diff --git a/hw/piix_pci.c b/hw/piix_pci.c index d183443b2f..d785d4ba07 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -288,6 +288,7 @@ static PCIBus *i440fx_common_init(const char *device_name, address_space_io, 0); s->bus = b; qdev_init_nofail(dev); + qdev_property_add_child(qdev_get_root(), "i440fx", dev, NULL); d = pci_create_simple(b, 0, device_name); *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d); @@ -323,6 +324,8 @@ static PCIBus *i440fx_common_init(const char *device_name, pci_create_simple_multifunction(b, -1, true, "PIIX3")); pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, PIIX_NUM_PIRQS); + + qdev_property_add_child(dev, "piix3", &piix3->dev.qdev, NULL); } piix3->pic = pic; @@ -83,6 +83,7 @@ static DeviceInfo *qdev_find_info(BusInfo *bus_info, const char *name) static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info) { DeviceState *dev; + Property *prop; assert(bus->info == info->bus_info); dev = g_malloc0(info->size); @@ -98,7 +99,19 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info) qdev_hot_added = true; } dev->instance_id_alias = -1; + QTAILQ_INIT(&dev->properties); dev->state = DEV_STATE_CREATED; + + for (prop = dev->info->props; prop && prop->name; prop++) { + qdev_property_add_legacy(dev, prop, NULL); + } + + for (prop = dev->info->bus_info->props; prop && prop->name; prop++) { + qdev_property_add_legacy(dev, prop, NULL); + } + + qdev_property_add_str(dev, "type", qdev_get_type, NULL, NULL); + return dev; } @@ -216,6 +229,32 @@ int qdev_device_help(QemuOpts *opts) return 1; } +static DeviceState *qdev_get_peripheral(void) +{ + static DeviceState *dev; + + if (dev == NULL) { + dev = qdev_create(NULL, "container"); + qdev_property_add_child(qdev_get_root(), "peripheral", dev, NULL); + qdev_init_nofail(dev); + } + + return dev; +} + +static DeviceState *qdev_get_peripheral_anon(void) +{ + static DeviceState *dev; + + if (dev == NULL) { + dev = qdev_create(NULL, "container"); + qdev_property_add_child(qdev_get_root(), "peripheral-anon", dev, NULL); + qdev_init_nofail(dev); + } + + return dev; +} + DeviceState *qdev_device_add(QemuOpts *opts) { const char *driver, *path, *id; @@ -267,7 +306,14 @@ DeviceState *qdev_device_add(QemuOpts *opts) id = qemu_opts_id(opts); if (id) { qdev->id = id; - } + qdev_property_add_child(qdev_get_peripheral(), qdev->id, qdev, NULL); + } else { + static int anon_count; + gchar *name = g_strdup_printf("device[%d]", anon_count++); + qdev_property_add_child(qdev_get_peripheral_anon(), name, + qdev, NULL); + g_free(name); + } if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) { qdev_free(qdev); return NULL; @@ -323,6 +369,11 @@ int qdev_unplug(DeviceState *dev) } assert(dev->info->unplug != NULL); + if (dev->ref != 0) { + qerror_report(QERR_DEVICE_IN_USE, dev->id?:""); + return -1; + } + qdev_hot_removed = true; return dev->info->unplug(dev); @@ -390,12 +441,31 @@ void qdev_init_nofail(DeviceState *dev) } } +static void qdev_property_del_all(DeviceState *dev) +{ + while (!QTAILQ_EMPTY(&dev->properties)) { + DeviceProperty *prop = QTAILQ_FIRST(&dev->properties); + + QTAILQ_REMOVE(&dev->properties, prop, node); + + if (prop->release) { + prop->release(dev, prop->name, prop->opaque); + } + + g_free(prop->name); + g_free(prop->type); + g_free(prop); + } +} + /* Unlink device from bus and free the structure. */ void qdev_free(DeviceState *dev) { BusState *bus; Property *prop; + qdev_property_del_all(dev); + if (dev->state == DEV_STATE_INITIALIZED) { while (dev->num_child_bus) { bus = QLIST_FIRST(&dev->child_bus); @@ -962,3 +1032,486 @@ char* qdev_get_fw_dev_path(DeviceState *dev) return strdup(path); } + +char *qdev_get_type(DeviceState *dev, Error **errp) +{ + return g_strdup(dev->info->name); +} + +void qdev_ref(DeviceState *dev) +{ + dev->ref++; +} + +void qdev_unref(DeviceState *dev) +{ + g_assert(dev->ref > 0); + dev->ref--; +} + +void qdev_property_add(DeviceState *dev, const char *name, const char *type, + DevicePropertyAccessor *get, DevicePropertyAccessor *set, + DevicePropertyRelease *release, + void *opaque, Error **errp) +{ + DeviceProperty *prop = g_malloc0(sizeof(*prop)); + + prop->name = g_strdup(name); + prop->type = g_strdup(type); + + prop->get = get; + prop->set = set; + prop->release = release; + prop->opaque = opaque; + + QTAILQ_INSERT_TAIL(&dev->properties, prop, node); +} + +static DeviceProperty *qdev_property_find(DeviceState *dev, const char *name) +{ + DeviceProperty *prop; + + QTAILQ_FOREACH(prop, &dev->properties, node) { + if (strcmp(prop->name, name) == 0) { + return prop; + } + } + + return NULL; +} + +void qdev_property_get(DeviceState *dev, Visitor *v, const char *name, + Error **errp) +{ + DeviceProperty *prop = qdev_property_find(dev, name); + + if (prop == NULL) { + error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name); + return; + } + + if (!prop->get) { + error_set(errp, QERR_PERMISSION_DENIED); + } else { + prop->get(dev, v, prop->opaque, name, errp); + } +} + +void qdev_property_set(DeviceState *dev, Visitor *v, const char *name, + Error **errp) +{ + DeviceProperty *prop = qdev_property_find(dev, name); + + if (prop == NULL) { + error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name); + return; + } + + if (!prop->set) { + error_set(errp, QERR_PERMISSION_DENIED); + } else { + prop->set(dev, prop->opaque, v, name, errp); + } +} + +const char *qdev_property_get_type(DeviceState *dev, const char *name, Error **errp) +{ + DeviceProperty *prop = qdev_property_find(dev, name); + + if (prop == NULL) { + error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name); + return NULL; + } + + return prop->type; +} + +/** + * Legacy property handling + */ + +static void qdev_get_legacy_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + Property *prop = opaque; + + if (prop->info->print) { + char buffer[1024]; + char *ptr = buffer; + + prop->info->print(dev, prop, buffer, sizeof(buffer)); + visit_type_str(v, &ptr, name, errp); + } else { + error_set(errp, QERR_PERMISSION_DENIED); + } +} + +static void qdev_set_legacy_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + Property *prop = opaque; + + if (dev->state != DEV_STATE_CREATED) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + if (prop->info->parse) { + Error *local_err = NULL; + char *ptr = NULL; + + visit_type_str(v, &ptr, name, &local_err); + if (!local_err) { + int ret; + ret = prop->info->parse(dev, prop, ptr); + if (ret != 0) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, + name, prop->info->name); + } + g_free(ptr); + } else { + error_propagate(errp, local_err); + } + } else { + error_set(errp, QERR_PERMISSION_DENIED); + } +} + +/** + * @qdev_add_legacy_property - adds a legacy property + * + * Do not use this is new code! Properties added through this interface will + * be given types in the "legacy<>" type namespace. + * + * Legacy properties are always processed as strings. The format of the string + * depends on the property type. + */ +void qdev_property_add_legacy(DeviceState *dev, Property *prop, + Error **errp) +{ + gchar *type; + + type = g_strdup_printf("legacy<%s>", prop->info->name); + + qdev_property_add(dev, prop->name, type, + qdev_get_legacy_property, + qdev_set_legacy_property, + NULL, + prop, errp); + + g_free(type); +} + +DeviceState *qdev_get_root(void) +{ + static DeviceState *qdev_root; + + if (!qdev_root) { + qdev_root = qdev_create(NULL, "container"); + qdev_init_nofail(qdev_root); + } + + return qdev_root; +} + +static void qdev_get_child_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *child = opaque; + gchar *path; + + path = qdev_get_canonical_path(child); + visit_type_str(v, &path, name, errp); + g_free(path); +} + +void qdev_property_add_child(DeviceState *dev, const char *name, + DeviceState *child, Error **errp) +{ + gchar *type; + + type = g_strdup_printf("child<%s>", child->info->name); + + qdev_property_add(dev, name, type, qdev_get_child_property, + NULL, NULL, child, errp); + + qdev_ref(child); + g_assert(child->parent == NULL); + child->parent = dev; + + g_free(type); +} + +static void qdev_get_link_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState **child = opaque; + gchar *path; + + if (*child) { + path = qdev_get_canonical_path(*child); + visit_type_str(v, &path, name, errp); + g_free(path); + } else { + path = (gchar *)""; + visit_type_str(v, &path, name, errp); + } +} + +static void qdev_set_link_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState **child = opaque; + bool ambiguous = false; + const char *type; + char *path; + + type = qdev_property_get_type(dev, name, NULL); + + visit_type_str(v, &path, name, errp); + + if (*child) { + qdev_unref(*child); + } + + if (strcmp(path, "") != 0) { + DeviceState *target; + + target = qdev_resolve_path(path, &ambiguous); + if (target) { + gchar *target_type; + + target_type = g_strdup_printf("link<%s>", target->info->name); + if (strcmp(target_type, type) == 0) { + *child = target; + qdev_ref(target); + } else { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, type); + } + + g_free(target_type); + } else { + error_set(errp, QERR_DEVICE_NOT_FOUND, path); + } + } else { + *child = NULL; + } + + g_free(path); +} + +void qdev_property_add_link(DeviceState *dev, const char *name, + const char *type, DeviceState **child, + Error **errp) +{ + gchar *full_type; + + full_type = g_strdup_printf("link<%s>", type); + + qdev_property_add(dev, name, full_type, + qdev_get_link_property, + qdev_set_link_property, + NULL, child, errp); + + g_free(full_type); +} + +gchar *qdev_get_canonical_path(DeviceState *dev) +{ + DeviceState *root = qdev_get_root(); + char *newpath = NULL, *path = NULL; + + while (dev != root) { + DeviceProperty *prop = NULL; + + g_assert(dev->parent != NULL); + + QTAILQ_FOREACH(prop, &dev->parent->properties, node) { + if (!strstart(prop->type, "child<", NULL)) { + continue; + } + + if (prop->opaque == dev) { + if (path) { + newpath = g_strdup_printf("%s/%s", prop->name, path); + g_free(path); + path = newpath; + } else { + path = g_strdup(prop->name); + } + break; + } + } + + g_assert(prop != NULL); + + dev = dev->parent; + } + + newpath = g_strdup_printf("/%s", path); + g_free(path); + + return newpath; +} + +static DeviceState *qdev_resolve_abs_path(DeviceState *parent, + gchar **parts, + int index) +{ + DeviceProperty *prop; + DeviceState *child; + + if (parts[index] == NULL) { + return parent; + } + + if (strcmp(parts[index], "") == 0) { + return qdev_resolve_abs_path(parent, parts, index + 1); + } + + prop = qdev_property_find(parent, parts[index]); + if (prop == NULL) { + return NULL; + } + + child = NULL; + if (strstart(prop->type, "link<", NULL)) { + DeviceState **pchild = prop->opaque; + if (*pchild) { + child = *pchild; + } + } else if (strstart(prop->type, "child<", NULL)) { + child = prop->opaque; + } + + if (!child) { + return NULL; + } + + return qdev_resolve_abs_path(child, parts, index + 1); +} + +static DeviceState *qdev_resolve_partial_path(DeviceState *parent, + gchar **parts, + bool *ambiguous) +{ + DeviceState *dev; + DeviceProperty *prop; + + dev = qdev_resolve_abs_path(parent, parts, 0); + + QTAILQ_FOREACH(prop, &parent->properties, node) { + DeviceState *found; + + if (!strstart(prop->type, "child<", NULL)) { + continue; + } + + found = qdev_resolve_partial_path(prop->opaque, parts, ambiguous); + if (found) { + if (dev) { + if (ambiguous) { + *ambiguous = true; + } + return NULL; + } + dev = found; + } + + if (ambiguous && *ambiguous) { + return NULL; + } + } + + return dev; +} + +DeviceState *qdev_resolve_path(const char *path, bool *ambiguous) +{ + bool partial_path = true; + DeviceState *dev; + gchar **parts; + + parts = g_strsplit(path, "/", 0); + if (parts == NULL || parts[0] == NULL) { + g_strfreev(parts); + return qdev_get_root(); + } + + if (strcmp(parts[0], "") == 0) { + partial_path = false; + } + + if (partial_path) { + if (ambiguous) { + *ambiguous = false; + } + dev = qdev_resolve_partial_path(qdev_get_root(), parts, ambiguous); + } else { + dev = qdev_resolve_abs_path(qdev_get_root(), parts, 1); + } + + g_strfreev(parts); + + return dev; +} + +typedef struct StringProperty +{ + char *(*get)(DeviceState *, Error **); + void (*set)(DeviceState *, const char *, Error **); +} StringProperty; + +static void qdev_property_get_str(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + StringProperty *prop = opaque; + char *value; + + value = prop->get(dev, errp); + if (value) { + visit_type_str(v, &value, name, errp); + g_free(value); + } +} + +static void qdev_property_set_str(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + StringProperty *prop = opaque; + char *value; + Error *local_err = NULL; + + visit_type_str(v, &value, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + prop->set(dev, value, errp); + g_free(value); +} + +static void qdev_property_release_str(DeviceState *dev, const char *name, + void *opaque) +{ + StringProperty *prop = opaque; + g_free(prop); +} + +void qdev_property_add_str(DeviceState *dev, const char *name, + char *(*get)(DeviceState *, Error **), + void (*set)(DeviceState *, const char *, Error **), + Error **errp) +{ + StringProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + prop->set = set; + + qdev_property_add(dev, name, "string", + get ? qdev_property_get_str : NULL, + set ? qdev_property_set_str : NULL, + qdev_property_release_str, + prop, errp); +} @@ -5,6 +5,7 @@ #include "qemu-queue.h" #include "qemu-char.h" #include "qemu-option.h" +#include "qapi/qapi-visit-core.h" typedef struct Property Property; @@ -27,6 +28,44 @@ enum { DEV_NVECTORS_UNSPECIFIED = -1, }; +/** + * @DevicePropertyAccessor - called when trying to get/set a property + * + * @dev the device that owns the property + * @v the visitor that contains the property data + * @opaque the device property opaque + * @name the name of the property + * @errp a pointer to an Error that is filled if getting/setting fails. + */ +typedef void (DevicePropertyAccessor)(DeviceState *dev, + Visitor *v, + void *opaque, + const char *name, + Error **errp); + +/** + * @DevicePropertyRelease - called when a property is removed from a device + * + * @dev the device that owns the property + * @name the name of the property + * @opaque the opaque registered with the property + */ +typedef void (DevicePropertyRelease)(DeviceState *dev, + const char *name, + void *opaque); + +typedef struct DeviceProperty +{ + gchar *name; + gchar *type; + DevicePropertyAccessor *get; + DevicePropertyAccessor *set; + DevicePropertyRelease *release; + void *opaque; + + QTAILQ_ENTRY(DeviceProperty) node; +} DeviceProperty; + /* This structure should not be accessed directly. We declare it here so that it can be embedded in individual device state structures. */ struct DeviceState { @@ -45,6 +84,18 @@ struct DeviceState { QTAILQ_ENTRY(DeviceState) sibling; int instance_id_alias; int alias_required_for_version; + + /** + * This tracks the number of references between devices. See @qdev_ref for + * more information. + */ + uint32_t ref; + + QTAILQ_HEAD(, DeviceProperty) properties; + + /* Do not, under any circumstance, use this parent link below anywhere + * outside of qdev.c. You have been warned. */ + DeviceState *parent; }; typedef void (*bus_dev_printfn)(Monitor *mon, DeviceState *dev, int indent); @@ -329,4 +380,234 @@ char *qdev_get_fw_dev_path(DeviceState *dev); /* This is a nasty hack to allow passing a NULL bus to qdev_create. */ extern struct BusInfo system_bus_info; +/** + * @qdev_ref + * + * Increase the reference count of a device. A device cannot be freed as long + * as its reference count is greater than zero. + * + * @dev - the device + */ +void qdev_ref(DeviceState *dev); + +/** + * @qdef_unref + * + * Decrease the reference count of a device. A device cannot be freed as long + * as its reference count is greater than zero. + * + * @dev - the device + */ +void qdev_unref(DeviceState *dev); + +/** + * @qdev_property_add - add a new property to a device + * + * @dev - the device to add a property to + * + * @name - the name of the property. This can contain any character except for + * a forward slash. In general, you should use hyphens '-' instead of + * underscores '_' when naming properties. + * + * @type - the type name of the property. This namespace is pretty loosely + * defined. Sub namespaces are constructed by using a prefix and then + * to angle brackets. For instance, the type 'virtio-net-pci' in the + * 'link' namespace would be 'link<virtio-net-pci>'. + * + * @get - the getter to be called to read a property. If this is NULL, then + * the property cannot be read. + * + * @set - the setter to be called to write a property. If this is NULL, + * then the property cannot be written. + * + * @release - called when the property is removed from the device. This is + * meant to allow a property to free its opaque upon device + * destruction. This may be NULL. + * + * @opaque - an opaque pointer to pass to the callbacks for the property + * + * @errp - returns an error if this function fails + */ +void qdev_property_add(DeviceState *dev, const char *name, const char *type, + DevicePropertyAccessor *get, DevicePropertyAccessor *set, + DevicePropertyRelease *release, + void *opaque, Error **errp); + +/** + * @qdev_property_get - reads a property from a device + * + * @dev - the device + * + * @v - the visitor that will receive the property value. This should be an + * Output visitor and the data will be written with @name as the name. + * + * @name - the name of the property + * + * @errp - returns an error if this function fails + */ +void qdev_property_get(DeviceState *dev, Visitor *v, const char *name, + Error **errp); + +/** + * @qdev_property_set - writes a property to a device + * + * @dev - the device + * + * @v - the visitor that will be used to write the property value. This should + * be an Input visitor and the data will be first read with @name as the + * name and then written as the property value. + * + * @name - the name of the property + * + * @errp - returns an error if this function fails + */ +void qdev_property_set(DeviceState *dev, Visitor *v, const char *name, + Error **errp); + +/** + * @qdev_property_get_type - returns the type of a property + * + * @dev - the device + * + * @name - the name of the property + * + * @errp - returns an error if this function fails + * + * Returns: + * The type name of the property. + */ +const char *qdev_property_get_type(DeviceState *dev, const char *name, + Error **errp); + +/** + * @qdev_property_add_legacy - add a legacy @Property to a device + * + * DO NOT USE THIS IN NEW CODE! + */ +void qdev_property_add_legacy(DeviceState *dev, Property *prop, Error **errp); + +/** + * @qdev_get_root - returns the root device of the composition tree + * + * Returns: + * The root of the composition tree. + */ +DeviceState *qdev_get_root(void); + +/** + * @qdev_get_canonical_path - returns the canonical path for a device. This + * is the path within the composition tree starting from the root. + * + * Returns: + * The canonical path in the composition tree. + */ +gchar *qdev_get_canonical_path(DeviceState *dev); + +/** + * @qdev_resolve_path - resolves a path returning a device + * + * There are two types of supported paths--absolute paths and partial paths. + * + * Absolute paths are derived from the root device and can follow child<> or + * link<> properties. Since they can follow link<> properties, they can be + * arbitrarily long. Absolute paths look like absolute filenames and are + * prefixed with a leading slash. + * + * Partial paths look like relative filenames. They do not begin with a + * prefix. The matching rules for partial paths are subtle but designed to make + * specifying devices easy. At each level of the composition tree, the partial + * path is matched as an absolute path. The first match is not returned. At + * least two matches are searched for. A successful result is only returned if + * only one match is founded. If more than one match is found, a flag is + * return to indicate that the match was ambiguous. + * + * @path - the path to resolve + * + * @ambiguous - returns true if the path resolution failed because of an + * ambiguous match + * + * Returns: + * The matched device or NULL on path lookup failure. + */ +DeviceState *qdev_resolve_path(const char *path, bool *ambiguous); + +/** + * @qdev_property_add_child - Add a child property to a device + * + * Child properties form the composition tree. All devices need to be a child + * of another device. Devices can only be a child of one device. + * + * There is no way for a child to determine what its parent is. It is not + * a bidirectional relationship. This is by design. + * + * @dev - the device to add a property to + * + * @name - the name of the property + * + * @child - the child device + * + * @errp - if an error occurs, a pointer to an area to store the area + */ +void qdev_property_add_child(DeviceState *dev, const char *name, + DeviceState *child, Error **errp); + +/** + * @qdev_property_add_link - Add a link property to a device + * + * Links establish relationships between devices. Links are unidirectional + * although two links can be combined to form a bidirectional relationship + * between devices. + * + * Links form the graph in the device model. + * + * @dev - the device to add a property to + * + * @name - the name of the property + * + * @type - the qdev type of the link + * + * @child - a pointer to where the link device reference is stored + * + * @errp - if an error occurs, a pointer to an area to store the area + */ +void qdev_property_add_link(DeviceState *dev, const char *name, + const char *type, DeviceState **child, + Error **errp); + +/** + * @qdev_property_add_str + * + * Add a string property using getters/setters. This function will add a + * property of type 'string'. + * + * @dev - the device to add a property to + * + * @name - the name of the property + * + * @get - the getter or NULL if the property is write-only. This function must + * return a string to be freed by @g_free(). + * + * @set - the setter or NULL if the property is read-only + * + * @errp - if an error occurs, a pointer to an area to store the error + */ +void qdev_property_add_str(DeviceState *dev, const char *name, + char *(*get)(DeviceState *, Error **), + void (*set)(DeviceState *, const char *, Error **), + Error **errp); + +/** + * @qdev_get_type + * + * Returns the string representation of the type of this object. + * + * @dev - the device + * + * @errp - if an error occurs, a pointer to an area to store the error + * + * Returns: a string representing the type. This must be freed by the caller + * with g_free(). + */ +char *qdev_get_type(DeviceState *dev, Error **errp); + #endif diff --git a/hw/usb-bus.c b/hw/usb-bus.c index 8cafb76fff..8203390929 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -77,23 +77,21 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base) QLIST_INIT(&dev->strings); rc = usb_claim_port(dev); if (rc != 0) { - goto err; + return rc; } rc = dev->info->init(dev); if (rc != 0) { - goto err; + usb_release_port(dev); + return rc; } if (dev->auto_attach) { rc = usb_device_attach(dev); if (rc != 0) { - goto err; + usb_qdev_exit(qdev); + return rc; } } return 0; - -err: - usb_qdev_exit(qdev); - return rc; } static int usb_qdev_exit(DeviceState *qdev) diff --git a/hw/vga-pci.c b/hw/vga-pci.c index 14bfadbfcf..a75dbf3974 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -70,10 +70,9 @@ static int pci_vga_initfn(PCIDevice *dev) return 0; } -int pci_vga_init(PCIBus *bus) +DeviceState *pci_vga_init(PCIBus *bus) { - pci_create_simple(bus, -1, "VGA"); - return 0; + return &pci_create_simple(bus, -1, "VGA")->qdev; } static PCIDeviceInfo vga_info = { diff --git a/hw/vmware_vga.h b/hw/vmware_vga.h index 5132573a56..db11cbfac8 100644 --- a/hw/vmware_vga.h +++ b/hw/vmware_vga.h @@ -4,15 +4,15 @@ #include "qemu-common.h" /* vmware_vga.c */ -static inline bool pci_vmsvga_init(PCIBus *bus) +static inline DeviceState *pci_vmsvga_init(PCIBus *bus) { PCIDevice *dev; dev = pci_try_create(bus, -1, "vmware-svga"); if (!dev || qdev_init(&dev->qdev) < 0) { - return false; + return NULL; } else { - return true; + return &dev->qdev; } } @@ -67,4 +67,8 @@ typedef void (MonitorCompletion)(void *opaque, QObject *ret_data); void monitor_set_error(Monitor *mon, QError *qerror); +int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret); + +int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret); + #endif /* !MONITOR_H */ @@ -346,15 +346,10 @@ static TAPState *net_tap_fd_init(VLANState *vlan, static int launch_script(const char *setup_script, const char *ifname, int fd) { - sigset_t oldmask, mask; int pid, status; char *args[3]; char **parg; - sigemptyset(&mask); - sigaddset(&mask, SIGCHLD); - sigprocmask(SIG_BLOCK, &mask, &oldmask); - /* try to launch network script */ pid = fork(); if (pid == 0) { @@ -378,7 +373,6 @@ static int launch_script(const char *setup_script, const char *ifname, int fd) while (waitpid(pid, &status, 0) != pid) { /* loop */ } - sigprocmask(SIG_SETMASK, &oldmask, NULL); if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { return 0; diff --git a/qapi-schema.json b/qapi-schema.json index f358b490b0..44cf764ec3 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1168,3 +1168,110 @@ # Since: 0.14.0 ## { 'command': 'migrate_set_speed', 'data': {'value': 'int'} } + +## +# @DevicePropertyInfo: +# +# @name: the name of the property +# +# @type: the type of the property. This will typically come in one of four +# forms: +# +# 1) A primitive type such as 'u8', 'u16', 'bool', 'str', or 'double'. +# These types are mapped to the appropriate JSON type. +# +# 2) A legacy type in the form 'legacy<subtype>' where subtype is the +# legacy qdev typename. These types are always treated as strings. +# +# 3) A child type in the form 'child<subtype>' where subtype is a qdev +# device type name. Child properties create the composition tree. +# +# 4) A link type in the form 'link<subtype>' where subtype is a qdev +# device type name. Link properties form the device model graph. +# +# Since: 1.1 +# +# Notes: This type is experimental. Its syntax may change in future releases. +## +{ 'type': 'DevicePropertyInfo', + 'data': { 'name': 'str', 'type': 'str' } } + +## +# @qom-list: +# +# This command will list any properties of a device given a path in the device +# model. +# +# @path: the path within the device model. See @qom-get for a description of +# this parameter. +# +# Returns: a list of @DevicePropertyInfo that describe the properties of the +# device. +# +# Since: 1.1 +# +# Notes: This command is experimental. It's syntax may change in future +# releases. +## +{ 'command': 'qom-list', + 'data': { 'path': 'str' }, + 'returns': [ 'DevicePropertyInfo' ] } + +## +# @qom-get: +# +# This command will get a property from a device model path and return the +# value. +# +# @path: The path within the device model. There are two forms of supported +# paths--absolute and partial paths. +# +# Absolute paths are derived from the root device and can follow child<> +# or link<> properties. Since they can follow link<> properties, they +# can be arbitrarily long. Absolute paths look like absolute filenames +# and are prefixed with a leading slash. +# +# Partial paths look like relative filenames. They do not begin +# with a prefix. The matching rules for partial paths are subtle but +# designed to make specifying devices easy. At each level of the +# composition tree, the partial path is matched as an absolute path. +# The first match is not returned. At least two matches are searched +# for. A successful result is only returned if only one match is +# found. If more than one match is found, a flag is return to +# indicate that the match was ambiguous. +# +# @property: The property name to read +# +# Returns: The property value. The type depends on the property type. legacy<> +# properties are returned as #str. child<> and link<> properties are +# returns as #str pathnames. All integer property types (u8, u16, etc) +# are returned as #int. +# +# Since: 1.1 +# +# Notes: This command is experimental and may change syntax in future releases. +## +{ 'command': 'qom-get', + 'data': { 'path': 'str', 'property': 'str' }, + 'returns': 'visitor', + 'gen': 'no' } + +## +# @qom-set: +# +# This command will set a property from a device model path. +# +# @path: see @qom-get for a description of this parameter +# +# @property: the property name to set +# +# @value: a value who's type is appropriate for the property type. See @qom-get +# for a description of type mapping. +# +# Since: 1.1 +# +# Notes: This command is experimental and may change syntax in future releases. +## +{ 'command': 'qom-set', + 'data': { 'path': 'str', 'property': 'str', 'value': 'visitor' }, + 'gen': 'no' } diff --git a/qemu-error.c b/qemu-error.c index 4b20d283a2..7cd5ffe1e9 100644 --- a/qemu-error.c +++ b/qemu-error.c @@ -157,6 +157,11 @@ void error_set_progname(const char *argv0) progname = p ? p + 1 : argv0; } +const char *error_get_progname(void) +{ + return progname; +} + /* * Print current location to current monitor if we have one, else to stderr. */ diff --git a/qemu-error.h b/qemu-error.h index 4d5c53700e..93d74b4a30 100644 --- a/qemu-error.h +++ b/qemu-error.h @@ -36,5 +36,6 @@ void error_printf_unless_qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2); void error_print_loc(void); void error_set_progname(const char *argv0); void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +const char *error_get_progname(void); #endif diff --git a/qemu-thread-win32.c b/qemu-thread-win32.c index a13ffcca69..fe9b931863 100644 --- a/qemu-thread-win32.c +++ b/qemu-thread-win32.c @@ -252,14 +252,10 @@ void *qemu_thread_join(QemuThread *thread) * discard the handle that _beginthreadex gives back, and * get another copy of the handle here. */ - EnterCriticalSection(&data->cs); - if (!data->exited) { - handle = OpenThread(SYNCHRONIZE, FALSE, thread->tid); - LeaveCriticalSection(&data->cs); + handle = qemu_thread_get_handle(thread); + if (handle) { WaitForSingleObject(handle, INFINITE); CloseHandle(handle); - } else { - LeaveCriticalSection(&data->cs); } ret = data->ret; DeleteCriticalSection(&data->cs); @@ -308,6 +304,27 @@ void qemu_thread_get_self(QemuThread *thread) thread->tid = GetCurrentThreadId(); } +HANDLE qemu_thread_get_handle(QemuThread *thread) +{ + QemuThreadData *data; + HANDLE handle; + + data = thread->data; + if (!data) { + return NULL; + } + + EnterCriticalSection(&data->cs); + if (!data->exited) { + handle = OpenThread(SYNCHRONIZE | THREAD_SUSPEND_RESUME, FALSE, + thread->tid); + } else { + handle = NULL; + } + LeaveCriticalSection(&data->cs); + return handle; +} + int qemu_thread_is_self(QemuThread *thread) { return GetCurrentThreadId() == thread->tid; diff --git a/qemu-thread-win32.h b/qemu-thread-win32.h index 2983490a58..b9d1be8478 100644 --- a/qemu-thread-win32.h +++ b/qemu-thread-win32.h @@ -19,4 +19,7 @@ struct QemuThread { unsigned tid; }; +/* Only valid for joinable threads. */ +HANDLE qemu_thread_get_handle(QemuThread *thread); + #endif @@ -186,6 +186,10 @@ static const QErrorStringTable qerror_table[] = { .desc = "Could not open '%(filename)'", }, { + .error_fmt = QERR_PERMISSION_DENIED, + .desc = "Insufficient permission to perform this operation", + }, + { .error_fmt = QERR_PROPERTY_NOT_FOUND, .desc = "Property '%(device).%(property)' not found", }, @@ -156,6 +156,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_OPEN_FILE_FAILED \ "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }" +#define QERR_PERMISSION_DENIED \ + "{ 'class': 'PermissionDenied', 'data': {} }" + #define QERR_PROPERTY_NOT_FOUND \ "{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }" diff --git a/qmp-commands.hx b/qmp-commands.hx index 002e7e8bd9..7e3f4b9a59 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -2009,3 +2009,21 @@ EQMP .args_type = "", .mhandler.cmd_new = qmp_marshal_input_query_balloon, }, + + { + .name = "qom-list", + .args_type = "path:s", + .mhandler.cmd_new = qmp_marshal_input_qom_list, + }, + + { + .name = "qom-set", + .args_type = "path:s,property:s,opts:O", + .mhandler.cmd_new = qmp_qom_set, + }, + + { + .name = "qom-get", + .args_type = "path:s,property:s", + .mhandler.cmd_new = qmp_qom_get, + }, @@ -16,6 +16,9 @@ #include "qmp-commands.h" #include "kvm.h" #include "arch_init.h" +#include "hw/qdev.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qmp-output-visitor.h" NameInfo *qmp_query_name(Error **errp) { @@ -154,3 +157,93 @@ void qmp_cont(Error **errp) vm_start(); } + +DevicePropertyInfoList *qmp_qom_list(const char *path, Error **errp) +{ + DeviceState *dev; + bool ambiguous = false; + DevicePropertyInfoList *props = NULL; + DeviceProperty *prop; + + dev = qdev_resolve_path(path, &ambiguous); + if (dev == NULL) { + error_set(errp, QERR_DEVICE_NOT_FOUND, path); + return NULL; + } + + QTAILQ_FOREACH(prop, &dev->properties, node) { + DevicePropertyInfoList *entry = g_malloc0(sizeof(*entry)); + + entry->value = g_malloc0(sizeof(DevicePropertyInfo)); + entry->next = props; + props = entry; + + entry->value->name = g_strdup(prop->name); + entry->value->type = g_strdup(prop->type); + } + + return props; +} + +/* FIXME: teach qapi about how to pass through Visitors */ +int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret) +{ + const char *path = qdict_get_str(qdict, "path"); + const char *property = qdict_get_str(qdict, "property"); + QObject *value = qdict_get(qdict, "value"); + Error *local_err = NULL; + QmpInputVisitor *mi; + DeviceState *dev; + + dev = qdev_resolve_path(path, NULL); + if (!dev) { + error_set(&local_err, QERR_DEVICE_NOT_FOUND, path); + goto out; + } + + mi = qmp_input_visitor_new(value); + qdev_property_set(dev, qmp_input_get_visitor(mi), property, &local_err); + + qmp_input_visitor_cleanup(mi); + +out: + if (local_err) { + qerror_report_err(local_err); + error_free(local_err); + return -1; + } + + return 0; +} + +int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret) +{ + const char *path = qdict_get_str(qdict, "path"); + const char *property = qdict_get_str(qdict, "property"); + Error *local_err = NULL; + QmpOutputVisitor *mo; + DeviceState *dev; + + dev = qdev_resolve_path(path, NULL); + if (!dev) { + error_set(&local_err, QERR_DEVICE_NOT_FOUND, path); + goto out; + } + + mo = qmp_output_visitor_new(); + qdev_property_get(dev, qmp_output_get_visitor(mo), property, &local_err); + if (!local_err) { + *ret = qmp_output_get_qobject(mo); + } + + qmp_output_visitor_cleanup(mo); + +out: + if (local_err) { + qerror_report_err(local_err); + error_free(local_err); + return -1; + } + + return 0; +} diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index f7def16662..54d1f5d659 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -405,6 +405,7 @@ except os.error, e: exprs = parse_schema(sys.stdin) commands = filter(lambda expr: expr.has_key('command'), exprs) +commands = filter(lambda expr: not expr.has_key('gen'), commands) if dispatch_type == "sync": fdecl = open(h_file, 'w') diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index f64d84c39e..267cb49b13 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -238,6 +238,7 @@ fdecl.write(mcgen(''' guard=guardname(h_file))) exprs = parse_schema(sys.stdin) +exprs = filter(lambda expr: not expr.has_key('gen'), exprs) for expr in exprs: ret = "\n" @@ -1513,7 +1513,7 @@ static void help(int exitcode) "ctrl-alt toggle mouse and keyboard grab\n" "\n" "When using -nographic, press 'ctrl-a h' to get some help.\n", - "qemu", + error_get_progname(), options_help); exit(exitcode); } |