mirror of https://github.com/proxmox/mirror_qemu
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>master
commit
549c272b3c
|
@ -308,3 +308,36 @@ void qemu_devtree_dumpdtb(void *fdt, int size)
|
||||||
exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1);
|
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));
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
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;
|
void *fdt = NULL;
|
||||||
char *filename;
|
char *filename;
|
||||||
int size, rc;
|
int size, rc;
|
||||||
uint32_t acells, scells, hival;
|
uint32_t acells, scells;
|
||||||
|
|
||||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename);
|
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename);
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
|
@ -255,29 +253,18 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_reg_propsize = acells + scells;
|
if (scells < 2 && binfo->ram_size >= (1ULL << 32)) {
|
||||||
mem_reg_property = g_new0(uint32_t, mem_reg_propsize);
|
/* This is user error so deserves a friendlier error message
|
||||||
mem_reg_property[acells - 1] = cpu_to_be32(binfo->loader_start);
|
* than the failure of setprop_sized_cells would provide
|
||||||
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) {
|
|
||||||
fprintf(stderr, "qemu: dtb file not compatible with "
|
fprintf(stderr, "qemu: dtb file not compatible with "
|
||||||
"RAM size > 4GB\n");
|
"RAM size > 4GB\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
|
rc = qemu_devtree_setprop_sized_cells(fdt, "/memory", "reg",
|
||||||
mem_reg_propsize * sizeof(uint32_t));
|
acells, binfo->loader_start,
|
||||||
|
scells, binfo->ram_size);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
fprintf(stderr, "couldn't set /memory/reg\n");
|
fprintf(stderr, "couldn't set /memory/reg\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -307,6 +294,11 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (binfo->modify_dtb) {
|
||||||
|
binfo->modify_dtb(binfo, fdt);
|
||||||
|
}
|
||||||
|
|
||||||
qemu_devtree_dumpdtb(fdt, size);
|
qemu_devtree_dumpdtb(fdt, size);
|
||||||
|
|
||||||
cpu_physical_memory_write(addr, 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;
|
info->entry = entry;
|
||||||
if (is_linux) {
|
if (is_linux) {
|
||||||
if (info->initrd_filename) {
|
if (info->initrd_filename) {
|
||||||
|
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,
|
initrd_size = load_image_targphys(info->initrd_filename,
|
||||||
info->initrd_start,
|
info->initrd_start,
|
||||||
info->ram_size -
|
info->ram_size -
|
||||||
info->initrd_start);
|
info->initrd_start);
|
||||||
|
}
|
||||||
if (initrd_size < 0) {
|
if (initrd_size < 0) {
|
||||||
fprintf(stderr, "qemu: could not load initrd '%s'\n",
|
fprintf(stderr, "qemu: could not load initrd '%s'\n",
|
||||||
info->initrd_filename);
|
info->initrd_filename);
|
||||||
|
|
|
@ -31,12 +31,17 @@
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
#include "sysemu/blockdev.h"
|
#include "sysemu/blockdev.h"
|
||||||
#include "hw/block/flash.h"
|
#include "hw/block/flash.h"
|
||||||
|
#include "sysemu/device_tree.h"
|
||||||
|
#include <libfdt.h>
|
||||||
|
|
||||||
#define VEXPRESS_BOARD_ID 0x8e0
|
#define VEXPRESS_BOARD_ID 0x8e0
|
||||||
#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
|
#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
|
||||||
#define VEXPRESS_FLASH_SECT_SIZE (256 * 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:
|
/* Address maps for peripherals:
|
||||||
* the Versatile Express motherboard has two possible maps,
|
* the Versatile Express motherboard has two possible maps,
|
||||||
|
@ -73,6 +78,7 @@ enum {
|
||||||
VE_ETHERNET,
|
VE_ETHERNET,
|
||||||
VE_USB,
|
VE_USB,
|
||||||
VE_DAPROM,
|
VE_DAPROM,
|
||||||
|
VE_VIRTIO,
|
||||||
};
|
};
|
||||||
|
|
||||||
static hwaddr motherboard_legacy_map[] = {
|
static hwaddr motherboard_legacy_map[] = {
|
||||||
|
@ -91,6 +97,7 @@ static hwaddr motherboard_legacy_map[] = {
|
||||||
[VE_WDT] = 0x1000f000,
|
[VE_WDT] = 0x1000f000,
|
||||||
[VE_TIMER01] = 0x10011000,
|
[VE_TIMER01] = 0x10011000,
|
||||||
[VE_TIMER23] = 0x10012000,
|
[VE_TIMER23] = 0x10012000,
|
||||||
|
[VE_VIRTIO] = 0x10013000,
|
||||||
[VE_SERIALDVI] = 0x10016000,
|
[VE_SERIALDVI] = 0x10016000,
|
||||||
[VE_RTC] = 0x10017000,
|
[VE_RTC] = 0x10017000,
|
||||||
[VE_COMPACTFLASH] = 0x1001a000,
|
[VE_COMPACTFLASH] = 0x1001a000,
|
||||||
|
@ -137,6 +144,7 @@ static hwaddr motherboard_aseries_map[] = {
|
||||||
[VE_WDT] = 0x1c0f0000,
|
[VE_WDT] = 0x1c0f0000,
|
||||||
[VE_TIMER01] = 0x1c110000,
|
[VE_TIMER01] = 0x1c110000,
|
||||||
[VE_TIMER23] = 0x1c120000,
|
[VE_TIMER23] = 0x1c120000,
|
||||||
|
[VE_VIRTIO] = 0x1c130000,
|
||||||
[VE_SERIALDVI] = 0x1c160000,
|
[VE_SERIALDVI] = 0x1c160000,
|
||||||
[VE_RTC] = 0x1c170000,
|
[VE_RTC] = 0x1c170000,
|
||||||
[VE_COMPACTFLASH] = 0x1c1a0000,
|
[VE_COMPACTFLASH] = 0x1c1a0000,
|
||||||
|
@ -153,6 +161,7 @@ typedef void DBoardInitFn(const VEDBoardInfo *daughterboard,
|
||||||
qemu_irq *pic);
|
qemu_irq *pic);
|
||||||
|
|
||||||
struct VEDBoardInfo {
|
struct VEDBoardInfo {
|
||||||
|
struct arm_boot_info bootinfo;
|
||||||
const hwaddr *motherboard_map;
|
const hwaddr *motherboard_map;
|
||||||
hwaddr loader_start;
|
hwaddr loader_start;
|
||||||
const hwaddr gic_cpu_if_addr;
|
const hwaddr gic_cpu_if_addr;
|
||||||
|
@ -272,7 +281,7 @@ static const uint32_t a9_clocks[] = {
|
||||||
66670000, /* Test chip reference clock: 66.67MHz */
|
66670000, /* Test chip reference clock: 66.67MHz */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const VEDBoardInfo a9_daughterboard = {
|
static VEDBoardInfo a9_daughterboard = {
|
||||||
.motherboard_map = motherboard_legacy_map,
|
.motherboard_map = motherboard_legacy_map,
|
||||||
.loader_start = 0x60000000,
|
.loader_start = 0x60000000,
|
||||||
.gic_cpu_if_addr = 0x1e000100,
|
.gic_cpu_if_addr = 0x1e000100,
|
||||||
|
@ -384,7 +393,7 @@ static const uint32_t a15_clocks[] = {
|
||||||
40000000, /* OSCCLK8: 40MHz : DDR2 PLL reference */
|
40000000, /* OSCCLK8: 40MHz : DDR2 PLL reference */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const VEDBoardInfo a15_daughterboard = {
|
static VEDBoardInfo a15_daughterboard = {
|
||||||
.motherboard_map = motherboard_aseries_map,
|
.motherboard_map = motherboard_aseries_map,
|
||||||
.loader_start = 0x80000000,
|
.loader_start = 0x80000000,
|
||||||
.gic_cpu_if_addr = 0x2c002000,
|
.gic_cpu_if_addr = 0x2c002000,
|
||||||
|
@ -396,7 +405,86 @@ static const VEDBoardInfo a15_daughterboard = {
|
||||||
.init = a15_daughterboard_init,
|
.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)
|
QEMUMachineInitArgs *args)
|
||||||
{
|
{
|
||||||
DeviceState *dev, *sysctl, *pl041;
|
DeviceState *dev, *sysctl, *pl041;
|
||||||
|
@ -524,17 +612,27 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard,
|
||||||
|
|
||||||
/* VE_DAPROM: not modelled */
|
/* VE_DAPROM: not modelled */
|
||||||
|
|
||||||
vexpress_binfo.ram_size = args->ram_size;
|
/* Create mmio transports, so the user can create virtio backends
|
||||||
vexpress_binfo.kernel_filename = args->kernel_filename;
|
* (which will be automatically plugged in to the transports). If
|
||||||
vexpress_binfo.kernel_cmdline = args->kernel_cmdline;
|
* no backend is created the transport will just sit harmlessly idle.
|
||||||
vexpress_binfo.initrd_filename = args->initrd_filename;
|
*/
|
||||||
vexpress_binfo.nb_cpus = smp_cpus;
|
for (i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) {
|
||||||
vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
|
sysbus_create_simple("virtio-mmio", map[VE_VIRTIO] + 0x200 * i,
|
||||||
vexpress_binfo.loader_start = daughterboard->loader_start;
|
pic[40 + i]);
|
||||||
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;
|
daughterboard->bootinfo.ram_size = args->ram_size;
|
||||||
arm_load_kernel(ARM_CPU(first_cpu), &vexpress_binfo);
|
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)
|
static void vexpress_a9_init(QEMUMachineInitArgs *args)
|
||||||
|
|
|
@ -434,15 +434,17 @@ static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load a U-Boot image. */
|
/* Load a U-Boot image. */
|
||||||
int load_uimage(const char *filename, hwaddr *ep,
|
static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
|
||||||
hwaddr *loadaddr, int *is_linux)
|
int *is_linux, uint8_t image_type)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
int size;
|
int size;
|
||||||
|
hwaddr address;
|
||||||
uboot_image_header_t h;
|
uboot_image_header_t h;
|
||||||
uboot_image_header_t *hdr = &h;
|
uboot_image_header_t *hdr = &h;
|
||||||
uint8_t *data = NULL;
|
uint8_t *data = NULL;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
int do_uncompress = 0;
|
||||||
|
|
||||||
fd = open(filename, O_RDONLY | O_BINARY);
|
fd = open(filename, O_RDONLY | O_BINARY);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
|
@ -457,15 +459,25 @@ int load_uimage(const char *filename, hwaddr *ep,
|
||||||
if (hdr->ih_magic != IH_MAGIC)
|
if (hdr->ih_magic != IH_MAGIC)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* TODO: Implement other image types. */
|
if (hdr->ih_type != image_type) {
|
||||||
if (hdr->ih_type != IH_TYPE_KERNEL) {
|
fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type,
|
||||||
fprintf(stderr, "Can only load u-boot image type \"kernel\"\n");
|
image_type);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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) {
|
switch (hdr->ih_comp) {
|
||||||
case IH_COMP_NONE:
|
case IH_COMP_NONE:
|
||||||
|
break;
|
||||||
case IH_COMP_GZIP:
|
case IH_COMP_GZIP:
|
||||||
|
do_uncompress = 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
@ -474,15 +486,28 @@ int load_uimage(const char *filename, hwaddr *ep,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Check CPU type. */
|
if (ep) {
|
||||||
if (is_linux) {
|
*ep = hdr->ih_ep;
|
||||||
if (hdr->ih_os == IH_OS_LINUX)
|
}
|
||||||
*is_linux = 1;
|
|
||||||
else
|
/* TODO: Check CPU type. */
|
||||||
*is_linux = 0;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
*ep = hdr->ih_ep;
|
|
||||||
data = g_malloc(hdr->ih_size);
|
data = g_malloc(hdr->ih_size);
|
||||||
|
|
||||||
if (read(fd, data, hdr->ih_size) != 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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdr->ih_comp == IH_COMP_GZIP) {
|
if (do_uncompress) {
|
||||||
uint8_t *compressed_data;
|
uint8_t *compressed_data;
|
||||||
size_t max_bytes;
|
size_t max_bytes;
|
||||||
ssize_t bytes;
|
ssize_t bytes;
|
||||||
|
@ -508,10 +533,7 @@ int load_uimage(const char *filename, hwaddr *ep,
|
||||||
hdr->ih_size = bytes;
|
hdr->ih_size = bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
rom_add_blob_fixed(filename, data, hdr->ih_size, hdr->ih_load);
|
rom_add_blob_fixed(filename, data, hdr->ih_size, address);
|
||||||
|
|
||||||
if (loadaddr)
|
|
||||||
*loadaddr = hdr->ih_load;
|
|
||||||
|
|
||||||
ret = hdr->ih_size;
|
ret = hdr->ih_size;
|
||||||
|
|
||||||
|
@ -522,6 +544,18 @@ out:
|
||||||
return ret;
|
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.
|
* Functions for reboot-persistent memory regions.
|
||||||
* - used for vga bios and option roms.
|
* - used for vga bios and option roms.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
common-obj-y += virtio-rng.o
|
common-obj-y += virtio-rng.o
|
||||||
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||||
common-obj-y += virtio-bus.o
|
common-obj-y += virtio-bus.o
|
||||||
|
common-obj-y += virtio-mmio.o
|
||||||
common-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += dataplane/
|
common-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += dataplane/
|
||||||
|
|
||||||
obj-y += virtio.o virtio-balloon.o
|
obj-y += virtio.o virtio-balloon.o
|
||||||
|
|
|
@ -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)
|
|
@ -19,8 +19,11 @@
|
||||||
#include "qemu/atomic.h"
|
#include "qemu/atomic.h"
|
||||||
#include "hw/virtio/virtio-bus.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
|
#define VIRTIO_PCI_VRING_ALIGN 4096
|
||||||
|
|
||||||
typedef struct VRingDesc
|
typedef struct VRingDesc
|
||||||
|
@ -54,6 +57,7 @@ typedef struct VRingUsed
|
||||||
typedef struct VRing
|
typedef struct VRing
|
||||||
{
|
{
|
||||||
unsigned int num;
|
unsigned int num;
|
||||||
|
unsigned int align;
|
||||||
hwaddr desc;
|
hwaddr desc;
|
||||||
hwaddr avail;
|
hwaddr avail;
|
||||||
hwaddr used;
|
hwaddr used;
|
||||||
|
@ -93,7 +97,7 @@ static void virtqueue_init(VirtQueue *vq)
|
||||||
vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc);
|
vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc);
|
||||||
vq->vring.used = vring_align(vq->vring.avail +
|
vq->vring.used = vring_align(vq->vring.avail +
|
||||||
offsetof(VRingAvail, ring[vq->vring.num]),
|
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)
|
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;
|
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)
|
int virtio_queue_get_num(VirtIODevice *vdev, int n)
|
||||||
{
|
{
|
||||||
return vdev->vq[n].vring.num;
|
return vdev->vq[n].vring.num;
|
||||||
|
@ -679,6 +691,21 @@ int virtio_queue_get_id(VirtQueue *vq)
|
||||||
return vq - &vdev->vq[0];
|
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)
|
void virtio_queue_notify_vq(VirtQueue *vq)
|
||||||
{
|
{
|
||||||
if (vq->vring.desc) {
|
if (vq->vring.desc) {
|
||||||
|
@ -719,6 +746,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
vdev->vq[i].vring.num = queue_size;
|
vdev->vq[i].vring.num = queue_size;
|
||||||
|
vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN;
|
||||||
vdev->vq[i].handle_output = handle_output;
|
vdev->vq[i].handle_output = handle_output;
|
||||||
|
|
||||||
return &vdev->vq[i];
|
return &vdev->vq[i];
|
||||||
|
@ -825,6 +853,9 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
qemu_put_be32(f, vdev->vq[i].vring.num);
|
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_be64(f, vdev->vq[i].pa);
|
||||||
qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
|
qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
|
||||||
if (k->save_queue) {
|
if (k->save_queue) {
|
||||||
|
@ -881,6 +912,9 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
vdev->vq[i].vring.num = qemu_get_be32(f);
|
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);
|
vdev->vq[i].pa = qemu_get_be64(f);
|
||||||
qemu_get_be16s(f, &vdev->vq[i].last_avail_idx);
|
qemu_get_be16s(f, &vdev->vq[i].last_avail_idx);
|
||||||
vdev->vq[i].signalled_used_valid = false;
|
vdev->vq[i].signalled_used_valid = false;
|
||||||
|
|
|
@ -55,6 +55,10 @@ struct arm_boot_info {
|
||||||
const struct arm_boot_info *info);
|
const struct arm_boot_info *info);
|
||||||
void (*secondary_cpu_reset_hook)(ARMCPU *cpu,
|
void (*secondary_cpu_reset_hook)(ARMCPU *cpu,
|
||||||
const struct arm_boot_info *info);
|
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 */
|
/* Used internally by arm_boot.c */
|
||||||
int is_linux;
|
int is_linux;
|
||||||
hwaddr initrd_start;
|
hwaddr initrd_start;
|
||||||
|
|
|
@ -17,6 +17,19 @@ int load_aout(const char *filename, hwaddr addr, int max_sz,
|
||||||
int load_uimage(const char *filename, hwaddr *ep,
|
int load_uimage(const char *filename, hwaddr *ep,
|
||||||
hwaddr *loadaddr, int *is_linux);
|
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,
|
ssize_t read_targphys(const char *name,
|
||||||
int fd, hwaddr dst_addr, size_t nbytes);
|
int fd, hwaddr dst_addr, size_t nbytes);
|
||||||
void pstrcpy_targphys(const char *name,
|
void pstrcpy_targphys(const char *name,
|
||||||
|
|
|
@ -62,6 +62,12 @@ typedef struct VirtioBusClass {
|
||||||
* This is called by virtio-bus just before the device is unplugged.
|
* This is called by virtio-bus just before the device is unplugged.
|
||||||
*/
|
*/
|
||||||
void (*device_unplug)(DeviceState *d);
|
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;
|
} VirtioBusClass;
|
||||||
|
|
||||||
struct VirtioBusState {
|
struct VirtioBusState {
|
||||||
|
|
|
@ -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_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data);
|
||||||
void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr);
|
void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr);
|
||||||
hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n);
|
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);
|
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);
|
void virtio_queue_notify(VirtIODevice *vdev, int n);
|
||||||
uint16_t virtio_queue_vector(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);
|
void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector);
|
||||||
|
|
|
@ -51,4 +51,63 @@ int qemu_devtree_add_subnode(void *fdt, const char *name);
|
||||||
|
|
||||||
void qemu_devtree_dumpdtb(void *fdt, int size);
|
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__ */
|
#endif /* __DEVICE_TREE_H__ */
|
||||||
|
|
Loading…
Reference in New Issue