提交 7634fe3c 编写于 作者: A Alexander Graf

sysbus: Add new platform bus helper device

We need to support spawning of sysbus devices dynamically via the command line.
The easiest way to represent these dynamically spawned devices in the guest's
memory and IRQ layout is by preallocating some space for dynamic sysbus devices.

This is what the "platform bus" device does. It is a sysbus device that exports
a configurably sized MMIO region and a configurable number of IRQ lines. When
this device encounters sysbus devices that have been dynamically created and not
manually wired up, it dynamically connects them to its own pool of resources.

The machine model can then loop through all of these devices and create a guest
configuration (device tree) to make them visible to the guest.
Signed-off-by: NAlexander Graf <agraf@suse.de>
上级 471a9bc1
......@@ -14,3 +14,4 @@ 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_SOFTMMU) += platform-bus.o
/*
* 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 "monitor/monitor.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)
#ifndef HW_PLATFORM_BUS_H
#define HW_PLATFORM_BUS_H 1
/*
* 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/sysbus.h"
typedef struct PlatformBusDevice PlatformBusDevice;
#define TYPE_PLATFORM_BUS_DEVICE "platform-bus-device"
#define PLATFORM_BUS_DEVICE(obj) \
OBJECT_CHECK(PlatformBusDevice, (obj), TYPE_PLATFORM_BUS_DEVICE)
#define PLATFORM_BUS_DEVICE_CLASS(klass) \
OBJECT_CLASS_CHECK(PlatformBusDeviceClass, (klass), TYPE_PLATFORM_BUS_DEVICE)
#define PLATFORM_BUS_DEVICE_GET_CLASS(obj) \
OBJECT_GET_CLASS(PlatformBusDeviceClass, (obj), TYPE_PLATFORM_BUS_DEVICE)
struct PlatformBusDevice {
/*< private >*/
SysBusDevice parent_obj;
Notifier notifier;
bool done_gathering;
/*< public >*/
uint32_t mmio_size;
MemoryRegion mmio;
uint32_t num_irqs;
qemu_irq *irqs;
unsigned long *used_irqs;
};
int platform_bus_get_irqn(PlatformBusDevice *platform_bus, SysBusDevice *sbdev,
int n);
hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev,
int n);
#endif /* !HW_PLATFORM_BUS_H */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册