diff options
Diffstat (limited to 'qemu/hw/core/sysbus.c')
-rw-r--r-- | qemu/hw/core/sysbus.c | 369 |
1 files changed, 369 insertions, 0 deletions
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) |