summaryrefslogtreecommitdiff
path: root/hw/core
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2013-03-01 13:59:19 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2013-04-08 18:13:12 +0200
commit49ab747f668f421138d5b40d83fa279c4c5e278d (patch)
tree943225a04eac885aed038731adf058f2250a2f40 /hw/core
parentce3b494cb504f96992f2d37ebc8f56deed202b06 (diff)
downloadqemu-49ab747f668f421138d5b40d83fa279c4c5e278d.tar.gz
hw: move target-independent files to subdirectories
This patch tackles all files that are compiled once, moving them to subdirectories of hw/. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'hw/core')
-rw-r--r--hw/core/Makefile.objs14
-rw-r--r--hw/core/empty_slot.c98
-rw-r--r--hw/core/irq.c136
-rw-r--r--hw/core/loader.c850
-rw-r--r--hw/core/null-machine.c36
-rw-r--r--hw/core/ptimer.c231
-rw-r--r--hw/core/qdev-addr.c78
-rw-r--r--hw/core/qdev-properties-system.c391
-rw-r--r--hw/core/qdev-properties.c1092
-rw-r--r--hw/core/qdev.c882
-rw-r--r--hw/core/stream.c23
-rw-r--r--hw/core/sysbus.c301
12 files changed, 4132 insertions, 0 deletions
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index e69de29bb2..94109f32e2 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -0,0 +1,14 @@
+# core qdev-related obj files, also used by *-user:
+common-obj-y += qdev.o qdev-properties.o
+# irq.o needed for qdev GPIO handling:
+common-obj-y += irq.o
+
+common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
+common-obj-$(CONFIG_XILINX_AXI) += stream.o
+common-obj-$(CONFIG_PTIMER) += ptimer.o
+common-obj-$(CONFIG_SOFTMMU) += sysbus.o
+common-obj-$(CONFIG_SOFTMMU) += null-machine.o
+common-obj-$(CONFIG_SOFTMMU) += loader.o
+common-obj-$(CONFIG_SOFTMMU) += qdev-addr.o
+common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
+
diff --git a/hw/core/empty_slot.c b/hw/core/empty_slot.c
new file mode 100644
index 0000000000..5234a4ddc6
--- /dev/null
+++ b/hw/core/empty_slot.c
@@ -0,0 +1,98 @@
+/*
+ * QEMU Empty Slot
+ *
+ * The empty_slot device emulates known to a bus but not connected devices.
+ *
+ * Copyright (c) 2010 Artyom Tarasenko
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any later
+ * version.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/empty_slot.h"
+
+//#define DEBUG_EMPTY_SLOT
+
+#ifdef DEBUG_EMPTY_SLOT
+#define DPRINTF(fmt, ...) \
+ do { printf("empty_slot: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+typedef struct EmptySlot {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint64_t size;
+} EmptySlot;
+
+static uint64_t empty_slot_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ DPRINTF("read from " TARGET_FMT_plx "\n", addr);
+ return 0;
+}
+
+static void empty_slot_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", (unsigned)val, addr);
+}
+
+static const MemoryRegionOps empty_slot_ops = {
+ .read = empty_slot_read,
+ .write = empty_slot_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+void empty_slot_init(hwaddr addr, uint64_t slot_size)
+{
+ if (slot_size > 0) {
+ /* Only empty slots larger than 0 byte need handling. */
+ DeviceState *dev;
+ SysBusDevice *s;
+ EmptySlot *e;
+
+ dev = qdev_create(NULL, "empty_slot");
+ s = SYS_BUS_DEVICE(dev);
+ e = FROM_SYSBUS(EmptySlot, s);
+ e->size = slot_size;
+
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(s, 0, addr);
+ }
+}
+
+static int empty_slot_init1(SysBusDevice *dev)
+{
+ EmptySlot *s = FROM_SYSBUS(EmptySlot, dev);
+
+ memory_region_init_io(&s->iomem, &empty_slot_ops, s,
+ "empty-slot", s->size);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static void empty_slot_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = empty_slot_init1;
+}
+
+static const TypeInfo empty_slot_info = {
+ .name = "empty_slot",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(EmptySlot),
+ .class_init = empty_slot_class_init,
+};
+
+static void empty_slot_register_types(void)
+{
+ type_register_static(&empty_slot_info);
+}
+
+type_init(empty_slot_register_types)
diff --git a/hw/core/irq.c b/hw/core/irq.c
new file mode 100644
index 0000000000..20785428ef
--- /dev/null
+++ b/hw/core/irq.c
@@ -0,0 +1,136 @@
+/*
+ * QEMU IRQ/GPIO common code.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "hw/irq.h"
+
+struct IRQState {
+ qemu_irq_handler handler;
+ void *opaque;
+ int n;
+};
+
+void qemu_set_irq(qemu_irq irq, int level)
+{
+ if (!irq)
+ return;
+
+ irq->handler(irq->opaque, irq->n, level);
+}
+
+qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler,
+ void *opaque, int n)
+{
+ qemu_irq *s;
+ struct IRQState *p;
+ int i;
+
+ if (!old) {
+ n_old = 0;
+ }
+ s = old ? g_renew(qemu_irq, old, n + n_old) : g_new(qemu_irq, n);
+ p = old ? g_renew(struct IRQState, s[0], n + n_old) :
+ g_new(struct IRQState, n);
+ for (i = 0; i < n + n_old; i++) {
+ if (i >= n_old) {
+ p->handler = handler;
+ p->opaque = opaque;
+ p->n = i;
+ }
+ s[i] = p;
+ p++;
+ }
+ return s;
+}
+
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
+{
+ return qemu_extend_irqs(NULL, 0, handler, opaque, n);
+}
+
+
+void qemu_free_irqs(qemu_irq *s)
+{
+ g_free(s[0]);
+ g_free(s);
+}
+
+static void qemu_notirq(void *opaque, int line, int level)
+{
+ struct IRQState *irq = opaque;
+
+ irq->handler(irq->opaque, irq->n, !level);
+}
+
+qemu_irq qemu_irq_invert(qemu_irq irq)
+{
+ /* The default state for IRQs is low, so raise the output now. */
+ qemu_irq_raise(irq);
+ return qemu_allocate_irqs(qemu_notirq, irq, 1)[0];
+}
+
+static void qemu_splitirq(void *opaque, int line, int level)
+{
+ struct IRQState **irq = opaque;
+ irq[0]->handler(irq[0]->opaque, irq[0]->n, level);
+ irq[1]->handler(irq[1]->opaque, irq[1]->n, level);
+}
+
+qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2)
+{
+ qemu_irq *s = g_malloc0(2 * sizeof(qemu_irq));
+ s[0] = irq1;
+ s[1] = irq2;
+ return qemu_allocate_irqs(qemu_splitirq, s, 1)[0];
+}
+
+static void proxy_irq_handler(void *opaque, int n, int level)
+{
+ qemu_irq **target = opaque;
+
+ if (*target) {
+ qemu_set_irq((*target)[n], level);
+ }
+}
+
+qemu_irq *qemu_irq_proxy(qemu_irq **target, int 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);
+}
diff --git a/hw/core/loader.c b/hw/core/loader.c
new file mode 100644
index 0000000000..2f5072dfa2
--- /dev/null
+++ b/hw/core/loader.c
@@ -0,0 +1,850 @@
+/*
+ * QEMU Executable loader
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Gunzip functionality in this file is derived from u-boot:
+ *
+ * (C) Copyright 2008 Semihalf
+ *
+ * (C) Copyright 2000-2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "disas/disas.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
+#include "hw/uboot_image.h"
+#include "hw/loader.h"
+#include "hw/nvram/fw_cfg.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+
+#include <zlib.h>
+
+static int roms_loaded;
+
+/* return the size or -1 if error */
+int get_image_size(const char *filename)
+{
+ int fd, size;
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+ size = lseek(fd, 0, SEEK_END);
+ close(fd);
+ return size;
+}
+
+/* return the size or -1 if error */
+/* deprecated, because caller does not specify buffer size! */
+int load_image(const char *filename, uint8_t *addr)
+{
+ int fd, size;
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+ size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, addr, size) != size) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return size;
+}
+
+/* read()-like version */
+ssize_t read_targphys(const char *name,
+ int fd, hwaddr dst_addr, size_t nbytes)
+{
+ uint8_t *buf;
+ ssize_t did;
+
+ buf = g_malloc(nbytes);
+ did = read(fd, buf, nbytes);
+ if (did > 0)
+ rom_add_blob_fixed("read", buf, did, dst_addr);
+ g_free(buf);
+ return did;
+}
+
+/* return the size or -1 if error */
+int load_image_targphys(const char *filename,
+ hwaddr addr, uint64_t max_sz)
+{
+ int size;
+
+ size = get_image_size(filename);
+ if (size > max_sz) {
+ return -1;
+ }
+ if (size > 0) {
+ rom_add_file_fixed(filename, addr, -1);
+ }
+ return size;
+}
+
+void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size,
+ const char *source)
+{
+ const char *nulp;
+ char *ptr;
+
+ if (buf_size <= 0) return;
+ nulp = memchr(source, 0, buf_size);
+ if (nulp) {
+ rom_add_blob_fixed(name, source, (nulp - source) + 1, dest);
+ } else {
+ rom_add_blob_fixed(name, source, buf_size, dest);
+ ptr = rom_ptr(dest + buf_size - 1);
+ *ptr = 0;
+ }
+}
+
+/* A.OUT loader */
+
+struct exec
+{
+ uint32_t a_info; /* Use macros N_MAGIC, etc for access */
+ uint32_t a_text; /* length of text, in bytes */
+ uint32_t a_data; /* length of data, in bytes */
+ uint32_t a_bss; /* length of uninitialized data area, in bytes */
+ uint32_t a_syms; /* length of symbol table data in file, in bytes */
+ uint32_t a_entry; /* start address */
+ uint32_t a_trsize; /* length of relocation info for text, in bytes */
+ uint32_t a_drsize; /* length of relocation info for data, in bytes */
+};
+
+static void bswap_ahdr(struct exec *e)
+{
+ bswap32s(&e->a_info);
+ bswap32s(&e->a_text);
+ bswap32s(&e->a_data);
+ bswap32s(&e->a_bss);
+ bswap32s(&e->a_syms);
+ bswap32s(&e->a_entry);
+ bswap32s(&e->a_trsize);
+ bswap32s(&e->a_drsize);
+}
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define OMAGIC 0407
+#define NMAGIC 0410
+#define ZMAGIC 0413
+#define QMAGIC 0314
+#define _N_HDROFF(x) (1024 - sizeof (struct exec))
+#define N_TXTOFF(x) \
+ (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \
+ (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
+#define N_TXTADDR(x, target_page_size) (N_MAGIC(x) == QMAGIC ? target_page_size : 0)
+#define _N_SEGMENT_ROUND(x, target_page_size) (((x) + target_page_size - 1) & ~(target_page_size - 1))
+
+#define _N_TXTENDADDR(x, target_page_size) (N_TXTADDR(x, target_page_size)+(x).a_text)
+
+#define N_DATADDR(x, target_page_size) \
+ (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x, target_page_size)) \
+ : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x, target_page_size), target_page_size)))
+
+
+int load_aout(const char *filename, hwaddr addr, int max_sz,
+ int bswap_needed, hwaddr target_page_size)
+{
+ int fd;
+ ssize_t size, ret;
+ struct exec e;
+ uint32_t magic;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+
+ size = read(fd, &e, sizeof(e));
+ if (size < 0)
+ goto fail;
+
+ if (bswap_needed) {
+ bswap_ahdr(&e);
+ }
+
+ magic = N_MAGIC(e);
+ switch (magic) {
+ case ZMAGIC:
+ case QMAGIC:
+ case OMAGIC:
+ if (e.a_text + e.a_data > max_sz)
+ goto fail;
+ lseek(fd, N_TXTOFF(e), SEEK_SET);
+ size = read_targphys(filename, fd, addr, e.a_text + e.a_data);
+ if (size < 0)
+ goto fail;
+ break;
+ case NMAGIC:
+ if (N_DATADDR(e, target_page_size) + e.a_data > max_sz)
+ goto fail;
+ lseek(fd, N_TXTOFF(e), SEEK_SET);
+ size = read_targphys(filename, fd, addr, e.a_text);
+ if (size < 0)
+ goto fail;
+ ret = read_targphys(filename, fd, addr + N_DATADDR(e, target_page_size),
+ e.a_data);
+ if (ret < 0)
+ goto fail;
+ size += ret;
+ break;
+ default:
+ goto fail;
+ }
+ close(fd);
+ return size;
+ fail:
+ close(fd);
+ return -1;
+}
+
+/* ELF loader */
+
+static void *load_at(int fd, int offset, int size)
+{
+ void *ptr;
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ return NULL;
+ ptr = g_malloc(size);
+ if (read(fd, ptr, size) != size) {
+ g_free(ptr);
+ return NULL;
+ }
+ return ptr;
+}
+
+#ifdef ELF_CLASS
+#undef ELF_CLASS
+#endif
+
+#define ELF_CLASS ELFCLASS32
+#include "elf.h"
+
+#define SZ 32
+#define elf_word uint32_t
+#define elf_sword int32_t
+#define bswapSZs bswap32s
+#include "hw/elf_ops.h"
+
+#undef elfhdr
+#undef elf_phdr
+#undef elf_shdr
+#undef elf_sym
+#undef elf_note
+#undef elf_word
+#undef elf_sword
+#undef bswapSZs
+#undef SZ
+#define elfhdr elf64_hdr
+#define elf_phdr elf64_phdr
+#define elf_note elf64_note
+#define elf_shdr elf64_shdr
+#define elf_sym elf64_sym
+#define elf_word uint64_t
+#define elf_sword int64_t
+#define bswapSZs bswap64s
+#define SZ 64
+#include "hw/elf_ops.h"
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
+ uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
+{
+ int fd, data_order, target_data_order, must_swab, ret;
+ uint8_t e_ident[EI_NIDENT];
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ perror(filename);
+ return -1;
+ }
+ if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident))
+ goto fail;
+ if (e_ident[0] != ELFMAG0 ||
+ e_ident[1] != ELFMAG1 ||
+ e_ident[2] != ELFMAG2 ||
+ e_ident[3] != ELFMAG3)
+ goto fail;
+#ifdef HOST_WORDS_BIGENDIAN
+ data_order = ELFDATA2MSB;
+#else
+ data_order = ELFDATA2LSB;
+#endif
+ must_swab = data_order != e_ident[EI_DATA];
+ if (big_endian) {
+ target_data_order = ELFDATA2MSB;
+ } else {
+ target_data_order = ELFDATA2LSB;
+ }
+
+ if (target_data_order != e_ident[EI_DATA]) {
+ goto fail;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+ if (e_ident[EI_CLASS] == ELFCLASS64) {
+ ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
+ pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+ } else {
+ ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
+ pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+ }
+
+ close(fd);
+ return ret;
+
+ fail:
+ close(fd);
+ return -1;
+}
+
+static void bswap_uboot_header(uboot_image_header_t *hdr)
+{
+#ifndef HOST_WORDS_BIGENDIAN
+ bswap32s(&hdr->ih_magic);
+ bswap32s(&hdr->ih_hcrc);
+ bswap32s(&hdr->ih_time);
+ bswap32s(&hdr->ih_size);
+ bswap32s(&hdr->ih_load);
+ bswap32s(&hdr->ih_ep);
+ bswap32s(&hdr->ih_dcrc);
+#endif
+}
+
+
+#define ZALLOC_ALIGNMENT 16
+
+static void *zalloc(void *x, unsigned items, unsigned size)
+{
+ void *p;
+
+ size *= items;
+ size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);
+
+ p = g_malloc(size);
+
+ return (p);
+}
+
+static void zfree(void *x, void *addr)
+{
+ g_free(addr);
+}
+
+
+#define HEAD_CRC 2
+#define EXTRA_FIELD 4
+#define ORIG_NAME 8
+#define COMMENT 0x10
+#define RESERVED 0xe0
+
+#define DEFLATED 8
+
+/* This is the usual maximum in uboot, so if a uImage overflows this, it would
+ * overflow on real hardware too. */
+#define UBOOT_MAX_GUNZIP_BYTES (64 << 20)
+
+static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
+ size_t srclen)
+{
+ z_stream s;
+ ssize_t dstbytes;
+ int r, i, flags;
+
+ /* skip header */
+ i = 10;
+ flags = src[3];
+ if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+ puts ("Error: Bad gzipped data\n");
+ return -1;
+ }
+ if ((flags & EXTRA_FIELD) != 0)
+ i = 12 + src[10] + (src[11] << 8);
+ if ((flags & ORIG_NAME) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & COMMENT) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & HEAD_CRC) != 0)
+ i += 2;
+ if (i >= srclen) {
+ puts ("Error: gunzip out of data in header\n");
+ return -1;
+ }
+
+ s.zalloc = zalloc;
+ s.zfree = zfree;
+
+ r = inflateInit2(&s, -MAX_WBITS);
+ if (r != Z_OK) {
+ printf ("Error: inflateInit2() returned %d\n", r);
+ return (-1);
+ }
+ s.next_in = src + i;
+ s.avail_in = srclen - i;
+ s.next_out = dst;
+ s.avail_out = dstlen;
+ r = inflate(&s, Z_FINISH);
+ if (r != Z_OK && r != Z_STREAM_END) {
+ printf ("Error: inflate() returned %d\n", r);
+ return -1;
+ }
+ dstbytes = s.next_out - (unsigned char *) dst;
+ inflateEnd(&s);
+
+ return dstbytes;
+}
+
+/* Load a U-Boot image. */
+int load_uimage(const char *filename, hwaddr *ep,
+ hwaddr *loadaddr, int *is_linux)
+{
+ int fd;
+ int size;
+ uboot_image_header_t h;
+ uboot_image_header_t *hdr = &h;
+ uint8_t *data = NULL;
+ int ret = -1;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+
+ size = read(fd, hdr, sizeof(uboot_image_header_t));
+ if (size < 0)
+ goto out;
+
+ bswap_uboot_header(hdr);
+
+ if (hdr->ih_magic != IH_MAGIC)
+ goto out;
+
+ /* TODO: Implement other image types. */
+ if (hdr->ih_type != IH_TYPE_KERNEL) {
+ fprintf(stderr, "Can only load u-boot image type \"kernel\"\n");
+ goto out;
+ }
+
+ switch (hdr->ih_comp) {
+ case IH_COMP_NONE:
+ case IH_COMP_GZIP:
+ break;
+ default:
+ fprintf(stderr,
+ "Unable to load u-boot images with compression type %d\n",
+ hdr->ih_comp);
+ goto out;
+ }
+
+ /* TODO: Check CPU type. */
+ if (is_linux) {
+ if (hdr->ih_os == IH_OS_LINUX)
+ *is_linux = 1;
+ else
+ *is_linux = 0;
+ }
+
+ *ep = hdr->ih_ep;
+ data = g_malloc(hdr->ih_size);
+
+ if (read(fd, data, hdr->ih_size) != hdr->ih_size) {
+ fprintf(stderr, "Error reading file\n");
+ goto out;
+ }
+
+ if (hdr->ih_comp == IH_COMP_GZIP) {
+ uint8_t *compressed_data;
+ size_t max_bytes;
+ ssize_t bytes;
+
+ compressed_data = data;
+ max_bytes = UBOOT_MAX_GUNZIP_BYTES;
+ data = g_malloc(max_bytes);
+
+ bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
+ g_free(compressed_data);
+ if (bytes < 0) {
+ fprintf(stderr, "Unable to decompress gzipped image!\n");
+ goto out;
+ }
+ hdr->ih_size = bytes;
+ }
+
+ rom_add_blob_fixed(filename, data, hdr->ih_size, hdr->ih_load);
+
+ if (loadaddr)
+ *loadaddr = hdr->ih_load;
+
+ ret = hdr->ih_size;
+
+out:
+ if (data)
+ g_free(data);
+ close(fd);
+ return ret;
+}
+
+/*
+ * Functions for reboot-persistent memory regions.
+ * - used for vga bios and option roms.
+ * - also linux kernel (-kernel / -initrd).
+ */
+
+typedef struct Rom Rom;
+
+struct Rom {
+ char *name;
+ char *path;
+
+ /* datasize is the amount of memory allocated in "data". If datasize is less
+ * than romsize, it means that the area from datasize to romsize is filled
+ * with zeros.
+ */
+ size_t romsize;
+ size_t datasize;
+
+ uint8_t *data;
+ int isrom;
+ char *fw_dir;
+ char *fw_file;
+
+ hwaddr addr;
+ QTAILQ_ENTRY(Rom) next;
+};
+
+static FWCfgState *fw_cfg;
+static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
+
+static void rom_insert(Rom *rom)
+{
+ Rom *item;
+
+ if (roms_loaded) {
+ hw_error ("ROM images must be loaded at startup\n");
+ }
+
+ /* list is ordered by load address */
+ QTAILQ_FOREACH(item, &roms, next) {
+ if (rom->addr >= item->addr)
+ continue;
+ QTAILQ_INSERT_BEFORE(item, rom, next);
+ return;
+ }
+ QTAILQ_INSERT_TAIL(&roms, rom, next);
+}
+
+int rom_add_file(const char *file, const char *fw_dir,
+ hwaddr addr, int32_t bootindex)
+{
+ Rom *rom;
+ int rc, fd = -1;
+ char devpath[100];
+
+ rom = g_malloc0(sizeof(*rom));
+ rom->name = g_strdup(file);
+ rom->path = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom->name);
+ if (rom->path == NULL) {
+ rom->path = g_strdup(file);
+ }
+
+ fd = open(rom->path, O_RDONLY | O_BINARY);
+ if (fd == -1) {
+ fprintf(stderr, "Could not open option rom '%s': %s\n",
+ rom->path, strerror(errno));
+ goto err;
+ }
+
+ if (fw_dir) {
+ rom->fw_dir = g_strdup(fw_dir);
+ rom->fw_file = g_strdup(file);
+ }
+ rom->addr = addr;
+ rom->romsize = lseek(fd, 0, SEEK_END);
+ rom->datasize = rom->romsize;
+ rom->data = g_malloc0(rom->datasize);
+ lseek(fd, 0, SEEK_SET);
+ rc = read(fd, rom->data, rom->datasize);
+ if (rc != rom->datasize) {
+ fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n",
+ rom->name, rc, rom->datasize);
+ goto err;
+ }
+ close(fd);
+ rom_insert(rom);
+ if (rom->fw_file && fw_cfg) {
+ const char *basename;
+ char fw_file_name[56];
+
+ basename = strrchr(rom->fw_file, '/');
+ if (basename) {
+ basename++;
+ } else {
+ basename = rom->fw_file;
+ }
+ snprintf(fw_file_name, sizeof(fw_file_name), "%s/%s", rom->fw_dir,
+ basename);
+ fw_cfg_add_file(fw_cfg, fw_file_name, rom->data, rom->romsize);
+ snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
+ } else {
+ snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
+ }
+
+ add_boot_device_path(bootindex, NULL, devpath);
+ return 0;
+
+err:
+ if (fd != -1)
+ close(fd);
+ g_free(rom->data);
+ g_free(rom->path);
+ g_free(rom->name);
+ g_free(rom);
+ return -1;
+}
+
+int rom_add_blob(const char *name, const void *blob, size_t len,
+ hwaddr addr)
+{
+ Rom *rom;
+
+ rom = g_malloc0(sizeof(*rom));
+ rom->name = g_strdup(name);
+ rom->addr = addr;
+ rom->romsize = len;
+ rom->datasize = len;
+ rom->data = g_malloc0(rom->datasize);
+ memcpy(rom->data, blob, len);
+ rom_insert(rom);
+ return 0;
+}
+
+/* This function is specific for elf program because we don't need to allocate
+ * all the rom. We just allocate the first part and the rest is just zeros. This
+ * is why romsize and datasize are different. Also, this function seize the
+ * memory ownership of "data", so we don't have to allocate and copy the buffer.
+ */
+int rom_add_elf_program(const char *name, void *data, size_t datasize,
+ size_t romsize, hwaddr addr)
+{
+ Rom *rom;
+
+ rom = g_malloc0(sizeof(*rom));
+ rom->name = g_strdup(name);
+ rom->addr = addr;
+ rom->datasize = datasize;
+ rom->romsize = romsize;
+ rom->data = data;
+ rom_insert(rom);
+ return 0;
+}
+
+int rom_add_vga(const char *file)
+{
+ return rom_add_file(file, "vgaroms", 0, -1);
+}
+
+int rom_add_option(const char *file, int32_t bootindex)
+{
+ return rom_add_file(file, "genroms", 0, bootindex);
+}
+
+static void rom_reset(void *unused)
+{
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (rom->fw_file) {
+ continue;
+ }
+ if (rom->data == NULL) {
+ continue;
+ }
+ cpu_physical_memory_write_rom(rom->addr, rom->data, rom->datasize);
+ if (rom->isrom) {
+ /* rom needs to be written only once */
+ g_free(rom->data);
+ rom->data = NULL;
+ }
+ }
+}
+
+int rom_load_all(void)
+{
+ hwaddr addr = 0;
+ MemoryRegionSection section;
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (rom->fw_file) {
+ continue;
+ }
+ if (addr > rom->addr) {
+ fprintf(stderr, "rom: requested regions overlap "
+ "(rom %s. free=0x" TARGET_FMT_plx
+ ", addr=0x" TARGET_FMT_plx ")\n",
+ rom->name, addr, rom->addr);
+ return -1;
+ }
+ addr = rom->addr;
+ addr += rom->romsize;
+ section = memory_region_find(get_system_memory(), rom->addr, 1);
+ rom->isrom = section.size && memory_region_is_rom(section.mr);
+ }
+ qemu_register_reset(rom_reset, NULL);
+ roms_loaded = 1;
+ return 0;
+}
+
+void rom_set_fw(void *f)
+{
+ fw_cfg = f;
+}
+
+static Rom *find_rom(hwaddr addr)
+{
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (rom->fw_file) {
+ continue;
+ }
+ if (rom->addr > addr) {
+ continue;
+ }
+ if (rom->addr + rom->romsize < addr) {
+ continue;
+ }
+ return rom;
+ }
+ return NULL;
+}
+
+/*
+ * Copies memory from registered ROMs to dest. Any memory that is contained in
+ * a ROM between addr and addr + size is copied. Note that this can involve
+ * multiple ROMs, which need not start at addr and need not end at addr + size.
+ */
+int rom_copy(uint8_t *dest, hwaddr addr, size_t size)
+{
+ hwaddr end = addr + size;
+ uint8_t *s, *d = dest;
+ size_t l = 0;
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (rom->fw_file) {
+ continue;
+ }
+ if (rom->addr + rom->romsize < addr) {
+ continue;
+ }
+ if (rom->addr > end) {
+ break;
+ }
+ if (!rom->data) {
+ continue;
+ }
+
+ d = dest + (rom->addr - addr);
+ s = rom->data;
+ l = rom->datasize;
+
+ if ((d + l) > (dest + size)) {
+ l = dest - d;
+ }
+
+ memcpy(d, s, l);
+
+ if (rom->romsize > rom->datasize) {
+ /* If datasize is less than romsize, it means that we didn't
+ * allocate all the ROM because the trailing data are only zeros.
+ */
+
+ d += l;
+ l = rom->romsize - rom->datasize;
+
+ if ((d + l) > (dest + size)) {
+ /* Rom size doesn't fit in the destination area. Adjust to avoid
+ * overflow.
+ */
+ l = dest - d;
+ }
+
+ if (l > 0) {
+ memset(d, 0x0, l);
+ }
+ }
+ }
+
+ return (d + l) - dest;
+}
+
+void *rom_ptr(hwaddr addr)
+{
+ Rom *rom;
+
+ rom = find_rom(addr);
+ if (!rom || !rom->data)
+ return NULL;
+ return rom->data + (addr - rom->addr);
+}
+
+void do_info_roms(Monitor *mon, const QDict *qdict)
+{
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (!rom->fw_file) {
+ monitor_printf(mon, "addr=" TARGET_FMT_plx
+ " size=0x%06zx mem=%s name=\"%s\"\n",
+ rom->addr, rom->romsize,
+ rom->isrom ? "rom" : "ram",
+ rom->name);
+ } else {
+ monitor_printf(mon, "fw=%s/%s"
+ " size=0x%06zx name=\"%s\"\n",
+ rom->fw_dir,
+ rom->fw_file,
+ rom->romsize,
+ rom->name);
+ }
+ }
+}
diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c
new file mode 100644
index 0000000000..bdf109fef1
--- /dev/null
+++ b/hw/core/null-machine.c
@@ -0,0 +1,36 @@
+/*
+ * Empty machine
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+static void machine_none_init(QEMUMachineInitArgs *args)
+{
+}
+
+static QEMUMachine machine_none = {
+ .name = "none",
+ .desc = "empty machine",
+ .init = machine_none_init,
+ .max_cpus = 0,
+ DEFAULT_MACHINE_OPTIONS,
+};
+
+static void register_machines(void)
+{
+ qemu_register_machine(&machine_none);
+}
+
+machine_init(register_machines);
+
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
new file mode 100644
index 0000000000..4bc96c9fa2
--- /dev/null
+++ b/hw/core/ptimer.c
@@ -0,0 +1,231 @@
+/*
+ * General purpose implementation of a simple periodic countdown timer.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licensed under the GNU LGPL.
+ */
+#include "hw/hw.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "qemu/host-utils.h"
+
+struct ptimer_state
+{
+ uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */
+ uint64_t limit;
+ uint64_t delta;
+ uint32_t period_frac;
+ int64_t period;
+ int64_t last_event;
+ int64_t next_event;
+ QEMUBH *bh;
+ QEMUTimer *timer;
+};
+
+/* Use a bottom-half routine to avoid reentrancy issues. */
+static void ptimer_trigger(ptimer_state *s)
+{
+ if (s->bh) {
+ qemu_bh_schedule(s->bh);
+ }
+}
+
+static void ptimer_reload(ptimer_state *s)
+{
+ if (s->delta == 0) {
+ ptimer_trigger(s);
+ s->delta = s->limit;
+ }
+ if (s->delta == 0 || s->period == 0) {
+ fprintf(stderr, "Timer with period zero, disabling\n");
+ s->enabled = 0;
+ return;
+ }
+
+ s->last_event = s->next_event;
+ s->next_event = s->last_event + s->delta * s->period;
+ if (s->period_frac) {
+ s->next_event += ((int64_t)s->period_frac * s->delta) >> 32;
+ }
+ qemu_mod_timer(s->timer, s->next_event);
+}
+
+static void ptimer_tick(void *opaque)
+{
+ ptimer_state *s = (ptimer_state *)opaque;
+ ptimer_trigger(s);
+ s->delta = 0;
+ if (s->enabled == 2) {
+ s->enabled = 0;
+ } else {
+ ptimer_reload(s);
+ }
+}
+
+uint64_t ptimer_get_count(ptimer_state *s)
+{
+ int64_t now;
+ uint64_t counter;
+
+ if (s->enabled) {
+ now = qemu_get_clock_ns(vm_clock);
+ /* Figure out the current counter value. */
+ if (now - s->next_event > 0
+ || s->period == 0) {
+ /* Prevent timer underflowing if it should already have
+ triggered. */
+ counter = 0;
+ } else {
+ uint64_t rem;
+ uint64_t div;
+ int clz1, clz2;
+ int shift;
+
+ /* We need to divide time by period, where time is stored in
+ rem (64-bit integer) and period is stored in period/period_frac
+ (64.32 fixed point).
+
+ Doing full precision division is hard, so scale values and
+ do a 64-bit division. The result should be rounded down,
+ so that the rounding error never causes the timer to go
+ backwards.
+ */
+
+ rem = s->next_event - now;
+ div = s->period;
+
+ clz1 = clz64(rem);
+ clz2 = clz64(div);
+ shift = clz1 < clz2 ? clz1 : clz2;
+
+ rem <<= shift;
+ div <<= shift;
+ if (shift >= 32) {
+ div |= ((uint64_t)s->period_frac << (shift - 32));
+ } else {
+ if (shift != 0)
+ div |= (s->period_frac >> (32 - shift));
+ /* Look at remaining bits of period_frac and round div up if
+ necessary. */
+ if ((uint32_t)(s->period_frac << shift))
+ div += 1;
+ }
+ counter = rem / div;
+ }
+ } else {
+ counter = s->delta;
+ }
+ return counter;
+}
+
+void ptimer_set_count(ptimer_state *s, uint64_t count)
+{
+ s->delta = count;
+ if (s->enabled) {
+ s->next_event = qemu_get_clock_ns(vm_clock);
+ ptimer_reload(s);
+ }
+}
+
+void ptimer_run(ptimer_state *s, int oneshot)
+{
+ if (s->enabled) {
+ return;
+ }
+ if (s->period == 0) {
+ fprintf(stderr, "Timer with period zero, disabling\n");
+ return;
+ }
+ s->enabled = oneshot ? 2 : 1;
+ s->next_event = qemu_get_clock_ns(vm_clock);
+ ptimer_reload(s);
+}
+
+/* Pause a timer. Note that this may cause it to "lose" time, even if it
+ is immediately restarted. */
+void ptimer_stop(ptimer_state *s)
+{
+ if (!s->enabled)
+ return;
+
+ s->delta = ptimer_get_count(s);
+ qemu_del_timer(s->timer);
+ s->enabled = 0;
+}
+
+/* Set counter increment interval in nanoseconds. */
+void ptimer_set_period(ptimer_state *s, int64_t period)
+{
+ s->period = period;
+ s->period_frac = 0;
+ if (s->enabled) {
+ s->next_event = qemu_get_clock_ns(vm_clock);
+ ptimer_reload(s);
+ }
+}
+
+/* Set counter frequency in Hz. */
+void ptimer_set_freq(ptimer_state *s, uint32_t freq)
+{
+ s->period = 1000000000ll / freq;
+ s->period_frac = (1000000000ll << 32) / freq;
+ if (s->enabled) {
+ s->next_event = qemu_get_clock_ns(vm_clock);
+ ptimer_reload(s);
+ }
+}
+
+/* Set the initial countdown value. If reload is nonzero then also set
+ count = limit. */
+void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
+{
+ /*
+ * Artificially limit timeout rate to something
+ * achievable under QEMU. Otherwise, QEMU spends all
+ * its time generating timer interrupts, and there
+ * is no forward progress.
+ * About ten microseconds is the fastest that really works
+ * on the current generation of host machines.
+ */
+
+ if (limit * s->period < 10000 && s->period) {
+ limit = 10000 / s->period;
+ }
+
+ s->limit = limit;
+ if (reload)
+ s->delta = limit;
+ if (s->enabled && reload) {
+ s->next_event = qemu_get_clock_ns(vm_clock);
+ ptimer_reload(s);
+ }
+}
+
+const VMStateDescription vmstate_ptimer = {
+ .name = "ptimer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(enabled, ptimer_state),
+ VMSTATE_UINT64(limit, ptimer_state),
+ VMSTATE_UINT64(delta, ptimer_state),
+ VMSTATE_UINT32(period_frac, ptimer_state),
+ VMSTATE_INT64(period, ptimer_state),
+ VMSTATE_INT64(last_event, ptimer_state),
+ VMSTATE_INT64(next_event, ptimer_state),
+ VMSTATE_TIMER(timer, ptimer_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+ptimer_state *ptimer_init(QEMUBH *bh)
+{
+ ptimer_state *s;
+
+ s = (ptimer_state *)g_malloc0(sizeof(ptimer_state));
+ s->bh = bh;
+ s->timer = qemu_new_timer_ns(vm_clock, ptimer_tick, s);
+ return s;
+}
diff --git a/hw/core/qdev-addr.c b/hw/core/qdev-addr.c
new file mode 100644
index 0000000000..80a38bb017
--- /dev/null
+++ b/hw/core/qdev-addr.c
@@ -0,0 +1,78 @@
+#include "hw/qdev.h"
+#include "hw/qdev-addr.h"
+#include "exec/hwaddr.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/visitor.h"
+
+/* --- target physical address --- */
+
+static int parse_taddr(DeviceState *dev, Property *prop, const char *str)
+{
+ hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
+
+ *ptr = strtoull(str, NULL, 16);
+ return 0;
+}
+
+static int print_taddr(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "0x" TARGET_FMT_plx, *ptr);
+}
+
+static void get_taddr(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
+ int64_t value;
+
+ value = *ptr;
+ visit_type_int64(v, &value, name, errp);
+}
+
+static void set_taddr(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
+ Error *local_err = NULL;
+ int64_t value;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_int64(v, &value, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ if ((uint64_t)value <= (uint64_t) ~(hwaddr)0) {
+ *ptr = value;
+ } else {
+ error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
+ dev->id?:"", name, value, (uint64_t) 0,
+ (uint64_t) ~(hwaddr)0);
+ }
+}
+
+
+PropertyInfo qdev_prop_taddr = {
+ .name = "taddr",
+ .parse = parse_taddr,
+ .print = print_taddr,
+ .get = get_taddr,
+ .set = set_taddr,
+};
+
+void qdev_prop_set_taddr(DeviceState *dev, const char *name, hwaddr value)
+{
+ Error *errp = NULL;
+ object_property_set_int(OBJECT(dev), value, name, &errp);
+ assert(!errp);
+
+}
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
new file mode 100644
index 0000000000..8c2e15205c
--- /dev/null
+++ b/hw/core/qdev-properties-system.c
@@ -0,0 +1,391 @@
+/*
+ * qdev property parsing and global properties
+ * (parts specific for qemu-system-*)
+ *
+ * This file is based on code from hw/qdev-properties.c from
+ * commit 074a86fccd185616469dfcdc0e157f438aebba18,
+ * Copyright (c) Gerd Hoffmann <kraxel@redhat.com> and other contributors.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "net/net.h"
+#include "hw/qdev.h"
+#include "qapi/qmp/qerror.h"
+#include "sysemu/blockdev.h"
+#include "hw/block/block.h"
+#include "net/hub.h"
+#include "qapi/visitor.h"
+#include "char/char.h"
+
+static void get_pointer(Object *obj, Visitor *v, Property *prop,
+ const char *(*print)(void *ptr),
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ void **ptr = qdev_get_prop_ptr(dev, prop);
+ char *p;
+
+ p = (char *) (*ptr ? print(*ptr) : "");
+ visit_type_str(v, &p, name, errp);
+}
+
+static void set_pointer(Object *obj, Visitor *v, Property *prop,
+ int (*parse)(DeviceState *dev, const char *str,
+ void **ptr),
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Error *local_err = NULL;
+ void **ptr = qdev_get_prop_ptr(dev, prop);
+ char *str;
+ int ret;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_str(v, &str, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ if (!*str) {
+ g_free(str);
+ *ptr = NULL;
+ return;
+ }
+ ret = parse(dev, str, ptr);
+ error_set_from_qdev_prop_error(errp, ret, dev, prop, str);
+ g_free(str);
+}
+
+/* --- drive --- */
+
+static int parse_drive(DeviceState *dev, const char *str, void **ptr)
+{
+ BlockDriverState *bs;
+
+ bs = bdrv_find(str);
+ if (bs == NULL) {
+ return -ENOENT;
+ }
+ if (bdrv_attach_dev(bs, dev) < 0) {
+ return -EEXIST;
+ }
+ *ptr = bs;
+ return 0;
+}
+
+static void release_drive(Object *obj, const char *name, void *opaque)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr) {
+ bdrv_detach_dev(*ptr, dev);
+ blockdev_auto_del(*ptr);
+ }
+}
+
+static const char *print_drive(void *ptr)
+{
+ return bdrv_get_device_name(ptr);
+}
+
+static void get_drive(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ get_pointer(obj, v, opaque, print_drive, name, errp);
+}
+
+static void set_drive(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ set_pointer(obj, v, opaque, parse_drive, name, errp);
+}
+
+PropertyInfo qdev_prop_drive = {
+ .name = "drive",
+ .get = get_drive,
+ .set = set_drive,
+ .release = release_drive,
+};
+
+/* --- character device --- */
+
+static int parse_chr(DeviceState *dev, const char *str, void **ptr)
+{
+ CharDriverState *chr = qemu_chr_find(str);
+ if (chr == NULL) {
+ return -ENOENT;
+ }
+ if (qemu_chr_fe_claim(chr) != 0) {
+ return -EEXIST;
+ }
+ *ptr = chr;
+ return 0;
+}
+
+static void release_chr(Object *obj, const char *name, void *opaque)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ CharDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+ CharDriverState *chr = *ptr;
+
+ if (chr) {
+ qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL);
+ qemu_chr_fe_release(chr);
+ }
+}
+
+
+static const char *print_chr(void *ptr)
+{
+ CharDriverState *chr = ptr;
+
+ return chr->label ? chr->label : "";
+}
+
+static void get_chr(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ get_pointer(obj, v, opaque, print_chr, name, errp);
+}
+
+static void set_chr(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ set_pointer(obj, v, opaque, parse_chr, name, errp);
+}
+
+PropertyInfo qdev_prop_chr = {
+ .name = "chr",
+ .get = get_chr,
+ .set = set_chr,
+ .release = release_chr,
+};
+
+/* --- netdev device --- */
+
+static int parse_netdev(DeviceState *dev, const char *str, void **ptr)
+{
+ NICPeers *peers_ptr = (NICPeers *)ptr;
+ NICConf *conf = container_of(peers_ptr, NICConf, peers);
+ NetClientState **ncs = peers_ptr->ncs;
+ NetClientState *peers[MAX_QUEUE_NUM];
+ int queues, i = 0;
+ int ret;
+
+ queues = qemu_find_net_clients_except(str, peers,
+ NET_CLIENT_OPTIONS_KIND_NIC,
+ MAX_QUEUE_NUM);
+ if (queues == 0) {
+ ret = -ENOENT;
+ goto err;
+ }
+
+ if (queues > MAX_QUEUE_NUM) {
+ ret = -E2BIG;
+ goto err;
+ }
+
+ for (i = 0; i < queues; i++) {
+ if (peers[i] == NULL) {
+ ret = -ENOENT;
+ goto err;
+ }
+
+ if (peers[i]->peer) {
+ ret = -EEXIST;
+ goto err;
+ }
+
+ ncs[i] = peers[i];
+ ncs[i]->queue_index = i;
+ }
+
+ conf->queues = queues;
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static const char *print_netdev(void *ptr)
+{
+ NetClientState *netdev = ptr;
+
+ return netdev->name ? netdev->name : "";
+}
+
+static void get_netdev(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ get_pointer(obj, v, opaque, print_netdev, name, errp);
+}
+
+static void set_netdev(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ set_pointer(obj, v, opaque, parse_netdev, name, errp);
+}
+
+PropertyInfo qdev_prop_netdev = {
+ .name = "netdev",
+ .get = get_netdev,
+ .set = set_netdev,
+};
+
+/* --- vlan --- */
+
+static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr) {
+ int id;
+ if (!net_hub_id_for_client(*ptr, &id)) {
+ return snprintf(dest, len, "%d", id);
+ }
+ }
+
+ return snprintf(dest, len, "<null>");
+}
+
+static void get_vlan(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
+ int32_t id = -1;
+
+ if (*ptr) {
+ int hub_id;
+ if (!net_hub_id_for_client(*ptr, &hub_id)) {
+ id = hub_id;
+ }
+ }
+
+ visit_type_int32(v, &id, name, errp);
+}
+
+static void set_vlan(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
+ NetClientState **ptr = &peers_ptr->ncs[0];
+ Error *local_err = NULL;
+ int32_t id;
+ NetClientState *hubport;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_int32(v, &id, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ if (id == -1) {
+ *ptr = NULL;
+ return;
+ }
+
+ hubport = net_hub_port_find(id);
+ if (!hubport) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+ name, prop->info->name);
+ return;
+ }
+ *ptr = hubport;
+}
+
+PropertyInfo qdev_prop_vlan = {
+ .name = "vlan",
+ .print = print_vlan,
+ .get = get_vlan,
+ .set = set_vlan,
+};
+
+int qdev_prop_set_drive(DeviceState *dev, const char *name,
+ BlockDriverState *value)
+{
+ Error *errp = NULL;
+ const char *bdrv_name = value ? bdrv_get_device_name(value) : "";
+ object_property_set_str(OBJECT(dev), bdrv_name,
+ name, &errp);
+ if (errp) {
+ qerror_report_err(errp);
+ error_free(errp);
+ return -1;
+ }
+ return 0;
+}
+
+void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name,
+ BlockDriverState *value)
+{
+ if (qdev_prop_set_drive(dev, name, value) < 0) {
+ exit(1);
+ }
+}
+void qdev_prop_set_chr(DeviceState *dev, const char *name,
+ CharDriverState *value)
+{
+ Error *errp = NULL;
+ assert(!value || value->label);
+ object_property_set_str(OBJECT(dev),
+ value ? value->label : "", name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_netdev(DeviceState *dev, const char *name,
+ NetClientState *value)
+{
+ Error *errp = NULL;
+ assert(!value || value->name);
+ object_property_set_str(OBJECT(dev),
+ value ? value->name : "", name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
+{
+ qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a);
+ if (nd->netdev) {
+ qdev_prop_set_netdev(dev, "netdev", nd->netdev);
+ }
+ if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
+ object_property_find(OBJECT(dev), "vectors", NULL)) {
+ qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
+ }
+ nd->instantiated = 1;
+}
+
+static int qdev_add_one_global(QemuOpts *opts, void *opaque)
+{
+ GlobalProperty *g;
+
+ g = g_malloc0(sizeof(*g));
+ g->driver = qemu_opt_get(opts, "driver");
+ g->property = qemu_opt_get(opts, "property");
+ g->value = qemu_opt_get(opts, "value");
+ qdev_prop_register_global(g);
+ return 0;
+}
+
+void qemu_add_globals(void)
+{
+ qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0);
+}
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
new file mode 100644
index 0000000000..9a0872d3b9
--- /dev/null
+++ b/hw/core/qdev-properties.c
@@ -0,0 +1,1092 @@
+#include "net/net.h"
+#include "hw/qdev.h"
+#include "qapi/qmp/qerror.h"
+#include "sysemu/blockdev.h"
+#include "hw/block/block.h"
+#include "net/hub.h"
+#include "qapi/visitor.h"
+#include "char/char.h"
+
+void qdev_prop_set_after_realize(DeviceState *dev, const char *name,
+ Error **errp)
+{
+ if (dev->id) {
+ error_setg(errp, "Attempt to set property '%s' on device '%s' "
+ "(type '%s') after it was realized", name, dev->id,
+ object_get_typename(OBJECT(dev)));
+ } else {
+ error_setg(errp, "Attempt to set property '%s' on anonymous device "
+ "(type '%s') after it was realized", name,
+ object_get_typename(OBJECT(dev)));
+ }
+}
+
+void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
+{
+ void *ptr = dev;
+ ptr += prop->offset;
+ return ptr;
+}
+
+static void get_enum(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ int *ptr = qdev_get_prop_ptr(dev, prop);
+
+ visit_type_enum(v, ptr, prop->info->enum_table,
+ prop->info->name, prop->name, errp);
+}
+
+static void set_enum(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ int *ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_enum(v, ptr, prop->info->enum_table,
+ prop->info->name, prop->name, errp);
+}
+
+/* Bit */
+
+static uint32_t qdev_get_prop_mask(Property *prop)
+{
+ assert(prop->info == &qdev_prop_bit);
+ return 0x1 << prop->bitnr;
+}
+
+static void bit_prop_set(DeviceState *dev, Property *props, bool val)
+{
+ uint32_t *p = qdev_get_prop_ptr(dev, props);
+ uint32_t mask = qdev_get_prop_mask(props);
+ if (val) {
+ *p |= mask;
+ } else {
+ *p &= ~mask;
+ }
+}
+
+static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint32_t *p = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, (*p & qdev_get_prop_mask(prop)) ? "on" : "off");
+}
+
+static void get_bit(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint32_t *p = qdev_get_prop_ptr(dev, prop);
+ bool value = (*p & qdev_get_prop_mask(prop)) != 0;
+
+ visit_type_bool(v, &value, name, errp);
+}
+
+static void set_bit(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ Error *local_err = NULL;
+ bool value;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_bool(v, &value, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ bit_prop_set(dev, prop, value);
+}
+
+PropertyInfo qdev_prop_bit = {
+ .name = "boolean",
+ .legacy_name = "on/off",
+ .print = print_bit,
+ .get = get_bit,
+ .set = set_bit,
+};
+
+/* --- 8bit integer --- */
+
+static void get_uint8(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ visit_type_uint8(v, ptr, name, errp);
+}
+
+static void set_uint8(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_uint8(v, ptr, name, errp);
+}
+
+PropertyInfo qdev_prop_uint8 = {
+ .name = "uint8",
+ .get = get_uint8,
+ .set = set_uint8,
+};
+
+/* --- 8bit hex value --- */
+
+static int parse_hex8(DeviceState *dev, Property *prop, const char *str)
+{
+ uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ if (str[0] != '0' || str[1] != 'x') {
+ return -EINVAL;
+ }
+
+ *ptr = strtoul(str, &end, 16);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_hex8(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "0x%" PRIx8, *ptr);
+}
+
+PropertyInfo qdev_prop_hex8 = {
+ .name = "uint8",
+ .legacy_name = "hex8",
+ .parse = parse_hex8,
+ .print = print_hex8,
+ .get = get_uint8,
+ .set = set_uint8,
+};
+
+/* --- 16bit integer --- */
+
+static void get_uint16(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ visit_type_uint16(v, ptr, name, errp);
+}
+
+static void set_uint16(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_uint16(v, ptr, name, errp);
+}
+
+PropertyInfo qdev_prop_uint16 = {
+ .name = "uint16",
+ .get = get_uint16,
+ .set = set_uint16,
+};
+
+/* --- 32bit integer --- */
+
+static void get_uint32(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ visit_type_uint32(v, ptr, name, errp);
+}
+
+static void set_uint32(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_uint32(v, ptr, name, errp);
+}
+
+static void get_int32(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ int32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ visit_type_int32(v, ptr, name, errp);
+}
+
+static void set_int32(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ int32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_int32(v, ptr, name, errp);
+}
+
+PropertyInfo qdev_prop_uint32 = {
+ .name = "uint32",
+ .get = get_uint32,
+ .set = set_uint32,
+};
+
+PropertyInfo qdev_prop_int32 = {
+ .name = "int32",
+ .get = get_int32,
+ .set = set_int32,
+};
+
+/* --- 32bit hex value --- */
+
+static int parse_hex32(DeviceState *dev, Property *prop, const char *str)
+{
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ if (str[0] != '0' || str[1] != 'x') {
+ return -EINVAL;
+ }
+
+ *ptr = strtoul(str, &end, 16);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_hex32(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "0x%" PRIx32, *ptr);
+}
+
+PropertyInfo qdev_prop_hex32 = {
+ .name = "uint32",
+ .legacy_name = "hex32",
+ .parse = parse_hex32,
+ .print = print_hex32,
+ .get = get_uint32,
+ .set = set_uint32,
+};
+
+/* --- 64bit integer --- */
+
+static void get_uint64(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ visit_type_uint64(v, ptr, name, errp);
+}
+
+static void set_uint64(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_uint64(v, ptr, name, errp);
+}
+
+PropertyInfo qdev_prop_uint64 = {
+ .name = "uint64",
+ .get = get_uint64,
+ .set = set_uint64,
+};
+
+/* --- 64bit hex value --- */
+
+static int parse_hex64(DeviceState *dev, Property *prop, const char *str)
+{
+ uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ if (str[0] != '0' || str[1] != 'x') {
+ return -EINVAL;
+ }
+
+ *ptr = strtoull(str, &end, 16);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_hex64(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "0x%" PRIx64, *ptr);
+}
+
+PropertyInfo qdev_prop_hex64 = {
+ .name = "uint64",
+ .legacy_name = "hex64",
+ .parse = parse_hex64,
+ .print = print_hex64,
+ .get = get_uint64,
+ .set = set_uint64,
+};
+
+/* --- string --- */
+
+static void release_string(Object *obj, const char *name, void *opaque)
+{
+ Property *prop = opaque;
+ g_free(*(char **)qdev_get_prop_ptr(DEVICE(obj), prop));
+}
+
+static int print_string(DeviceState *dev, Property *prop, char *dest,
+ size_t len)
+{
+ char **ptr = qdev_get_prop_ptr(dev, prop);
+ if (!*ptr) {
+ return snprintf(dest, len, "<null>");
+ }
+ return snprintf(dest, len, "\"%s\"", *ptr);
+}
+
+static void get_string(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ char **ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (!*ptr) {
+ char *str = (char *)"";
+ visit_type_str(v, &str, name, errp);
+ } else {
+ visit_type_str(v, ptr, name, errp);
+ }
+}
+
+static void set_string(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ char **ptr = qdev_get_prop_ptr(dev, prop);
+ Error *local_err = NULL;
+ char *str;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_str(v, &str, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ if (*ptr) {
+ g_free(*ptr);
+ }
+ *ptr = str;
+}
+
+PropertyInfo qdev_prop_string = {
+ .name = "string",
+ .print = print_string,
+ .release = release_string,
+ .get = get_string,
+ .set = set_string,
+};
+
+/* --- pointer --- */
+
+/* Not a proper property, just for dirty hacks. TODO Remove it! */
+PropertyInfo qdev_prop_ptr = {
+ .name = "ptr",
+};
+
+/* --- mac address --- */
+
+/*
+ * accepted syntax versions:
+ * 01:02:03:04:05:06
+ * 01-02-03-04-05-06
+ */
+static void get_mac(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ MACAddr *mac = qdev_get_prop_ptr(dev, prop);
+ char buffer[2 * 6 + 5 + 1];
+ char *p = buffer;
+
+ snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac->a[0], mac->a[1], mac->a[2],
+ mac->a[3], mac->a[4], mac->a[5]);
+
+ visit_type_str(v, &p, name, errp);
+}
+
+static void set_mac(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ MACAddr *mac = qdev_get_prop_ptr(dev, prop);
+ Error *local_err = NULL;
+ int i, pos;
+ char *str, *p;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_str(v, &str, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ for (i = 0, pos = 0; i < 6; i++, pos += 3) {
+ if (!qemu_isxdigit(str[pos])) {
+ goto inval;
+ }
+ if (!qemu_isxdigit(str[pos+1])) {
+ goto inval;
+ }
+ if (i == 5) {
+ if (str[pos+2] != '\0') {
+ goto inval;
+ }
+ } else {
+ if (str[pos+2] != ':' && str[pos+2] != '-') {
+ goto inval;
+ }
+ }
+ mac->a[i] = strtol(str+pos, &p, 16);
+ }
+ g_free(str);
+ return;
+
+inval:
+ error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
+ g_free(str);
+}
+
+PropertyInfo qdev_prop_macaddr = {
+ .name = "macaddr",
+ .get = get_mac,
+ .set = set_mac,
+};
+
+/* --- lost tick policy --- */
+
+static const char *lost_tick_policy_table[LOST_TICK_MAX+1] = {
+ [LOST_TICK_DISCARD] = "discard",
+ [LOST_TICK_DELAY] = "delay",
+ [LOST_TICK_MERGE] = "merge",
+ [LOST_TICK_SLEW] = "slew",
+ [LOST_TICK_MAX] = NULL,
+};
+
+QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int));
+
+PropertyInfo qdev_prop_losttickpolicy = {
+ .name = "LostTickPolicy",
+ .enum_table = lost_tick_policy_table,
+ .get = get_enum,
+ .set = set_enum,
+};
+
+/* --- BIOS CHS translation */
+
+static const char *bios_chs_trans_table[] = {
+ [BIOS_ATA_TRANSLATION_AUTO] = "auto",
+ [BIOS_ATA_TRANSLATION_NONE] = "none",
+ [BIOS_ATA_TRANSLATION_LBA] = "lba",
+};
+
+PropertyInfo qdev_prop_bios_chs_trans = {
+ .name = "bios-chs-trans",
+ .enum_table = bios_chs_trans_table,
+ .get = get_enum,
+ .set = set_enum,
+};
+
+/* --- pci address --- */
+
+/*
+ * bus-local address, i.e. "$slot" or "$slot.$fn"
+ */
+static void set_pci_devfn(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ int32_t value, *ptr = qdev_get_prop_ptr(dev, prop);
+ unsigned int slot, fn, n;
+ Error *local_err = NULL;
+ char *str;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_str(v, &str, name, &local_err);
+ if (local_err) {
+ error_free(local_err);
+ local_err = NULL;
+ visit_type_int32(v, &value, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ } else if (value < -1 || value > 255) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+ "pci_devfn");
+ } else {
+ *ptr = value;
+ }
+ return;
+ }
+
+ if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) {
+ fn = 0;
+ if (sscanf(str, "%x%n", &slot, &n) != 1) {
+ goto invalid;
+ }
+ }
+ if (str[n] != '\0' || fn > 7 || slot > 31) {
+ goto invalid;
+ }
+ *ptr = slot << 3 | fn;
+ g_free(str);
+ return;
+
+invalid:
+ error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
+ g_free(str);
+}
+
+static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest,
+ size_t len)
+{
+ int32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr == -1) {
+ return snprintf(dest, len, "<unset>");
+ } else {
+ return snprintf(dest, len, "%02x.%x", *ptr >> 3, *ptr & 7);
+ }
+}
+
+PropertyInfo qdev_prop_pci_devfn = {
+ .name = "int32",
+ .legacy_name = "pci-devfn",
+ .print = print_pci_devfn,
+ .get = get_int32,
+ .set = set_pci_devfn,
+};
+
+/* --- blocksize --- */
+
+static void set_blocksize(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint16_t value, *ptr = qdev_get_prop_ptr(dev, prop);
+ Error *local_err = NULL;
+ const int64_t min = 512;
+ const int64_t max = 32768;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_uint16(v, &value, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ if (value < min || value > max) {
+ error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
+ dev->id?:"", name, (int64_t)value, min, max);
+ return;
+ }
+
+ /* We rely on power-of-2 blocksizes for bitmasks */
+ if ((value & (value - 1)) != 0) {
+ error_set(errp, QERR_PROPERTY_VALUE_NOT_POWER_OF_2,
+ dev->id?:"", name, (int64_t)value);
+ return;
+ }
+
+ *ptr = value;
+}
+
+PropertyInfo qdev_prop_blocksize = {
+ .name = "blocksize",
+ .get = get_uint16,
+ .set = set_blocksize,
+};
+
+/* --- pci host address --- */
+
+static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop);
+ char buffer[] = "xxxx:xx:xx.x";
+ char *p = buffer;
+ int rc = 0;
+
+ rc = snprintf(buffer, sizeof(buffer), "%04x:%02x:%02x.%d",
+ addr->domain, addr->bus, addr->slot, addr->function);
+ assert(rc == sizeof(buffer) - 1);
+
+ visit_type_str(v, &p, name, errp);
+}
+
+/*
+ * Parse [<domain>:]<bus>:<slot>.<func>
+ * if <domain> is not supplied, it's assumed to be 0.
+ */
+static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop);
+ Error *local_err = NULL;
+ char *str, *p;
+ char *e;
+ unsigned long val;
+ unsigned long dom = 0, bus = 0;
+ unsigned int slot = 0, func = 0;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_str(v, &str, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ p = str;
+ val = strtoul(p, &e, 16);
+ if (e == p || *e != ':') {
+ goto inval;
+ }
+ bus = val;
+
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p) {
+ goto inval;
+ }
+ if (*e == ':') {
+ dom = bus;
+ bus = val;
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p) {
+ goto inval;
+ }
+ }
+ slot = val;
+
+ if (*e != '.') {
+ goto inval;
+ }
+ p = e + 1;
+ val = strtoul(p, &e, 10);
+ if (e == p) {
+ goto inval;
+ }
+ func = val;
+
+ if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) {
+ goto inval;
+ }
+
+ if (*e) {
+ goto inval;
+ }
+
+ addr->domain = dom;
+ addr->bus = bus;
+ addr->slot = slot;
+ addr->function = func;
+
+ g_free(str);
+ return;
+
+inval:
+ error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
+ g_free(str);
+}
+
+PropertyInfo qdev_prop_pci_host_devaddr = {
+ .name = "pci-host-devaddr",
+ .get = get_pci_host_devaddr,
+ .set = set_pci_host_devaddr,
+};
+
+/* --- support for array properties --- */
+
+/* Used as an opaque for the object properties we add for each
+ * array element. Note that the struct Property must be first
+ * in the struct so that a pointer to this works as the opaque
+ * for the underlying element's property hooks as well as for
+ * our own release callback.
+ */
+typedef struct {
+ struct Property prop;
+ char *propname;
+ ObjectPropertyRelease *release;
+} ArrayElementProperty;
+
+/* object property release callback for array element properties:
+ * we call the underlying element's property release hook, and
+ * then free the memory we allocated when we added the property.
+ */
+static void array_element_release(Object *obj, const char *name, void *opaque)
+{
+ ArrayElementProperty *p = opaque;
+ if (p->release) {
+ p->release(obj, name, opaque);
+ }
+ g_free(p->propname);
+ g_free(p);
+}
+
+static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ /* Setter for the property which defines the length of a
+ * variable-sized property array. As well as actually setting the
+ * array-length field in the device struct, we have to create the
+ * array itself and dynamically add the corresponding properties.
+ */
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint32_t *alenptr = qdev_get_prop_ptr(dev, prop);
+ void **arrayptr = (void *)dev + prop->arrayoffset;
+ void *eltptr;
+ const char *arrayname;
+ int i;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+ if (*alenptr) {
+ error_setg(errp, "array size property %s may not be set more than once",
+ name);
+ return;
+ }
+ visit_type_uint32(v, alenptr, name, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ if (!*alenptr) {
+ return;
+ }
+
+ /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix;
+ * strip it off so we can get the name of the array itself.
+ */
+ assert(strncmp(name, PROP_ARRAY_LEN_PREFIX,
+ strlen(PROP_ARRAY_LEN_PREFIX)) == 0);
+ arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX);
+
+ /* Note that it is the responsibility of the individual device's deinit
+ * to free the array proper.
+ */
+ *arrayptr = eltptr = g_malloc0(*alenptr * prop->arrayfieldsize);
+ for (i = 0; i < *alenptr; i++, eltptr += prop->arrayfieldsize) {
+ char *propname = g_strdup_printf("%s[%d]", arrayname, i);
+ ArrayElementProperty *arrayprop = g_new0(ArrayElementProperty, 1);
+ arrayprop->release = prop->arrayinfo->release;
+ arrayprop->propname = propname;
+ arrayprop->prop.info = prop->arrayinfo;
+ arrayprop->prop.name = propname;
+ /* This ugly piece of pointer arithmetic sets up the offset so
+ * that when the underlying get/set hooks call qdev_get_prop_ptr
+ * they get the right answer despite the array element not actually
+ * being inside the device struct.
+ */
+ arrayprop->prop.offset = eltptr - (void *)dev;
+ assert(qdev_get_prop_ptr(dev, &arrayprop->prop) == eltptr);
+ object_property_add(obj, propname,
+ arrayprop->prop.info->name,
+ arrayprop->prop.info->get,
+ arrayprop->prop.info->set,
+ array_element_release,
+ arrayprop, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ }
+}
+
+PropertyInfo qdev_prop_arraylen = {
+ .name = "uint32",
+ .get = get_uint32,
+ .set = set_prop_arraylen,
+};
+
+/* --- public helpers --- */
+
+static Property *qdev_prop_walk(Property *props, const char *name)
+{
+ if (!props) {
+ return NULL;
+ }
+ while (props->name) {
+ if (strcmp(props->name, name) == 0) {
+ return props;
+ }
+ props++;
+ }
+ return NULL;
+}
+
+static Property *qdev_prop_find(DeviceState *dev, const char *name)
+{
+ ObjectClass *class;
+ Property *prop;
+
+ /* device properties */
+ class = object_get_class(OBJECT(dev));
+ do {
+ prop = qdev_prop_walk(DEVICE_CLASS(class)->props, name);
+ if (prop) {
+ return prop;
+ }
+ class = object_class_get_parent(class);
+ } while (class != object_class_by_name(TYPE_DEVICE));
+
+ return NULL;
+}
+
+void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev,
+ Property *prop, const char *value)
+{
+ switch (ret) {
+ case -EEXIST:
+ error_set(errp, QERR_PROPERTY_VALUE_IN_USE,
+ object_get_typename(OBJECT(dev)), prop->name, value);
+ break;
+ default:
+ case -EINVAL:
+ error_set(errp, QERR_PROPERTY_VALUE_BAD,
+ object_get_typename(OBJECT(dev)), prop->name, value);
+ break;
+ case -ENOENT:
+ error_set(errp, QERR_PROPERTY_VALUE_NOT_FOUND,
+ object_get_typename(OBJECT(dev)), prop->name, value);
+ break;
+ case 0:
+ break;
+ }
+}
+
+int qdev_prop_parse(DeviceState *dev, const char *name, const char *value)
+{
+ char *legacy_name;
+ Error *err = NULL;
+
+ legacy_name = g_strdup_printf("legacy-%s", name);
+ if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
+ object_property_parse(OBJECT(dev), value, legacy_name, &err);
+ } else {
+ object_property_parse(OBJECT(dev), value, name, &err);
+ }
+ g_free(legacy_name);
+
+ if (err) {
+ qerror_report_err(err);
+ error_free(err);
+ return -1;
+ }
+ return 0;
+}
+
+void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value)
+{
+ Error *errp = NULL;
+ object_property_set_bool(OBJECT(dev), value, name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value)
+{
+ Error *errp = NULL;
+ object_property_set_int(OBJECT(dev), value, name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value)
+{
+ Error *errp = NULL;
+ object_property_set_int(OBJECT(dev), value, name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value)
+{
+ Error *errp = NULL;
+ object_property_set_int(OBJECT(dev), value, name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value)
+{
+ Error *errp = NULL;
+ object_property_set_int(OBJECT(dev), value, name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value)
+{
+ Error *errp = NULL;
+ object_property_set_int(OBJECT(dev), value, name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value)
+{
+ Error *errp = NULL;
+ object_property_set_str(OBJECT(dev), value, name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value)
+{
+ Error *errp = NULL;
+ char str[2 * 6 + 5 + 1];
+ snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
+ value[0], value[1], value[2], value[3], value[4], value[5]);
+
+ object_property_set_str(OBJECT(dev), str, name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
+{
+ Property *prop;
+ Error *errp = NULL;
+
+ prop = qdev_prop_find(dev, name);
+ object_property_set_str(OBJECT(dev), prop->info->enum_table[value],
+ name, &errp);
+ assert_no_error(errp);
+}
+
+void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value)
+{
+ Property *prop;
+ void **ptr;
+
+ prop = qdev_prop_find(dev, name);
+ assert(prop && prop->info == &qdev_prop_ptr);
+ ptr = qdev_get_prop_ptr(dev, prop);
+ *ptr = value;
+}
+
+static QTAILQ_HEAD(, GlobalProperty) global_props =
+ QTAILQ_HEAD_INITIALIZER(global_props);
+
+void qdev_prop_register_global(GlobalProperty *prop)
+{
+ QTAILQ_INSERT_TAIL(&global_props, prop, next);
+}
+
+void qdev_prop_register_global_list(GlobalProperty *props)
+{
+ int i;
+
+ for (i = 0; props[i].driver != NULL; i++) {
+ qdev_prop_register_global(props+i);
+ }
+}
+
+void qdev_prop_set_globals(DeviceState *dev)
+{
+ ObjectClass *class = object_get_class(OBJECT(dev));
+
+ do {
+ GlobalProperty *prop;
+ QTAILQ_FOREACH(prop, &global_props, next) {
+ if (strcmp(object_class_get_name(class), prop->driver) != 0) {
+ continue;
+ }
+ if (qdev_prop_parse(dev, prop->property, prop->value) != 0) {
+ exit(1);
+ }
+ }
+ class = object_class_get_parent(class);
+ } while (class);
+}
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
new file mode 100644
index 0000000000..e2bb37dc37
--- /dev/null
+++ b/hw/core/qdev.c
@@ -0,0 +1,882 @@
+/*
+ * Dynamic device configuration and creation.
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* The theory here is that it should be possible to create a machine without
+ knowledge of specific devices. Historically board init routines have
+ passed a bunch of arguments to each device, requiring the board know
+ exactly which device it is dealing with. This file provides an abstract
+ API for device configuration and initialization. Devices will generally
+ inherit from a particular bus (e.g. PCI or I2C) rather than
+ this API directly. */
+
+#include "hw/qdev.h"
+#include "sysemu/sysemu.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/visitor.h"
+#include "qapi/qmp/qjson.h"
+#include "monitor/monitor.h"
+
+int qdev_hotplug = 0;
+static bool qdev_hot_added = false;
+static bool qdev_hot_removed = false;
+
+const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ return dc->vmsd;
+}
+
+const char *qdev_fw_name(DeviceState *dev)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+
+ if (dc->fw_name) {
+ return dc->fw_name;
+ }
+
+ return object_get_typename(OBJECT(dev));
+}
+
+static void qdev_property_add_legacy(DeviceState *dev, Property *prop,
+ Error **errp);
+
+static void bus_remove_child(BusState *bus, DeviceState *child)
+{
+ BusChild *kid;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ if (kid->child == child) {
+ char name[32];
+
+ snprintf(name, sizeof(name), "child[%d]", kid->index);
+ QTAILQ_REMOVE(&bus->children, kid, sibling);
+
+ /* This gives back ownership of kid->child back to us. */
+ object_property_del(OBJECT(bus), name, NULL);
+ object_unref(OBJECT(kid->child));
+ g_free(kid);
+ return;
+ }
+ }
+}
+
+static void bus_add_child(BusState *bus, DeviceState *child)
+{
+ char name[32];
+ BusChild *kid = g_malloc0(sizeof(*kid));
+
+ if (qdev_hotplug) {
+ assert(bus->allow_hotplug);
+ }
+
+ kid->index = bus->max_index++;
+ kid->child = child;
+ object_ref(OBJECT(kid->child));
+
+ QTAILQ_INSERT_HEAD(&bus->children, kid, sibling);
+
+ /* This transfers ownership of kid->child to the property. */
+ snprintf(name, sizeof(name), "child[%d]", kid->index);
+ object_property_add_link(OBJECT(bus), name,
+ object_get_typename(OBJECT(child)),
+ (Object **)&kid->child,
+ NULL);
+}
+
+void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
+{
+ dev->parent_bus = bus;
+ object_ref(OBJECT(bus));
+ bus_add_child(bus, dev);
+}
+
+/* Create a new device. This only initializes the device state structure
+ and allows properties to be set. qdev_init should be called to
+ initialize the actual device emulation. */
+DeviceState *qdev_create(BusState *bus, const char *name)
+{
+ DeviceState *dev;
+
+ dev = qdev_try_create(bus, name);
+ if (!dev) {
+ if (bus) {
+ error_report("Unknown device '%s' for bus '%s'", name,
+ object_get_typename(OBJECT(bus)));
+ } else {
+ error_report("Unknown device '%s' for default sysbus", name);
+ }
+ abort();
+ }
+
+ return dev;
+}
+
+DeviceState *qdev_try_create(BusState *bus, const char *type)
+{
+ DeviceState *dev;
+
+ if (object_class_by_name(type) == NULL) {
+ return NULL;
+ }
+ dev = DEVICE(object_new(type));
+ if (!dev) {
+ return NULL;
+ }
+
+ if (!bus) {
+ bus = sysbus_get_default();
+ }
+
+ qdev_set_parent_bus(dev, bus);
+ object_unref(OBJECT(dev));
+ return dev;
+}
+
+/* Initialize a device. Device properties should be set before calling
+ this function. IRQs and MMIO regions should be connected/mapped after
+ calling this function.
+ On failure, destroy the device and return negative value.
+ Return 0 on success. */
+int qdev_init(DeviceState *dev)
+{
+ Error *local_err = NULL;
+
+ assert(!dev->realized);
+
+ object_property_set_bool(OBJECT(dev), true, "realized", &local_err);
+ if (local_err != NULL) {
+ error_free(local_err);
+ qdev_free(dev);
+ return -1;
+ }
+ return 0;
+}
+
+static void device_realize(DeviceState *dev, Error **err)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+
+ if (dc->init) {
+ int rc = dc->init(dev);
+ if (rc < 0) {
+ error_setg(err, "Device initialization failed.");
+ return;
+ }
+ }
+}
+
+void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
+ int required_for_version)
+{
+ assert(!dev->realized);
+ dev->instance_id_alias = alias_id;
+ dev->alias_required_for_version = required_for_version;
+}
+
+void qdev_unplug(DeviceState *dev, Error **errp)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+
+ if (!dev->parent_bus->allow_hotplug) {
+ error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
+ return;
+ }
+ assert(dc->unplug != NULL);
+
+ qdev_hot_removed = true;
+
+ if (dc->unplug(dev) < 0) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ return;
+ }
+}
+
+static int qdev_reset_one(DeviceState *dev, void *opaque)
+{
+ device_reset(dev);
+
+ return 0;
+}
+
+static int qbus_reset_one(BusState *bus, void *opaque)
+{
+ BusClass *bc = BUS_GET_CLASS(bus);
+ if (bc->reset) {
+ return bc->reset(bus);
+ }
+ return 0;
+}
+
+void qdev_reset_all(DeviceState *dev)
+{
+ qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
+}
+
+void qbus_reset_all(BusState *bus)
+{
+ qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
+}
+
+void qbus_reset_all_fn(void *opaque)
+{
+ BusState *bus = opaque;
+ qbus_reset_all(bus);
+}
+
+/* can be used as ->unplug() callback for the simple cases */
+int qdev_simple_unplug_cb(DeviceState *dev)
+{
+ /* just zap it */
+ qdev_free(dev);
+ return 0;
+}
+
+
+/* Like qdev_init(), but terminate program via error_report() instead of
+ returning an error value. This is okay during machine creation.
+ Don't use for hotplug, because there callers need to recover from
+ failure. Exception: if you know the device's init() callback can't
+ fail, then qdev_init_nofail() can't fail either, and is therefore
+ usable even then. But relying on the device implementation that
+ way is somewhat unclean, and best avoided. */
+void qdev_init_nofail(DeviceState *dev)
+{
+ const char *typename = object_get_typename(OBJECT(dev));
+
+ if (qdev_init(dev) < 0) {
+ error_report("Initialization of device %s failed", typename);
+ exit(1);
+ }
+}
+
+/* Unlink device from bus and free the structure. */
+void qdev_free(DeviceState *dev)
+{
+ object_unparent(OBJECT(dev));
+}
+
+void qdev_machine_creation_done(void)
+{
+ /*
+ * ok, initial machine setup is done, starting from now we can
+ * only create hotpluggable devices
+ */
+ qdev_hotplug = 1;
+}
+
+bool qdev_machine_modified(void)
+{
+ return qdev_hot_added || qdev_hot_removed;
+}
+
+BusState *qdev_get_parent_bus(DeviceState *dev)
+{
+ return dev->parent_bus;
+}
+
+void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
+{
+ dev->gpio_in = qemu_extend_irqs(dev->gpio_in, dev->num_gpio_in, handler,
+ dev, n);
+ dev->num_gpio_in += n;
+}
+
+void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
+{
+ assert(dev->num_gpio_out == 0);
+ dev->num_gpio_out = n;
+ dev->gpio_out = pins;
+}
+
+qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
+{
+ assert(n >= 0 && n < dev->num_gpio_in);
+ return dev->gpio_in[n];
+}
+
+void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
+{
+ assert(n >= 0 && n < dev->num_gpio_out);
+ dev->gpio_out[n] = pin;
+}
+
+BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
+{
+ BusState *bus;
+
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ if (strcmp(name, bus->name) == 0) {
+ return bus;
+ }
+ }
+ return NULL;
+}
+
+int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
+ qbus_walkerfn *busfn, void *opaque)
+{
+ BusChild *kid;
+ int err;
+
+ if (busfn) {
+ err = busfn(bus, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ err = qdev_walk_children(kid->child, devfn, busfn, opaque);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
+ qbus_walkerfn *busfn, void *opaque)
+{
+ BusState *bus;
+ int err;
+
+ if (devfn) {
+ err = devfn(dev, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ err = qbus_walk_children(bus, devfn, busfn, opaque);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+DeviceState *qdev_find_recursive(BusState *bus, const char *id)
+{
+ BusChild *kid;
+ DeviceState *ret;
+ BusState *child;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+
+ if (dev->id && strcmp(dev->id, id) == 0) {
+ return dev;
+ }
+
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ ret = qdev_find_recursive(child, id);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
+{
+ const char *typename = object_get_typename(OBJECT(bus));
+ char *buf;
+ int i,len;
+
+ bus->parent = parent;
+
+ if (name) {
+ bus->name = g_strdup(name);
+ } else if (bus->parent && bus->parent->id) {
+ /* parent device has id -> use it for bus name */
+ len = strlen(bus->parent->id) + 16;
+ buf = g_malloc(len);
+ snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus);
+ bus->name = buf;
+ } else {
+ /* no id -> use lowercase bus type for bus name */
+ len = strlen(typename) + 16;
+ buf = g_malloc(len);
+ len = snprintf(buf, len, "%s.%d", typename,
+ bus->parent ? bus->parent->num_child_bus : 0);
+ for (i = 0; i < len; i++)
+ buf[i] = qemu_tolower(buf[i]);
+ bus->name = buf;
+ }
+
+ if (bus->parent) {
+ QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling);
+ bus->parent->num_child_bus++;
+ object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL);
+ object_unref(OBJECT(bus));
+ } else if (bus != sysbus_get_default()) {
+ /* TODO: once all bus devices are qdevified,
+ only reset handler for main_system_bus should be registered here. */
+ qemu_register_reset(qbus_reset_all_fn, bus);
+ }
+}
+
+static void bus_unparent(Object *obj)
+{
+ BusState *bus = BUS(obj);
+ BusChild *kid;
+
+ while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) {
+ DeviceState *dev = kid->child;
+ qdev_free(dev);
+ }
+ if (bus->parent) {
+ QLIST_REMOVE(bus, sibling);
+ bus->parent->num_child_bus--;
+ bus->parent = NULL;
+ } else {
+ assert(bus != sysbus_get_default()); /* main_system_bus is never freed */
+ qemu_unregister_reset(qbus_reset_all_fn, bus);
+ }
+}
+
+void qbus_create_inplace(void *bus, const char *typename,
+ DeviceState *parent, const char *name)
+{
+ object_initialize(bus, typename);
+ qbus_realize(bus, parent, name);
+}
+
+BusState *qbus_create(const char *typename, DeviceState *parent, const char *name)
+{
+ BusState *bus;
+
+ bus = BUS(object_new(typename));
+ qbus_realize(bus, parent, name);
+
+ return bus;
+}
+
+void qbus_free(BusState *bus)
+{
+ object_unparent(OBJECT(bus));
+}
+
+static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev)
+{
+ BusClass *bc = BUS_GET_CLASS(bus);
+
+ if (bc->get_fw_dev_path) {
+ return bc->get_fw_dev_path(dev);
+ }
+
+ return NULL;
+}
+
+static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size)
+{
+ int l = 0;
+
+ if (dev && dev->parent_bus) {
+ char *d;
+ l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size);
+ d = bus_get_fw_dev_path(dev->parent_bus, dev);
+ if (d) {
+ l += snprintf(p + l, size - l, "%s", d);
+ g_free(d);
+ } else {
+ l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev)));
+ }
+ }
+ l += snprintf(p + l , size - l, "/");
+
+ return l;
+}
+
+char* qdev_get_fw_dev_path(DeviceState *dev)
+{
+ char path[128];
+ int l;
+
+ l = qdev_get_fw_dev_path_helper(dev, path, 128);
+
+ path[l-1] = '\0';
+
+ return g_strdup(path);
+}
+
+char *qdev_get_dev_path(DeviceState *dev)
+{
+ BusClass *bc;
+
+ if (!dev || !dev->parent_bus) {
+ return NULL;
+ }
+
+ bc = BUS_GET_CLASS(dev->parent_bus);
+ if (bc->get_dev_path) {
+ return bc->get_dev_path(dev);
+ }
+
+ return NULL;
+}
+
+/**
+ * Legacy property handling
+ */
+
+static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+
+ char buffer[1024];
+ char *ptr = buffer;
+
+ prop->info->print(dev, prop, buffer, sizeof(buffer));
+ visit_type_str(v, &ptr, name, errp);
+}
+
+static void qdev_set_legacy_property(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ Error *local_err = NULL;
+ char *ptr = NULL;
+ int ret;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_str(v, &ptr, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ ret = prop->info->parse(dev, prop, ptr);
+ error_set_from_qdev_prop_error(errp, ret, dev, prop, ptr);
+ g_free(ptr);
+}
+
+/**
+ * @qdev_add_legacy_property - adds a legacy property
+ *
+ * Do not use this is new code! Properties added through this interface will
+ * be given names and types in the "legacy" namespace.
+ *
+ * Legacy properties are string versions of other OOM properties. The format
+ * of the string depends on the property type.
+ */
+void qdev_property_add_legacy(DeviceState *dev, Property *prop,
+ Error **errp)
+{
+ gchar *name, *type;
+
+ /* Register pointer properties as legacy properties */
+ if (!prop->info->print && !prop->info->parse &&
+ (prop->info->set || prop->info->get)) {
+ return;
+ }
+
+ name = g_strdup_printf("legacy-%s", prop->name);
+ type = g_strdup_printf("legacy<%s>",
+ prop->info->legacy_name ?: prop->info->name);
+
+ object_property_add(OBJECT(dev), name, type,
+ prop->info->print ? qdev_get_legacy_property : prop->info->get,
+ prop->info->parse ? qdev_set_legacy_property : prop->info->set,
+ NULL,
+ prop, errp);
+
+ g_free(type);
+ g_free(name);
+}
+
+/**
+ * @qdev_property_add_static - add a @Property to a device.
+ *
+ * Static properties access data in a struct. The actual type of the
+ * property and the field depends on the property type.
+ */
+void qdev_property_add_static(DeviceState *dev, Property *prop,
+ Error **errp)
+{
+ Error *local_err = NULL;
+ Object *obj = OBJECT(dev);
+
+ /*
+ * TODO qdev_prop_ptr does not have getters or setters. It must
+ * go now that it can be replaced with links. The test should be
+ * removed along with it: all static properties are read/write.
+ */
+ if (!prop->info->get && !prop->info->set) {
+ return;
+ }
+
+ object_property_add(obj, prop->name, prop->info->name,
+ prop->info->get, prop->info->set,
+ prop->info->release,
+ prop, &local_err);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ if (prop->qtype == QTYPE_NONE) {
+ return;
+ }
+
+ if (prop->qtype == QTYPE_QBOOL) {
+ object_property_set_bool(obj, prop->defval, prop->name, &local_err);
+ } else if (prop->info->enum_table) {
+ object_property_set_str(obj, prop->info->enum_table[prop->defval],
+ prop->name, &local_err);
+ } else if (prop->qtype == QTYPE_QINT) {
+ object_property_set_int(obj, prop->defval, prop->name, &local_err);
+ }
+ assert_no_error(local_err);
+}
+
+static bool device_get_realized(Object *obj, Error **err)
+{
+ DeviceState *dev = DEVICE(obj);
+ return dev->realized;
+}
+
+static void device_set_realized(Object *obj, bool value, Error **err)
+{
+ DeviceState *dev = DEVICE(obj);
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ Error *local_err = NULL;
+
+ if (value && !dev->realized) {
+ if (dc->realize) {
+ dc->realize(dev, &local_err);
+ }
+
+ if (!obj->parent && local_err == NULL) {
+ static int unattached_count;
+ gchar *name = g_strdup_printf("device[%d]", unattached_count++);
+
+ object_property_add_child(container_get(qdev_get_machine(),
+ "/unattached"),
+ name, obj, &local_err);
+ g_free(name);
+ }
+
+ if (qdev_get_vmsd(dev) && local_err == NULL) {
+ vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev,
+ dev->instance_id_alias,
+ dev->alias_required_for_version);
+ }
+ if (dev->hotplugged && local_err == NULL) {
+ device_reset(dev);
+ }
+ } else if (!value && dev->realized) {
+ if (dc->unrealize) {
+ dc->unrealize(dev, &local_err);
+ }
+ }
+
+ if (local_err != NULL) {
+ error_propagate(err, local_err);
+ return;
+ }
+
+ dev->realized = value;
+}
+
+static void device_initfn(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ ObjectClass *class;
+ Property *prop;
+ Error *err = NULL;
+
+ if (qdev_hotplug) {
+ dev->hotplugged = 1;
+ qdev_hot_added = true;
+ }
+
+ dev->instance_id_alias = -1;
+ dev->realized = false;
+
+ object_property_add_bool(obj, "realized",
+ device_get_realized, device_set_realized, NULL);
+
+ class = object_get_class(OBJECT(dev));
+ do {
+ for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) {
+ qdev_property_add_legacy(dev, prop, &err);
+ assert_no_error(err);
+ qdev_property_add_static(dev, prop, &err);
+ assert_no_error(err);
+ }
+ class = object_class_get_parent(class);
+ } while (class != object_class_by_name(TYPE_DEVICE));
+ qdev_prop_set_globals(dev);
+
+ object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS,
+ (Object **)&dev->parent_bus, &err);
+ assert_no_error(err);
+}
+
+/* Unlink device from bus and free the structure. */
+static void device_finalize(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ if (dev->opts) {
+ qemu_opts_del(dev->opts);
+ }
+}
+
+static void device_class_base_init(ObjectClass *class, void *data)
+{
+ DeviceClass *klass = DEVICE_CLASS(class);
+
+ /* We explicitly look up properties in the superclasses,
+ * so do not propagate them to the subclasses.
+ */
+ klass->props = NULL;
+}
+
+static void device_unparent(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ BusState *bus;
+ QObject *event_data;
+ bool have_realized = dev->realized;
+
+ while (dev->num_child_bus) {
+ bus = QLIST_FIRST(&dev->child_bus);
+ qbus_free(bus);
+ }
+ if (dev->realized) {
+ if (qdev_get_vmsd(dev)) {
+ vmstate_unregister(dev, qdev_get_vmsd(dev), dev);
+ }
+ if (dc->exit) {
+ dc->exit(dev);
+ }
+ }
+ if (dev->parent_bus) {
+ bus_remove_child(dev->parent_bus, dev);
+ object_unref(OBJECT(dev->parent_bus));
+ dev->parent_bus = NULL;
+ }
+
+ /* Only send event if the device had been completely realized */
+ if (have_realized) {
+ gchar *path = object_get_canonical_path(OBJECT(dev));
+
+ if (dev->id) {
+ event_data = qobject_from_jsonf("{ 'device': %s, 'path': %s }",
+ dev->id, path);
+ } else {
+ event_data = qobject_from_jsonf("{ 'path': %s }", path);
+ }
+ monitor_protocol_event(QEVENT_DEVICE_DELETED, event_data);
+ qobject_decref(event_data);
+ g_free(path);
+ }
+}
+
+static void device_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+
+ class->unparent = device_unparent;
+ dc->realize = device_realize;
+}
+
+void device_reset(DeviceState *dev)
+{
+ DeviceClass *klass = DEVICE_GET_CLASS(dev);
+
+ if (klass->reset) {
+ klass->reset(dev);
+ }
+}
+
+Object *qdev_get_machine(void)
+{
+ static Object *dev;
+
+ if (dev == NULL) {
+ dev = container_get(object_get_root(), "/machine");
+ }
+
+ return dev;
+}
+
+static const TypeInfo device_type_info = {
+ .name = TYPE_DEVICE,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(DeviceState),
+ .instance_init = device_initfn,
+ .instance_finalize = device_finalize,
+ .class_base_init = device_class_base_init,
+ .class_init = device_class_init,
+ .abstract = true,
+ .class_size = sizeof(DeviceClass),
+};
+
+static void qbus_initfn(Object *obj)
+{
+ BusState *bus = BUS(obj);
+
+ QTAILQ_INIT(&bus->children);
+}
+
+static void bus_class_init(ObjectClass *class, void *data)
+{
+ class->unparent = bus_unparent;
+}
+
+static void qbus_finalize(Object *obj)
+{
+ BusState *bus = BUS(obj);
+
+ g_free((char *)bus->name);
+}
+
+static const TypeInfo bus_info = {
+ .name = TYPE_BUS,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(BusState),
+ .abstract = true,
+ .class_size = sizeof(BusClass),
+ .instance_init = qbus_initfn,
+ .instance_finalize = qbus_finalize,
+ .class_init = bus_class_init,
+};
+
+static void qdev_register_types(void)
+{
+ type_register_static(&bus_info);
+ type_register_static(&device_type_info);
+}
+
+type_init(qdev_register_types)
diff --git a/hw/core/stream.c b/hw/core/stream.c
new file mode 100644
index 0000000000..a07d6a56d3
--- /dev/null
+++ b/hw/core/stream.c
@@ -0,0 +1,23 @@
+#include "hw/stream.h"
+
+void
+stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app)
+{
+ StreamSlaveClass *k = STREAM_SLAVE_GET_CLASS(sink);
+
+ k->push(sink, buf, len, app);
+}
+
+static const TypeInfo stream_slave_info = {
+ .name = TYPE_STREAM_SLAVE,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(StreamSlaveClass),
+};
+
+
+static void stream_slave_register_types(void)
+{
+ type_register_static(&stream_slave_info);
+}
+
+type_init(stream_slave_register_types)
diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c
new file mode 100644
index 0000000000..9004d8c543
--- /dev/null
+++ b/hw/core/sysbus.c
@@ -0,0 +1,301 @@
+/*
+ * System (CPU) Bus device support code
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+#include "monitor/monitor.h"
+#include "exec/address-spaces.h"
+
+static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent);
+static char *sysbus_get_fw_dev_path(DeviceState *dev);
+
+static void system_bus_class_init(ObjectClass *klass, void *data)
+{
+ BusClass *k = BUS_CLASS(klass);
+
+ k->print_dev = sysbus_dev_print;
+ k->get_fw_dev_path = sysbus_get_fw_dev_path;
+}
+
+static const TypeInfo system_bus_info = {
+ .name = TYPE_SYSTEM_BUS,
+ .parent = TYPE_BUS,
+ .instance_size = sizeof(BusState),
+ .class_init = system_bus_class_init,
+};
+
+void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq)
+{
+ assert(n >= 0 && n < dev->num_irq);
+ dev->irqs[n] = NULL;
+ if (dev->irqp[n]) {
+ *dev->irqp[n] = irq;
+ }
+}
+
+static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr,
+ bool may_overlap, unsigned priority)
+{
+ assert(n >= 0 && n < dev->num_mmio);
+
+ if (dev->mmio[n].addr == addr) {
+ /* ??? region already mapped here. */
+ return;
+ }
+ if (dev->mmio[n].addr != (hwaddr)-1) {
+ /* Unregister previous mapping. */
+ memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory);
+ }
+ dev->mmio[n].addr = addr;
+ if (may_overlap) {
+ memory_region_add_subregion_overlap(get_system_memory(),
+ addr,
+ dev->mmio[n].memory,
+ priority);
+ }
+ else {
+ memory_region_add_subregion(get_system_memory(),
+ addr,
+ dev->mmio[n].memory);
+ }
+}
+
+void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
+{
+ sysbus_mmio_map_common(dev, n, addr, false, 0);
+}
+
+void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr,
+ unsigned priority)
+{
+ sysbus_mmio_map_common(dev, n, addr, true, priority);
+}
+
+/* Request an IRQ source. The actual IRQ object may be populated later. */
+void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
+{
+ int n;
+
+ assert(dev->num_irq < QDEV_MAX_IRQ);
+ n = dev->num_irq++;
+ dev->irqp[n] = p;
+}
+
+/* Pass IRQs from a target device. */
+void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target)
+{
+ int i;
+ assert(dev->num_irq == 0);
+ dev->num_irq = target->num_irq;
+ for (i = 0; i < dev->num_irq; i++) {
+ dev->irqp[i] = target->irqp[i];
+ }
+}
+
+void sysbus_init_mmio(SysBusDevice *dev, MemoryRegion *memory)
+{
+ int n;
+
+ assert(dev->num_mmio < QDEV_MAX_MMIO);
+ n = dev->num_mmio++;
+ dev->mmio[n].addr = -1;
+ dev->mmio[n].memory = memory;
+}
+
+MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n)
+{
+ return dev->mmio[n].memory;
+}
+
+void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size)
+{
+ pio_addr_t i;
+
+ for (i = 0; i < size; i++) {
+ assert(dev->num_pio < QDEV_MAX_PIO);
+ dev->pio[dev->num_pio++] = ioport++;
+ }
+}
+
+static int sysbus_device_init(DeviceState *dev)
+{
+ SysBusDevice *sd = SYS_BUS_DEVICE(dev);
+ SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(sd);
+
+ if (!sbc->init) {
+ return 0;
+ }
+ return sbc->init(sd);
+}
+
+DeviceState *sysbus_create_varargs(const char *name,
+ hwaddr addr, ...)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ va_list va;
+ qemu_irq irq;
+ int n;
+
+ dev = qdev_create(NULL, name);
+ s = SYS_BUS_DEVICE(dev);
+ qdev_init_nofail(dev);
+ if (addr != (hwaddr)-1) {
+ sysbus_mmio_map(s, 0, addr);
+ }
+ va_start(va, addr);
+ n = 0;
+ while (1) {
+ irq = va_arg(va, qemu_irq);
+ if (!irq) {
+ break;
+ }
+ sysbus_connect_irq(s, n, irq);
+ n++;
+ }
+ va_end(va);
+ return dev;
+}
+
+DeviceState *sysbus_try_create_varargs(const char *name,
+ hwaddr addr, ...)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ va_list va;
+ qemu_irq irq;
+ int n;
+
+ dev = qdev_try_create(NULL, name);
+ if (!dev) {
+ return NULL;
+ }
+ s = SYS_BUS_DEVICE(dev);
+ qdev_init_nofail(dev);
+ if (addr != (hwaddr)-1) {
+ sysbus_mmio_map(s, 0, addr);
+ }
+ va_start(va, addr);
+ n = 0;
+ while (1) {
+ irq = va_arg(va, qemu_irq);
+ if (!irq) {
+ break;
+ }
+ sysbus_connect_irq(s, n, irq);
+ n++;
+ }
+ va_end(va);
+ return dev;
+}
+
+static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+ SysBusDevice *s = SYS_BUS_DEVICE(dev);
+ hwaddr size;
+ int i;
+
+ monitor_printf(mon, "%*sirq %d\n", indent, "", s->num_irq);
+ for (i = 0; i < s->num_mmio; i++) {
+ size = memory_region_size(s->mmio[i].memory);
+ monitor_printf(mon, "%*smmio " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
+ indent, "", s->mmio[i].addr, size);
+ }
+}
+
+static char *sysbus_get_fw_dev_path(DeviceState *dev)
+{
+ SysBusDevice *s = SYS_BUS_DEVICE(dev);
+ char path[40];
+ int off;
+
+ off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
+
+ if (s->num_mmio) {
+ snprintf(path + off, sizeof(path) - off, "@"TARGET_FMT_plx,
+ s->mmio[0].addr);
+ } else if (s->num_pio) {
+ snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]);
+ }
+
+ return g_strdup(path);
+}
+
+void sysbus_add_io(SysBusDevice *dev, hwaddr addr,
+ MemoryRegion *mem)
+{
+ memory_region_add_subregion(get_system_io(), addr, mem);
+}
+
+void sysbus_del_io(SysBusDevice *dev, MemoryRegion *mem)
+{
+ memory_region_del_subregion(get_system_io(), mem);
+}
+
+MemoryRegion *sysbus_address_space(SysBusDevice *dev)
+{
+ return get_system_memory();
+}
+
+static void sysbus_device_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *k = DEVICE_CLASS(klass);
+ k->init = sysbus_device_init;
+ k->bus_type = TYPE_SYSTEM_BUS;
+}
+
+static const TypeInfo sysbus_device_type_info = {
+ .name = TYPE_SYS_BUS_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(SysBusDevice),
+ .abstract = true,
+ .class_size = sizeof(SysBusDeviceClass),
+ .class_init = sysbus_device_class_init,
+};
+
+/* This is a nasty hack to allow passing a NULL bus to qdev_create. */
+static BusState *main_system_bus;
+
+static void main_system_bus_create(void)
+{
+ /* assign main_system_bus before qbus_create_inplace()
+ * in order to make "if (bus != sysbus_get_default())" work */
+ main_system_bus = g_malloc0(system_bus_info.instance_size);
+ qbus_create_inplace(main_system_bus, TYPE_SYSTEM_BUS, NULL,
+ "main-system-bus");
+ OBJECT(main_system_bus)->free = g_free;
+ object_property_add_child(container_get(qdev_get_machine(),
+ "/unattached"),
+ "sysbus", OBJECT(main_system_bus), NULL);
+}
+
+BusState *sysbus_get_default(void)
+{
+ if (!main_system_bus) {
+ main_system_bus_create();
+ }
+ return main_system_bus;
+}
+
+static void sysbus_register_types(void)
+{
+ type_register_static(&system_bus_info);
+ type_register_static(&sysbus_device_type_info);
+}
+
+type_init(sysbus_register_types)