diff options
Diffstat (limited to 'qemu/hw/core')
-rw-r--r-- | qemu/hw/core/Makefile.objs | 17 | ||||
-rw-r--r-- | qemu/hw/core/empty_slot.c | 102 | ||||
-rw-r--r-- | qemu/hw/core/fw-path-provider.c | 52 | ||||
-rw-r--r-- | qemu/hw/core/hotplug.c | 59 | ||||
-rw-r--r-- | qemu/hw/core/irq.c | 158 | ||||
-rw-r--r-- | qemu/hw/core/loader.c | 1089 | ||||
-rw-r--r-- | qemu/hw/core/machine.c | 485 | ||||
-rw-r--r-- | qemu/hw/core/nmi.c | 104 | ||||
-rw-r--r-- | qemu/hw/core/null-machine.c | 35 | ||||
-rw-r--r-- | qemu/hw/core/platform-bus.c | 252 | ||||
-rw-r--r-- | qemu/hw/core/ptimer.c | 230 | ||||
-rw-r--r-- | qemu/hw/core/qdev-properties-system.c | 425 | ||||
-rw-r--r-- | qemu/hw/core/qdev-properties.c | 1113 | ||||
-rw-r--r-- | qemu/hw/core/qdev.c | 1349 | ||||
-rw-r--r-- | qemu/hw/core/stream.c | 32 | ||||
-rw-r--r-- | qemu/hw/core/sysbus.c | 369 | ||||
-rw-r--r-- | qemu/hw/core/uboot_image.h | 158 |
17 files changed, 6029 insertions, 0 deletions
diff --git a/qemu/hw/core/Makefile.objs b/qemu/hw/core/Makefile.objs new file mode 100644 index 000000000..abb3560be --- /dev/null +++ b/qemu/hw/core/Makefile.objs @@ -0,0 +1,17 @@ +# core qdev-related obj files, also used by *-user: +common-obj-y += qdev.o qdev-properties.o +common-obj-y += fw-path-provider.o +# irq.o needed for qdev GPIO handling: +common-obj-y += irq.o +common-obj-y += hotplug.o +common-obj-y += nmi.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) += machine.o +common-obj-$(CONFIG_SOFTMMU) += null-machine.o +common-obj-$(CONFIG_SOFTMMU) += loader.o +common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o +common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o diff --git a/qemu/hw/core/empty_slot.c b/qemu/hw/core/empty_slot.c new file mode 100644 index 000000000..612b1093a --- /dev/null +++ b/qemu/hw/core/empty_slot.c @@ -0,0 +1,102 @@ +/* + * 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 + +#define TYPE_EMPTY_SLOT "empty_slot" +#define EMPTY_SLOT(obj) OBJECT_CHECK(EmptySlot, (obj), TYPE_EMPTY_SLOT) + +typedef struct EmptySlot { + SysBusDevice parent_obj; + + 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, TYPE_EMPTY_SLOT); + s = SYS_BUS_DEVICE(dev); + e = EMPTY_SLOT(dev); + e->size = slot_size; + + qdev_init_nofail(dev); + + sysbus_mmio_map(s, 0, addr); + } +} + +static int empty_slot_init1(SysBusDevice *dev) +{ + EmptySlot *s = EMPTY_SLOT(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &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 = TYPE_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/qemu/hw/core/fw-path-provider.c b/qemu/hw/core/fw-path-provider.c new file mode 100644 index 000000000..7442d322d --- /dev/null +++ b/qemu/hw/core/fw-path-provider.c @@ -0,0 +1,52 @@ +/* + * Firmware path provider class and helpers. + * + * 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/fw-path-provider.h" + +char *fw_path_provider_get_dev_path(FWPathProvider *p, BusState *bus, + DeviceState *dev) +{ + FWPathProviderClass *k = FW_PATH_PROVIDER_GET_CLASS(p); + + return k->get_dev_path(p, bus, dev); +} + +char *fw_path_provider_try_get_dev_path(Object *o, BusState *bus, + DeviceState *dev) +{ + FWPathProvider *p = (FWPathProvider *) + object_dynamic_cast(o, TYPE_FW_PATH_PROVIDER); + + if (p) { + return fw_path_provider_get_dev_path(p, bus, dev); + } + + return NULL; +} + +static const TypeInfo fw_path_provider_info = { + .name = TYPE_FW_PATH_PROVIDER, + .parent = TYPE_INTERFACE, + .class_size = sizeof(FWPathProviderClass), +}; + +static void fw_path_provider_register_types(void) +{ + type_register_static(&fw_path_provider_info); +} + +type_init(fw_path_provider_register_types) diff --git a/qemu/hw/core/hotplug.c b/qemu/hw/core/hotplug.c new file mode 100644 index 000000000..4e0107455 --- /dev/null +++ b/qemu/hw/core/hotplug.c @@ -0,0 +1,59 @@ +/* + * Hotplug handler interface. + * + * Copyright (c) 2014 Red Hat Inc. + * + * Authors: + * Igor Mammedov <imammedo@redhat.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 "hw/hotplug.h" +#include "qemu/module.h" + +void hotplug_handler_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->plug) { + hdc->plug(plug_handler, plugged_dev, errp); + } +} + +void hotplug_handler_unplug_request(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->unplug_request) { + hdc->unplug_request(plug_handler, plugged_dev, errp); + } +} + +void hotplug_handler_unplug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->unplug) { + hdc->unplug(plug_handler, plugged_dev, errp); + } +} + +static const TypeInfo hotplug_handler_info = { + .name = TYPE_HOTPLUG_HANDLER, + .parent = TYPE_INTERFACE, + .class_size = sizeof(HotplugHandlerClass), +}; + +static void hotplug_handler_register_types(void) +{ + type_register_static(&hotplug_handler_info); +} + +type_init(hotplug_handler_register_types) diff --git a/qemu/hw/core/irq.c b/qemu/hw/core/irq.c new file mode 100644 index 000000000..8a62a36d5 --- /dev/null +++ b/qemu/hw/core/irq.c @@ -0,0 +1,158 @@ +/* + * 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" +#include "qom/object.h" + +#define IRQ(obj) OBJECT_CHECK(struct IRQState, (obj), TYPE_IRQ) + +struct IRQState { + Object parent_obj; + + 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; + int i; + + if (!old) { + n_old = 0; + } + s = old ? g_renew(qemu_irq, old, n + n_old) : g_new(qemu_irq, n); + for (i = n_old; i < n + n_old; i++) { + s[i] = qemu_allocate_irq(handler, opaque, i); + } + return s; +} + +qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n) +{ + return qemu_extend_irqs(NULL, 0, handler, opaque, n); +} + +qemu_irq qemu_allocate_irq(qemu_irq_handler handler, void *opaque, int n) +{ + struct IRQState *irq; + + irq = IRQ(object_new(TYPE_IRQ)); + irq->handler = handler; + irq->opaque = opaque; + irq->n = n; + + return irq; +} + +void qemu_free_irqs(qemu_irq *s, int n) +{ + int i; + for (i = 0; i < n; i++) { + qemu_free_irq(s[i]); + } + g_free(s); +} + +void qemu_free_irq(qemu_irq irq) +{ + object_unref(OBJECT(irq)); +} + +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_irq(qemu_notirq, irq, 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_irq(qemu_splitirq, s, 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[i]; + } +} + +static const TypeInfo irq_type_info = { + .name = TYPE_IRQ, + .parent = TYPE_OBJECT, + .instance_size = sizeof(struct IRQState), +}; + +static void irq_register_types(void) +{ + type_register_static(&irq_type_info); +} + +type_init(irq_register_types) diff --git a/qemu/hw/core/loader.c b/qemu/hw/core/loader.c new file mode 100644 index 000000000..216eeeb91 --- /dev/null +++ b/qemu/hw/core/loader.c @@ -0,0 +1,1089 @@ +/* + * 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 "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> + +bool option_rom_has_mr = false; +bool rom_file_has_mr = true; + +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); + if (size == -1) { + fprintf(stderr, "file %-20s: get size error: %s\n", + filename, strerror(errno)); + close(fd); + return -1; + } + + lseek(fd, 0, SEEK_SET); + if (read(fd, addr, size) != size) { + close(fd); + return -1; + } + close(fd); + return size; +} + +/* return the size or -1 if error */ +ssize_t load_image_size(const char *filename, void *addr, size_t size) +{ + int fd; + ssize_t actsize; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) { + return -1; + } + + actsize = read(fd, addr, size); + if (actsize < 0) { + close(fd); + return -1; + } + close(fd); + + return actsize; +} + +/* 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, off_t offset, size_t 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_rela +#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_rela elf64_rela +#define elf_word uint64_t +#define elf_sword int64_t +#define bswapSZs bswap64s +#define SZ 64 +#include "hw/elf_ops.h" + +const char *load_elf_strerror(int error) +{ + switch (error) { + case 0: + return "No error"; + case ELF_LOAD_FAILED: + return "Failed to load ELF"; + case ELF_LOAD_NOT_ELF: + return "The image is not ELF"; + case ELF_LOAD_WRONG_ARCH: + return "The image is from incompatible architecture"; + case ELF_LOAD_WRONG_ENDIAN: + return "The image has incorrect endianness"; + default: + return "Unknown error"; + } +} + +/* 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 = ELF_LOAD_FAILED; + 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) { + ret = ELF_LOAD_NOT_ELF; + 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]) { + ret = ELF_LOAD_WRONG_ENDIAN; + 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); + } + + fail: + close(fd); + return ret; +} + +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. */ +static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, + int *is_linux, uint8_t image_type, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque) +{ + int fd; + int size; + hwaddr address; + uboot_image_header_t h; + uboot_image_header_t *hdr = &h; + uint8_t *data = NULL; + int ret = -1; + int do_uncompress = 0; + + 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; + + if (hdr->ih_type != image_type) { + fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type, + image_type); + goto out; + } + + /* TODO: Implement other image types. */ + switch (hdr->ih_type) { + case IH_TYPE_KERNEL: + address = hdr->ih_load; + if (translate_fn) { + address = translate_fn(translate_opaque, address); + } + if (loadaddr) { + *loadaddr = hdr->ih_load; + } + + switch (hdr->ih_comp) { + case IH_COMP_NONE: + break; + case IH_COMP_GZIP: + do_uncompress = 1; + break; + default: + fprintf(stderr, + "Unable to load u-boot images with compression type %d\n", + hdr->ih_comp); + goto out; + } + + if (ep) { + *ep = hdr->ih_ep; + } + + /* TODO: Check CPU type. */ + if (is_linux) { + if (hdr->ih_os == IH_OS_LINUX) { + *is_linux = 1; + } else { + *is_linux = 0; + } + } + + break; + case IH_TYPE_RAMDISK: + address = *loadaddr; + break; + default: + fprintf(stderr, "Unsupported u-boot image type %d\n", hdr->ih_type); + goto out; + } + + 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 (do_uncompress) { + 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, address); + + ret = hdr->ih_size; + +out: + if (data) + g_free(data); + close(fd); + return ret; +} + +int load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr, + int *is_linux, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque) +{ + return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL, + translate_fn, translate_opaque); +} + +/* Load a ramdisk. */ +int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) +{ + return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK, + NULL, NULL); +} + +/* Load a gzip-compressed kernel to a dynamically allocated buffer. */ +int load_image_gzipped_buffer(const char *filename, uint64_t max_sz, + uint8_t **buffer) +{ + uint8_t *compressed_data = NULL; + uint8_t *data = NULL; + gsize len; + ssize_t bytes; + int ret = -1; + + if (!g_file_get_contents(filename, (char **) &compressed_data, &len, + NULL)) { + goto out; + } + + /* Is it a gzip-compressed file? */ + if (len < 2 || + compressed_data[0] != 0x1f || + compressed_data[1] != 0x8b) { + goto out; + } + + if (max_sz > LOAD_IMAGE_MAX_GUNZIP_BYTES) { + max_sz = LOAD_IMAGE_MAX_GUNZIP_BYTES; + } + + data = g_malloc(max_sz); + bytes = gunzip(data, max_sz, compressed_data, len); + if (bytes < 0) { + fprintf(stderr, "%s: unable to decompress gzipped kernel file\n", + filename); + goto out; + } + + /* trim to actual size and return to caller */ + *buffer = g_realloc(data, bytes); + ret = bytes; + /* ownership has been transferred to caller */ + data = NULL; + + out: + g_free(compressed_data); + g_free(data); + return ret; +} + +/* Load a gzip-compressed kernel. */ +int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) +{ + int bytes; + uint8_t *data; + + bytes = load_image_gzipped_buffer(filename, max_sz, &data); + if (bytes != -1) { + rom_add_blob_fixed(filename, data, bytes, addr); + g_free(data); + } + return bytes; +} + +/* + * 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; + MemoryRegion *mr; + 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); +} + +static void fw_cfg_resized(const char *id, uint64_t length, void *host) +{ + if (fw_cfg) { + fw_cfg_modify_file(fw_cfg, id + strlen("/rom@"), host, length); + } +} + +static void *rom_set_mr(Rom *rom, Object *owner, const char *name) +{ + void *data; + + rom->mr = g_malloc(sizeof(*rom->mr)); + memory_region_init_resizeable_ram(rom->mr, owner, name, + rom->datasize, rom->romsize, + fw_cfg_resized, + &error_abort); + memory_region_set_readonly(rom->mr, true); + vmstate_register_ram_global(rom->mr); + + data = memory_region_get_ram_ptr(rom->mr); + memcpy(data, rom->data, rom->datasize); + + return data; +} + +int rom_add_file(const char *file, const char *fw_dir, + hwaddr addr, int32_t bootindex, + bool option_rom) +{ + 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); + if (rom->romsize == -1) { + fprintf(stderr, "rom: file %-20s: get size error: %s\n", + rom->name, strerror(errno)); + goto err; + } + + 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[FW_CFG_MAX_FILE_PATH]; + void *data; + + 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); + snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); + + if ((!option_rom || option_rom_has_mr) && rom_file_has_mr) { + data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); + } else { + data = rom->data; + } + + fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize); + } 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; +} + +MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, + size_t max_len, hwaddr addr, const char *fw_file_name, + FWCfgReadCallback fw_callback, void *callback_opaque) +{ + Rom *rom; + MemoryRegion *mr = NULL; + + rom = g_malloc0(sizeof(*rom)); + rom->name = g_strdup(name); + rom->addr = addr; + rom->romsize = max_len ? max_len : len; + rom->datasize = len; + rom->data = g_malloc0(rom->datasize); + memcpy(rom->data, blob, len); + rom_insert(rom); + if (fw_file_name && fw_cfg) { + char devpath[100]; + void *data; + + snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); + + if (rom_file_has_mr) { + data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); + mr = rom->mr; + } else { + data = rom->data; + } + + fw_cfg_add_file_callback(fw_cfg, fw_file_name, + fw_callback, callback_opaque, + data, rom->datasize); + } + return mr; +} + +/* 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, true); +} + +int rom_add_option(const char *file, int32_t bootindex) +{ + return rom_add_file(file, "genroms", 0, bootindex, true); +} + +static void rom_reset(void *unused) +{ + Rom *rom; + + QTAILQ_FOREACH(rom, &roms, next) { + if (rom->fw_file) { + continue; + } + if (rom->data == NULL) { + continue; + } + if (rom->mr) { + void *host = memory_region_get_ram_ptr(rom->mr); + memcpy(host, rom->data, rom->datasize); + } else { + cpu_physical_memory_write_rom(&address_space_memory, + rom->addr, rom->data, rom->datasize); + } + if (rom->isrom) { + /* rom needs to be written only once */ + g_free(rom->data); + rom->data = NULL; + } + /* + * The rom loader is really on the same level as firmware in the guest + * shadowing a ROM into RAM. Such a shadowing mechanism needs to ensure + * that the instruction cache for that new region is clear, so that the + * CPU definitely fetches its instructions from the just written data. + */ + cpu_flush_icache_range(rom->addr, rom->datasize); + } +} + +int rom_check_and_register_reset(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 = int128_nz(section.size) && memory_region_is_rom(section.mr); + memory_region_unref(section.mr); + } + qemu_register_reset(rom_reset, NULL); + roms_loaded = 1; + return 0; +} + +void rom_set_fw(FWCfgState *f) +{ + fw_cfg = f; +} + +static Rom *find_rom(hwaddr addr) +{ + Rom *rom; + + QTAILQ_FOREACH(rom, &roms, next) { + if (rom->fw_file) { + continue; + } + if (rom->mr) { + 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->mr) { + continue; + } + if (rom->addr + rom->romsize < addr) { + continue; + } + if (rom->addr > end) { + break; + } + + d = dest + (rom->addr - addr); + s = rom->data; + l = rom->datasize; + + if ((d + l) > (dest + size)) { + l = dest - d; + } + + if (l > 0) { + 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 hmp_info_roms(Monitor *mon, const QDict *qdict) +{ + Rom *rom; + + QTAILQ_FOREACH(rom, &roms, next) { + if (rom->mr) { + monitor_printf(mon, "%s" + " size=0x%06zx name=\"%s\"\n", + memory_region_name(rom->mr), + rom->romsize, + rom->name); + } else 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/qemu/hw/core/machine.c b/qemu/hw/core/machine.c new file mode 100644 index 000000000..ac4654e9d --- /dev/null +++ b/qemu/hw/core/machine.c @@ -0,0 +1,485 @@ +/* + * QEMU Machine + * + * Copyright (C) 2014 Red Hat Inc + * + * Authors: + * Marcel Apfelbaum <marcel.a@redhat.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 "hw/boards.h" +#include "qapi/visitor.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "qemu/error-report.h" + +static char *machine_get_accel(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->accel); +} + +static void machine_set_accel(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->accel); + ms->accel = g_strdup(value); +} + +static void machine_set_kernel_irqchip(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->kernel_irqchip_allowed = value; + ms->kernel_irqchip_required = value; +} + +static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + int64_t value = ms->kvm_shadow_mem; + + visit_type_int(v, &value, name, errp); +} + +static void machine_set_kvm_shadow_mem(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + Error *error = NULL; + int64_t value; + + visit_type_int(v, &value, name, &error); + if (error) { + error_propagate(errp, error); + return; + } + + ms->kvm_shadow_mem = value; +} + +static char *machine_get_kernel(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->kernel_filename); +} + +static void machine_set_kernel(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->kernel_filename); + ms->kernel_filename = g_strdup(value); +} + +static char *machine_get_initrd(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->initrd_filename); +} + +static void machine_set_initrd(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->initrd_filename); + ms->initrd_filename = g_strdup(value); +} + +static char *machine_get_append(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->kernel_cmdline); +} + +static void machine_set_append(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->kernel_cmdline); + ms->kernel_cmdline = g_strdup(value); +} + +static char *machine_get_dtb(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->dtb); +} + +static void machine_set_dtb(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->dtb); + ms->dtb = g_strdup(value); +} + +static char *machine_get_dumpdtb(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->dumpdtb); +} + +static void machine_set_dumpdtb(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->dumpdtb); + ms->dumpdtb = g_strdup(value); +} + +static void machine_get_phandle_start(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + int64_t value = ms->phandle_start; + + visit_type_int(v, &value, name, errp); +} + +static void machine_set_phandle_start(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + Error *error = NULL; + int64_t value; + + visit_type_int(v, &value, name, &error); + if (error) { + error_propagate(errp, error); + return; + } + + ms->phandle_start = value; +} + +static char *machine_get_dt_compatible(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->dt_compatible); +} + +static void machine_set_dt_compatible(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->dt_compatible); + ms->dt_compatible = g_strdup(value); +} + +static bool machine_get_dump_guest_core(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->dump_guest_core; +} + +static void machine_set_dump_guest_core(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->dump_guest_core = value; +} + +static bool machine_get_mem_merge(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->mem_merge; +} + +static void machine_set_mem_merge(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->mem_merge = value; +} + +static bool machine_get_usb(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->usb; +} + +static void machine_set_usb(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->usb = value; + ms->usb_disabled = !value; +} + +static char *machine_get_firmware(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->firmware); +} + +static void machine_set_firmware(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->firmware); + ms->firmware = g_strdup(value); +} + +static bool machine_get_iommu(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->iommu; +} + +static void machine_set_iommu(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->iommu = value; +} + +static void machine_set_suppress_vmdesc(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->suppress_vmdesc = value; +} + +static bool machine_get_suppress_vmdesc(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->suppress_vmdesc; +} + +static int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque) +{ + error_report("Option '-device %s' cannot be handled by this machine", + object_class_get_name(object_get_class(OBJECT(sbdev)))); + exit(1); +} + +static void machine_init_notify(Notifier *notifier, void *data) +{ + Object *machine = qdev_get_machine(); + ObjectClass *oc = object_get_class(machine); + MachineClass *mc = MACHINE_CLASS(oc); + + if (mc->has_dynamic_sysbus) { + /* Our machine can handle dynamic sysbus devices, we're all good */ + return; + } + + /* + * Loop through all dynamically created devices and check whether there + * are sysbus devices among them. If there are, error out. + */ + foreach_dynamic_sysbus_device(error_on_sysbus_device, NULL); +} + +static void machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + /* Default 128 MB as guest ram size */ + mc->default_ram_size = 128 * M_BYTE; +} + +static void machine_initfn(Object *obj) +{ + MachineState *ms = MACHINE(obj); + + ms->kernel_irqchip_allowed = true; + ms->kvm_shadow_mem = -1; + ms->dump_guest_core = true; + ms->mem_merge = true; + + object_property_add_str(obj, "accel", + machine_get_accel, machine_set_accel, NULL); + object_property_set_description(obj, "accel", + "Accelerator list", + NULL); + object_property_add_bool(obj, "kernel-irqchip", + NULL, + machine_set_kernel_irqchip, + NULL); + object_property_set_description(obj, "kernel-irqchip", + "Use KVM in-kernel irqchip", + NULL); + object_property_add(obj, "kvm-shadow-mem", "int", + machine_get_kvm_shadow_mem, + machine_set_kvm_shadow_mem, + NULL, NULL, NULL); + object_property_set_description(obj, "kvm-shadow-mem", + "KVM shadow MMU size", + NULL); + object_property_add_str(obj, "kernel", + machine_get_kernel, machine_set_kernel, NULL); + object_property_set_description(obj, "kernel", + "Linux kernel image file", + NULL); + object_property_add_str(obj, "initrd", + machine_get_initrd, machine_set_initrd, NULL); + object_property_set_description(obj, "initrd", + "Linux initial ramdisk file", + NULL); + object_property_add_str(obj, "append", + machine_get_append, machine_set_append, NULL); + object_property_set_description(obj, "append", + "Linux kernel command line", + NULL); + object_property_add_str(obj, "dtb", + machine_get_dtb, machine_set_dtb, NULL); + object_property_set_description(obj, "dtb", + "Linux kernel device tree file", + NULL); + object_property_add_str(obj, "dumpdtb", + machine_get_dumpdtb, machine_set_dumpdtb, NULL); + object_property_set_description(obj, "dumpdtb", + "Dump current dtb to a file and quit", + NULL); + object_property_add(obj, "phandle-start", "int", + machine_get_phandle_start, + machine_set_phandle_start, + NULL, NULL, NULL); + object_property_set_description(obj, "phandle-start", + "The first phandle ID we may generate dynamically", + NULL); + object_property_add_str(obj, "dt-compatible", + machine_get_dt_compatible, + machine_set_dt_compatible, + NULL); + object_property_set_description(obj, "dt-compatible", + "Overrides the \"compatible\" property of the dt root node", + NULL); + object_property_add_bool(obj, "dump-guest-core", + machine_get_dump_guest_core, + machine_set_dump_guest_core, + NULL); + object_property_set_description(obj, "dump-guest-core", + "Include guest memory in a core dump", + NULL); + object_property_add_bool(obj, "mem-merge", + machine_get_mem_merge, + machine_set_mem_merge, NULL); + object_property_set_description(obj, "mem-merge", + "Enable/disable memory merge support", + NULL); + object_property_add_bool(obj, "usb", + machine_get_usb, + machine_set_usb, NULL); + object_property_set_description(obj, "usb", + "Set on/off to enable/disable usb", + NULL); + object_property_add_str(obj, "firmware", + machine_get_firmware, + machine_set_firmware, NULL); + object_property_set_description(obj, "firmware", + "Firmware image", + NULL); + object_property_add_bool(obj, "iommu", + machine_get_iommu, + machine_set_iommu, NULL); + object_property_set_description(obj, "iommu", + "Set on/off to enable/disable Intel IOMMU (VT-d)", + NULL); + object_property_add_bool(obj, "suppress-vmdesc", + machine_get_suppress_vmdesc, + machine_set_suppress_vmdesc, NULL); + object_property_set_description(obj, "suppress-vmdesc", + "Set on to disable self-describing migration", + NULL); + + /* Register notifier when init is done for sysbus sanity checks */ + ms->sysbus_notifier.notify = machine_init_notify; + qemu_add_machine_init_done_notifier(&ms->sysbus_notifier); +} + +static void machine_finalize(Object *obj) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->accel); + g_free(ms->kernel_filename); + g_free(ms->initrd_filename); + g_free(ms->kernel_cmdline); + g_free(ms->dtb); + g_free(ms->dumpdtb); + g_free(ms->dt_compatible); + g_free(ms->firmware); +} + +bool machine_usb(MachineState *machine) +{ + return machine->usb; +} + +bool machine_iommu(MachineState *machine) +{ + return machine->iommu; +} + +bool machine_kernel_irqchip_allowed(MachineState *machine) +{ + return machine->kernel_irqchip_allowed; +} + +bool machine_kernel_irqchip_required(MachineState *machine) +{ + return machine->kernel_irqchip_required; +} + +int machine_kvm_shadow_mem(MachineState *machine) +{ + return machine->kvm_shadow_mem; +} + +int machine_phandle_start(MachineState *machine) +{ + return machine->phandle_start; +} + +bool machine_dump_guest_core(MachineState *machine) +{ + return machine->dump_guest_core; +} + +bool machine_mem_merge(MachineState *machine) +{ + return machine->mem_merge; +} + +static const TypeInfo machine_info = { + .name = TYPE_MACHINE, + .parent = TYPE_OBJECT, + .abstract = true, + .class_size = sizeof(MachineClass), + .class_init = machine_class_init, + .instance_size = sizeof(MachineState), + .instance_init = machine_initfn, + .instance_finalize = machine_finalize, +}; + +static void machine_register_types(void) +{ + type_register_static(&machine_info); +} + +type_init(machine_register_types) diff --git a/qemu/hw/core/nmi.c b/qemu/hw/core/nmi.c new file mode 100644 index 000000000..de1d1f8cb --- /dev/null +++ b/qemu/hw/core/nmi.c @@ -0,0 +1,104 @@ +/* + * NMI monitor handler class and helpers. + * + * Copyright IBM Corp., 2014 + * + * Author: Alexey Kardashevskiy <aik@ozlabs.ru> + * + * 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/nmi.h" +#include "qapi/qmp/qerror.h" +#include "monitor/monitor.h" + +struct do_nmi_s { + int cpu_index; + Error *errp; + bool handled; +}; + +static void nmi_children(Object *o, struct do_nmi_s *ns); + +static int do_nmi(Object *o, void *opaque) +{ + struct do_nmi_s *ns = opaque; + NMIState *n = (NMIState *) object_dynamic_cast(o, TYPE_NMI); + + if (n) { + NMIClass *nc = NMI_GET_CLASS(n); + + ns->handled = true; + nc->nmi_monitor_handler(n, ns->cpu_index, &ns->errp); + if (ns->errp) { + return -1; + } + } + nmi_children(o, ns); + + return 0; +} + +static void nmi_children(Object *o, struct do_nmi_s *ns) +{ + object_child_foreach(o, do_nmi, ns); +} + +void nmi_monitor_handle(int cpu_index, Error **errp) +{ + struct do_nmi_s ns = { + .cpu_index = cpu_index, + .errp = NULL, + .handled = false + }; + + nmi_children(object_get_root(), &ns); + if (ns.handled) { + error_propagate(errp, ns.errp); + } else { + error_setg(errp, QERR_UNSUPPORTED); + } +} + +void inject_nmi(void) +{ +#if defined(TARGET_I386) + CPUState *cs; + + CPU_FOREACH(cs) { + X86CPU *cpu = X86_CPU(cs); + + if (!cpu->apic_state) { + cpu_interrupt(cs, CPU_INTERRUPT_NMI); + } else { + apic_deliver_nmi(cpu->apic_state); + } + } +#else + nmi_monitor_handle(0, NULL); +#endif +} + +static const TypeInfo nmi_info = { + .name = TYPE_NMI, + .parent = TYPE_INTERFACE, + .class_size = sizeof(NMIClass), +}; + +static void nmi_register_types(void) +{ + type_register_static(&nmi_info); +} + +type_init(nmi_register_types) diff --git a/qemu/hw/core/null-machine.c b/qemu/hw/core/null-machine.c new file mode 100644 index 000000000..1ec7c3bbe --- /dev/null +++ b/qemu/hw/core/null-machine.c @@ -0,0 +1,35 @@ +/* + * 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(MachineState *machine) +{ +} + +static QEMUMachine machine_none = { + .name = "none", + .desc = "empty machine", + .init = machine_none_init, + .max_cpus = 0, +}; + +static void register_machines(void) +{ + qemu_register_machine(&machine_none); +} + +machine_init(register_machines); + diff --git a/qemu/hw/core/platform-bus.c b/qemu/hw/core/platform-bus.c new file mode 100644 index 000000000..70e051890 --- /dev/null +++ b/qemu/hw/core/platform-bus.c @@ -0,0 +1,252 @@ +/* + * Platform Bus device to support dynamic Sysbus devices + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Alexander Graf, <agraf@suse.de> + * + * 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/platform-bus.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" + + +/* + * Returns the PlatformBus IRQ number for a SysBusDevice irq number or -1 if + * the IRQ is not mapped on this Platform bus. + */ +int platform_bus_get_irqn(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) +{ + qemu_irq sbirq = sysbus_get_connected_irq(sbdev, n); + int i; + + for (i = 0; i < pbus->num_irqs; i++) { + if (pbus->irqs[i] == sbirq) { + return i; + } + } + + /* IRQ not mapped on platform bus */ + return -1; +} + +/* + * Returns the PlatformBus MMIO region offset for Region n of a SysBusDevice or + * -1 if the region is not mapped on this Platform bus. + */ +hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) +{ + MemoryRegion *pbus_mr = &pbus->mmio; + MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n); + Object *pbus_mr_obj = OBJECT(pbus_mr); + Object *parent_mr; + + if (!memory_region_is_mapped(sbdev_mr)) { + /* Region is not mapped? */ + return -1; + } + + parent_mr = object_property_get_link(OBJECT(sbdev_mr), "container", NULL); + + assert(parent_mr); + if (parent_mr != pbus_mr_obj) { + /* MMIO region is not mapped on platform bus */ + return -1; + } + + return object_property_get_int(OBJECT(sbdev_mr), "addr", NULL); +} + +static int platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque) +{ + PlatformBusDevice *pbus = opaque; + qemu_irq sbirq; + int n, i; + + for (n = 0; ; n++) { + if (!sysbus_has_irq(sbdev, n)) { + break; + } + + sbirq = sysbus_get_connected_irq(sbdev, n); + for (i = 0; i < pbus->num_irqs; i++) { + if (pbus->irqs[i] == sbirq) { + bitmap_set(pbus->used_irqs, i, 1); + break; + } + } + } + + return 0; +} + +/* + * Loop through all sysbus devices and look for unassigned IRQ lines as well as + * unassociated MMIO regions. Connect them to the platform bus if available. + */ +static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus) +{ + bitmap_zero(pbus->used_irqs, pbus->num_irqs); + foreach_dynamic_sysbus_device(platform_bus_count_irqs, pbus); + pbus->done_gathering = true; +} + +static int platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) +{ + int max_irqs = pbus->num_irqs; + int irqn; + + if (sysbus_is_irq_connected(sbdev, n)) { + /* IRQ is already mapped, nothing to do */ + return 0; + } + + irqn = find_first_zero_bit(pbus->used_irqs, max_irqs); + if (irqn >= max_irqs) { + hw_error("Platform Bus: Can not fit IRQ line"); + return -1; + } + + set_bit(irqn, pbus->used_irqs); + sysbus_connect_irq(sbdev, n, pbus->irqs[irqn]); + + return 0; +} + +static int platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) +{ + MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n); + uint64_t size = memory_region_size(sbdev_mr); + uint64_t alignment = (1ULL << (63 - clz64(size + size - 1))); + uint64_t off; + bool found_region = false; + + if (memory_region_is_mapped(sbdev_mr)) { + /* Region is already mapped, nothing to do */ + return 0; + } + + /* + * Look for empty space in the MMIO space that is naturally aligned with + * the target device's memory region + */ + for (off = 0; off < pbus->mmio_size; off += alignment) { + if (!memory_region_find(&pbus->mmio, off, size).mr) { + found_region = true; + break; + } + } + + if (!found_region) { + hw_error("Platform Bus: Can not fit MMIO region of size %"PRIx64, size); + } + + /* Map the device's region into our Platform Bus MMIO space */ + memory_region_add_subregion(&pbus->mmio, off, sbdev_mr); + + return 0; +} + +/* + * For each sysbus device, look for unassigned IRQ lines as well as + * unassociated MMIO regions. Connect them to the platform bus if available. + */ +static int link_sysbus_device(SysBusDevice *sbdev, void *opaque) +{ + PlatformBusDevice *pbus = opaque; + int i; + + for (i = 0; sysbus_has_irq(sbdev, i); i++) { + platform_bus_map_irq(pbus, sbdev, i); + } + + for (i = 0; sysbus_has_mmio(sbdev, i); i++) { + platform_bus_map_mmio(pbus, sbdev, i); + } + + return 0; +} + +static void platform_bus_init_notify(Notifier *notifier, void *data) +{ + PlatformBusDevice *pb = container_of(notifier, PlatformBusDevice, notifier); + + /* + * Generate a bitmap of used IRQ lines, as the user might have specified + * them on the command line. + */ + plaform_bus_refresh_irqs(pb); + + foreach_dynamic_sysbus_device(link_sysbus_device, pb); +} + +static void platform_bus_realize(DeviceState *dev, Error **errp) +{ + PlatformBusDevice *pbus; + SysBusDevice *d; + int i; + + d = SYS_BUS_DEVICE(dev); + pbus = PLATFORM_BUS_DEVICE(dev); + + memory_region_init(&pbus->mmio, NULL, "platform bus", pbus->mmio_size); + sysbus_init_mmio(d, &pbus->mmio); + + pbus->used_irqs = bitmap_new(pbus->num_irqs); + pbus->irqs = g_new0(qemu_irq, pbus->num_irqs); + for (i = 0; i < pbus->num_irqs; i++) { + sysbus_init_irq(d, &pbus->irqs[i]); + } + + /* + * Register notifier that allows us to gather dangling devices once the + * machine is completely assembled + */ + pbus->notifier.notify = platform_bus_init_notify; + qemu_add_machine_init_done_notifier(&pbus->notifier); +} + +static Property platform_bus_properties[] = { + DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0), + DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void platform_bus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = platform_bus_realize; + dc->props = platform_bus_properties; +} + +static const TypeInfo platform_bus_info = { + .name = TYPE_PLATFORM_BUS_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PlatformBusDevice), + .class_init = platform_bus_class_init, +}; + +static void platform_bus_register_types(void) +{ + type_register_static(&platform_bus_info); +} + +type_init(platform_bus_register_types) diff --git a/qemu/hw/core/ptimer.c b/qemu/hw/core/ptimer.c new file mode 100644 index 000000000..8437bd6e8 --- /dev/null +++ b/qemu/hw/core/ptimer.c @@ -0,0 +1,230 @@ +/* + * 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; + } + timer_mod(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_clock_get_ns(QEMU_CLOCK_VIRTUAL); + /* 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_clock_get_ns(QEMU_CLOCK_VIRTUAL); + 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_clock_get_ns(QEMU_CLOCK_VIRTUAL); + 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); + timer_del(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_clock_get_ns(QEMU_CLOCK_VIRTUAL); + 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_clock_get_ns(QEMU_CLOCK_VIRTUAL); + 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 (!use_icount && 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_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_reload(s); + } +} + +const VMStateDescription vmstate_ptimer = { + .name = "ptimer", + .version_id = 1, + .minimum_version_id = 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_PTR(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 = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s); + return s; +} diff --git a/qemu/hw/core/qdev-properties-system.c b/qemu/hw/core/qdev-properties-system.c new file mode 100644 index 000000000..921e799db --- /dev/null +++ b/qemu/hw/core/qdev-properties-system.c @@ -0,0 +1,425 @@ +/* + * 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/block-backend.h" +#include "sysemu/blockdev.h" +#include "hw/block/block.h" +#include "net/hub.h" +#include "qapi/visitor.h" +#include "sysemu/char.h" +#include "sysemu/iothread.h" + +static void get_pointer(Object *obj, Visitor *v, Property *prop, + char *(*print)(void *ptr), + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + void **ptr = qdev_get_prop_ptr(dev, prop); + char *p; + + p = *ptr ? print(*ptr) : g_strdup(""); + visit_type_str(v, &p, name, errp); + g_free(p); +} + +static void set_pointer(Object *obj, Visitor *v, Property *prop, + void (*parse)(DeviceState *dev, const char *str, + void **ptr, const char *propname, + Error **errp), + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Error *local_err = NULL; + void **ptr = qdev_get_prop_ptr(dev, prop); + 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 (!*str) { + g_free(str); + *ptr = NULL; + return; + } + parse(dev, str, ptr, prop->name, errp); + g_free(str); +} + +/* --- drive --- */ + +static void parse_drive(DeviceState *dev, const char *str, void **ptr, + const char *propname, Error **errp) +{ + BlockBackend *blk; + + blk = blk_by_name(str); + if (!blk) { + error_setg(errp, "Property '%s.%s' can't find value '%s'", + object_get_typename(OBJECT(dev)), propname, str); + return; + } + if (blk_attach_dev(blk, dev) < 0) { + DriveInfo *dinfo = blk_legacy_dinfo(blk); + + if (dinfo->type != IF_NONE) { + error_setg(errp, "Drive '%s' is already in use because " + "it has been automatically connected to another " + "device (did you need 'if=none' in the drive options?)", + str); + } else { + error_setg(errp, "Drive '%s' is already in use by another device", + str); + } + return; + } + *ptr = blk; +} + +static void release_drive(Object *obj, const char *name, void *opaque) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + BlockBackend **ptr = qdev_get_prop_ptr(dev, prop); + + if (*ptr) { + blk_detach_dev(*ptr, dev); + blockdev_auto_del(*ptr); + } +} + +static char *print_drive(void *ptr) +{ + return g_strdup(blk_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 = "str", + .description = "ID of a drive to use as a backend", + .get = get_drive, + .set = set_drive, + .release = release_drive, +}; + +/* --- character device --- */ + +static void parse_chr(DeviceState *dev, const char *str, void **ptr, + const char *propname, Error **errp) +{ + CharDriverState *chr = qemu_chr_find(str); + if (chr == NULL) { + error_setg(errp, "Property '%s.%s' can't find value '%s'", + object_get_typename(OBJECT(dev)), propname, str); + return; + } + if (qemu_chr_fe_claim(chr) != 0) { + error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use", + object_get_typename(OBJECT(dev)), propname, str); + return; + } + *ptr = chr; +} + +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 char *print_chr(void *ptr) +{ + CharDriverState *chr = ptr; + const char *val = chr->label ? chr->label : ""; + + return g_strdup(val); +} + +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 = "str", + .description = "ID of a chardev to use as a backend", + .get = get_chr, + .set = set_chr, + .release = release_chr, +}; + +/* --- netdev device --- */ +static void get_netdev(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); + char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); + + visit_type_str(v, &p, name, errp); + g_free(p); +} + +static void set_netdev(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 **ncs = peers_ptr->ncs; + NetClientState *peers[MAX_QUEUE_NUM]; + Error *local_err = NULL; + int queues, err = 0, i = 0; + 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; + } + + queues = qemu_find_net_clients_except(str, peers, + NET_CLIENT_OPTIONS_KIND_NIC, + MAX_QUEUE_NUM); + if (queues == 0) { + err = -ENOENT; + goto out; + } + + if (queues > MAX_QUEUE_NUM) { + error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)", + str, queues, MAX_QUEUE_NUM); + goto out; + } + + for (i = 0; i < queues; i++) { + if (peers[i] == NULL) { + err = -ENOENT; + goto out; + } + + if (peers[i]->peer) { + err = -EEXIST; + goto out; + } + + if (ncs[i]) { + err = -EINVAL; + goto out; + } + + ncs[i] = peers[i]; + ncs[i]->queue_index = i; + } + + peers_ptr->queues = queues; + +out: + error_set_from_qdev_prop_error(errp, err, dev, prop, str); + g_free(str); +} + +PropertyInfo qdev_prop_netdev = { + .name = "str", + .description = "ID of a netdev to use as a backend", + .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; + } + if (*ptr) { + error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name); + return; + } + + hubport = net_hub_port_find(id); + if (!hubport) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + name, prop->info->name); + return; + } + *ptr = hubport; +} + +PropertyInfo qdev_prop_vlan = { + .name = "int32", + .description = "Integer VLAN id to connect to", + .print = print_vlan, + .get = get_vlan, + .set = set_vlan, +}; + +void qdev_prop_set_drive(DeviceState *dev, const char *name, + BlockBackend *value, Error **errp) +{ + object_property_set_str(OBJECT(dev), value ? blk_name(value) : "", + name, errp); +} + +void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, + BlockBackend *value) +{ + Error *err = NULL; + + qdev_prop_set_drive(dev, name, value, &err); + if (err) { + error_report_err(err); + exit(1); + } +} + +void qdev_prop_set_chr(DeviceState *dev, const char *name, + CharDriverState *value) +{ + assert(!value || value->label); + object_property_set_str(OBJECT(dev), + value ? value->label : "", name, &error_abort); +} + +void qdev_prop_set_netdev(DeviceState *dev, const char *name, + NetClientState *value) +{ + assert(!value || value->name); + object_property_set_str(OBJECT(dev), + value ? value->name : "", name, &error_abort); +} + +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(void *opaque, QemuOpts *opts, Error **errp) +{ + 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"); + g->user_provided = true; + qdev_prop_register_global(g); + return 0; +} + +void qemu_add_globals(void) +{ + qemu_opts_foreach(qemu_find_opts("global"), + qdev_add_one_global, NULL, NULL); +} diff --git a/qemu/hw/core/qdev-properties.c b/qemu/hw/core/qdev-properties.c new file mode 100644 index 000000000..04fd80a4d --- /dev/null +++ b/qemu/hw/core/qdev-properties.c @@ -0,0 +1,1113 @@ +#include "net/net.h" +#include "hw/qdev.h" +#include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" +#include "sysemu/block-backend.h" +#include "hw/block/block.h" +#include "net/hub.h" +#include "qapi/visitor.h" +#include "sysemu/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_prop_allow_set_link_before_realize(Object *obj, const char *name, + Object *val, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + + if (dev->realized) { + error_setg(errp, "Attempt to set link property '%s' on device '%s' " + "(type '%s') after it was realized", + name, dev->id, object_get_typename(obj)); + } +} + +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 void prop_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 prop_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 = "bool", + .description = "on/off", + .get = prop_get_bit, + .set = prop_set_bit, +}; + +/* Bit64 */ + +static uint64_t qdev_get_prop_mask64(Property *prop) +{ + assert(prop->info == &qdev_prop_bit64); + return 0x1ull << prop->bitnr; +} + +static void bit64_prop_set(DeviceState *dev, Property *props, bool val) +{ + uint64_t *p = qdev_get_prop_ptr(dev, props); + uint64_t mask = qdev_get_prop_mask64(props); + if (val) { + *p |= mask; + } else { + *p &= ~mask; + } +} + +static void prop_get_bit64(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + uint64_t *p = qdev_get_prop_ptr(dev, prop); + bool value = (*p & qdev_get_prop_mask64(prop)) != 0; + + visit_type_bool(v, &value, name, errp); +} + +static void prop_set_bit64(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; + } + bit64_prop_set(dev, prop, value); +} + +PropertyInfo qdev_prop_bit64 = { + .name = "bool", + .description = "on/off", + .get = prop_get_bit64, + .set = prop_set_bit64, +}; + +/* --- bool --- */ + +static void get_bool(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + bool *ptr = qdev_get_prop_ptr(dev, prop); + + visit_type_bool(v, ptr, name, errp); +} + +static void set_bool(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + bool *ptr = qdev_get_prop_ptr(dev, prop); + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_bool(v, ptr, name, errp); +} + +PropertyInfo qdev_prop_bool = { + .name = "bool", + .get = get_bool, + .set = set_bool, +}; + +/* --- 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, +}; + +/* --- 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, +}; + +/* --- 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, +}; + +/* --- 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 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 = "str", + .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 = "str", + .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", + .get = get_mac, + .set = set_mac, +}; + +/* --- lost tick policy --- */ + +QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); + +PropertyInfo qdev_prop_losttickpolicy = { + .name = "LostTickPolicy", + .enum_table = LostTickPolicy_lookup, + .get = get_enum, + .set = set_enum, +}; + +/* --- BIOS CHS translation */ + +QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); + +PropertyInfo qdev_prop_bios_chs_trans = { + .name = "BiosAtaTranslation", + .description = "Logical CHS translation algorithm, " + "auto/none/lba/large/rechs", + .enum_table = BiosAtaTranslation_lookup, + .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_setg(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", + .description = "Slot and optional function number, example: 06.0 or 06", + .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; + } + /* value of 0 means "unset" */ + if (value && (value < min || value > max)) { + error_setg(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_setg(errp, + "Property %s.%s doesn't take value '%" PRId64 "', it's not a power of 2", + dev->id ?: "", name, (int64_t)value); + return; + } + + *ptr = value; +} + +PropertyInfo qdev_prop_blocksize = { + .name = "uint16", + .description = "A power of two between 512 and 32768", + .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 = "str", + .description = "Address (bus/device/function) of " + "the host device, example: 04:10.0", + .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; + Error *local_err = NULL; + 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, &local_err); + if (local_err) { + error_propagate(errp, local_err); + 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, &local_err); + if (local_err) { + error_propagate(errp, local_err); + 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_setg(errp, "Property '%s.%s' can't take value '%s', it's in use", + object_get_typename(OBJECT(dev)), prop->name, value); + break; + default: + case -EINVAL: + error_setg(errp, QERR_PROPERTY_VALUE_BAD, + object_get_typename(OBJECT(dev)), prop->name, value); + break; + case -ENOENT: + error_setg(errp, "Property '%s.%s' can't find value '%s'", + object_get_typename(OBJECT(dev)), prop->name, value); + break; + case 0: + break; + } +} + +void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value) +{ + object_property_set_bool(OBJECT(dev), value, name, &error_abort); +} + +void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value) +{ + object_property_set_int(OBJECT(dev), value, name, &error_abort); +} + +void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value) +{ + object_property_set_int(OBJECT(dev), value, name, &error_abort); +} + +void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value) +{ + object_property_set_int(OBJECT(dev), value, name, &error_abort); +} + +void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value) +{ + object_property_set_int(OBJECT(dev), value, name, &error_abort); +} + +void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value) +{ + object_property_set_int(OBJECT(dev), value, name, &error_abort); +} + +void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value) +{ + object_property_set_str(OBJECT(dev), value, name, &error_abort); +} + +void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value) +{ + 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, &error_abort); +} + +void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) +{ + Property *prop; + + prop = qdev_prop_find(dev, name); + object_property_set_str(OBJECT(dev), prop->info->enum_table[value], + name, &error_abort); +} + +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); + } +} + +int qdev_prop_check_globals(void) +{ + GlobalProperty *prop; + int ret = 0; + + QTAILQ_FOREACH(prop, &global_props, next) { + ObjectClass *oc; + DeviceClass *dc; + if (prop->used) { + continue; + } + if (!prop->user_provided) { + continue; + } + oc = object_class_by_name(prop->driver); + oc = object_class_dynamic_cast(oc, TYPE_DEVICE); + if (!oc) { + error_report("Warning: global %s.%s has invalid class name", + prop->driver, prop->property); + ret = 1; + continue; + } + dc = DEVICE_CLASS(oc); + if (!dc->hotpluggable && !prop->used) { + error_report("Warning: global %s.%s=%s not used", + prop->driver, prop->property, prop->value); + ret = 1; + continue; + } + } + return ret; +} + +static void qdev_prop_set_globals_for_type(DeviceState *dev, + const char *typename) +{ + GlobalProperty *prop; + + QTAILQ_FOREACH(prop, &global_props, next) { + Error *err = NULL; + + if (strcmp(typename, prop->driver) != 0) { + continue; + } + prop->used = true; + object_property_parse(OBJECT(dev), prop->value, prop->property, &err); + if (err != NULL) { + assert(prop->user_provided); + error_report("Warning: global %s.%s=%s ignored (%s)", + prop->driver, prop->property, prop->value, + error_get_pretty(err)); + error_free(err); + return; + } + } +} + +void qdev_prop_set_globals(DeviceState *dev) +{ + ObjectClass *class = object_get_class(OBJECT(dev)); + + do { + qdev_prop_set_globals_for_type(dev, object_class_get_name(class)); + class = object_class_get_parent(class); + } while (class); +} + +/* --- 64bit unsigned int 'size' type --- */ + +static void get_size(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_size(v, ptr, name, errp); +} + +static void set_size(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_size(v, ptr, name, errp); +} + +PropertyInfo qdev_prop_size = { + .name = "size", + .get = get_size, + .set = set_size, +}; diff --git a/qemu/hw/core/qdev.c b/qemu/hw/core/qdev.c new file mode 100644 index 000000000..b2f404a76 --- /dev/null +++ b/qemu/hw/core/qdev.c @@ -0,0 +1,1349 @@ +/* + * 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 "hw/fw-path-provider.h" +#include "sysemu/sysemu.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qapi/visitor.h" +#include "qapi/qmp/qjson.h" +#include "qemu/error-report.h" +#include "hw/hotplug.h" +#include "hw/boards.h" +#include "qapi-event.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)); + + 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, /* read-only property */ + 0, /* return ownership on prop deletion */ + NULL); +} + +void qdev_set_parent_bus(DeviceState *dev, BusState *bus) +{ + dev->parent_bus = bus; + object_ref(OBJECT(bus)); + bus_add_child(bus, dev); +} + +static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler, + Error **errp) +{ + + object_property_set_link(OBJECT(bus), OBJECT(handler), + QDEV_HOTPLUG_HANDLER_PROPERTY, errp); +} + +void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp) +{ + qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp); +} + +void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp) +{ + qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp); +} + +/* Create a new device. This only initializes the device state + structure and allows properties to be set. The device still needs + to be realized. See qdev-core.h. */ +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; +} + +static QTAILQ_HEAD(device_listeners, DeviceListener) device_listeners + = QTAILQ_HEAD_INITIALIZER(device_listeners); + +enum ListenerDirection { Forward, Reverse }; + +#define DEVICE_LISTENER_CALL(_callback, _direction, _args...) \ + do { \ + DeviceListener *_listener; \ + \ + switch (_direction) { \ + case Forward: \ + QTAILQ_FOREACH(_listener, &device_listeners, link) { \ + if (_listener->_callback) { \ + _listener->_callback(_listener, ##_args); \ + } \ + } \ + break; \ + case Reverse: \ + QTAILQ_FOREACH_REVERSE(_listener, &device_listeners, \ + device_listeners, link) { \ + if (_listener->_callback) { \ + _listener->_callback(_listener, ##_args); \ + } \ + } \ + break; \ + default: \ + abort(); \ + } \ + } while (0) + +static int device_listener_add(DeviceState *dev, void *opaque) +{ + DEVICE_LISTENER_CALL(realize, Forward, dev); + + return 0; +} + +void device_listener_register(DeviceListener *listener) +{ + QTAILQ_INSERT_TAIL(&device_listeners, listener, link); + + qbus_walk_children(sysbus_get_default(), NULL, NULL, device_listener_add, + NULL, NULL); +} + +void device_listener_unregister(DeviceListener *listener) +{ + QTAILQ_REMOVE(&device_listeners, listener, link); +} + +static void device_realize(DeviceState *dev, Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (dc->init) { + int rc = dc->init(dev); + if (rc < 0) { + error_setg(errp, "Device initialization failed."); + return; + } + } +} + +static void device_unrealize(DeviceState *dev, Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (dc->exit) { + int rc = dc->exit(dev); + if (rc < 0) { + error_setg(errp, "Device exit 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; +} + +HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) +{ + HotplugHandler *hotplug_ctrl = NULL; + + if (dev->parent_bus && dev->parent_bus->hotplug_handler) { + hotplug_ctrl = dev->parent_bus->hotplug_handler; + } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) { + MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + + if (mc->get_hotplug_handler) { + hotplug_ctrl = mc->get_hotplug_handler(machine, dev); + } + } + return hotplug_ctrl; +} + +void qdev_unplug(DeviceState *dev, Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + HotplugHandler *hotplug_ctrl; + HotplugHandlerClass *hdc; + + if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { + error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); + return; + } + + if (!dc->hotpluggable) { + error_setg(errp, QERR_DEVICE_NO_HOTPLUG, + object_get_typename(OBJECT(dev))); + return; + } + + qdev_hot_removed = true; + + hotplug_ctrl = qdev_get_hotplug_handler(dev); + /* hotpluggable device MUST have HotplugHandler, if it doesn't + * then something is very wrong with it */ + g_assert(hotplug_ctrl); + + /* If device supports async unplug just request it to be done, + * otherwise just remove it synchronously */ + hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl); + if (hdc->unplug_request) { + hotplug_handler_unplug_request(hotplug_ctrl, dev, errp); + } else { + hotplug_handler_unplug(hotplug_ctrl, dev, errp); + } +} + +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) { + bc->reset(bus); + } + return 0; +} + +void qdev_reset_all(DeviceState *dev) +{ + qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL); +} + +void qbus_reset_all(BusState *bus) +{ + qbus_walk_children(bus, NULL, NULL, 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 */ +void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + /* just zap it */ + object_unparent(OBJECT(dev)); +} + +/* + * Realize @dev. + * Device properties should be set before calling this function. IRQs + * and MMIO regions should be connected/mapped after calling this + * function. + * On failure, report an error with error_report() and terminate the + * program. 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) +{ + Error *err = NULL; + + assert(!dev->realized); + + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_report("Initialization of device %s failed: %s", + object_get_typename(OBJECT(dev)), + error_get_pretty(err)); + exit(1); + } +} + +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; +} + +static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev, + const char *name) +{ + NamedGPIOList *ngl; + + QLIST_FOREACH(ngl, &dev->gpios, node) { + /* NULL is a valid and matchable name, otherwise do a normal + * strcmp match. + */ + if ((!ngl->name && !name) || + (name && ngl->name && strcmp(name, ngl->name) == 0)) { + return ngl; + } + } + + ngl = g_malloc0(sizeof(*ngl)); + ngl->name = g_strdup(name); + QLIST_INSERT_HEAD(&dev->gpios, ngl, node); + return ngl; +} + +void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler, + const char *name, int n) +{ + int i; + NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); + char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-in"); + + assert(gpio_list->num_out == 0 || !name); + gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler, + dev, n); + + for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) { + object_property_add_child(OBJECT(dev), propname, + OBJECT(gpio_list->in[i]), &error_abort); + } + g_free(propname); + + gpio_list->num_in += n; +} + +void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n) +{ + qdev_init_gpio_in_named(dev, handler, NULL, n); +} + +void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, + const char *name, int n) +{ + int i; + NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); + char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-out"); + + assert(gpio_list->num_in == 0 || !name); + gpio_list->num_out += n; + + for (i = 0; i < n; ++i) { + memset(&pins[i], 0, sizeof(*pins)); + object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, + (Object **)&pins[i], + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); + } + g_free(propname); +} + +void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n) +{ + qdev_init_gpio_out_named(dev, pins, NULL, n); +} + +qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n) +{ + NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); + + assert(n >= 0 && n < gpio_list->num_in); + return gpio_list->in[n]; +} + +qemu_irq qdev_get_gpio_in(DeviceState *dev, int n) +{ + return qdev_get_gpio_in_named(dev, NULL, n); +} + +void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, + qemu_irq pin) +{ + char *propname = g_strdup_printf("%s[%d]", + name ? name : "unnamed-gpio-out", n); + if (pin) { + /* We need a name for object_property_set_link to work. If the + * object has a parent, object_property_add_child will come back + * with an error without doing anything. If it has none, it will + * never fail. So we can just call it with a NULL Error pointer. + */ + object_property_add_child(container_get(qdev_get_machine(), + "/unattached"), + "non-qdev-gpio[*]", OBJECT(pin), NULL); + } + object_property_set_link(OBJECT(dev), OBJECT(pin), propname, &error_abort); + g_free(propname); +} + +qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n) +{ + char *propname = g_strdup_printf("%s[%d]", + name ? name : "unnamed-gpio-out", n); + + qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, + NULL); + + return ret; +} + +/* disconnect a GPIO ouput, returning the disconnected input (if any) */ + +static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev, + const char *name, int n) +{ + char *propname = g_strdup_printf("%s[%d]", + name ? name : "unnamed-gpio-out", n); + + qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, + NULL); + if (ret) { + object_property_set_link(OBJECT(dev), NULL, propname, NULL); + } + g_free(propname); + return ret; +} + +qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt, + const char *name, int n) +{ + qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n); + qdev_connect_gpio_out_named(dev, name, n, icpt); + return disconnected; +} + +void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin) +{ + qdev_connect_gpio_out_named(dev, NULL, n, pin); +} + +void qdev_pass_gpios(DeviceState *dev, DeviceState *container, + const char *name) +{ + int i; + NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name); + + for (i = 0; i < ngl->num_in; i++) { + const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in"; + char *propname = g_strdup_printf("%s[%d]", nm, i); + + object_property_add_alias(OBJECT(container), propname, + OBJECT(dev), propname, + &error_abort); + g_free(propname); + } + for (i = 0; i < ngl->num_out; i++) { + const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out"; + char *propname = g_strdup_printf("%s[%d]", nm, i); + + object_property_add_alias(OBJECT(container), propname, + OBJECT(dev), propname, + &error_abort); + g_free(propname); + } + QLIST_REMOVE(ngl, node); + QLIST_INSERT_HEAD(&container->gpios, ngl, node); +} + +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 *pre_devfn, qbus_walkerfn *pre_busfn, + qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, + void *opaque) +{ + BusChild *kid; + int err; + + if (pre_busfn) { + err = pre_busfn(bus, opaque); + if (err) { + return err; + } + } + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + err = qdev_walk_children(kid->child, + pre_devfn, pre_busfn, + post_devfn, post_busfn, opaque); + if (err < 0) { + return err; + } + } + + if (post_busfn) { + err = post_busfn(bus, opaque); + if (err) { + return err; + } + } + + return 0; +} + +int qdev_walk_children(DeviceState *dev, + qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, + qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, + void *opaque) +{ + BusState *bus; + int err; + + if (pre_devfn) { + err = pre_devfn(dev, opaque); + if (err) { + return err; + } + } + + QLIST_FOREACH(bus, &dev->child_bus, sibling) { + err = qbus_walk_children(bus, pre_devfn, pre_busfn, + post_devfn, post_busfn, opaque); + if (err < 0) { + return err; + } + } + + if (post_devfn) { + err = post_devfn(dev, opaque); + if (err) { + 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)); + BusClass *bc; + char *buf; + int i, len, bus_id; + + bus->parent = parent; + + if (name) { + bus->name = g_strdup(name); + } else if (bus->parent && bus->parent->id) { + /* parent device has id -> use it plus parent-bus-id for bus name */ + bus_id = bus->parent->num_child_bus; + + len = strlen(bus->parent->id) + 16; + buf = g_malloc(len); + snprintf(buf, len, "%s.%d", bus->parent->id, bus_id); + bus->name = buf; + } else { + /* no id -> use lowercase bus type plus global bus-id for bus name */ + bc = BUS_GET_CLASS(bus); + bus_id = bc->automatic_ids++; + + len = strlen(typename) + 16; + buf = g_malloc(len); + len = snprintf(buf, len, "%s.%d", typename, bus_id); + 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; + object_unparent(OBJECT(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); + } +} + +static bool bus_get_realized(Object *obj, Error **errp) +{ + BusState *bus = BUS(obj); + + return bus->realized; +} + +static void bus_set_realized(Object *obj, bool value, Error **errp) +{ + BusState *bus = BUS(obj); + BusClass *bc = BUS_GET_CLASS(bus); + BusChild *kid; + Error *local_err = NULL; + + if (value && !bus->realized) { + if (bc->realize) { + bc->realize(bus, &local_err); + } + + /* TODO: recursive realization */ + } else if (!value && bus->realized) { + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + object_property_set_bool(OBJECT(dev), false, "realized", + &local_err); + if (local_err != NULL) { + break; + } + } + if (bc->unrealize && local_err == NULL) { + bc->unrealize(bus, &local_err); + } + } + + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + bus->realized = value; +} + +void qbus_create_inplace(void *bus, size_t size, const char *typename, + DeviceState *parent, const char *name) +{ + object_initialize(bus, size, 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; +} + +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 char *qdev_get_fw_dev_path_from_handler(BusState *bus, DeviceState *dev) +{ + Object *obj = OBJECT(dev); + char *d = NULL; + + while (!d && obj->parent) { + obj = obj->parent; + d = fw_path_provider_try_get_dev_path(obj, bus, dev); + } + return d; +} + +char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev) +{ + Object *obj = OBJECT(dev); + + return fw_path_provider_try_get_dev_path(obj, bus, dev); +} + +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 = qdev_get_fw_dev_path_from_handler(dev->parent_bus, dev); + if (!d) { + d = bus_get_fw_dev_path(dev->parent_bus, dev); + } + if (d) { + l += snprintf(p + l, size - l, "%s", d); + g_free(d); + } else { + return l; + } + } + 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); +} + +/** + * @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. + */ +static void qdev_property_add_legacy(DeviceState *dev, Property *prop, + Error **errp) +{ + gchar *name; + + /* Register pointer properties as legacy properties */ + if (!prop->info->print && prop->info->get) { + return; + } + + name = g_strdup_printf("legacy-%s", prop->name); + object_property_add(OBJECT(dev), name, "str", + prop->info->print ? qdev_get_legacy_property : prop->info->get, + NULL, + NULL, + prop, errp); + + 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; + } + + object_property_set_description(obj, prop->name, + prop->info->description, + &error_abort); + + if (prop->qtype == QTYPE_NONE) { + return; + } + + if (prop->qtype == QTYPE_QBOOL) { + object_property_set_bool(obj, prop->defval, prop->name, &error_abort); + } else if (prop->info->enum_table) { + object_property_set_str(obj, prop->info->enum_table[prop->defval], + prop->name, &error_abort); + } else if (prop->qtype == QTYPE_QINT) { + object_property_set_int(obj, prop->defval, prop->name, &error_abort); + } +} + +/* @qdev_alias_all_properties - Add alias properties to the source object for + * all qdev properties on the target DeviceState. + */ +void qdev_alias_all_properties(DeviceState *target, Object *source) +{ + ObjectClass *class; + Property *prop; + + class = object_get_class(OBJECT(target)); + do { + DeviceClass *dc = DEVICE_CLASS(class); + + for (prop = dc->props; prop && prop->name; prop++) { + object_property_add_alias(source, prop->name, + OBJECT(target), prop->name, + &error_abort); + } + class = object_class_get_parent(class); + } while (class != object_class_by_name(TYPE_DEVICE)); +} + +static int qdev_add_hotpluggable_device(Object *obj, void *opaque) +{ + GSList **list = opaque; + DeviceState *dev = (DeviceState *)object_dynamic_cast(OBJECT(obj), + TYPE_DEVICE); + + if (dev == NULL) { + return 0; + } + + if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) { + *list = g_slist_append(*list, dev); + } + + return 0; +} + +GSList *qdev_build_hotpluggable_device_list(Object *peripheral) +{ + GSList *list = NULL; + + object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list); + + return list; +} + +static bool device_get_realized(Object *obj, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + return dev->realized; +} + +static void device_set_realized(Object *obj, bool value, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + DeviceClass *dc = DEVICE_GET_CLASS(dev); + HotplugHandler *hotplug_ctrl; + BusState *bus; + Error *local_err = NULL; + + if (dev->hotplugged && !dc->hotpluggable) { + error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj)); + return; + } + + if (value && !dev->realized) { + if (!obj->parent) { + 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, &error_abort); + g_free(name); + } + + if (dc->realize) { + dc->realize(dev, &local_err); + } + + if (local_err != NULL) { + goto fail; + } + + DEVICE_LISTENER_CALL(realize, Forward, dev); + + hotplug_ctrl = qdev_get_hotplug_handler(dev); + if (hotplug_ctrl) { + hotplug_handler_plug(hotplug_ctrl, dev, &local_err); + } + + if (local_err != NULL) { + goto post_realize_fail; + } + + if (qdev_get_vmsd(dev)) { + vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, + dev->instance_id_alias, + dev->alias_required_for_version); + } + + QLIST_FOREACH(bus, &dev->child_bus, sibling) { + object_property_set_bool(OBJECT(bus), true, "realized", + &local_err); + if (local_err != NULL) { + goto child_realize_fail; + } + } + if (dev->hotplugged) { + device_reset(dev); + } + dev->pending_deleted_event = false; + } else if (!value && dev->realized) { + Error **local_errp = NULL; + QLIST_FOREACH(bus, &dev->child_bus, sibling) { + local_errp = local_err ? NULL : &local_err; + object_property_set_bool(OBJECT(bus), false, "realized", + local_errp); + } + if (qdev_get_vmsd(dev)) { + vmstate_unregister(dev, qdev_get_vmsd(dev), dev); + } + if (dc->unrealize) { + local_errp = local_err ? NULL : &local_err; + dc->unrealize(dev, local_errp); + } + dev->pending_deleted_event = true; + DEVICE_LISTENER_CALL(unrealize, Reverse, dev); + } + + if (local_err != NULL) { + goto fail; + } + + dev->realized = value; + return; + +child_realize_fail: + QLIST_FOREACH(bus, &dev->child_bus, sibling) { + object_property_set_bool(OBJECT(bus), false, "realized", + NULL); + } + + if (qdev_get_vmsd(dev)) { + vmstate_unregister(dev, qdev_get_vmsd(dev), dev); + } + +post_realize_fail: + if (dc->unrealize) { + dc->unrealize(dev, NULL); + } + +fail: + error_propagate(errp, local_err); + return; +} + +static bool device_get_hotpluggable(Object *obj, Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(obj); + DeviceState *dev = DEVICE(obj); + + return dc->hotpluggable && (dev->parent_bus == NULL || + qbus_is_hotpluggable(dev->parent_bus)); +} + +static bool device_get_hotplugged(Object *obj, Error **err) +{ + DeviceState *dev = DEVICE(obj); + + return dev->hotplugged; +} + +static void device_set_hotplugged(Object *obj, bool value, Error **err) +{ + DeviceState *dev = DEVICE(obj); + + dev->hotplugged = value; +} + +static void device_initfn(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + ObjectClass *class; + Property *prop; + + 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); + object_property_add_bool(obj, "hotpluggable", + device_get_hotpluggable, NULL, NULL); + object_property_add_bool(obj, "hotplugged", + device_get_hotplugged, device_set_hotplugged, + &error_abort); + + class = object_get_class(OBJECT(dev)); + do { + for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) { + qdev_property_add_legacy(dev, prop, &error_abort); + qdev_property_add_static(dev, prop, &error_abort); + } + class = object_class_get_parent(class); + } while (class != object_class_by_name(TYPE_DEVICE)); + + object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS, + (Object **)&dev->parent_bus, NULL, 0, + &error_abort); + QLIST_INIT(&dev->gpios); +} + +static void device_post_init(Object *obj) +{ + qdev_prop_set_globals(DEVICE(obj)); +} + +/* Unlink device from bus and free the structure. */ +static void device_finalize(Object *obj) +{ + NamedGPIOList *ngl, *next; + + DeviceState *dev = DEVICE(obj); + qemu_opts_del(dev->opts); + + QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) { + QLIST_REMOVE(ngl, node); + qemu_free_irqs(ngl->in, ngl->num_in); + g_free(ngl->name); + g_free(ngl); + /* ngl->out irqs are owned by the other end and should not be freed + * here + */ + } +} + +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); + BusState *bus; + + if (dev->realized) { + object_property_set_bool(obj, false, "realized", NULL); + } + while (dev->num_child_bus) { + bus = QLIST_FIRST(&dev->child_bus); + object_unparent(OBJECT(bus)); + } + 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 (dev->pending_deleted_event) { + gchar *path = object_get_canonical_path(OBJECT(dev)); + + qapi_event_send_device_deleted(!!dev->id, dev->id, path, &error_abort); + 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; + dc->unrealize = device_unrealize; + + /* by default all devices were considered as hotpluggable, + * so with intent to check it in generic qdev_unplug() / + * device_set_realized() functions make every device + * hotpluggable. Devices that shouldn't be hotpluggable, + * should override it in their class_init() + */ + dc->hotpluggable = true; +} + +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_post_init = device_post_init, + .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); + object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY, + TYPE_HOTPLUG_HANDLER, + (Object **)&bus->hotplug_handler, + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + NULL); + object_property_add_bool(obj, "realized", + bus_get_realized, bus_set_realized, NULL); +} + +static char *default_bus_get_fw_dev_path(DeviceState *dev) +{ + return g_strdup(object_get_typename(OBJECT(dev))); +} + +static void bus_class_init(ObjectClass *class, void *data) +{ + BusClass *bc = BUS_CLASS(class); + + class->unparent = bus_unparent; + bc->get_fw_dev_path = default_bus_get_fw_dev_path; +} + +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/qemu/hw/core/stream.c b/qemu/hw/core/stream.c new file mode 100644 index 000000000..e6a05a543 --- /dev/null +++ b/qemu/hw/core/stream.c @@ -0,0 +1,32 @@ +#include "hw/stream.h" + +size_t +stream_push(StreamSlave *sink, uint8_t *buf, size_t len) +{ + StreamSlaveClass *k = STREAM_SLAVE_GET_CLASS(sink); + + return k->push(sink, buf, len); +} + +bool +stream_can_push(StreamSlave *sink, StreamCanPushNotifyFn notify, + void *notify_opaque) +{ + StreamSlaveClass *k = STREAM_SLAVE_GET_CLASS(sink); + + return k->can_push ? k->can_push(sink, notify, notify_opaque) : true; +} + +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/qemu/hw/core/sysbus.c b/qemu/hw/core/sysbus.c new file mode 100644 index 000000000..3c5862989 --- /dev/null +++ b/qemu/hw/core/sysbus.c @@ -0,0 +1,369 @@ +/* + * 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); + +typedef struct SysBusFind { + void *opaque; + FindSysbusDeviceFunc *func; +} SysBusFind; + +/* Run func() for every sysbus device, traverse the tree for everything else */ +static int find_sysbus_device(Object *obj, void *opaque) +{ + SysBusFind *find = opaque; + Object *dev; + SysBusDevice *sbdev; + + dev = object_dynamic_cast(obj, TYPE_SYS_BUS_DEVICE); + sbdev = (SysBusDevice *)dev; + + if (!sbdev) { + /* Container, traverse it for children */ + return object_child_foreach(obj, find_sysbus_device, opaque); + } + + find->func(sbdev, find->opaque); + + return 0; +} + +/* + * Loop through all dynamically created sysbus devices and call + * func() for each instance. + */ +void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque) +{ + Object *container; + SysBusFind find = { + .func = func, + .opaque = opaque, + }; + + /* Loop through all sysbus devices that were spawened outside the machine */ + container = container_get(qdev_get_machine(), "/peripheral"); + find_sysbus_device(container, &find); + container = container_get(qdev_get_machine(), "/peripheral-anon"); + find_sysbus_device(container, &find); +} + + +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, +}; + +/* Check whether an IRQ source exists */ +bool sysbus_has_irq(SysBusDevice *dev, int n) +{ + char *prop = g_strdup_printf("%s[%d]", SYSBUS_DEVICE_GPIO_IRQ, n); + ObjectProperty *r; + + r = object_property_find(OBJECT(dev), prop, NULL); + g_free(prop); + + return (r != NULL); +} + +bool sysbus_is_irq_connected(SysBusDevice *dev, int n) +{ + return !!sysbus_get_connected_irq(dev, n); +} + +qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n) +{ + DeviceState *d = DEVICE(dev); + return qdev_get_gpio_out_connector(d, SYSBUS_DEVICE_GPIO_IRQ, n); +} + +void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq) +{ + SysBusDeviceClass *sbd = SYS_BUS_DEVICE_GET_CLASS(dev); + + qdev_connect_gpio_out_named(DEVICE(dev), SYSBUS_DEVICE_GPIO_IRQ, n, irq); + + if (sbd->connect_irq_notifier) { + sbd->connect_irq_notifier(dev, irq); + } +} + +/* Check whether an MMIO region exists */ +bool sysbus_has_mmio(SysBusDevice *dev, unsigned int n) +{ + return (n < dev->num_mmio); +} + +static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr, + bool may_overlap, int 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, + int 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) +{ + qdev_init_gpio_out_named(DEVICE(dev), p, SYSBUS_DEVICE_GPIO_IRQ, 1); +} + +/* Pass IRQs from a target device. */ +void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target) +{ + qdev_pass_gpios(DEVICE(target), DEVICE(dev), SYSBUS_DEVICE_GPIO_IRQ); +} + +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; + + 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); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(s); + /* for the explicit unit address fallback case: */ + char *addr, *fw_dev_path; + + if (s->num_mmio) { + return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev), + s->mmio[0].addr); + } + if (s->num_pio) { + return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]); + } + if (sbc->explicit_ofw_unit_address) { + addr = sbc->explicit_ofw_unit_address(s); + if (addr) { + fw_dev_path = g_strdup_printf("%s@%s", qdev_fw_name(dev), addr); + g_free(addr); + return fw_dev_path; + } + } + return g_strdup(qdev_fw_name(dev)); +} + +void sysbus_add_io(SysBusDevice *dev, hwaddr addr, + MemoryRegion *mem) +{ + memory_region_add_subregion(get_system_io(), addr, 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, system_bus_info.instance_size, + 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) diff --git a/qemu/hw/core/uboot_image.h b/qemu/hw/core/uboot_image.h new file mode 100644 index 000000000..9fc2760b5 --- /dev/null +++ b/qemu/hw/core/uboot_image.h @@ -0,0 +1,158 @@ +/* + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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/>. + * + ******************************************************************** + * NOTE: This header file defines an interface to U-Boot. Including + * this (unmodified) header file in another file is considered normal + * use of U-Boot, and does *not* fall under the heading of "derived + * work". + ******************************************************************** + */ + +#ifndef __UBOOT_IMAGE_H__ +#define __UBOOT_IMAGE_H__ + +/* + * Operating System Codes + */ +#define IH_OS_INVALID 0 /* Invalid OS */ +#define IH_OS_OPENBSD 1 /* OpenBSD */ +#define IH_OS_NETBSD 2 /* NetBSD */ +#define IH_OS_FREEBSD 3 /* FreeBSD */ +#define IH_OS_4_4BSD 4 /* 4.4BSD */ +#define IH_OS_LINUX 5 /* Linux */ +#define IH_OS_SVR4 6 /* SVR4 */ +#define IH_OS_ESIX 7 /* Esix */ +#define IH_OS_SOLARIS 8 /* Solaris */ +#define IH_OS_IRIX 9 /* Irix */ +#define IH_OS_SCO 10 /* SCO */ +#define IH_OS_DELL 11 /* Dell */ +#define IH_OS_NCR 12 /* NCR */ +#define IH_OS_LYNXOS 13 /* LynxOS */ +#define IH_OS_VXWORKS 14 /* VxWorks */ +#define IH_OS_PSOS 15 /* pSOS */ +#define IH_OS_QNX 16 /* QNX */ +#define IH_OS_U_BOOT 17 /* Firmware */ +#define IH_OS_RTEMS 18 /* RTEMS */ +#define IH_OS_ARTOS 19 /* ARTOS */ +#define IH_OS_UNITY 20 /* Unity OS */ + +/* + * CPU Architecture Codes (supported by Linux) + */ +#define IH_CPU_INVALID 0 /* Invalid CPU */ +#define IH_CPU_ALPHA 1 /* Alpha */ +#define IH_CPU_ARM 2 /* ARM */ +#define IH_CPU_I386 3 /* Intel x86 */ +#define IH_CPU_IA64 4 /* IA64 */ +#define IH_CPU_MIPS 5 /* MIPS */ +#define IH_CPU_MIPS64 6 /* MIPS 64 Bit */ +#define IH_CPU_PPC 7 /* PowerPC */ +#define IH_CPU_S390 8 /* IBM S390 */ +#define IH_CPU_SH 9 /* SuperH */ +#define IH_CPU_SPARC 10 /* Sparc */ +#define IH_CPU_SPARC64 11 /* Sparc 64 Bit */ +#define IH_CPU_M68K 12 /* M68K */ +#define IH_CPU_NIOS 13 /* Nios-32 */ +#define IH_CPU_MICROBLAZE 14 /* MicroBlaze */ +#define IH_CPU_NIOS2 15 /* Nios-II */ +#define IH_CPU_BLACKFIN 16 /* Blackfin */ +#define IH_CPU_AVR32 17 /* AVR32 */ + +/* + * Image Types + * + * "Standalone Programs" are directly runnable in the environment + * provided by U-Boot; it is expected that (if they behave + * well) you can continue to work in U-Boot after return from + * the Standalone Program. + * "OS Kernel Images" are usually images of some Embedded OS which + * will take over control completely. Usually these programs + * will install their own set of exception handlers, device + * drivers, set up the MMU, etc. - this means, that you cannot + * expect to re-enter U-Boot except by resetting the CPU. + * "RAMDisk Images" are more or less just data blocks, and their + * parameters (address, size) are passed to an OS kernel that is + * being started. + * "Multi-File Images" contain several images, typically an OS + * (Linux) kernel image and one or more data images like + * RAMDisks. This construct is useful for instance when you want + * to boot over the network using BOOTP etc., where the boot + * server provides just a single image file, but you want to get + * for instance an OS kernel and a RAMDisk image. + * + * "Multi-File Images" start with a list of image sizes, each + * image size (in bytes) specified by an "uint32_t" in network + * byte order. This list is terminated by an "(uint32_t)0". + * Immediately after the terminating 0 follow the images, one by + * one, all aligned on "uint32_t" boundaries (size rounded up to + * a multiple of 4 bytes - except for the last file). + * + * "Firmware Images" are binary images containing firmware (like + * U-Boot or FPGA images) which usually will be programmed to + * flash memory. + * + * "Script files" are command sequences that will be executed by + * U-Boot's command interpreter; this feature is especially + * useful when you configure U-Boot to use a real shell (hush) + * as command interpreter (=> Shell Scripts). + */ + +#define IH_TYPE_INVALID 0 /* Invalid Image */ +#define IH_TYPE_STANDALONE 1 /* Standalone Program */ +#define IH_TYPE_KERNEL 2 /* OS Kernel Image */ +#define IH_TYPE_RAMDISK 3 /* RAMDisk Image */ +#define IH_TYPE_MULTI 4 /* Multi-File Image */ +#define IH_TYPE_FIRMWARE 5 /* Firmware Image */ +#define IH_TYPE_SCRIPT 6 /* Script file */ +#define IH_TYPE_FILESYSTEM 7 /* Filesystem Image (any type) */ +#define IH_TYPE_FLATDT 8 /* Binary Flat Device Tree Blob */ + +/* + * Compression Types + */ +#define IH_COMP_NONE 0 /* No Compression Used */ +#define IH_COMP_GZIP 1 /* gzip Compression Used */ +#define IH_COMP_BZIP2 2 /* bzip2 Compression Used */ + +#define IH_MAGIC 0x27051956 /* Image Magic Number */ +#define IH_NMLEN 32 /* Image Name Length */ + +/* + * all data in network byte order (aka natural aka bigendian) + */ + +typedef struct uboot_image_header { + uint32_t ih_magic; /* Image Header Magic Number */ + uint32_t ih_hcrc; /* Image Header CRC Checksum */ + uint32_t ih_time; /* Image Creation Timestamp */ + uint32_t ih_size; /* Image Data Size */ + uint32_t ih_load; /* Data Load Address */ + uint32_t ih_ep; /* Entry Point Address */ + uint32_t ih_dcrc; /* Image Data CRC Checksum */ + uint8_t ih_os; /* Operating System */ + uint8_t ih_arch; /* CPU architecture */ + uint8_t ih_type; /* Image Type */ + uint8_t ih_comp; /* Compression Type */ + uint8_t ih_name[IH_NMLEN]; /* Image Name */ +} uboot_image_header_t; + + +#endif /* __IMAGE_H__ */ |