mirror of https://gitee.com/openkylin/qemu.git
target-arm queue:
* more EL2 preparation patches * revert a no-longer-necessary workaround for old glib versions * add GICv2m support to virt board (MSI support) * pl061: fix wrong calculation of GPIOMIS register * support MSI via irqfd * remove a confusing v8_ prefix from some variable names * add dynamic sysbus device support to the virt board -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJVbdouAAoJEDwlJe0UNgzeQSYP/iXkogCzrNAqkcAGNzzVox5o 54HFUMBFC05hnOBQxxUcGwXDetidYIEFuSt2uUooZapWYzL6ProIDXcgYMoVJTzL XaYUzuV3M3pxl8A7DtKRv39e9HTDL4EmT+dd8mNuRHfbhmvlFlWw3TzO/DHxl29e iOIrNDaLAPjI4QyFR0k5kHTmijTm13Sd5/Un/8m6bjKtrKst8k2HmqemtsjcrVUK +/9k+60+uTYJb4xKKcCY7w0zbbJGvlW9216bf3ccfAvGAbaGDxH+hRn0E1xd2BoR JmXofWYL55tT8cQxO7ZjCDMzhiJsQ/hFlo1ds5DkdcYuaYXUHiRB62mleSb2zc+T kcLFWCBQp/YWULpngZrnu5bzKUN0BwFtTOoMMv5WGR/N4hAcj6rgIIEaGeRsGrhV XrGeLmk25IwrIvn4Nwr0Ve70g6rdL5NauVYq21Bx2GLK18NEXXsUR1Z0X38WSVrN HXBNFHFECf0S1CNp8KVcyyfE+XZx2Cb5jFpS2jiy648KoXHgHYZUjCqJd4JzRAQB dEjoNKA6yod72UkgpoeaOTHsF9razOpqG+ymJzsVTiDBpg/eE6ZT/0jCBZ+92NqN qf2IUwubQH9jAFcxDuzoHDx+XCycdkYVEnoBGtBcS2QfaJd4dwfJkFAOOHR70XkH Kvj419eJjO0uhItsEODA =s7uF -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20150602' into staging target-arm queue: * more EL2 preparation patches * revert a no-longer-necessary workaround for old glib versions * add GICv2m support to virt board (MSI support) * pl061: fix wrong calculation of GPIOMIS register * support MSI via irqfd * remove a confusing v8_ prefix from some variable names * add dynamic sysbus device support to the virt board # gpg: Signature made Tue Jun 2 17:30:38 2015 BST using RSA key ID 14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" * remotes/pmaydell/tags/pull-target-arm-20150602: (22 commits) hw/arm/virt: change indentation in a15memmap hw/arm/virt: add dynamic sysbus device support hw/arm/boot: arm_load_kernel implemented as a machine init done notifier hw/arm/sysbus-fdt: helpers for platform bus nodes addition target-arm: Remove v8_ prefix from names of non-v8-specific cpreg arrays arm_gicv2m: set kvm_gsi_direct_mapping and kvm_msi_via_irqfd_allowed kvm: introduce kvm_arch_msi_data_to_gsi pl061: fix wrong calculation of GPIOMIS register target-arm: Add the GICv2m to the virt board target-arm: Extend the gic node properties arm_gicv2m: Add GICv2m widget to support MSIs target-arm: Add GIC phandle to VirtBoardInfo Revert "target-arm: Avoid g_hash_table_get_keys()" target-arm: Add TLBI_VAE2{IS} target-arm: Add TLBI_ALLE2 target-arm: Add TLBI_ALLE1{IS} target-arm: Add TTBR0_EL2 target-arm: Add TPIDR_EL2 target-arm: Add SCTLR_EL2 target-arm: Add TCR_EL2 ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
d2ceeb1d68
|
@ -5,6 +5,7 @@ obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
|
|||
obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
|
||||
obj-$(CONFIG_ACPI) += virt-acpi-build.o
|
||||
obj-y += netduino2.o
|
||||
obj-y += sysbus-fdt.o
|
||||
|
||||
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
|
||||
obj-$(CONFIG_DIGIC) += digic.o
|
||||
|
|
|
@ -557,7 +557,7 @@ static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
|
|||
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
|
||||
}
|
||||
|
||||
void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
||||
{
|
||||
CPUState *cs;
|
||||
int kernel_size;
|
||||
|
@ -568,6 +568,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
|||
hwaddr entry, kernel_load_offset;
|
||||
int big_endian;
|
||||
static const ARMInsnFixup *primary_loader;
|
||||
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
||||
notifier, notifier);
|
||||
ARMCPU *cpu = n->cpu;
|
||||
struct arm_boot_info *info =
|
||||
container_of(n, struct arm_boot_info, load_kernel_notifier);
|
||||
|
||||
/* CPU objects (unlike devices) are not automatically reset on system
|
||||
* reset, so we must always register a handler to do so. If we're
|
||||
|
@ -775,3 +780,10 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
|||
ARM_CPU(cs)->env.boot_info = info;
|
||||
}
|
||||
}
|
||||
|
||||
void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
{
|
||||
info->load_kernel_notifier.cpu = cpu;
|
||||
info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify;
|
||||
qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* ARM Platform Bus device tree generation helpers
|
||||
*
|
||||
* Copyright (c) 2014 Linaro Limited
|
||||
*
|
||||
* Authors:
|
||||
* Alex Graf <agraf@suse.de>
|
||||
* Eric Auger <eric.auger@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/arm/sysbus-fdt.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
/*
|
||||
* internal struct that contains the information to create dynamic
|
||||
* sysbus device node
|
||||
*/
|
||||
typedef struct PlatformBusFDTData {
|
||||
void *fdt; /* device tree handle */
|
||||
int irq_start; /* index of the first IRQ usable by platform bus devices */
|
||||
const char *pbus_node_name; /* name of the platform bus node */
|
||||
PlatformBusDevice *pbus;
|
||||
} PlatformBusFDTData;
|
||||
|
||||
/*
|
||||
* struct used when calling the machine init done notifier
|
||||
* that constructs the fdt nodes of platform bus devices
|
||||
*/
|
||||
typedef struct PlatformBusFDTNotifierParams {
|
||||
Notifier notifier;
|
||||
ARMPlatformBusFDTParams *fdt_params;
|
||||
} PlatformBusFDTNotifierParams;
|
||||
|
||||
/* struct that associates a device type name and a node creation function */
|
||||
typedef struct NodeCreationPair {
|
||||
const char *typename;
|
||||
int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque);
|
||||
} NodeCreationPair;
|
||||
|
||||
/* list of supported dynamic sysbus devices */
|
||||
static const NodeCreationPair add_fdt_node_functions[] = {
|
||||
{"", NULL}, /* last element */
|
||||
};
|
||||
|
||||
/**
|
||||
* add_fdt_node - add the device tree node of a dynamic sysbus device
|
||||
*
|
||||
* @sbdev: handle to the sysbus device
|
||||
* @opaque: handle to the PlatformBusFDTData
|
||||
*
|
||||
* Checks the sysbus type belongs to the list of device types that
|
||||
* are dynamically instantiable and if so call the node creation
|
||||
* function.
|
||||
*/
|
||||
static int add_fdt_node(SysBusDevice *sbdev, void *opaque)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(add_fdt_node_functions); i++) {
|
||||
if (!strcmp(object_get_typename(OBJECT(sbdev)),
|
||||
add_fdt_node_functions[i].typename)) {
|
||||
ret = add_fdt_node_functions[i].add_fdt_node_fn(sbdev, opaque);
|
||||
assert(!ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
error_report("Device %s can not be dynamically instantiated",
|
||||
qdev_fw_name(DEVICE(sbdev)));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* add_all_platform_bus_fdt_nodes - create all the platform bus nodes
|
||||
*
|
||||
* builds the parent platform bus node and all the nodes of dynamic
|
||||
* sysbus devices attached to it.
|
||||
*/
|
||||
static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params)
|
||||
{
|
||||
const char platcomp[] = "qemu,platform\0simple-bus";
|
||||
PlatformBusDevice *pbus;
|
||||
DeviceState *dev;
|
||||
gchar *node;
|
||||
uint64_t addr, size;
|
||||
int irq_start, dtb_size;
|
||||
struct arm_boot_info *info = fdt_params->binfo;
|
||||
const ARMPlatformBusSystemParams *params = fdt_params->system_params;
|
||||
const char *intc = fdt_params->intc;
|
||||
void *fdt = info->get_dtb(info, &dtb_size);
|
||||
|
||||
/*
|
||||
* If the user provided a dtb, we assume the dynamic sysbus nodes
|
||||
* already are integrated there. This corresponds to a use case where
|
||||
* the dynamic sysbus nodes are complex and their generation is not yet
|
||||
* supported. In that case the user can take charge of the guest dt
|
||||
* while qemu takes charge of the qom stuff.
|
||||
*/
|
||||
if (info->dtb_filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(fdt);
|
||||
|
||||
node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base);
|
||||
addr = params->platform_bus_base;
|
||||
size = params->platform_bus_size;
|
||||
irq_start = params->platform_bus_first_irq;
|
||||
|
||||
/* Create a /platform node that we can put all devices into */
|
||||
qemu_fdt_add_subnode(fdt, node);
|
||||
qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp));
|
||||
|
||||
/* Our platform bus region is less than 32bits, so 1 cell is enough for
|
||||
* address and size
|
||||
*/
|
||||
qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1);
|
||||
qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1);
|
||||
qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size);
|
||||
|
||||
qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc);
|
||||
|
||||
dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE);
|
||||
pbus = PLATFORM_BUS_DEVICE(dev);
|
||||
|
||||
/* We can only create dt nodes for dynamic devices when they're ready */
|
||||
assert(pbus->done_gathering);
|
||||
|
||||
PlatformBusFDTData data = {
|
||||
.fdt = fdt,
|
||||
.irq_start = irq_start,
|
||||
.pbus_node_name = node,
|
||||
.pbus = pbus,
|
||||
};
|
||||
|
||||
/* Loop through all dynamic sysbus devices and create their node */
|
||||
foreach_dynamic_sysbus_device(add_fdt_node, &data);
|
||||
|
||||
g_free(node);
|
||||
}
|
||||
|
||||
static void platform_bus_fdt_notify(Notifier *notifier, void *data)
|
||||
{
|
||||
PlatformBusFDTNotifierParams *p = DO_UPCAST(PlatformBusFDTNotifierParams,
|
||||
notifier, notifier);
|
||||
|
||||
add_all_platform_bus_fdt_nodes(p->fdt_params);
|
||||
g_free(p->fdt_params);
|
||||
g_free(p);
|
||||
}
|
||||
|
||||
void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params)
|
||||
{
|
||||
PlatformBusFDTNotifierParams *p = g_new(PlatformBusFDTNotifierParams, 1);
|
||||
|
||||
p->fdt_params = fdt_params;
|
||||
p->notifier.notify = platform_bus_fdt_notify;
|
||||
qemu_add_machine_init_done_notifier(&p->notifier);
|
||||
}
|
159
hw/arm/virt.c
159
hw/arm/virt.c
|
@ -45,9 +45,11 @@
|
|||
#include "qemu/error-report.h"
|
||||
#include "hw/pci-host/gpex.h"
|
||||
#include "hw/arm/virt-acpi-build.h"
|
||||
#include "hw/arm/sysbus-fdt.h"
|
||||
#include "hw/platform-bus.h"
|
||||
|
||||
/* Number of external interrupt lines to configure the GIC with */
|
||||
#define NUM_IRQS 128
|
||||
#define NUM_IRQS 256
|
||||
|
||||
#define GIC_FDT_IRQ_TYPE_SPI 0
|
||||
#define GIC_FDT_IRQ_TYPE_PPI 1
|
||||
|
@ -60,6 +62,10 @@
|
|||
#define GIC_FDT_IRQ_PPI_CPU_START 8
|
||||
#define GIC_FDT_IRQ_PPI_CPU_WIDTH 8
|
||||
|
||||
#define PLATFORM_BUS_NUM_IRQS 64
|
||||
|
||||
static ARMPlatformBusSystemParams platform_bus_params;
|
||||
|
||||
typedef struct VirtBoardInfo {
|
||||
struct arm_boot_info bootinfo;
|
||||
const char *cpu_model;
|
||||
|
@ -69,6 +75,8 @@ typedef struct VirtBoardInfo {
|
|||
void *fdt;
|
||||
int fdt_size;
|
||||
uint32_t clock_phandle;
|
||||
uint32_t gic_phandle;
|
||||
uint32_t v2m_phandle;
|
||||
} VirtBoardInfo;
|
||||
|
||||
typedef struct {
|
||||
|
@ -103,20 +111,22 @@ typedef struct {
|
|||
*/
|
||||
static const MemMapEntry a15memmap[] = {
|
||||
/* Space up to 0x8000000 is reserved for a boot ROM */
|
||||
[VIRT_FLASH] = { 0, 0x08000000 },
|
||||
[VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 },
|
||||
[VIRT_FLASH] = { 0, 0x08000000 },
|
||||
[VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 },
|
||||
/* GIC distributor and CPU interfaces sit inside the CPU peripheral space */
|
||||
[VIRT_GIC_DIST] = { 0x08000000, 0x00010000 },
|
||||
[VIRT_GIC_CPU] = { 0x08010000, 0x00010000 },
|
||||
[VIRT_UART] = { 0x09000000, 0x00001000 },
|
||||
[VIRT_RTC] = { 0x09010000, 0x00001000 },
|
||||
[VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
|
||||
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
||||
[VIRT_GIC_DIST] = { 0x08000000, 0x00010000 },
|
||||
[VIRT_GIC_CPU] = { 0x08010000, 0x00010000 },
|
||||
[VIRT_GIC_V2M] = { 0x08020000, 0x00001000 },
|
||||
[VIRT_UART] = { 0x09000000, 0x00001000 },
|
||||
[VIRT_RTC] = { 0x09010000, 0x00001000 },
|
||||
[VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
|
||||
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
||||
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
||||
[VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
|
||||
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
|
||||
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
|
||||
[VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
|
||||
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
|
||||
[VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
|
||||
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
|
||||
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
|
||||
[VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
|
||||
};
|
||||
|
||||
static const int a15irqmap[] = {
|
||||
|
@ -124,6 +134,8 @@ static const int a15irqmap[] = {
|
|||
[VIRT_RTC] = 2,
|
||||
[VIRT_PCIE] = 3, /* ... to 6 */
|
||||
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
|
||||
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
|
||||
[VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
|
||||
};
|
||||
|
||||
static VirtBoardInfo machines[] = {
|
||||
|
@ -299,12 +311,23 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
|
|||
}
|
||||
}
|
||||
|
||||
static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi)
|
||||
static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi)
|
||||
{
|
||||
uint32_t gic_phandle;
|
||||
vbi->v2m_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
|
||||
qemu_fdt_add_subnode(vbi->fdt, "/intc/v2m");
|
||||
qemu_fdt_setprop_string(vbi->fdt, "/intc/v2m", "compatible",
|
||||
"arm,gic-v2m-frame");
|
||||
qemu_fdt_setprop(vbi->fdt, "/intc/v2m", "msi-controller", NULL, 0);
|
||||
qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc/v2m", "reg",
|
||||
2, vbi->memmap[VIRT_GIC_V2M].base,
|
||||
2, vbi->memmap[VIRT_GIC_V2M].size);
|
||||
qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle);
|
||||
}
|
||||
|
||||
gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
|
||||
qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle);
|
||||
static void fdt_add_gic_node(VirtBoardInfo *vbi)
|
||||
{
|
||||
vbi->gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
|
||||
qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", vbi->gic_phandle);
|
||||
|
||||
qemu_fdt_add_subnode(vbi->fdt, "/intc");
|
||||
/* 'cortex-a15-gic' means 'GIC v2' */
|
||||
|
@ -317,12 +340,32 @@ static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi)
|
|||
2, vbi->memmap[VIRT_GIC_DIST].size,
|
||||
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;
|
||||
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0);
|
||||
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle);
|
||||
}
|
||||
|
||||
static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
|
||||
static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic)
|
||||
{
|
||||
int i;
|
||||
int irq = vbi->irqmap[VIRT_GIC_V2M];
|
||||
DeviceState *dev;
|
||||
|
||||
dev = qdev_create(NULL, "arm-gicv2m");
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vbi->memmap[VIRT_GIC_V2M].base);
|
||||
qdev_prop_set_uint32(dev, "base-spi", irq);
|
||||
qdev_prop_set_uint32(dev, "num-spi", NUM_GICV2M_SPIS);
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
for (i = 0; i < NUM_GICV2M_SPIS; i++) {
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
|
||||
}
|
||||
|
||||
fdt_add_v2m_gic_node(vbi);
|
||||
}
|
||||
|
||||
static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic)
|
||||
{
|
||||
/* We create a standalone GIC v2 */
|
||||
DeviceState *gicdev;
|
||||
|
@ -371,7 +414,9 @@ static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
|
|||
pic[i] = qdev_get_gpio_in(gicdev, i);
|
||||
}
|
||||
|
||||
return fdt_add_gic_node(vbi);
|
||||
fdt_add_gic_node(vbi);
|
||||
|
||||
create_v2m(vbi, pic);
|
||||
}
|
||||
|
||||
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
|
||||
|
@ -587,7 +632,7 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
|
|||
int first_irq, const char *nodename)
|
||||
{
|
||||
int devfn, pin;
|
||||
uint32_t full_irq_map[4 * 4 * 8] = { 0 };
|
||||
uint32_t full_irq_map[4 * 4 * 10] = { 0 };
|
||||
uint32_t *irq_map = full_irq_map;
|
||||
|
||||
for (devfn = 0; devfn <= 0x18; devfn += 0x8) {
|
||||
|
@ -600,13 +645,13 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
|
|||
uint32_t map[] = {
|
||||
devfn << 8, 0, 0, /* devfn */
|
||||
pin + 1, /* PCI pin */
|
||||
gic_phandle, irq_type, irq_nr, irq_level }; /* GIC irq */
|
||||
gic_phandle, 0, 0, irq_type, irq_nr, irq_level }; /* GIC irq */
|
||||
|
||||
/* Convert map to big endian */
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (i = 0; i < 10; i++) {
|
||||
irq_map[i] = cpu_to_be32(map[i]);
|
||||
}
|
||||
irq_map += 8;
|
||||
irq_map += 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -618,8 +663,7 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
|
|||
0x7 /* PCI irq */);
|
||||
}
|
||||
|
||||
static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
|
||||
uint32_t gic_phandle)
|
||||
static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic)
|
||||
{
|
||||
hwaddr base_mmio = vbi->memmap[VIRT_PCIE_MMIO].base;
|
||||
hwaddr size_mmio = vbi->memmap[VIRT_PCIE_MMIO].size;
|
||||
|
@ -676,6 +720,8 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
|
|||
qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0,
|
||||
nr_pcie_buses - 1);
|
||||
|
||||
qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", vbi->v2m_phandle);
|
||||
|
||||
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
||||
2, base_ecam, 2, size_ecam);
|
||||
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges",
|
||||
|
@ -685,11 +731,52 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
|
|||
2, base_mmio, 2, size_mmio);
|
||||
|
||||
qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1);
|
||||
create_pcie_irq_map(vbi, gic_phandle, irq, nodename);
|
||||
create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename);
|
||||
|
||||
g_free(nodename);
|
||||
}
|
||||
|
||||
static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic)
|
||||
{
|
||||
DeviceState *dev;
|
||||
SysBusDevice *s;
|
||||
int i;
|
||||
ARMPlatformBusFDTParams *fdt_params = g_new(ARMPlatformBusFDTParams, 1);
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
|
||||
platform_bus_params.platform_bus_base = vbi->memmap[VIRT_PLATFORM_BUS].base;
|
||||
platform_bus_params.platform_bus_size = vbi->memmap[VIRT_PLATFORM_BUS].size;
|
||||
platform_bus_params.platform_bus_first_irq = vbi->irqmap[VIRT_PLATFORM_BUS];
|
||||
platform_bus_params.platform_bus_num_irqs = PLATFORM_BUS_NUM_IRQS;
|
||||
|
||||
fdt_params->system_params = &platform_bus_params;
|
||||
fdt_params->binfo = &vbi->bootinfo;
|
||||
fdt_params->intc = "/intc";
|
||||
/*
|
||||
* register a machine init done notifier that creates the device tree
|
||||
* nodes of the platform bus and its children dynamic sysbus devices
|
||||
*/
|
||||
arm_register_platform_bus_fdt_creator(fdt_params);
|
||||
|
||||
dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE);
|
||||
dev->id = TYPE_PLATFORM_BUS_DEVICE;
|
||||
qdev_prop_set_uint32(dev, "num_irqs",
|
||||
platform_bus_params.platform_bus_num_irqs);
|
||||
qdev_prop_set_uint32(dev, "mmio_size",
|
||||
platform_bus_params.platform_bus_size);
|
||||
qdev_init_nofail(dev);
|
||||
s = SYS_BUS_DEVICE(dev);
|
||||
|
||||
for (i = 0; i < platform_bus_params.platform_bus_num_irqs; i++) {
|
||||
int irqn = platform_bus_params.platform_bus_first_irq + i;
|
||||
sysbus_connect_irq(s, i, pic[irqn]);
|
||||
}
|
||||
|
||||
memory_region_add_subregion(sysmem,
|
||||
platform_bus_params.platform_bus_base,
|
||||
sysbus_mmio_get_region(s, 0));
|
||||
}
|
||||
|
||||
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
|
||||
{
|
||||
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo;
|
||||
|
@ -717,7 +804,6 @@ static void machvirt_init(MachineState *machine)
|
|||
VirtBoardInfo *vbi;
|
||||
VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
|
||||
VirtGuestInfo *guest_info = &guest_info_state->info;
|
||||
uint32_t gic_phandle;
|
||||
char **cpustr;
|
||||
|
||||
if (!cpu_model) {
|
||||
|
@ -794,13 +880,13 @@ static void machvirt_init(MachineState *machine)
|
|||
|
||||
create_flash(vbi);
|
||||
|
||||
gic_phandle = create_gic(vbi, pic);
|
||||
create_gic(vbi, pic);
|
||||
|
||||
create_uart(vbi, pic);
|
||||
|
||||
create_rtc(vbi, pic);
|
||||
|
||||
create_pcie(vbi, pic, gic_phandle);
|
||||
create_pcie(vbi, pic);
|
||||
|
||||
/* Create mmio transports, so the user can create virtio backends
|
||||
* (which will be automatically plugged in to the transports). If
|
||||
|
@ -828,6 +914,14 @@ static void machvirt_init(MachineState *machine)
|
|||
vbi->bootinfo.get_dtb = machvirt_dtb;
|
||||
vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
|
||||
arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo);
|
||||
|
||||
/*
|
||||
* arm_load_kernel machine init done notifier registration must
|
||||
* happen before the platform_bus_create call. In this latter,
|
||||
* another notifier is registered which adds platform bus nodes.
|
||||
* Notifiers are executed in registration reverse order.
|
||||
*/
|
||||
create_platform_bus(vbi, pic);
|
||||
}
|
||||
|
||||
static bool virt_get_secure(Object *obj, Error **errp)
|
||||
|
@ -866,6 +960,7 @@ static void virt_class_init(ObjectClass *oc, void *data)
|
|||
mc->desc = "ARM Virtual Machine",
|
||||
mc->init = machvirt_init;
|
||||
mc->max_cpus = 8;
|
||||
mc->has_dynamic_sysbus = true;
|
||||
}
|
||||
|
||||
static const TypeInfo machvirt_info = {
|
||||
|
|
|
@ -173,7 +173,7 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
|
|||
case 0x414: /* Raw interrupt status */
|
||||
return s->istate;
|
||||
case 0x418: /* Masked interrupt status */
|
||||
return s->istate | s->im;
|
||||
return s->istate & s->im;
|
||||
case 0x420: /* Alternate function select */
|
||||
return s->afsel;
|
||||
case 0x500: /* 2mA drive */
|
||||
|
|
|
@ -11,6 +11,7 @@ common-obj-$(CONFIG_SLAVIO) += slavio_intctl.o
|
|||
common-obj-$(CONFIG_IOAPIC) += ioapic_common.o
|
||||
common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o
|
||||
common-obj-$(CONFIG_ARM_GIC) += arm_gic.o
|
||||
common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o
|
||||
common-obj-$(CONFIG_OPENPIC) += openpic.o
|
||||
|
||||
obj-$(CONFIG_APIC) += apic.o apic_common.o
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* GICv2m extension for MSI/MSI-x support with a GICv2-based system
|
||||
*
|
||||
* Copyright (C) 2015 Linaro, All rights reserved.
|
||||
*
|
||||
* Author: Christoffer Dall <christoffer.dall@linaro.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This file implements an emulated GICv2m widget as described in the ARM
|
||||
* Server Base System Architecture (SBSA) specification Version 2.2
|
||||
* (ARM-DEN-0029 v2.2) pages 35-39 without any optional implementation defined
|
||||
* identification registers and with a single non-secure MSI register frame.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/pci/msi.h"
|
||||
|
||||
#define TYPE_ARM_GICV2M "arm-gicv2m"
|
||||
#define ARM_GICV2M(obj) OBJECT_CHECK(ARMGICv2mState, (obj), TYPE_ARM_GICV2M)
|
||||
|
||||
#define GICV2M_NUM_SPI_MAX 128
|
||||
|
||||
#define V2M_MSI_TYPER 0x008
|
||||
#define V2M_MSI_SETSPI_NS 0x040
|
||||
#define V2M_MSI_IIDR 0xFCC
|
||||
#define V2M_IIDR0 0xFD0
|
||||
#define V2M_IIDR11 0xFFC
|
||||
|
||||
#define PRODUCT_ID_QEMU 0x51 /* ASCII code Q */
|
||||
|
||||
typedef struct ARMGICv2mState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq spi[GICV2M_NUM_SPI_MAX];
|
||||
|
||||
uint32_t base_spi;
|
||||
uint32_t num_spi;
|
||||
} ARMGICv2mState;
|
||||
|
||||
static void gicv2m_set_irq(void *opaque, int irq)
|
||||
{
|
||||
ARMGICv2mState *s = (ARMGICv2mState *)opaque;
|
||||
|
||||
qemu_irq_pulse(s->spi[irq]);
|
||||
}
|
||||
|
||||
static uint64_t gicv2m_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
ARMGICv2mState *s = (ARMGICv2mState *)opaque;
|
||||
uint32_t val;
|
||||
|
||||
if (size != 4) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_read: bad size %u\n", size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case V2M_MSI_TYPER:
|
||||
val = (s->base_spi + 32) << 16;
|
||||
val |= s->num_spi;
|
||||
return val;
|
||||
case V2M_MSI_IIDR:
|
||||
/* We don't have any valid implementor so we leave that field as zero
|
||||
* and we return 0 in the arch revision as per the spec.
|
||||
*/
|
||||
return (PRODUCT_ID_QEMU << 20);
|
||||
case V2M_IIDR0 ... V2M_IIDR11:
|
||||
/* We do not implement any optional identification registers and the
|
||||
* mandatory MSI_PIDR2 register reads as 0x0, so we capture all
|
||||
* implementation defined registers here.
|
||||
*/
|
||||
return 0;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"gicv2m_read: Bad offset %x\n", (int)offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void gicv2m_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
ARMGICv2mState *s = (ARMGICv2mState *)opaque;
|
||||
|
||||
if (size != 2 && size != 4) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_write: bad size %u\n", size);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case V2M_MSI_SETSPI_NS: {
|
||||
int spi;
|
||||
|
||||
spi = (value & 0x3ff) - (s->base_spi + 32);
|
||||
if (spi >= 0 && spi < s->num_spi) {
|
||||
gicv2m_set_irq(s, spi);
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"gicv2m_write: Bad offset %x\n", (int)offset);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps gicv2m_ops = {
|
||||
.read = gicv2m_read,
|
||||
.write = gicv2m_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void gicv2m_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ARMGICv2mState *s = ARM_GICV2M(dev);
|
||||
int i;
|
||||
|
||||
if (s->num_spi > GICV2M_NUM_SPI_MAX) {
|
||||
error_setg(errp,
|
||||
"requested %u SPIs exceeds GICv2m frame maximum %d",
|
||||
s->num_spi, GICV2M_NUM_SPI_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->base_spi + 32 > 1020 - s->num_spi) {
|
||||
error_setg(errp,
|
||||
"requested base SPI %u+%u exceeds max. number 1020",
|
||||
s->base_spi + 32, s->num_spi);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < s->num_spi; i++) {
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->spi[i]);
|
||||
}
|
||||
|
||||
msi_supported = true;
|
||||
kvm_gsi_direct_mapping = true;
|
||||
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
|
||||
}
|
||||
|
||||
static void gicv2m_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
ARMGICv2mState *s = ARM_GICV2M(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &gicv2m_ops, s,
|
||||
"gicv2m", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static Property gicv2m_properties[] = {
|
||||
DEFINE_PROP_UINT32("base-spi", ARMGICv2mState, base_spi, 0),
|
||||
DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void gicv2m_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->props = gicv2m_properties;
|
||||
dc->realize = gicv2m_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo gicv2m_info = {
|
||||
.name = TYPE_ARM_GICV2M,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ARMGICv2mState),
|
||||
.instance_init = gicv2m_init,
|
||||
.class_init = gicv2m_class_init,
|
||||
};
|
||||
|
||||
static void gicv2m_register_types(void)
|
||||
{
|
||||
type_register_static(&gicv2m_info);
|
||||
}
|
||||
|
||||
type_init(gicv2m_register_types)
|
|
@ -13,11 +13,21 @@
|
|||
|
||||
#include "exec/memory.h"
|
||||
#include "hw/irq.h"
|
||||
#include "qemu/notify.h"
|
||||
|
||||
/* armv7m.c */
|
||||
qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
|
||||
const char *kernel_filename, const char *cpu_model);
|
||||
|
||||
/*
|
||||
* struct used as a parameter of the arm_load_kernel machine init
|
||||
* done notifier
|
||||
*/
|
||||
typedef struct {
|
||||
Notifier notifier; /* actual notifier */
|
||||
ARMCPU *cpu; /* handle to the first cpu object */
|
||||
} ArmLoadKernelNotifier;
|
||||
|
||||
/* arm_boot.c */
|
||||
struct arm_boot_info {
|
||||
uint64_t ram_size;
|
||||
|
@ -64,6 +74,8 @@ struct arm_boot_info {
|
|||
* the user it should implement this hook.
|
||||
*/
|
||||
void (*modify_dtb)(const struct arm_boot_info *info, void *fdt);
|
||||
/* machine init done notifier executing arm_load_dtb */
|
||||
ArmLoadKernelNotifier load_kernel_notifier;
|
||||
/* Used internally by arm_boot.c */
|
||||
int is_linux;
|
||||
hwaddr initrd_start;
|
||||
|
@ -75,6 +87,22 @@ struct arm_boot_info {
|
|||
*/
|
||||
bool firmware_loaded;
|
||||
};
|
||||
|
||||
/**
|
||||
* arm_load_kernel - Loads memory with everything needed to boot
|
||||
*
|
||||
* @cpu: handle to the first CPU object
|
||||
* @info: handle to the boot info struct
|
||||
* Registers a machine init done notifier that copies to memory
|
||||
* everything needed to boot, depending on machine and user options:
|
||||
* kernel image, boot loaders, initrd, dtb. Also registers the CPU
|
||||
* reset handler.
|
||||
*
|
||||
* In case the machine file supports the platform bus device and its
|
||||
* dynamically instantiable sysbus devices, this function must be called
|
||||
* before sysbus-fdt arm_register_platform_bus_fdt_creator. Indeed the
|
||||
* machine init done notifiers are called in registration reverse order.
|
||||
*/
|
||||
void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info);
|
||||
|
||||
/* Multiplication factor to convert from system clock ticks to qemu timer
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Dynamic sysbus device tree node generation API
|
||||
*
|
||||
* Copyright Linaro Limited, 2014
|
||||
*
|
||||
* Authors:
|
||||
* Alex Graf <agraf@suse.de>
|
||||
* Eric Auger <eric.auger@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_ARM_SYSBUS_FDT_H
|
||||
#define HW_ARM_SYSBUS_FDT_H
|
||||
|
||||
#include "hw/arm/arm.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
/*
|
||||
* struct that contains dimensioning parameters of the platform bus
|
||||
*/
|
||||
typedef struct {
|
||||
hwaddr platform_bus_base; /* start address of the bus */
|
||||
hwaddr platform_bus_size; /* size of the bus */
|
||||
int platform_bus_first_irq; /* first hwirq assigned to the bus */
|
||||
int platform_bus_num_irqs; /* number of hwirq assigned to the bus */
|
||||
} ARMPlatformBusSystemParams;
|
||||
|
||||
/*
|
||||
* struct that contains all relevant info to build the fdt nodes of
|
||||
* platform bus and attached dynamic sysbus devices
|
||||
* in the future might be augmented with additional info
|
||||
* such as PHY, CLK handles ...
|
||||
*/
|
||||
typedef struct {
|
||||
const ARMPlatformBusSystemParams *system_params;
|
||||
struct arm_boot_info *binfo;
|
||||
const char *intc; /* parent interrupt controller name */
|
||||
} ARMPlatformBusFDTParams;
|
||||
|
||||
/**
|
||||
* arm_register_platform_bus_fdt_creator - register a machine init done
|
||||
* notifier that creates the device tree nodes of the platform bus and
|
||||
* associated dynamic sysbus devices
|
||||
*/
|
||||
void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params);
|
||||
|
||||
#endif
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "qemu-common.h"
|
||||
|
||||
#define NUM_GICV2M_SPIS 64
|
||||
#define NUM_VIRTIO_TRANSPORTS 32
|
||||
|
||||
#define ARCH_TIMER_VIRT_IRQ 11
|
||||
|
@ -53,6 +54,8 @@ enum {
|
|||
VIRT_PCIE_MMIO,
|
||||
VIRT_PCIE_PIO,
|
||||
VIRT_PCIE_ECAM,
|
||||
VIRT_GIC_V2M,
|
||||
VIRT_PLATFORM_BUS,
|
||||
};
|
||||
|
||||
typedef struct MemMapEntry {
|
||||
|
|
|
@ -287,6 +287,8 @@ void kvm_arch_init_irq_routing(KVMState *s);
|
|||
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
||||
uint64_t address, uint32_t data);
|
||||
|
||||
int kvm_arch_msi_data_to_gsi(uint32_t data);
|
||||
|
||||
int kvm_set_irq(KVMState *s, int irq, int level);
|
||||
int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg);
|
||||
|
||||
|
|
|
@ -1228,7 +1228,7 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg)
|
|||
int virq;
|
||||
|
||||
if (kvm_gsi_direct_mapping()) {
|
||||
return msg.data & 0xffff;
|
||||
return kvm_arch_msi_data_to_gsi(msg.data);
|
||||
}
|
||||
|
||||
if (!kvm_gsi_routing_enabled()) {
|
||||
|
|
|
@ -294,23 +294,15 @@ static gint cpreg_key_compare(gconstpointer a, gconstpointer b)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void cpreg_make_keylist(gpointer key, gpointer value, gpointer udata)
|
||||
{
|
||||
GList **plist = udata;
|
||||
|
||||
*plist = g_list_prepend(*plist, key);
|
||||
}
|
||||
|
||||
void init_cpreg_list(ARMCPU *cpu)
|
||||
{
|
||||
/* Initialise the cpreg_tuples[] array based on the cp_regs hash.
|
||||
* Note that we require cpreg_tuples[] to be sorted by key ID.
|
||||
*/
|
||||
GList *keys = NULL;
|
||||
GList *keys;
|
||||
int arraylen;
|
||||
|
||||
g_hash_table_foreach(cpu->cp_regs, cpreg_make_keylist, &keys);
|
||||
|
||||
keys = g_hash_table_get_keys(cpu->cp_regs);
|
||||
keys = g_list_sort(keys, cpreg_key_compare);
|
||||
|
||||
cpu->cpreg_array_len = 0;
|
||||
|
@ -492,10 +484,16 @@ static const ARMCPRegInfo not_v8_cp_reginfo[] = {
|
|||
.writefn = dacr_write, .raw_writefn = raw_write,
|
||||
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s),
|
||||
offsetoflow32(CPUARMState, cp15.dacr_ns) } },
|
||||
/* ??? This covers not just the impdef TLB lockdown registers but also
|
||||
* some v7VMSA registers relating to TEX remap, so it is overly broad.
|
||||
/* ARMv7 allocates a range of implementation defined TLB LOCKDOWN regs.
|
||||
* For v6 and v5, these mappings are overly broad.
|
||||
*/
|
||||
{ .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = CP_ANY,
|
||||
{ .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 0,
|
||||
.opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP },
|
||||
{ .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 1,
|
||||
.opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP },
|
||||
{ .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 4,
|
||||
.opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP },
|
||||
{ .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 8,
|
||||
.opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP },
|
||||
/* Cache maintenance ops; some of this space may be overridden later. */
|
||||
{ .name = "CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY,
|
||||
|
@ -555,6 +553,10 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = {
|
|||
{ .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY,
|
||||
.opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write,
|
||||
.type = ARM_CP_NO_RAW },
|
||||
{ .name = "PRRR", .cp = 15, .crn = 10, .crm = 2,
|
||||
.opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NOP },
|
||||
{ .name = "NMRR", .cp = 15, .crn = 10, .crm = 2,
|
||||
.opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_NOP },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
|
@ -1021,19 +1023,17 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
|
|||
.resetvalue = 0 },
|
||||
/* For non-long-descriptor page tables these are PRRR and NMRR;
|
||||
* regardless they still act as reads-as-written for QEMU.
|
||||
* The override is necessary because of the overly-broad TLB_LOCKDOWN
|
||||
* definition.
|
||||
*/
|
||||
/* MAIR0/1 are defined separately from their 64-bit counterpart which
|
||||
* allows them to assign the correct fieldoffset based on the endianness
|
||||
* handled in the field definitions.
|
||||
*/
|
||||
{ .name = "MAIR0", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE,
|
||||
{ .name = "MAIR0", .state = ARM_CP_STATE_AA32,
|
||||
.cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW,
|
||||
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair0_s),
|
||||
offsetof(CPUARMState, cp15.mair0_ns) },
|
||||
.resetfn = arm_cp_reset_ignore },
|
||||
{ .name = "MAIR1", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE,
|
||||
{ .name = "MAIR1", .state = ARM_CP_STATE_AA32,
|
||||
.cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, .access = PL1_RW,
|
||||
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair1_s),
|
||||
offsetof(CPUARMState, cp15.mair1_ns) },
|
||||
|
@ -2088,16 +2088,14 @@ static const ARMCPRegInfo mpidr_cp_reginfo[] = {
|
|||
};
|
||||
|
||||
static const ARMCPRegInfo lpae_cp_reginfo[] = {
|
||||
/* NOP AMAIR0/1: the override is because these clash with the rather
|
||||
* broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo.
|
||||
*/
|
||||
/* NOP AMAIR0/1 */
|
||||
{ .name = "AMAIR0", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST,
|
||||
.resetvalue = 0 },
|
||||
/* AMAIR1 is mapped to AMAIR_EL1[63:32] */
|
||||
{ .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST,
|
||||
.resetvalue = 0 },
|
||||
{ .name = "PAR", .cp = 15, .crm = 7, .opc1 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_64BIT, .resetvalue = 0,
|
||||
|
@ -2362,6 +2360,14 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
|
|||
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2,
|
||||
.access = PL1_W, .type = ARM_CP_NOP },
|
||||
/* TLBI operations */
|
||||
{ .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4,
|
||||
.access = PL2_W, .type = ARM_CP_NO_RAW,
|
||||
.writefn = tlbiall_write },
|
||||
{ .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4,
|
||||
.access = PL2_W, .type = ARM_CP_NO_RAW,
|
||||
.writefn = tlbiall_write },
|
||||
{ .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0,
|
||||
.access = PL1_W, .type = ARM_CP_NO_RAW,
|
||||
|
@ -2498,7 +2504,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
|
|||
};
|
||||
|
||||
/* Used to describe the behaviour of EL2 regs when EL2 does not exist. */
|
||||
static const ARMCPRegInfo v8_el3_no_el2_cp_reginfo[] = {
|
||||
static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = {
|
||||
{ .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0,
|
||||
.access = PL2_RW,
|
||||
|
@ -2511,6 +2517,28 @@ static const ARMCPRegInfo v8_el3_no_el2_cp_reginfo[] = {
|
|||
{ .name = "CPTR_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2,
|
||||
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
{ .name = "MAIR_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 0,
|
||||
.access = PL2_RW, .type = ARM_CP_CONST,
|
||||
.resetvalue = 0 },
|
||||
{ .name = "HMAIR1", .state = ARM_CP_STATE_AA32,
|
||||
.opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1,
|
||||
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
{ .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2,
|
||||
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
{ .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0,
|
||||
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
{ .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2,
|
||||
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
{ .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0,
|
||||
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
{ .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2,
|
||||
.access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_CONST,
|
||||
.resetvalue = 0 },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
|
@ -2539,7 +2567,7 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
|||
raw_write(env, ri, value);
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo v8_el2_cp_reginfo[] = {
|
||||
static const ARMCPRegInfo el2_cp_reginfo[] = {
|
||||
{ .name = "HCR_EL2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0,
|
||||
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2),
|
||||
|
@ -2582,6 +2610,47 @@ static const ARMCPRegInfo v8_el2_cp_reginfo[] = {
|
|||
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2,
|
||||
.access = PL2_RW, .accessfn = cptr_access, .resetvalue = 0,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.cptr_el[2]) },
|
||||
{ .name = "MAIR_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 0,
|
||||
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[2]),
|
||||
.resetvalue = 0 },
|
||||
{ .name = "HMAIR1", .state = ARM_CP_STATE_AA32,
|
||||
.opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1,
|
||||
.access = PL2_RW, .type = ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el[2]) },
|
||||
{ .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2,
|
||||
.access = PL2_RW, .writefn = vmsa_tcr_el1_write,
|
||||
.resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.tcr_el[2]) },
|
||||
{ .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0,
|
||||
.access = PL2_RW, .raw_writefn = raw_write, .writefn = sctlr_write,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.sctlr_el[2]) },
|
||||
{ .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2,
|
||||
.access = PL2_RW, .resetvalue = 0,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[2]) },
|
||||
{ .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0,
|
||||
.access = PL2_RW, .resetvalue = 0,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) },
|
||||
{ .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2,
|
||||
.access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS,
|
||||
.resetvalue = 0,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) },
|
||||
{ .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0,
|
||||
.type = ARM_CP_NO_RAW, .access = PL2_W,
|
||||
.writefn = tlbiall_write },
|
||||
{ .name = "TLBI_VAE2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1,
|
||||
.type = ARM_CP_NO_RAW, .access = PL2_W,
|
||||
.writefn = tlbi_aa64_vaa_write },
|
||||
{ .name = "TLBI_VAE2IS", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1,
|
||||
.type = ARM_CP_NO_RAW, .access = PL2_W,
|
||||
.writefn = tlbi_aa64_vaa_write },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
|
@ -3243,7 +3312,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
|||
define_arm_cp_regs(cpu, v8_cp_reginfo);
|
||||
}
|
||||
if (arm_feature(env, ARM_FEATURE_EL2)) {
|
||||
define_arm_cp_regs(cpu, v8_el2_cp_reginfo);
|
||||
define_arm_cp_regs(cpu, el2_cp_reginfo);
|
||||
/* RVBAR_EL2 is only implemented if EL2 is the highest EL */
|
||||
if (!arm_feature(env, ARM_FEATURE_EL3)) {
|
||||
ARMCPRegInfo rvbar = {
|
||||
|
@ -3258,7 +3327,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
|||
* register the no_el2 reginfos.
|
||||
*/
|
||||
if (arm_feature(env, ARM_FEATURE_EL3)) {
|
||||
define_arm_cp_regs(cpu, v8_el3_no_el2_cp_reginfo);
|
||||
define_arm_cp_regs(cpu, el3_no_el2_cp_reginfo);
|
||||
}
|
||||
}
|
||||
if (arm_feature(env, ARM_FEATURE_EL3)) {
|
||||
|
|
|
@ -600,3 +600,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_msi_data_to_gsi(uint32_t data)
|
||||
{
|
||||
return (data - 32) & 0xffff;
|
||||
}
|
||||
|
|
|
@ -2766,3 +2766,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_msi_data_to_gsi(uint32_t data)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
|
|
@ -696,3 +696,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_msi_data_to_gsi(uint32_t data)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
|
|
@ -2410,3 +2410,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_msi_data_to_gsi(uint32_t data)
|
||||
{
|
||||
return data & 0xffff;
|
||||
}
|
||||
|
|
|
@ -2216,3 +2216,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
|||
route->u.adapter.adapter_id = pbdev->routes.adapter.adapter_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_msi_data_to_gsi(uint32_t data)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue