Skip to content

Commit

Permalink
XXX gpex draft
Browse files Browse the repository at this point in the history
  • Loading branch information
agraf committed Jan 6, 2015
1 parent ab0302e commit ff56c2b
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 7 deletions.
2 changes: 2 additions & 0 deletions default-configs/arm-softmmu.mak
Expand Up @@ -82,6 +82,8 @@ CONFIG_ZYNQ=y
CONFIG_VERSATILE_PCI=y
CONFIG_VERSATILE_I2C=y

CONFIG_PCI_GENERIC=y

CONFIG_SDHCI=y
CONFIG_INTEGRATOR_DEBUG=y

Expand Down
83 changes: 78 additions & 5 deletions hw/arm/virt.c
Expand Up @@ -42,6 +42,7 @@
#include "exec/address-spaces.h"
#include "qemu/bitops.h"
#include "qemu/error-report.h"
#include "hw/pci-host/gpex.h"

#define NUM_VIRTIO_TRANSPORTS 32

Expand Down Expand Up @@ -69,6 +70,7 @@ enum {
VIRT_MMIO,
VIRT_RTC,
VIRT_FW_CFG,
VIRT_PCIE,
};

typedef struct MemMapEntry {
Expand Down Expand Up @@ -129,13 +131,14 @@ static const MemMapEntry a15memmap[] = {
[VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
/* 0x10000000 .. 0x40000000 reserved for PCI */
[VIRT_PCIE] = { 0x10000000, 0x30000000 },
[VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
};

static const int a15irqmap[] = {
[VIRT_UART] = 1,
[VIRT_RTC] = 2,
[VIRT_PCIE] = 3,
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
};

Expand Down Expand Up @@ -312,7 +315,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
}
}

static void fdt_add_gic_node(const VirtBoardInfo *vbi)
static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi)
{
uint32_t gic_phandle;

Expand All @@ -331,9 +334,11 @@ static void fdt_add_gic_node(const VirtBoardInfo *vbi)
2, vbi->memmap[VIRT_GIC_CPU].base,
2, vbi->memmap[VIRT_GIC_CPU].size);
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle);

return gic_phandle;
}

static void create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
{
/* We create a standalone GIC v2 */
DeviceState *gicdev;
Expand Down Expand Up @@ -380,7 +385,7 @@ static void create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
pic[i] = qdev_get_gpio_in(gicdev, i);
}

fdt_add_gic_node(vbi);
return fdt_add_gic_node(vbi);
}

static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
Expand Down Expand Up @@ -556,6 +561,71 @@ static void create_fw_cfg(const VirtBoardInfo *vbi)
g_free(nodename);
}

static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
uint32_t gic_phandle)
{
hwaddr base = vbi->memmap[VIRT_PCIE].base;
hwaddr size = vbi->memmap[VIRT_PCIE].size;
hwaddr size_ioport = 64 * 1024;
hwaddr size_ecam = PCIE_MMCFG_SIZE_MIN;
hwaddr size_mmio = size - size_ecam - size_ioport;
hwaddr base_mmio = base;
hwaddr base_ioport = base_mmio + size_mmio;
hwaddr base_ecam = base_ioport + size_ioport;
int irq = vbi->irqmap[VIRT_PCIE];
MemoryRegion *mmio_alias;
MemoryRegion *mmio_reg;
DeviceState *dev;
char *nodename;

dev = qdev_create(NULL, TYPE_GPEX_HOST);

qdev_prop_set_uint64(dev, "mmio_window_size", size_mmio);
qdev_init_nofail(dev);

sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base_ecam);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_ioport);

/* Map the MMIO window at the same spot in bus and cpu layouts */
mmio_alias = g_new0(MemoryRegion, 1);
mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio",
mmio_reg, base_mmio, size_mmio);
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);

sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irq]);

nodename = g_strdup_printf("/pcie@%" PRIx64, base);
qemu_fdt_add_subnode(vbi->fdt, nodename);
qemu_fdt_setprop_string(vbi->fdt, nodename,
"compatible", "pci-host-ecam-generic");
qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "pci");
qemu_fdt_setprop_cell(vbi->fdt, nodename, "#address-cells", 3);
qemu_fdt_setprop_cell(vbi->fdt, nodename, "#size-cells", 2);
qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0, 1);

qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
2, base_ecam, 2, size_ecam);
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges",
1, 0x01000000, 2, 0,
2, base_ioport, 2, size_ioport,

1, 0x02000000, 2, base_mmio,
2, base_mmio, 2, size_mmio);

qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1);
qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupt-map",
0, 0, 0, /* device */
0, /* PCI irq */
gic_phandle, GIC_FDT_IRQ_TYPE_SPI, irq,
GIC_FDT_IRQ_FLAGS_LEVEL_HI /* system irq */);
qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupt-map-mask",
0, 0, 0, /* device */
0 /* PCI irq */);

g_free(nodename);
}

static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
{
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo;
Expand All @@ -573,6 +643,7 @@ static void machvirt_init(MachineState *machine)
MemoryRegion *ram = g_new(MemoryRegion, 1);
const char *cpu_model = machine->cpu_model;
VirtBoardInfo *vbi;
uint32_t gic_phandle;

if (!cpu_model) {
cpu_model = "cortex-a15";
Expand Down Expand Up @@ -634,12 +705,14 @@ static void machvirt_init(MachineState *machine)

create_flash(vbi);

create_gic(vbi, pic);
gic_phandle = create_gic(vbi, pic);

create_uart(vbi, pic);

create_rtc(vbi, pic);

create_pcie(vbi, pic, gic_phandle);

/* Create mmio transports, so the user can create virtio backends
* (which will be automatically plugged in to the transports). If
* no backend is created the transport will just sit harmlessly idle.
Expand Down
1 change: 1 addition & 0 deletions hw/pci-host/Makefile.objs
Expand Up @@ -15,3 +15,4 @@ common-obj-$(CONFIG_PCI_APB) += apb.o
common-obj-$(CONFIG_FULONG) += bonito.o
common-obj-$(CONFIG_PCI_PIIX) += piix.o
common-obj-$(CONFIG_PCI_Q35) += q35.o
common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
156 changes: 156 additions & 0 deletions hw/pci-host/gpex.c
@@ -0,0 +1,156 @@
/*
* QEMU Generic PCI Express Bridge Emulation
*
* Copyright (C) 2015 Alexander Graf <agraf@suse.de>
*
* Code loosely based on q35.c.
*
* 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 "hw/hw.h"
#include "hw/pci-host/gpex.h"

/****************************************************************************
* GPEX host
*/

static void gpex_set_irq(void *opaque, int irq_num, int level)
{
GPEXHost *s = opaque;

qemu_set_irq(s->irq, level);
}

static int gpex_map_irq(PCIDevice *pci_dev, int irq_num)
{
/* We only support one IRQ line so far */
return 0;
}

static void gpex_host_realize(DeviceState *dev, Error **errp)
{
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
GPEXHost *s = GPEX_HOST(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);

pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MIN);
memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", s->mmio_window_size);
memory_region_init(&s->io_ioport, OBJECT(s), "gpex_ioport", 64 * 1024);

sysbus_init_mmio(sbd, &pex->mmio);
sysbus_init_mmio(sbd, &s->io_mmio);
sysbus_init_mmio(sbd, &s->io_ioport);
sysbus_init_irq(sbd, &s->irq);

pci->bus = pci_register_bus(dev, "pcie.0", gpex_set_irq, gpex_map_irq, s,
&s->io_mmio, &s->io_ioport, 0, 1, TYPE_PCIE_BUS);

qdev_set_parent_bus(DEVICE(&s->gpex_root), BUS(pci->bus));
qdev_init_nofail(DEVICE(&s->gpex_root));
}

static const char *gpex_host_root_bus_path(PCIHostState *host_bridge,
PCIBus *rootbus)
{
return "0000:00";
}

static Property gpex_root_props[] = {
DEFINE_PROP_UINT64("mmio_window_size", GPEXHost, mmio_window_size, 1ULL << 32),
DEFINE_PROP_END_OF_LIST(),
};

static void gpex_host_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);

hc->root_bus_path = gpex_host_root_bus_path;
dc->realize = gpex_host_realize;
dc->props = gpex_root_props;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->fw_name = "pci";
}

static void gpex_host_initfn(Object *obj)
{
GPEXHost *s = GPEX_HOST(obj);

object_initialize(&s->gpex_root, sizeof(s->gpex_root), TYPE_GPEX_ROOT_DEVICE);
object_property_add_child(OBJECT(s), "gpex_root", OBJECT(&s->gpex_root), NULL);
qdev_prop_set_uint32(DEVICE(&s->gpex_root), "addr", PCI_DEVFN(0, 0));
qdev_prop_set_bit(DEVICE(&s->gpex_root), "multifunction", false);
}

static const TypeInfo gpex_host_info = {
.name = TYPE_GPEX_HOST,
.parent = TYPE_PCIE_HOST_BRIDGE,
.instance_size = sizeof(GPEXHost),
.instance_init = gpex_host_initfn,
.class_init = gpex_host_class_init,
};

/****************************************************************************
* GPEX Root D0:F0
*/

static const VMStateDescription vmstate_gpex_root = {
.name = "gpex_root",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, GPEXRootState),
VMSTATE_END_OF_LIST()
}
};

static void gpex_root_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);

set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->desc = "Host bridge";
dc->vmsd = &vmstate_gpex_root;
k->vendor_id = PCI_VENDOR_ID_REDHAT;
k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
k->revision = 0;
k->class_id = PCI_CLASS_BRIDGE_HOST;
/*
* PCI-facing part of the host bridge, not usable without the
* host-facing part, which can't be device_add'ed, yet.
*/
dc->cannot_instantiate_with_device_add_yet = true;
}

static const TypeInfo gpex_root_info = {
.name = TYPE_GPEX_ROOT_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(GPEXRootState),
.class_init = gpex_root_class_init,
};

static void gpex_register(void)
{
type_register_static(&gpex_root_info);
type_register_static(&gpex_host_info);
}

type_init(gpex_register);
9 changes: 7 additions & 2 deletions hw/pci/pcie_host.c
Expand Up @@ -98,15 +98,20 @@ void pcie_host_mmcfg_unmap(PCIExpressHost *e)
}
}

void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr,
uint32_t size)
void pcie_host_mmcfg_init(PCIExpressHost *e, uint32_t size)
{
assert(!(size & (size - 1))); /* power of 2 */
assert(size >= PCIE_MMCFG_SIZE_MIN);
assert(size <= PCIE_MMCFG_SIZE_MAX);
e->size = size;
memory_region_init_io(&e->mmio, OBJECT(e), &pcie_mmcfg_ops, e,
"pcie-mmcfg", e->size);
}

void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr,
uint32_t size)
{
pcie_host_mmcfg_init(e, size);
e->base_addr = addr;
memory_region_add_subregion(get_system_memory(), e->base_addr, &e->mmio);
}
Expand Down

0 comments on commit ff56c2b

Please sign in to comment.