arm-devs queue

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABCAAGBQJR7RnMAAoJEDwlJe0UNgze9BQP/iuj/QKrw71vrMMCHlrzqjRc
 WXNaGkGHGaRw1r1X/XSiEqI3Ti2frHhsJ+annso4Q3forfGCbnB1Qaqvs/KzQW09
 KQBK3b2AZ9m4b35ZpZYpmbNaIS60XVV1VVB9tshXKJgyYObGlHRWj8MpepSrl3Rr
 texchdyNgZnqCS7Ep6oxzaR2bLqcr1Mi8+NG4dLJfw/z8BREPasQfxOYQoKxDVKV
 Cg2gd31ZAVzqJXtUuwdtkuM7JddfOnGk/MfDkZEBFhQ/fnRE5GSGYTuOHQp9hYdt
 bKnJbT0tqorP5+xg4dzVTqOJ+TsWm+ZfQrzQzkWSM34msYSoohCsF3/BA3xkF3/9
 6iE4ZfHrM6R/XO3A61NbtE9CvhFq9YsLPq7TcAAEzapBFXZlQAGCbZNJlGqn72p1
 XSTFwB02c2+gOXhhUtCwh0OKVbX79J99TQkBR1bEXr3C0yokxa0bIy7kJy+X2+vF
 NOMzoWhEteylZn18tvDfjPCXXzO4kJ8+3sYtvyYAWRadG1QcCq+8xMwUgcVQgmnM
 3TO2r+i4Cs+Ut9m6krW3P3ctL4cCoZj4bDqOu/8Fd7OVBK6u6LtXwej6LoiIDSPD
 3D2Bns65EhEZVucoObgNxG2h+JFLcLm3qRKY51VxD0lJh4Nn90jo317I43FHWONe
 HZZqqO8yPPf7LG/QGTzA
 =AvPS
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'pmaydell/tags/pull-arm-devs-20130722' into staging

arm-devs queue

# gpg: Signature made Mon 22 Jul 2013 06:38:52 AM CDT using RSA key ID 14360CDE
# gpg: Can't check signature: public key not found

# By Peter Maydell (8) and Soren Brinkmann (2)
# Via Peter Maydell
* pmaydell/tags/pull-arm-devs-20130722:
  hw/arm: Use 'load_ramdisk()' for loading ramdisks w/ U-Boot header
  hw/loader: Support ramdisk with u-boot header
  vexpress: Add virtio-mmio transports
  vexpress: Make VEDBoardInfo extend arm_boot_info
  arm/boot: Allow boards to modify the FDT blob
  virtio: Implement MMIO based virtio transport
  virtio: Support transports which can specify the vring alignment
  virtio: Add support for guest setting of queue size
  arm/boot: Use qemu_devtree_setprop_sized_cells()
  device_tree: Add qemu_devtree_setprop_sized_cells() utility functions

Message-id: 1374493427-3254-1-git-send-email-peter.maydell@linaro.org
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Anthony Liguori 2013-07-22 10:14:24 -05:00
commit 549c272b3c
12 changed files with 771 additions and 68 deletions

View File

@ -308,3 +308,36 @@ void qemu_devtree_dumpdtb(void *fdt, int size)
exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
}
}
int qemu_devtree_setprop_sized_cells_from_array(void *fdt,
const char *node_path,
const char *property,
int numvalues,
uint64_t *values)
{
uint32_t *propcells;
uint64_t value;
int cellnum, vnum, ncells;
uint32_t hival;
propcells = g_new0(uint32_t, numvalues * 2);
cellnum = 0;
for (vnum = 0; vnum < numvalues; vnum++) {
ncells = values[vnum * 2];
if (ncells != 1 && ncells != 2) {
return -1;
}
value = values[vnum * 2 + 1];
hival = cpu_to_be32(value >> 32);
if (ncells > 1) {
propcells[cellnum++] = hival;
} else if (hival != 0) {
return -1;
}
propcells[cellnum++] = cpu_to_be32(value);
}
return qemu_devtree_setprop(fdt, node_path, property, propcells,
cellnum * sizeof(uint32_t));
}

View File

@ -227,12 +227,10 @@ static void set_kernel_args_old(const struct arm_boot_info *info)
static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
{
uint32_t *mem_reg_property;
uint32_t mem_reg_propsize;
void *fdt = NULL;
char *filename;
int size, rc;
uint32_t acells, scells, hival;
uint32_t acells, scells;
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename);
if (!filename) {
@ -255,29 +253,18 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
goto fail;
}
mem_reg_propsize = acells + scells;
mem_reg_property = g_new0(uint32_t, mem_reg_propsize);
mem_reg_property[acells - 1] = cpu_to_be32(binfo->loader_start);
hival = cpu_to_be32(binfo->loader_start >> 32);
if (acells > 1) {
mem_reg_property[acells - 2] = hival;
} else if (hival != 0) {
fprintf(stderr, "qemu: dtb file not compatible with "
"RAM start address > 4GB\n");
goto fail;
}
mem_reg_property[acells + scells - 1] = cpu_to_be32(binfo->ram_size);
hival = cpu_to_be32(binfo->ram_size >> 32);
if (scells > 1) {
mem_reg_property[acells + scells - 2] = hival;
} else if (hival != 0) {
if (scells < 2 && binfo->ram_size >= (1ULL << 32)) {
/* This is user error so deserves a friendlier error message
* than the failure of setprop_sized_cells would provide
*/
fprintf(stderr, "qemu: dtb file not compatible with "
"RAM size > 4GB\n");
goto fail;
}
rc = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
mem_reg_propsize * sizeof(uint32_t));
rc = qemu_devtree_setprop_sized_cells(fdt, "/memory", "reg",
acells, binfo->loader_start,
scells, binfo->ram_size);
if (rc < 0) {
fprintf(stderr, "couldn't set /memory/reg\n");
goto fail;
@ -307,6 +294,11 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
goto fail;
}
}
if (binfo->modify_dtb) {
binfo->modify_dtb(binfo, fdt);
}
qemu_devtree_dumpdtb(fdt, size);
cpu_physical_memory_write(addr, fdt, size);
@ -419,10 +411,16 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
info->entry = entry;
if (is_linux) {
if (info->initrd_filename) {
initrd_size = load_image_targphys(info->initrd_filename,
info->initrd_start,
info->ram_size -
info->initrd_start);
initrd_size = load_ramdisk(info->initrd_filename,
info->initrd_start,
info->ram_size -
info->initrd_start);
if (initrd_size < 0) {
initrd_size = load_image_targphys(info->initrd_filename,
info->initrd_start,
info->ram_size -
info->initrd_start);
}
if (initrd_size < 0) {
fprintf(stderr, "qemu: could not load initrd '%s'\n",
info->initrd_filename);

View File

@ -31,12 +31,17 @@
#include "exec/address-spaces.h"
#include "sysemu/blockdev.h"
#include "hw/block/flash.h"
#include "sysemu/device_tree.h"
#include <libfdt.h>
#define VEXPRESS_BOARD_ID 0x8e0
#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
#define VEXPRESS_FLASH_SECT_SIZE (256 * 1024)
static struct arm_boot_info vexpress_binfo;
/* Number of virtio transports to create (0..8; limited by
* number of available IRQ lines).
*/
#define NUM_VIRTIO_TRANSPORTS 4
/* Address maps for peripherals:
* the Versatile Express motherboard has two possible maps,
@ -73,6 +78,7 @@ enum {
VE_ETHERNET,
VE_USB,
VE_DAPROM,
VE_VIRTIO,
};
static hwaddr motherboard_legacy_map[] = {
@ -91,6 +97,7 @@ static hwaddr motherboard_legacy_map[] = {
[VE_WDT] = 0x1000f000,
[VE_TIMER01] = 0x10011000,
[VE_TIMER23] = 0x10012000,
[VE_VIRTIO] = 0x10013000,
[VE_SERIALDVI] = 0x10016000,
[VE_RTC] = 0x10017000,
[VE_COMPACTFLASH] = 0x1001a000,
@ -137,6 +144,7 @@ static hwaddr motherboard_aseries_map[] = {
[VE_WDT] = 0x1c0f0000,
[VE_TIMER01] = 0x1c110000,
[VE_TIMER23] = 0x1c120000,
[VE_VIRTIO] = 0x1c130000,
[VE_SERIALDVI] = 0x1c160000,
[VE_RTC] = 0x1c170000,
[VE_COMPACTFLASH] = 0x1c1a0000,
@ -153,6 +161,7 @@ typedef void DBoardInitFn(const VEDBoardInfo *daughterboard,
qemu_irq *pic);
struct VEDBoardInfo {
struct arm_boot_info bootinfo;
const hwaddr *motherboard_map;
hwaddr loader_start;
const hwaddr gic_cpu_if_addr;
@ -272,7 +281,7 @@ static const uint32_t a9_clocks[] = {
66670000, /* Test chip reference clock: 66.67MHz */
};
static const VEDBoardInfo a9_daughterboard = {
static VEDBoardInfo a9_daughterboard = {
.motherboard_map = motherboard_legacy_map,
.loader_start = 0x60000000,
.gic_cpu_if_addr = 0x1e000100,
@ -384,7 +393,7 @@ static const uint32_t a15_clocks[] = {
40000000, /* OSCCLK8: 40MHz : DDR2 PLL reference */
};
static const VEDBoardInfo a15_daughterboard = {
static VEDBoardInfo a15_daughterboard = {
.motherboard_map = motherboard_aseries_map,
.loader_start = 0x80000000,
.gic_cpu_if_addr = 0x2c002000,
@ -396,7 +405,86 @@ static const VEDBoardInfo a15_daughterboard = {
.init = a15_daughterboard_init,
};
static void vexpress_common_init(const VEDBoardInfo *daughterboard,
static int add_virtio_mmio_node(void *fdt, uint32_t acells, uint32_t scells,
hwaddr addr, hwaddr size, uint32_t intc,
int irq)
{
/* Add a virtio_mmio node to the device tree blob:
* virtio_mmio@ADDRESS {
* compatible = "virtio,mmio";
* reg = <ADDRESS, SIZE>;
* interrupt-parent = <&intc>;
* interrupts = <0, irq, 1>;
* }
* (Note that the format of the interrupts property is dependent on the
* interrupt controller that interrupt-parent points to; these are for
* the ARM GIC and indicate an SPI interrupt, rising-edge-triggered.)
*/
int rc;
char *nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, addr);
rc = qemu_devtree_add_subnode(fdt, nodename);
rc |= qemu_devtree_setprop_string(fdt, nodename,
"compatible", "virtio,mmio");
rc |= qemu_devtree_setprop_sized_cells(fdt, nodename, "reg",
acells, addr, scells, size);
qemu_devtree_setprop_cells(fdt, nodename, "interrupt-parent", intc);
qemu_devtree_setprop_cells(fdt, nodename, "interrupts", 0, irq, 1);
g_free(nodename);
if (rc) {
return -1;
}
return 0;
}
static uint32_t find_int_controller(void *fdt)
{
/* Find the FDT node corresponding to the interrupt controller
* for virtio-mmio devices. We do this by scanning the fdt for
* a node with the right compatibility, since we know there is
* only one GIC on a vexpress board.
* We return the phandle of the node, or 0 if none was found.
*/
const char *compat = "arm,cortex-a9-gic";
int offset;
offset = fdt_node_offset_by_compatible(fdt, -1, compat);
if (offset >= 0) {
return fdt_get_phandle(fdt, offset);
}
return 0;
}
static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt)
{
uint32_t acells, scells, intc;
const VEDBoardInfo *daughterboard = (const VEDBoardInfo *)info;
acells = qemu_devtree_getprop_cell(fdt, "/", "#address-cells");
scells = qemu_devtree_getprop_cell(fdt, "/", "#size-cells");
intc = find_int_controller(fdt);
if (!intc) {
/* Not fatal, we just won't provide virtio. This will
* happen with older device tree blobs.
*/
fprintf(stderr, "QEMU: warning: couldn't find interrupt controller in "
"dtb; will not include virtio-mmio devices in the dtb.\n");
} else {
int i;
const hwaddr *map = daughterboard->motherboard_map;
/* We iterate backwards here because adding nodes
* to the dtb puts them in last-first.
*/
for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) {
add_virtio_mmio_node(fdt, acells, scells,
map[VE_VIRTIO] + 0x200 * i,
0x200, intc, 40 + i);
}
}
}
static void vexpress_common_init(VEDBoardInfo *daughterboard,
QEMUMachineInitArgs *args)
{
DeviceState *dev, *sysctl, *pl041;
@ -524,17 +612,27 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard,
/* VE_DAPROM: not modelled */
vexpress_binfo.ram_size = args->ram_size;
vexpress_binfo.kernel_filename = args->kernel_filename;
vexpress_binfo.kernel_cmdline = args->kernel_cmdline;
vexpress_binfo.initrd_filename = args->initrd_filename;
vexpress_binfo.nb_cpus = smp_cpus;
vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
vexpress_binfo.loader_start = daughterboard->loader_start;
vexpress_binfo.smp_loader_start = map[VE_SRAM];
vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
vexpress_binfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
arm_load_kernel(ARM_CPU(first_cpu), &vexpress_binfo);
/* 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.
*/
for (i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) {
sysbus_create_simple("virtio-mmio", map[VE_VIRTIO] + 0x200 * i,
pic[40 + i]);
}
daughterboard->bootinfo.ram_size = args->ram_size;
daughterboard->bootinfo.kernel_filename = args->kernel_filename;
daughterboard->bootinfo.kernel_cmdline = args->kernel_cmdline;
daughterboard->bootinfo.initrd_filename = args->initrd_filename;
daughterboard->bootinfo.nb_cpus = smp_cpus;
daughterboard->bootinfo.board_id = VEXPRESS_BOARD_ID;
daughterboard->bootinfo.loader_start = daughterboard->loader_start;
daughterboard->bootinfo.smp_loader_start = map[VE_SRAM];
daughterboard->bootinfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
daughterboard->bootinfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
daughterboard->bootinfo.modify_dtb = vexpress_modify_dtb;
arm_load_kernel(ARM_CPU(first_cpu), &daughterboard->bootinfo);
}
static void vexpress_a9_init(QEMUMachineInitArgs *args)

View File

@ -434,15 +434,17 @@ static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
}
/* Load a U-Boot image. */
int load_uimage(const char *filename, hwaddr *ep,
hwaddr *loadaddr, int *is_linux)
static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
int *is_linux, uint8_t image_type)
{
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)
@ -457,32 +459,55 @@ int load_uimage(const char *filename, hwaddr *ep,
if (hdr->ih_magic != IH_MAGIC)
goto out;
/* TODO: Implement other image types. */
if (hdr->ih_type != IH_TYPE_KERNEL) {
fprintf(stderr, "Can only load u-boot image type \"kernel\"\n");
if (hdr->ih_type != image_type) {
fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type,
image_type);
goto out;
}
switch (hdr->ih_comp) {
case IH_COMP_NONE:
case IH_COMP_GZIP:
/* TODO: Implement other image types. */
switch (hdr->ih_type) {
case IH_TYPE_KERNEL:
address = hdr->ih_load;
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,
"Unable to load u-boot images with compression type %d\n",
hdr->ih_comp);
fprintf(stderr, "Unsupported u-boot image type %d\n", hdr->ih_type);
goto out;
}
/* TODO: Check CPU type. */
if (is_linux) {
if (hdr->ih_os == IH_OS_LINUX)
*is_linux = 1;
else
*is_linux = 0;
}
*ep = hdr->ih_ep;
data = g_malloc(hdr->ih_size);
if (read(fd, data, hdr->ih_size) != hdr->ih_size) {
@ -490,7 +515,7 @@ int load_uimage(const char *filename, hwaddr *ep,
goto out;
}
if (hdr->ih_comp == IH_COMP_GZIP) {
if (do_uncompress) {
uint8_t *compressed_data;
size_t max_bytes;
ssize_t bytes;
@ -508,10 +533,7 @@ int load_uimage(const char *filename, hwaddr *ep,
hdr->ih_size = bytes;
}
rom_add_blob_fixed(filename, data, hdr->ih_size, hdr->ih_load);
if (loadaddr)
*loadaddr = hdr->ih_load;
rom_add_blob_fixed(filename, data, hdr->ih_size, address);
ret = hdr->ih_size;
@ -522,6 +544,18 @@ out:
return ret;
}
int load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr,
int *is_linux)
{
return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL);
}
/* 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);
}
/*
* Functions for reboot-persistent memory regions.
* - used for vga bios and option roms.

View File

@ -1,6 +1,7 @@
common-obj-y += virtio-rng.o
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
common-obj-y += virtio-bus.o
common-obj-y += virtio-mmio.o
common-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += dataplane/
obj-y += virtio.o virtio-balloon.o

421
hw/virtio/virtio-mmio.c Normal file
View File

@ -0,0 +1,421 @@
/*
* Virtio MMIO bindings
*
* Copyright (c) 2011 Linaro Limited
*
* Author:
* Peter Maydell <peter.maydell@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License; 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/sysbus.h"
#include "hw/virtio/virtio.h"
#include "qemu/host-utils.h"
#include "hw/virtio/virtio-bus.h"
/* #define DEBUG_VIRTIO_MMIO */
#ifdef DEBUG_VIRTIO_MMIO
#define DPRINTF(fmt, ...) \
do { printf("virtio_mmio: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do {} while (0)
#endif
/* QOM macros */
/* virtio-mmio-bus */
#define TYPE_VIRTIO_MMIO_BUS "virtio-mmio-bus"
#define VIRTIO_MMIO_BUS(obj) \
OBJECT_CHECK(VirtioBusState, (obj), TYPE_VIRTIO_MMIO_BUS)
#define VIRTIO_MMIO_BUS_GET_CLASS(obj) \
OBJECT_GET_CLASS(VirtioBusClass, (obj), TYPE_VIRTIO_MMIO_BUS)
#define VIRTIO_MMIO_BUS_CLASS(klass) \
OBJECT_CLASS_CHECK(VirtioBusClass, (klass), TYPE_VIRTIO_MMIO_BUS)
/* virtio-mmio */
#define TYPE_VIRTIO_MMIO "virtio-mmio"
#define VIRTIO_MMIO(obj) \
OBJECT_CHECK(VirtIOMMIOProxy, (obj), TYPE_VIRTIO_MMIO)
/* Memory mapped register offsets */
#define VIRTIO_MMIO_MAGIC 0x0
#define VIRTIO_MMIO_VERSION 0x4
#define VIRTIO_MMIO_DEVICEID 0x8
#define VIRTIO_MMIO_VENDORID 0xc
#define VIRTIO_MMIO_HOSTFEATURES 0x10
#define VIRTIO_MMIO_HOSTFEATURESSEL 0x14
#define VIRTIO_MMIO_GUESTFEATURES 0x20
#define VIRTIO_MMIO_GUESTFEATURESSEL 0x24
#define VIRTIO_MMIO_GUESTPAGESIZE 0x28
#define VIRTIO_MMIO_QUEUESEL 0x30
#define VIRTIO_MMIO_QUEUENUMMAX 0x34
#define VIRTIO_MMIO_QUEUENUM 0x38
#define VIRTIO_MMIO_QUEUEALIGN 0x3c
#define VIRTIO_MMIO_QUEUEPFN 0x40
#define VIRTIO_MMIO_QUEUENOTIFY 0x50
#define VIRTIO_MMIO_INTERRUPTSTATUS 0x60
#define VIRTIO_MMIO_INTERRUPTACK 0x64
#define VIRTIO_MMIO_STATUS 0x70
/* Device specific config space starts here */
#define VIRTIO_MMIO_CONFIG 0x100
#define VIRT_MAGIC 0x74726976 /* 'virt' */
#define VIRT_VERSION 1
#define VIRT_VENDOR 0x554D4551 /* 'QEMU' */
typedef struct {
/* Generic */
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
uint32_t host_features;
/* Guest accessible state needing migration and reset */
uint32_t host_features_sel;
uint32_t guest_features_sel;
uint32_t guest_page_shift;
/* virtio-bus */
VirtioBusState bus;
} VirtIOMMIOProxy;
static void virtio_mmio_bus_new(VirtioBusState *bus, VirtIOMMIOProxy *dev);
static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
{
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
VirtIODevice *vdev = proxy->bus.vdev;
DPRINTF("virtio_mmio_read offset 0x%x\n", (int)offset);
if (!vdev) {
/* If no backend is present, we treat most registers as
* read-as-zero, except for the magic number, version and
* vendor ID. This is not strictly sanctioned by the virtio
* spec, but it allows us to provide transports with no backend
* plugged in which don't confuse Linux's virtio code: the
* probe won't complain about the bad magic number, but the
* device ID of zero means no backend will claim it.
*/
switch (offset) {
case VIRTIO_MMIO_MAGIC:
return VIRT_MAGIC;
case VIRTIO_MMIO_VERSION:
return VIRT_VERSION;
case VIRTIO_MMIO_VENDORID:
return VIRT_VENDOR;
default:
return 0;
}
}
if (offset >= VIRTIO_MMIO_CONFIG) {
offset -= VIRTIO_MMIO_CONFIG;
switch (size) {
case 1:
return virtio_config_readb(vdev, offset);
case 2:
return virtio_config_readw(vdev, offset);
case 4:
return virtio_config_readl(vdev, offset);
default:
abort();
}
}
if (size != 4) {
DPRINTF("wrong size access to register!\n");
return 0;
}
switch (offset) {
case VIRTIO_MMIO_MAGIC:
return VIRT_MAGIC;
case VIRTIO_MMIO_VERSION:
return VIRT_VERSION;
case VIRTIO_MMIO_DEVICEID:
return vdev->device_id;
case VIRTIO_MMIO_VENDORID:
return VIRT_VENDOR;
case VIRTIO_MMIO_HOSTFEATURES:
if (proxy->host_features_sel) {
return 0;
}
return proxy->host_features;
case VIRTIO_MMIO_QUEUENUMMAX:
return VIRTQUEUE_MAX_SIZE;
case VIRTIO_MMIO_QUEUEPFN:
return virtio_queue_get_addr(vdev, vdev->queue_sel)
>> proxy->guest_page_shift;
case VIRTIO_MMIO_INTERRUPTSTATUS:
return vdev->isr;
case VIRTIO_MMIO_STATUS:
return vdev->status;
case VIRTIO_MMIO_HOSTFEATURESSEL:
case VIRTIO_MMIO_GUESTFEATURES:
case VIRTIO_MMIO_GUESTFEATURESSEL:
case VIRTIO_MMIO_GUESTPAGESIZE:
case VIRTIO_MMIO_QUEUESEL:
case VIRTIO_MMIO_QUEUENUM:
case VIRTIO_MMIO_QUEUEALIGN:
case VIRTIO_MMIO_QUEUENOTIFY:
case VIRTIO_MMIO_INTERRUPTACK:
DPRINTF("read of write-only register\n");
return 0;
default:
DPRINTF("bad register offset\n");
return 0;
}
return 0;
}
static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
VirtIODevice *vdev = proxy->bus.vdev;
DPRINTF("virtio_mmio_write offset 0x%x value 0x%" PRIx64 "\n",
(int)offset, value);
if (!vdev) {
/* If no backend is present, we just make all registers
* write-ignored. This allows us to provide transports with
* no backend plugged in.
*/
return;
}
if (offset >= VIRTIO_MMIO_CONFIG) {
offset -= VIRTIO_MMIO_CONFIG;
switch (size) {
case 1:
virtio_config_writeb(vdev, offset, value);
break;
case 2:
virtio_config_writew(vdev, offset, value);
break;
case 4:
virtio_config_writel(vdev, offset, value);
break;
default:
abort();
}
return;
}
if (size != 4) {
DPRINTF("wrong size access to register!\n");
return;
}
switch (offset) {
case VIRTIO_MMIO_HOSTFEATURESSEL:
proxy->host_features_sel = value;
break;
case VIRTIO_MMIO_GUESTFEATURES:
if (!proxy->guest_features_sel) {
virtio_set_features(vdev, value);
}
break;
case VIRTIO_MMIO_GUESTFEATURESSEL:
proxy->guest_features_sel = value;
break;
case VIRTIO_MMIO_GUESTPAGESIZE:
proxy->guest_page_shift = ctz32(value);
if (proxy->guest_page_shift > 31) {
proxy->guest_page_shift = 0;
}
DPRINTF("guest page size %" PRIx64 " shift %d\n", value,
proxy->guest_page_shift);
break;
case VIRTIO_MMIO_QUEUESEL:
if (value < VIRTIO_PCI_QUEUE_MAX) {
vdev->queue_sel = value;
}
break;
case VIRTIO_MMIO_QUEUENUM:
DPRINTF("mmio_queue write %d max %d\n", (int)value, VIRTQUEUE_MAX_SIZE);
virtio_queue_set_num(vdev, vdev->queue_sel, value);
break;
case VIRTIO_MMIO_QUEUEALIGN:
virtio_queue_set_align(vdev, vdev->queue_sel, value);
break;
case VIRTIO_MMIO_QUEUEPFN:
if (value == 0) {
virtio_reset(vdev);
} else {
virtio_queue_set_addr(vdev, vdev->queue_sel,
value << proxy->guest_page_shift);
}
break;
case VIRTIO_MMIO_QUEUENOTIFY:
if (value < VIRTIO_PCI_QUEUE_MAX) {
virtio_queue_notify(vdev, value);
}
break;
case VIRTIO_MMIO_INTERRUPTACK:
vdev->isr &= ~value;
virtio_update_irq(vdev);
break;
case VIRTIO_MMIO_STATUS:
virtio_set_status(vdev, value & 0xff);
if (vdev->status == 0) {
virtio_reset(vdev);
}
break;
case VIRTIO_MMIO_MAGIC:
case VIRTIO_MMIO_VERSION:
case VIRTIO_MMIO_DEVICEID:
case VIRTIO_MMIO_VENDORID:
case VIRTIO_MMIO_HOSTFEATURES:
case VIRTIO_MMIO_QUEUENUMMAX:
case VIRTIO_MMIO_INTERRUPTSTATUS:
DPRINTF("write to readonly register\n");
break;
default:
DPRINTF("bad register offset\n");
}
}
static const MemoryRegionOps virtio_mem_ops = {
.read = virtio_mmio_read,
.write = virtio_mmio_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
int level;
if (!proxy->bus.vdev) {
return;
}
level = (proxy->bus.vdev->isr != 0);
DPRINTF("virtio_mmio setting IRQ %d\n", level);
qemu_set_irq(proxy->irq, level);
}
static unsigned int virtio_mmio_get_features(DeviceState *opaque)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
return proxy->host_features;
}
static int virtio_mmio_load_config(DeviceState *opaque, QEMUFile *f)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
proxy->host_features_sel = qemu_get_be32(f);
proxy->guest_features_sel = qemu_get_be32(f);
proxy->guest_page_shift = qemu_get_be32(f);
return 0;
}
static void virtio_mmio_save_config(DeviceState *opaque, QEMUFile *f)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
qemu_put_be32(f, proxy->host_features_sel);
qemu_put_be32(f, proxy->guest_features_sel);
qemu_put_be32(f, proxy->guest_page_shift);
}
static void virtio_mmio_reset(DeviceState *d)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
virtio_bus_reset(&proxy->bus);
proxy->host_features_sel = 0;
proxy->guest_features_sel = 0;
proxy->guest_page_shift = 0;
}
/* virtio-mmio device */
/* This is called by virtio-bus just after the device is plugged. */
static void virtio_mmio_device_plugged(DeviceState *opaque)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
proxy->host_features |= (0x1 << VIRTIO_F_NOTIFY_ON_EMPTY);
proxy->host_features = virtio_bus_get_vdev_features(&proxy->bus,
proxy->host_features);
}
static void virtio_mmio_realizefn(DeviceState *d, Error **errp)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
SysBusDevice *sbd = SYS_BUS_DEVICE(d);
virtio_mmio_bus_new(&proxy->bus, proxy);
sysbus_init_irq(sbd, &proxy->irq);
memory_region_init_io(&proxy->iomem, OBJECT(d), &virtio_mem_ops, proxy,
TYPE_VIRTIO_MMIO, 0x200);
sysbus_init_mmio(sbd, &proxy->iomem);
}
static void virtio_mmio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = virtio_mmio_realizefn;
dc->reset = virtio_mmio_reset;
}
static const TypeInfo virtio_mmio_info = {
.name = TYPE_VIRTIO_MMIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(VirtIOMMIOProxy),
.class_init = virtio_mmio_class_init,
};
/* virtio-mmio-bus. */
static void virtio_mmio_bus_new(VirtioBusState *bus, VirtIOMMIOProxy *dev)
{
DeviceState *qdev = DEVICE(dev);
BusState *qbus;
qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_MMIO_BUS, qdev, NULL);
qbus = BUS(bus);
qbus->allow_hotplug = 0;
}
static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *bus_class = BUS_CLASS(klass);
VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
k->notify = virtio_mmio_update_irq;
k->save_config = virtio_mmio_save_config;
k->load_config = virtio_mmio_load_config;
k->get_features = virtio_mmio_get_features;
k->device_plugged = virtio_mmio_device_plugged;
k->has_variable_vring_alignment = true;
bus_class->max_dev = 1;
}
static const TypeInfo virtio_mmio_bus_info = {
.name = TYPE_VIRTIO_MMIO_BUS,
.parent = TYPE_VIRTIO_BUS,
.instance_size = sizeof(VirtioBusState),
.class_init = virtio_mmio_bus_class_init,
};
static void virtio_mmio_register_types(void)
{
type_register_static(&virtio_mmio_bus_info);
type_register_static(&virtio_mmio_info);
}
type_init(virtio_mmio_register_types)

View File

@ -19,8 +19,11 @@
#include "qemu/atomic.h"
#include "hw/virtio/virtio-bus.h"
/* The alignment to use between consumer and producer parts of vring.
* x86 pagesize again. */
/*
* The alignment to use between consumer and producer parts of vring.
* x86 pagesize again. This is the default, used by transports like PCI
* which don't provide a means for the guest to tell the host the alignment.
*/
#define VIRTIO_PCI_VRING_ALIGN 4096
typedef struct VRingDesc
@ -54,6 +57,7 @@ typedef struct VRingUsed
typedef struct VRing
{
unsigned int num;
unsigned int align;
hwaddr desc;
hwaddr avail;
hwaddr used;
@ -93,7 +97,7 @@ static void virtqueue_init(VirtQueue *vq)
vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc);
vq->vring.used = vring_align(vq->vring.avail +
offsetof(VRingAvail, ring[vq->vring.num]),
VIRTIO_PCI_VRING_ALIGN);
vq->vring.align);
}
static inline uint64_t vring_desc_addr(hwaddr desc_pa, int i)
@ -667,6 +671,14 @@ hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n)
return vdev->vq[n].pa;
}
void virtio_queue_set_num(VirtIODevice *vdev, int n, int num)
{
if (num <= VIRTQUEUE_MAX_SIZE) {
vdev->vq[n].vring.num = num;
virtqueue_init(&vdev->vq[n]);
}
}
int virtio_queue_get_num(VirtIODevice *vdev, int n)
{
return vdev->vq[n].vring.num;
@ -679,6 +691,21 @@ int virtio_queue_get_id(VirtQueue *vq)
return vq - &vdev->vq[0];
}
void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
{
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
/* Check that the transport told us it was going to do this
* (so a buggy transport will immediately assert rather than
* silently failing to migrate this state)
*/
assert(k->has_variable_vring_alignment);
vdev->vq[n].vring.align = align;
virtqueue_init(&vdev->vq[n]);
}
void virtio_queue_notify_vq(VirtQueue *vq)
{
if (vq->vring.desc) {
@ -719,6 +746,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
abort();
vdev->vq[i].vring.num = queue_size;
vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN;
vdev->vq[i].handle_output = handle_output;
return &vdev->vq[i];
@ -825,6 +853,9 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
break;
qemu_put_be32(f, vdev->vq[i].vring.num);
if (k->has_variable_vring_alignment) {
qemu_put_be32(f, vdev->vq[i].vring.align);
}
qemu_put_be64(f, vdev->vq[i].pa);
qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
if (k->save_queue) {
@ -881,6 +912,9 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
for (i = 0; i < num; i++) {
vdev->vq[i].vring.num = qemu_get_be32(f);
if (k->has_variable_vring_alignment) {
vdev->vq[i].vring.align = qemu_get_be32(f);
}
vdev->vq[i].pa = qemu_get_be64(f);
qemu_get_be16s(f, &vdev->vq[i].last_avail_idx);
vdev->vq[i].signalled_used_valid = false;

View File

@ -55,6 +55,10 @@ struct arm_boot_info {
const struct arm_boot_info *info);
void (*secondary_cpu_reset_hook)(ARMCPU *cpu,
const struct arm_boot_info *info);
/* if a board needs to be able to modify a device tree provided by
* the user it should implement this hook.
*/
void (*modify_dtb)(const struct arm_boot_info *info, void *fdt);
/* Used internally by arm_boot.c */
int is_linux;
hwaddr initrd_start;

View File

@ -17,6 +17,19 @@ int load_aout(const char *filename, hwaddr addr, int max_sz,
int load_uimage(const char *filename, hwaddr *ep,
hwaddr *loadaddr, int *is_linux);
/**
* load_ramdisk:
* @filename: Path to the ramdisk image
* @addr: Memory address to load the ramdisk to
* @max_sz: Maximum allowed ramdisk size (for non-u-boot ramdisks)
*
* Load a ramdisk image with U-Boot header to the specified memory
* address.
*
* Returns the size of the loaded image on success, -1 otherwise.
*/
int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz);
ssize_t read_targphys(const char *name,
int fd, hwaddr dst_addr, size_t nbytes);
void pstrcpy_targphys(const char *name,

View File

@ -62,6 +62,12 @@ typedef struct VirtioBusClass {
* This is called by virtio-bus just before the device is unplugged.
*/
void (*device_unplug)(DeviceState *d);
/*
* Does the transport have variable vring alignment?
* (ie can it ever call virtio_queue_set_align()?)
* Note that changing this will break migration for this transport.
*/
bool has_variable_vring_alignment;
} VirtioBusClass;
struct VirtioBusState {

View File

@ -200,7 +200,9 @@ void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data);
void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data);
void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr);
hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n);
void virtio_queue_set_num(VirtIODevice *vdev, int n, int num);
int virtio_queue_get_num(VirtIODevice *vdev, int n);
void virtio_queue_set_align(VirtIODevice *vdev, int n, int align);
void virtio_queue_notify(VirtIODevice *vdev, int n);
uint16_t virtio_queue_vector(VirtIODevice *vdev, int n);
void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector);

View File

@ -51,4 +51,63 @@ int qemu_devtree_add_subnode(void *fdt, const char *name);
void qemu_devtree_dumpdtb(void *fdt, int size);
/**
* qemu_devtree_setprop_sized_cells_from_array:
* @fdt: device tree blob
* @node_path: node to set property on
* @property: property to set
* @numvalues: number of values
* @values: array of number-of-cells, value pairs
*
* Set the specified property on the specified node in the device tree
* to be an array of cells. The values of the cells are specified via
* the values list, which alternates between "number of cells used by
* this value" and "value".
* number-of-cells must be either 1 or 2 (other values will result in
* an error being returned). If a value is too large to fit in the
* number of cells specified for it, an error is returned.
*
* This function is useful because device tree nodes often have cell arrays
* which are either lists of addresses or lists of address,size tuples, but
* the number of cells used for each element vary depending on the
* #address-cells and #size-cells properties of their parent node.
* If you know all your cell elements are one cell wide you can use the
* simpler qemu_devtree_setprop_cells(). If you're not setting up the
* array programmatically, qemu_devtree_setprop_sized_cells may be more
* convenient.
*
* Return value: 0 on success, <0 on error.
*/
int qemu_devtree_setprop_sized_cells_from_array(void *fdt,
const char *node_path,
const char *property,
int numvalues,
uint64_t *values);
/**
* qemu_devtree_setprop_sized_cells:
* @fdt: device tree blob
* @node_path: node to set property on
* @property: property to set
* @...: list of number-of-cells, value pairs
*
* Set the specified property on the specified node in the device tree
* to be an array of cells. The values of the cells are specified via
* the variable arguments, which alternates between "number of cells
* used by this value" and "value".
*
* This is a convenience wrapper for the function
* qemu_devtree_setprop_sized_cells_from_array().
*
* Return value: 0 on success, <0 on error.
*/
#define qemu_devtree_setprop_sized_cells(fdt, node_path, property, ...) \
({ \
uint64_t qdt_tmp[] = { __VA_ARGS__ }; \
qemu_devtree_setprop_sized_cells_from_array(fdt, node_path, \
property, \
ARRAY_SIZE(qdt_tmp) / 2, \
qdt_tmp); \
})
#endif /* __DEVICE_TREE_H__ */