diff options
Diffstat (limited to 'hw/core')
-rw-r--r-- | hw/core/Makefile.objs | 14 | ||||
-rw-r--r-- | hw/core/empty_slot.c | 98 | ||||
-rw-r--r-- | hw/core/irq.c | 136 | ||||
-rw-r--r-- | hw/core/loader.c | 850 | ||||
-rw-r--r-- | hw/core/null-machine.c | 36 | ||||
-rw-r--r-- | hw/core/ptimer.c | 231 | ||||
-rw-r--r-- | hw/core/qdev-addr.c | 78 | ||||
-rw-r--r-- | hw/core/qdev-properties-system.c | 391 | ||||
-rw-r--r-- | hw/core/qdev-properties.c | 1092 | ||||
-rw-r--r-- | hw/core/qdev.c | 882 | ||||
-rw-r--r-- | hw/core/stream.c | 23 | ||||
-rw-r--r-- | hw/core/sysbus.c | 301 |
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) |