target-arm queue:
* i.MX: Add i.MX7 SOC implementation and i.MX7 Sabre board * Report the correct core count in A53 L2CTLR on the ZynqMP board * linux-user: preliminary SVE support work (signal handling) * hw/arm/boot: fix memory leak in case of error loading ELF file * hw/arm/boot: avoid reading off end of buffer if passed very small image file * hw/arm: Use more CONFIG switches for the object files * target/arm: Add "-cpu max" support * hw/arm/virt: Support -machine gic-version=max * hw/sd: improve debug tracing * hw/sd: sdcard: Add the Tuning Command (CMD 19) * MAINTAINERS: add Philippe as odd-fixes maintainer for SD -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJaosNHAAoJEDwlJe0UNgzexcoP/RHqkdKk91Dzg4MirndihMrJ nCu4n1J68uEOt79SlS4ES+EVKmfPvo2DP94Kp8L4bjqiHuvSMQfjX7YnPzCvQKOC Idz4BklbjYg3QP+UWFysoHvv5vXvytRhwu6LeVoTgebpBIwvKKyQh/89mwp1hKRm ZkpdkTRP2lsQUCG36kYUiAyhcJH+9nxQBtecfYjpzKsQg49Piltt999l9c8VzTfw yY72rEw4vFKFUjHfkbi0m2lPhZWIwGMoU0/qFNfIrMRi4vp6WDeQaRYgDgxpGfwy ZCbHVQeuQg87xD48HQMoQO+F3iaCvbjllDKnqAL80W8NreAyKJX8e8Cz9FD2k0n5 RvDeQ6QOq5jzOW6uSDlJgT71kajiEzJH43TLLB6k7/mdJICt/JGU7EWVSP6C0fZz cEjRLfx8JoZN4HmFy2f8K+IwdWpGkshzTVO1XmFsWZmSHUD+6vcUv9Nd1MP3ACGN BPqmbjk2guiacuKs3gbOSCB1mLWXCu4HMAc6ppO1d3pVHRWaR9UAVCiYJNuXd/VU dXELAbcP6WOwHteBxgwGnEALcgz40gx149+gePD3MLc3ImZEqWba91Ghp8T6NNSu p8ZJqN7Dsow6mOxaS5iYfFfXdf8K13cTmB371Q+0IOi3Z0cO2CgEmFZ8fj5yKCSK wjtYIFhwzFdWqzGb4e/Q =CWr5 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180309' into staging target-arm queue: * i.MX: Add i.MX7 SOC implementation and i.MX7 Sabre board * Report the correct core count in A53 L2CTLR on the ZynqMP board * linux-user: preliminary SVE support work (signal handling) * hw/arm/boot: fix memory leak in case of error loading ELF file * hw/arm/boot: avoid reading off end of buffer if passed very small image file * hw/arm: Use more CONFIG switches for the object files * target/arm: Add "-cpu max" support * hw/arm/virt: Support -machine gic-version=max * hw/sd: improve debug tracing * hw/sd: sdcard: Add the Tuning Command (CMD 19) * MAINTAINERS: add Philippe as odd-fixes maintainer for SD # gpg: Signature made Fri 09 Mar 2018 17:24:23 GMT # gpg: using RSA key 3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20180309: (25 commits) MAINTAINERS: Add entries for SD (SDHCI, SDBus, SDCard) sdhci: Fix a typo in comment sdcard: Add the Tuning Command (CMD19) sdcard: Display which protocol is used when tracing (SD or SPI) sdcard: Display command name when tracing CMD/ACMD sdcard: Do not trace CMD55, except when we already expect an ACMD hw/arm/virt: Support -machine gic-version=max hw/arm/virt: Add "max" to the list of CPU types "virt" supports target/arm: Make 'any' CPU just an alias for 'max' target/arm: Add "-cpu max" support target/arm: Move definition of 'host' cpu type into cpu.c target/arm: Query host CPU features on-demand at instance init arm: avoid heap-buffer-overflow in load_aarch64_image arm: fix load ELF error leak hw/arm: Use more CONFIG switches for the object files aarch64-linux-user: Add support for SVE signal frame records aarch64-linux-user: Add support for EXTRA signal frame records aarch64-linux-user: Remove struct target_aux_context aarch64-linux-user: Split out helpers for guest signal handling linux-user: Implement aarch64 PR_SVE_SET/GET_VL ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
5df089564b
@ -1100,6 +1100,14 @@ M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/ssi/xilinx_*
|
||||
|
||||
SD (Secure Card)
|
||||
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
S: Odd Fixes
|
||||
F: include/hw/sd/sd*
|
||||
F: hw/sd/core.c
|
||||
F: hw/sd/sd*
|
||||
F: tests/sd*
|
||||
|
||||
USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
|
@ -47,6 +47,7 @@ CONFIG_A9MPCORE=y
|
||||
CONFIG_A15MPCORE=y
|
||||
|
||||
CONFIG_ARM_V7M=y
|
||||
CONFIG_NETDUINO2=y
|
||||
|
||||
CONFIG_ARM_GIC=y
|
||||
CONFIG_ARM_GIC_KVM=$(CONFIG_KVM)
|
||||
@ -109,6 +110,7 @@ CONFIG_TZ_PPC=y
|
||||
CONFIG_IOTKIT=y
|
||||
CONFIG_IOTKIT_SECCTL=y
|
||||
|
||||
CONFIG_VERSATILE=y
|
||||
CONFIG_VERSATILE_PCI=y
|
||||
CONFIG_VERSATILE_I2C=y
|
||||
|
||||
@ -117,6 +119,7 @@ CONFIG_VFIO_XGMAC=y
|
||||
CONFIG_VFIO_AMD_XGBE=y
|
||||
|
||||
CONFIG_SDHCI=y
|
||||
CONFIG_INTEGRATOR=y
|
||||
CONFIG_INTEGRATOR_DEBUG=y
|
||||
|
||||
CONFIG_ALLWINNER_A10_PIT=y
|
||||
@ -126,6 +129,7 @@ CONFIG_ALLWINNER_A10=y
|
||||
CONFIG_FSL_IMX6=y
|
||||
CONFIG_FSL_IMX31=y
|
||||
CONFIG_FSL_IMX25=y
|
||||
CONFIG_FSL_IMX7=y
|
||||
|
||||
CONFIG_IMX_I2C=y
|
||||
|
||||
@ -140,3 +144,8 @@ CONFIG_GPIO_KEY=y
|
||||
CONFIG_MSF2=y
|
||||
CONFIG_FW_CFG_DMA=y
|
||||
CONFIG_XILINX_AXI=y
|
||||
CONFIG_PCI_DESIGNWARE=y
|
||||
|
||||
CONFIG_STRONGARM=y
|
||||
CONFIG_HIGHBANK=y
|
||||
CONFIG_MUSICPAL=y
|
||||
|
@ -1,15 +1,27 @@
|
||||
obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
|
||||
obj-$(CONFIG_DIGIC) += digic_boards.o
|
||||
obj-y += integratorcp.o mainstone.o musicpal.o nseries.o
|
||||
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-y += boot.o virt.o sysbus-fdt.o
|
||||
obj-$(CONFIG_ACPI) += virt-acpi-build.o
|
||||
obj-y += netduino2.o
|
||||
obj-y += sysbus-fdt.o
|
||||
obj-$(CONFIG_DIGIC) += digic_boards.o
|
||||
obj-$(CONFIG_EXYNOS4) += exynos4_boards.o
|
||||
obj-$(CONFIG_HIGHBANK) += highbank.o
|
||||
obj-$(CONFIG_INTEGRATOR) += integratorcp.o
|
||||
obj-$(CONFIG_MAINSTONE) += mainstone.o
|
||||
obj-$(CONFIG_MUSICPAL) += musicpal.o
|
||||
obj-$(CONFIG_NETDUINO2) += netduino2.o
|
||||
obj-$(CONFIG_NSERIES) += nseries.o
|
||||
obj-$(CONFIG_OMAP) += omap_sx1.o palm.o
|
||||
obj-$(CONFIG_PXA2XX) += gumstix.o spitz.o tosa.o z2.o
|
||||
obj-$(CONFIG_REALVIEW) += realview.o
|
||||
obj-$(CONFIG_STELLARIS) += stellaris.o
|
||||
obj-$(CONFIG_STRONGARM) += collie.o
|
||||
obj-$(CONFIG_VERSATILE) += vexpress.o versatilepb.o
|
||||
obj-$(CONFIG_ZYNQ) += xilinx_zynq.o
|
||||
|
||||
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
|
||||
obj-$(CONFIG_ARM_V7M) += armv7m.o
|
||||
obj-$(CONFIG_EXYNOS4) += exynos4210.o
|
||||
obj-$(CONFIG_PXA2XX) += pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
|
||||
obj-$(CONFIG_DIGIC) += digic.o
|
||||
obj-y += omap1.o omap2.o strongarm.o
|
||||
obj-$(CONFIG_OMAP) += omap1.o omap2.o
|
||||
obj-$(CONFIG_STRONGARM) += strongarm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
|
||||
obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
|
||||
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
|
||||
@ -22,3 +34,4 @@ obj-$(CONFIG_MPS2) += mps2.o
|
||||
obj-$(CONFIG_MPS2) += mps2-tz.o
|
||||
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
|
||||
obj-$(CONFIG_IOTKIT) += iotkit.o
|
||||
obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
|
||||
|
@ -829,6 +829,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
|
||||
|
||||
load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
|
||||
if (err) {
|
||||
error_free(err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -890,7 +891,8 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
|
||||
}
|
||||
|
||||
/* check the arm64 magic header value -- very old kernels may not have it */
|
||||
if (memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
|
||||
if (size > ARM64_MAGIC_OFFSET + 4 &&
|
||||
memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
|
||||
uint64_t hdrvals[2];
|
||||
|
||||
/* The arm64 Image header has text_offset and image_size fields at 8 and
|
||||
|
582
hw/arm/fsl-imx7.c
Normal file
582
hw/arm/fsl-imx7.c
Normal file
@ -0,0 +1,582 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Impinj, Inc.
|
||||
*
|
||||
* i.MX7 SoC definitions
|
||||
*
|
||||
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
|
||||
*
|
||||
* Based on hw/arm/fsl-imx6.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; 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.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/arm/fsl-imx7.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define NAME_SIZE 20
|
||||
|
||||
static void fsl_imx7_init(Object *obj)
|
||||
{
|
||||
BusState *sysbus = sysbus_get_default();
|
||||
FslIMX7State *s = FSL_IMX7(obj);
|
||||
char name[NAME_SIZE];
|
||||
int i;
|
||||
|
||||
if (smp_cpus > FSL_IMX7_NUM_CPUS) {
|
||||
error_report("%s: Only %d CPUs are supported (%d requested)",
|
||||
TYPE_FSL_IMX7, FSL_IMX7_NUM_CPUS, smp_cpus);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
object_initialize(&s->cpu[i], sizeof(s->cpu[i]),
|
||||
ARM_CPU_TYPE_NAME("cortex-a7"));
|
||||
snprintf(name, NAME_SIZE, "cpu%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->cpu[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* A7MPCORE
|
||||
*/
|
||||
object_initialize(&s->a7mpcore, sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV);
|
||||
qdev_set_parent_bus(DEVICE(&s->a7mpcore), sysbus);
|
||||
object_property_add_child(obj, "a7mpcore",
|
||||
OBJECT(&s->a7mpcore), &error_fatal);
|
||||
|
||||
/*
|
||||
* GPIOs 1 to 7
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
|
||||
object_initialize(&s->gpio[i], sizeof(s->gpio[i]),
|
||||
TYPE_IMX_GPIO);
|
||||
qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "gpio%d", i);
|
||||
object_property_add_child(obj, name,
|
||||
OBJECT(&s->gpio[i]), &error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* GPT1, 2, 3, 4
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
|
||||
object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX7_GPT);
|
||||
qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "gpt%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->gpt[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* CCM
|
||||
*/
|
||||
object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM);
|
||||
qdev_set_parent_bus(DEVICE(&s->ccm), sysbus);
|
||||
object_property_add_child(obj, "ccm", OBJECT(&s->ccm), &error_fatal);
|
||||
|
||||
/*
|
||||
* Analog
|
||||
*/
|
||||
object_initialize(&s->analog, sizeof(s->analog), TYPE_IMX7_ANALOG);
|
||||
qdev_set_parent_bus(DEVICE(&s->analog), sysbus);
|
||||
object_property_add_child(obj, "analog", OBJECT(&s->analog), &error_fatal);
|
||||
|
||||
/*
|
||||
* GPCv2
|
||||
*/
|
||||
object_initialize(&s->gpcv2, sizeof(s->gpcv2), TYPE_IMX_GPCV2);
|
||||
qdev_set_parent_bus(DEVICE(&s->gpcv2), sysbus);
|
||||
object_property_add_child(obj, "gpcv2", OBJECT(&s->gpcv2), &error_fatal);
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
|
||||
object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI);
|
||||
qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
|
||||
snprintf(name, NAME_SIZE, "spi%d", i + 1);
|
||||
object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL);
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
|
||||
object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
|
||||
qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
|
||||
snprintf(name, NAME_SIZE, "i2c%d", i + 1);
|
||||
object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* UART
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
|
||||
object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
|
||||
qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "uart%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->uart[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
|
||||
object_initialize(&s->eth[i], sizeof(s->eth[i]), TYPE_IMX_ENET);
|
||||
qdev_set_parent_bus(DEVICE(&s->eth[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "eth%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->eth[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* SDHCI
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
|
||||
object_initialize(&s->usdhc[i], sizeof(s->usdhc[i]),
|
||||
TYPE_IMX_USDHC);
|
||||
qdev_set_parent_bus(DEVICE(&s->usdhc[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "usdhc%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->usdhc[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* SNVS
|
||||
*/
|
||||
object_initialize(&s->snvs, sizeof(s->snvs), TYPE_IMX7_SNVS);
|
||||
qdev_set_parent_bus(DEVICE(&s->snvs), sysbus);
|
||||
object_property_add_child(obj, "snvs", OBJECT(&s->snvs), &error_fatal);
|
||||
|
||||
/*
|
||||
* Watchdog
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
|
||||
object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_IMX2_WDT);
|
||||
qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "wdt%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->wdt[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* GPR
|
||||
*/
|
||||
object_initialize(&s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR);
|
||||
qdev_set_parent_bus(DEVICE(&s->gpr), sysbus);
|
||||
object_property_add_child(obj, "gpr", OBJECT(&s->gpr), &error_fatal);
|
||||
|
||||
object_initialize(&s->pcie, sizeof(s->pcie), TYPE_DESIGNWARE_PCIE_HOST);
|
||||
qdev_set_parent_bus(DEVICE(&s->pcie), sysbus);
|
||||
object_property_add_child(obj, "pcie", OBJECT(&s->pcie), &error_fatal);
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
|
||||
object_initialize(&s->usb[i],
|
||||
sizeof(s->usb[i]), TYPE_CHIPIDEA);
|
||||
qdev_set_parent_bus(DEVICE(&s->usb[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "usb%d", i);
|
||||
object_property_add_child(obj, name,
|
||||
OBJECT(&s->usb[i]), &error_fatal);
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_imx7_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
FslIMX7State *s = FSL_IMX7(dev);
|
||||
Object *o;
|
||||
int i;
|
||||
qemu_irq irq;
|
||||
char name[NAME_SIZE];
|
||||
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
o = OBJECT(&s->cpu[i]);
|
||||
|
||||
object_property_set_int(o, QEMU_PSCI_CONDUIT_SMC,
|
||||
"psci-conduit", &error_abort);
|
||||
|
||||
/* On uniprocessor, the CBAR is set to 0 */
|
||||
if (smp_cpus > 1) {
|
||||
object_property_set_int(o, FSL_IMX7_A7MPCORE_ADDR,
|
||||
"reset-cbar", &error_abort);
|
||||
}
|
||||
|
||||
if (i) {
|
||||
/* Secondary CPUs start in PSCI powered-down state */
|
||||
object_property_set_bool(o, true,
|
||||
"start-powered-off", &error_abort);
|
||||
}
|
||||
|
||||
object_property_set_bool(o, true, "realized", &error_abort);
|
||||
}
|
||||
|
||||
/*
|
||||
* A7MPCORE
|
||||
*/
|
||||
object_property_set_int(OBJECT(&s->a7mpcore), smp_cpus, "num-cpu",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&s->a7mpcore),
|
||||
FSL_IMX7_MAX_IRQ + GIC_INTERNAL,
|
||||
"num-irq", &error_abort);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->a7mpcore), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX7_A7MPCORE_ADDR);
|
||||
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore);
|
||||
DeviceState *d = DEVICE(qemu_get_cpu(i));
|
||||
|
||||
irq = qdev_get_gpio_in(d, ARM_CPU_IRQ);
|
||||
sysbus_connect_irq(sbd, i, irq);
|
||||
irq = qdev_get_gpio_in(d, ARM_CPU_FIQ);
|
||||
sysbus_connect_irq(sbd, i + smp_cpus, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* A7MPCORE DAP
|
||||
*/
|
||||
create_unimplemented_device("a7mpcore-dap", FSL_IMX7_A7MPCORE_DAP_ADDR,
|
||||
0x100000);
|
||||
|
||||
/*
|
||||
* GPT1, 2, 3, 4
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
|
||||
static const hwaddr FSL_IMX7_GPTn_ADDR[FSL_IMX7_NUM_GPTS] = {
|
||||
FSL_IMX7_GPT1_ADDR,
|
||||
FSL_IMX7_GPT2_ADDR,
|
||||
FSL_IMX7_GPT3_ADDR,
|
||||
FSL_IMX7_GPT4_ADDR,
|
||||
};
|
||||
|
||||
s->gpt[i].ccm = IMX_CCM(&s->ccm);
|
||||
object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, FSL_IMX7_GPTn_ADDR[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
|
||||
static const hwaddr FSL_IMX7_GPIOn_ADDR[FSL_IMX7_NUM_GPIOS] = {
|
||||
FSL_IMX7_GPIO1_ADDR,
|
||||
FSL_IMX7_GPIO2_ADDR,
|
||||
FSL_IMX7_GPIO3_ADDR,
|
||||
FSL_IMX7_GPIO4_ADDR,
|
||||
FSL_IMX7_GPIO5_ADDR,
|
||||
FSL_IMX7_GPIO6_ADDR,
|
||||
FSL_IMX7_GPIO7_ADDR,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, FSL_IMX7_GPIOn_ADDR[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* IOMUXC and IOMUXC_LPSR
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_IOMUXCS; i++) {
|
||||
static const hwaddr FSL_IMX7_IOMUXCn_ADDR[FSL_IMX7_NUM_IOMUXCS] = {
|
||||
FSL_IMX7_IOMUXC_ADDR,
|
||||
FSL_IMX7_IOMUXC_LPSR_ADDR,
|
||||
};
|
||||
|
||||
snprintf(name, NAME_SIZE, "iomuxc%d", i);
|
||||
create_unimplemented_device(name, FSL_IMX7_IOMUXCn_ADDR[i],
|
||||
FSL_IMX7_IOMUXCn_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* CCM
|
||||
*/
|
||||
object_property_set_bool(OBJECT(&s->ccm), true, "realized", &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX7_CCM_ADDR);
|
||||
|
||||
/*
|
||||
* Analog
|
||||
*/
|
||||
object_property_set_bool(OBJECT(&s->analog), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, FSL_IMX7_ANALOG_ADDR);
|
||||
|
||||
/*
|
||||
* GPCv2
|
||||
*/
|
||||
object_property_set_bool(OBJECT(&s->gpcv2), true,
|
||||
"realized", &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpcv2), 0, FSL_IMX7_GPC_ADDR);
|
||||
|
||||
/* Initialize all ECSPI */
|
||||
for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
|
||||
static const hwaddr FSL_IMX7_SPIn_ADDR[FSL_IMX7_NUM_ECSPIS] = {
|
||||
FSL_IMX7_ECSPI1_ADDR,
|
||||
FSL_IMX7_ECSPI2_ADDR,
|
||||
FSL_IMX7_ECSPI3_ADDR,
|
||||
FSL_IMX7_ECSPI4_ADDR,
|
||||
};
|
||||
|
||||
static const hwaddr FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = {
|
||||
FSL_IMX7_ECSPI1_IRQ,
|
||||
FSL_IMX7_ECSPI2_IRQ,
|
||||
FSL_IMX7_ECSPI3_IRQ,
|
||||
FSL_IMX7_ECSPI4_IRQ,
|
||||
};
|
||||
|
||||
/* Initialize the SPI */
|
||||
object_property_set_bool(OBJECT(&s->spi[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0,
|
||||
FSL_IMX7_SPIn_ADDR[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->a7mpcore),
|
||||
FSL_IMX7_SPIn_IRQ[i]));
|
||||
}
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
|
||||
static const hwaddr FSL_IMX7_I2Cn_ADDR[FSL_IMX7_NUM_I2CS] = {
|
||||
FSL_IMX7_I2C1_ADDR,
|
||||
FSL_IMX7_I2C2_ADDR,
|
||||
FSL_IMX7_I2C3_ADDR,
|
||||
FSL_IMX7_I2C4_ADDR,
|
||||
};
|
||||
|
||||
static const hwaddr FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = {
|
||||
FSL_IMX7_I2C1_IRQ,
|
||||
FSL_IMX7_I2C2_IRQ,
|
||||
FSL_IMX7_I2C3_IRQ,
|
||||
FSL_IMX7_I2C4_IRQ,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX7_I2Cn_ADDR[i]);
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->a7mpcore),
|
||||
FSL_IMX7_I2Cn_IRQ[i]));
|
||||
}
|
||||
|
||||
/*
|
||||
* UART
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
|
||||
static const hwaddr FSL_IMX7_UARTn_ADDR[FSL_IMX7_NUM_UARTS] = {
|
||||
FSL_IMX7_UART1_ADDR,
|
||||
FSL_IMX7_UART2_ADDR,
|
||||
FSL_IMX7_UART3_ADDR,
|
||||
FSL_IMX7_UART4_ADDR,
|
||||
FSL_IMX7_UART5_ADDR,
|
||||
FSL_IMX7_UART6_ADDR,
|
||||
FSL_IMX7_UART7_ADDR,
|
||||
};
|
||||
|
||||
static const int FSL_IMX7_UARTn_IRQ[FSL_IMX7_NUM_UARTS] = {
|
||||
FSL_IMX7_UART1_IRQ,
|
||||
FSL_IMX7_UART2_IRQ,
|
||||
FSL_IMX7_UART3_IRQ,
|
||||
FSL_IMX7_UART4_IRQ,
|
||||
FSL_IMX7_UART5_IRQ,
|
||||
FSL_IMX7_UART6_IRQ,
|
||||
FSL_IMX7_UART7_IRQ,
|
||||
};
|
||||
|
||||
|
||||
if (i < MAX_SERIAL_PORTS) {
|
||||
qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]);
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->uart[i]), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, FSL_IMX7_UARTn_ADDR[i]);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_UARTn_IRQ[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
|
||||
static const hwaddr FSL_IMX7_ENETn_ADDR[FSL_IMX7_NUM_ETHS] = {
|
||||
FSL_IMX7_ENET1_ADDR,
|
||||
FSL_IMX7_ENET2_ADDR,
|
||||
};
|
||||
|
||||
object_property_set_uint(OBJECT(&s->eth[i]), FSL_IMX7_ETH_NUM_TX_RINGS,
|
||||
"tx-ring-num", &error_abort);
|
||||
qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]);
|
||||
object_property_set_bool(OBJECT(&s->eth[i]), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, FSL_IMX7_ENETn_ADDR[i]);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 0));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 3));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* USDHC
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
|
||||
static const hwaddr FSL_IMX7_USDHCn_ADDR[FSL_IMX7_NUM_USDHCS] = {
|
||||
FSL_IMX7_USDHC1_ADDR,
|
||||
FSL_IMX7_USDHC2_ADDR,
|
||||
FSL_IMX7_USDHC3_ADDR,
|
||||
};
|
||||
|
||||
static const int FSL_IMX7_USDHCn_IRQ[FSL_IMX7_NUM_USDHCS] = {
|
||||
FSL_IMX7_USDHC1_IRQ,
|
||||
FSL_IMX7_USDHC2_IRQ,
|
||||
FSL_IMX7_USDHC3_IRQ,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->usdhc[i]), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0,
|
||||
FSL_IMX7_USDHCn_ADDR[i]);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USDHCn_IRQ[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* SNVS
|
||||
*/
|
||||
object_property_set_bool(OBJECT(&s->snvs), true, "realized", &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX7_SNVS_ADDR);
|
||||
|
||||
/*
|
||||
* SRC
|
||||
*/
|
||||
create_unimplemented_device("sdma", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE);
|
||||
|
||||
/*
|
||||
* Watchdog
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
|
||||
static const hwaddr FSL_IMX7_WDOGn_ADDR[FSL_IMX7_NUM_WDTS] = {
|
||||
FSL_IMX7_WDOG1_ADDR,
|
||||
FSL_IMX7_WDOG2_ADDR,
|
||||
FSL_IMX7_WDOG3_ADDR,
|
||||
FSL_IMX7_WDOG4_ADDR,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX7_WDOGn_ADDR[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* SDMA
|
||||
*/
|
||||
create_unimplemented_device("sdma", FSL_IMX7_SDMA_ADDR, FSL_IMX7_SDMA_SIZE);
|
||||
|
||||
|
||||
object_property_set_bool(OBJECT(&s->gpr), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX7_GPR_ADDR);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->pcie), true,
|
||||
"realized", &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX7_PCIE_REG_ADDR);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTA_IRQ);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTB_IRQ);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTC_IRQ);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_IRQ);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq);
|
||||
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
|
||||
static const hwaddr FSL_IMX7_USBMISCn_ADDR[FSL_IMX7_NUM_USBS] = {
|
||||
FSL_IMX7_USBMISC1_ADDR,
|
||||
FSL_IMX7_USBMISC2_ADDR,
|
||||
FSL_IMX7_USBMISC3_ADDR,
|
||||
};
|
||||
|
||||
static const hwaddr FSL_IMX7_USBn_ADDR[FSL_IMX7_NUM_USBS] = {
|
||||
FSL_IMX7_USB1_ADDR,
|
||||
FSL_IMX7_USB2_ADDR,
|
||||
FSL_IMX7_USB3_ADDR,
|
||||
};
|
||||
|
||||
static const hwaddr FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = {
|
||||
FSL_IMX7_USB1_IRQ,
|
||||
FSL_IMX7_USB2_IRQ,
|
||||
FSL_IMX7_USB3_IRQ,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->usb[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0,
|
||||
FSL_IMX7_USBn_ADDR[i]);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USBn_IRQ[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, irq);
|
||||
|
||||
snprintf(name, NAME_SIZE, "usbmisc%d", i);
|
||||
create_unimplemented_device(name, FSL_IMX7_USBMISCn_ADDR[i],
|
||||
FSL_IMX7_USBMISCn_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* ADCs
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_ADCS; i++) {
|
||||
static const hwaddr FSL_IMX7_ADCn_ADDR[FSL_IMX7_NUM_ADCS] = {
|
||||
FSL_IMX7_ADC1_ADDR,
|
||||
FSL_IMX7_ADC2_ADDR,
|
||||
};
|
||||
|
||||
snprintf(name, NAME_SIZE, "adc%d", i);
|
||||
create_unimplemented_device(name, FSL_IMX7_ADCn_ADDR[i],
|
||||
FSL_IMX7_ADCn_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* LCD
|
||||
*/
|
||||
create_unimplemented_device("lcdif", FSL_IMX7_LCDIF_ADDR,
|
||||
FSL_IMX7_LCDIF_SIZE);
|
||||
}
|
||||
|
||||
static void fsl_imx7_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = fsl_imx7_realize;
|
||||
|
||||
/* Reason: Uses serial_hds and nd_table in realize() directly */
|
||||
dc->user_creatable = false;
|
||||
dc->desc = "i.MX7 SOC";
|
||||
}
|
||||
|
||||
static const TypeInfo fsl_imx7_type_info = {
|
||||
.name = TYPE_FSL_IMX7,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(FslIMX7State),
|
||||
.instance_init = fsl_imx7_init,
|
||||
.class_init = fsl_imx7_class_init,
|
||||
};
|
||||
|
||||
static void fsl_imx7_register_types(void)
|
||||
{
|
||||
type_register_static(&fsl_imx7_type_info);
|
||||
}
|
||||
type_init(fsl_imx7_register_types)
|
90
hw/arm/mcimx7d-sabre.c
Normal file
90
hw/arm/mcimx7d-sabre.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Impinj, Inc.
|
||||
*
|
||||
* MCIMX7D_SABRE Board System emulation.
|
||||
*
|
||||
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
|
||||
*
|
||||
* This code is licensed under the GPL, version 2 or later.
|
||||
* See the file `COPYING' in the top level directory.
|
||||
*
|
||||
* It (partially) emulates a mcimx7d_sabre board, with a Freescale
|
||||
* i.MX7 SoC
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/arm/fsl-imx7.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "net/net.h"
|
||||
|
||||
typedef struct {
|
||||
FslIMX7State soc;
|
||||
MemoryRegion ram;
|
||||
} MCIMX7Sabre;
|
||||
|
||||
static void mcimx7d_sabre_init(MachineState *machine)
|
||||
{
|
||||
static struct arm_boot_info boot_info;
|
||||
MCIMX7Sabre *s = g_new0(MCIMX7Sabre, 1);
|
||||
Object *soc;
|
||||
int i;
|
||||
|
||||
if (machine->ram_size > FSL_IMX7_MMDC_SIZE) {
|
||||
error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)",
|
||||
machine->ram_size, FSL_IMX7_MMDC_SIZE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
boot_info = (struct arm_boot_info) {
|
||||
.loader_start = FSL_IMX7_MMDC_ADDR,
|
||||
.board_id = -1,
|
||||
.ram_size = machine->ram_size,
|
||||
.kernel_filename = machine->kernel_filename,
|
||||
.kernel_cmdline = machine->kernel_cmdline,
|
||||
.initrd_filename = machine->initrd_filename,
|
||||
.nb_cpus = smp_cpus,
|
||||
};
|
||||
|
||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX7);
|
||||
soc = OBJECT(&s->soc);
|
||||
object_property_add_child(OBJECT(machine), "soc", soc, &error_fatal);
|
||||
object_property_set_bool(soc, true, "realized", &error_fatal);
|
||||
|
||||
memory_region_allocate_system_memory(&s->ram, NULL, "mcimx7d-sabre.ram",
|
||||
machine->ram_size);
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
FSL_IMX7_MMDC_ADDR, &s->ram);
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
|
||||
BusState *bus;
|
||||
DeviceState *carddev;
|
||||
DriveInfo *di;
|
||||
BlockBackend *blk;
|
||||
|
||||
di = drive_get_next(IF_SD);
|
||||
blk = di ? blk_by_legacy_dinfo(di) : NULL;
|
||||
bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus");
|
||||
carddev = qdev_create(bus, TYPE_SD_CARD);
|
||||
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
|
||||
object_property_set_bool(OBJECT(carddev), true,
|
||||
"realized", &error_fatal);
|
||||
}
|
||||
|
||||
if (!qtest_enabled()) {
|
||||
arm_load_kernel(&s->soc.cpu[0], &boot_info);
|
||||
}
|
||||
}
|
||||
|
||||
static void mcimx7d_sabre_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "Freescale i.MX7 DUAL SABRE (Cortex A7)";
|
||||
mc->init = mcimx7d_sabre_init;
|
||||
mc->max_cpus = FSL_IMX7_NUM_CPUS;
|
||||
}
|
||||
DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init)
|
@ -169,6 +169,7 @@ static const char *valid_cpus[] = {
|
||||
ARM_CPU_TYPE_NAME("cortex-a53"),
|
||||
ARM_CPU_TYPE_NAME("cortex-a57"),
|
||||
ARM_CPU_TYPE_NAME("host"),
|
||||
ARM_CPU_TYPE_NAME("max"),
|
||||
};
|
||||
|
||||
static bool cpu_type_valid(const char *cpu)
|
||||
@ -1206,16 +1207,23 @@ static void machvirt_init(MachineState *machine)
|
||||
/* We can probe only here because during property set
|
||||
* KVM is not available yet
|
||||
*/
|
||||
if (!vms->gic_version) {
|
||||
if (vms->gic_version <= 0) {
|
||||
/* "host" or "max" */
|
||||
if (!kvm_enabled()) {
|
||||
error_report("gic-version=host requires KVM");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
vms->gic_version = kvm_arm_vgic_probe();
|
||||
if (!vms->gic_version) {
|
||||
error_report("Unable to determine GIC version supported by host");
|
||||
exit(1);
|
||||
if (vms->gic_version == 0) {
|
||||
error_report("gic-version=host requires KVM");
|
||||
exit(1);
|
||||
} else {
|
||||
/* "max": currently means 3 for TCG */
|
||||
vms->gic_version = 3;
|
||||
}
|
||||
} else {
|
||||
vms->gic_version = kvm_arm_vgic_probe();
|
||||
if (!vms->gic_version) {
|
||||
error_report(
|
||||
"Unable to determine GIC version supported by host");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1479,9 +1487,11 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
|
||||
vms->gic_version = 2;
|
||||
} else if (!strcmp(value, "host")) {
|
||||
vms->gic_version = 0; /* Will probe later */
|
||||
} else if (!strcmp(value, "max")) {
|
||||
vms->gic_version = -1; /* Will probe later */
|
||||
} else {
|
||||
error_setg(errp, "Invalid gic-version value");
|
||||
error_append_hint(errp, "Valid values are 3, 2, host.\n");
|
||||
error_append_hint(errp, "Valid values are 3, 2, host, max.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,6 +282,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
s->virt, "has_el2", NULL);
|
||||
object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
|
||||
"reset-cbar", &error_abort);
|
||||
object_property_set_int(OBJECT(&s->apu_cpu[i]), num_apus,
|
||||
"core-count", &error_abort);
|
||||
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
|
||||
&err);
|
||||
if (err) {
|
||||
|
@ -17,3 +17,5 @@ common-obj-$(CONFIG_PCI_PIIX) += piix.o
|
||||
common-obj-$(CONFIG_PCI_Q35) += q35.o
|
||||
common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
|
||||
common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o
|
||||
|
||||
common-obj-$(CONFIG_PCI_DESIGNWARE) += designware.o
|
||||
|
754
hw/pci-host/designware.c
Normal file
754
hw/pci-host/designware.c
Normal file
@ -0,0 +1,754 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Impinj, Inc.
|
||||
*
|
||||
* Designware PCIe IP block emulation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/pci-host/designware.h"
|
||||
|
||||
#define DESIGNWARE_PCIE_PORT_LINK_CONTROL 0x710
|
||||
#define DESIGNWARE_PCIE_PHY_DEBUG_R1 0x72C
|
||||
#define DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP BIT(4)
|
||||
#define DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
|
||||
#define DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE BIT(17)
|
||||
#define DESIGNWARE_PCIE_MSI_ADDR_LO 0x820
|
||||
#define DESIGNWARE_PCIE_MSI_ADDR_HI 0x824
|
||||
#define DESIGNWARE_PCIE_MSI_INTR0_ENABLE 0x828
|
||||
#define DESIGNWARE_PCIE_MSI_INTR0_MASK 0x82C
|
||||
#define DESIGNWARE_PCIE_MSI_INTR0_STATUS 0x830
|
||||
#define DESIGNWARE_PCIE_ATU_VIEWPORT 0x900
|
||||
#define DESIGNWARE_PCIE_ATU_REGION_INBOUND BIT(31)
|
||||
#define DESIGNWARE_PCIE_ATU_CR1 0x904
|
||||
#define DESIGNWARE_PCIE_ATU_TYPE_MEM (0x0 << 0)
|
||||
#define DESIGNWARE_PCIE_ATU_CR2 0x908
|
||||
#define DESIGNWARE_PCIE_ATU_ENABLE BIT(31)
|
||||
#define DESIGNWARE_PCIE_ATU_LOWER_BASE 0x90C
|
||||
#define DESIGNWARE_PCIE_ATU_UPPER_BASE 0x910
|
||||
#define DESIGNWARE_PCIE_ATU_LIMIT 0x914
|
||||
#define DESIGNWARE_PCIE_ATU_LOWER_TARGET 0x918
|
||||
#define DESIGNWARE_PCIE_ATU_BUS(x) (((x) >> 24) & 0xff)
|
||||
#define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff)
|
||||
#define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C
|
||||
|
||||
static DesignwarePCIEHost *
|
||||
designware_pcie_root_to_host(DesignwarePCIERoot *root)
|
||||
{
|
||||
BusState *bus = qdev_get_parent_bus(DEVICE(root));
|
||||
return DESIGNWARE_PCIE_HOST(bus->parent);
|
||||
}
|
||||
|
||||
static void designware_pcie_root_msi_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned len)
|
||||
{
|
||||
DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
|
||||
DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
|
||||
|
||||
root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable;
|
||||
|
||||
if (root->msi.intr[0].status & ~root->msi.intr[0].mask) {
|
||||
qemu_set_irq(host->pci.irqs[0], 1);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps designware_pci_host_msi_ops = {
|
||||
.write = designware_pcie_root_msi_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root)
|
||||
|
||||
{
|
||||
MemoryRegion *mem = &root->msi.iomem;
|
||||
const uint64_t base = root->msi.base;
|
||||
const bool enable = root->msi.intr[0].enable;
|
||||
|
||||
memory_region_set_address(mem, base);
|
||||
memory_region_set_enabled(mem, enable);
|
||||
}
|
||||
|
||||
static DesignwarePCIEViewport *
|
||||
designware_pcie_root_get_current_viewport(DesignwarePCIERoot *root)
|
||||
{
|
||||
const unsigned int idx = root->atu_viewport & 0xF;
|
||||
const unsigned int dir =
|
||||
!!(root->atu_viewport & DESIGNWARE_PCIE_ATU_REGION_INBOUND);
|
||||
return &root->viewports[dir][idx];
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len)
|
||||
{
|
||||
DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
|
||||
DesignwarePCIEViewport *viewport =
|
||||
designware_pcie_root_get_current_viewport(root);
|
||||
|
||||
uint32_t val;
|
||||
|
||||
switch (address) {
|
||||
case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
|
||||
/*
|
||||
* Linux guest uses this register only to configure number of
|
||||
* PCIE lane (which in our case is irrelevant) and doesn't
|
||||
* really care about the value it reads from this register
|
||||
*/
|
||||
val = 0xDEADBEEF;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
|
||||
/*
|
||||
* To make sure that any code in guest waiting for speed
|
||||
* change does not time out we always report
|
||||
* PORT_LOGIC_SPEED_CHANGE as set
|
||||
*/
|
||||
val = DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_LO:
|
||||
val = root->msi.base;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_HI:
|
||||
val = root->msi.base >> 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_ENABLE:
|
||||
val = root->msi.intr[0].enable;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_MASK:
|
||||
val = root->msi.intr[0].mask;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
|
||||
val = root->msi.intr[0].status;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_PHY_DEBUG_R1:
|
||||
val = DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_VIEWPORT:
|
||||
val = root->atu_viewport;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LOWER_BASE:
|
||||
val = viewport->base;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_UPPER_BASE:
|
||||
val = viewport->base >> 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
|
||||
val = viewport->target;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
|
||||
val = viewport->target >> 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LIMIT:
|
||||
val = viewport->limit;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_CR1:
|
||||
case DESIGNWARE_PCIE_ATU_CR2: /* FALLTHROUGH */
|
||||
val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) /
|
||||
sizeof(uint32_t)];
|
||||
break;
|
||||
|
||||
default:
|
||||
val = pci_default_read_config(d, address, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint64_t designware_pcie_root_data_access(void *opaque, hwaddr addr,
|
||||
uint64_t *val, unsigned len)
|
||||
{
|
||||
DesignwarePCIEViewport *viewport = opaque;
|
||||
DesignwarePCIERoot *root = viewport->root;
|
||||
|
||||
const uint8_t busnum = DESIGNWARE_PCIE_ATU_BUS(viewport->target);
|
||||
const uint8_t devfn = DESIGNWARE_PCIE_ATU_DEVFN(viewport->target);
|
||||
PCIBus *pcibus = pci_get_bus(PCI_DEVICE(root));
|
||||
PCIDevice *pcidev = pci_find_device(pcibus, busnum, devfn);
|
||||
|
||||
if (pcidev) {
|
||||
addr &= pci_config_size(pcidev) - 1;
|
||||
|
||||
if (val) {
|
||||
pci_host_config_write_common(pcidev, addr,
|
||||
pci_config_size(pcidev),
|
||||
*val, len);
|
||||
} else {
|
||||
return pci_host_config_read_common(pcidev, addr,
|
||||
pci_config_size(pcidev),
|
||||
len);
|
||||
}
|
||||
}
|
||||
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
static uint64_t designware_pcie_root_data_read(void *opaque, hwaddr addr,
|
||||
unsigned len)
|
||||
{
|
||||
return designware_pcie_root_data_access(opaque, addr, NULL, len);
|
||||
}
|
||||
|
||||
static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned len)
|
||||
{
|
||||
designware_pcie_root_data_access(opaque, addr, &val, len);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps designware_pci_host_conf_ops = {
|
||||
.read = designware_pcie_root_data_read,
|
||||
.write = designware_pcie_root_data_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
|
||||
DesignwarePCIEViewport *viewport)
|
||||
{
|
||||
const uint64_t target = viewport->target;
|
||||
const uint64_t base = viewport->base;
|
||||
const uint64_t size = (uint64_t)viewport->limit - base + 1;
|
||||
const bool enabled = viewport->cr[1] & DESIGNWARE_PCIE_ATU_ENABLE;
|
||||
|
||||
MemoryRegion *current, *other;
|
||||
|
||||
if (viewport->cr[0] == DESIGNWARE_PCIE_ATU_TYPE_MEM) {
|
||||
current = &viewport->mem;
|
||||
other = &viewport->cfg;
|
||||
memory_region_set_alias_offset(current, target);
|
||||
} else {
|
||||
current = &viewport->cfg;
|
||||
other = &viewport->mem;
|
||||
}
|
||||
|
||||
/*
|
||||
* An outbound viewport can be reconfigure from being MEM to CFG,
|
||||
* to account for that we disable the "other" memory region that
|
||||
* becomes unused due to that fact.
|
||||
*/
|
||||
memory_region_set_enabled(other, false);
|
||||
if (enabled) {
|
||||
memory_region_set_size(current, size);
|
||||
memory_region_set_address(current, base);
|
||||
}
|
||||
memory_region_set_enabled(current, enabled);
|
||||
}
|
||||
|
||||
static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
|
||||
DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
|
||||
DesignwarePCIEViewport *viewport =
|
||||
designware_pcie_root_get_current_viewport(root);
|
||||
|
||||
switch (address) {
|
||||
case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
|
||||
case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
|
||||
case DESIGNWARE_PCIE_PHY_DEBUG_R1:
|
||||
/* No-op */
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_LO:
|
||||
root->msi.base &= 0xFFFFFFFF00000000ULL;
|
||||
root->msi.base |= val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_HI:
|
||||
root->msi.base &= 0x00000000FFFFFFFFULL;
|
||||
root->msi.base |= (uint64_t)val << 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: {
|
||||
const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val;
|
||||
|
||||
root->msi.intr[0].enable = val;
|
||||
|
||||
if (update_msi_mapping) {
|
||||
designware_pcie_root_update_msi_mapping(root);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_MASK:
|
||||
root->msi.intr[0].mask = val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
|
||||
root->msi.intr[0].status ^= val;
|
||||
if (!root->msi.intr[0].status) {
|
||||
qemu_set_irq(host->pci.irqs[0], 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_VIEWPORT:
|
||||
root->atu_viewport = val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LOWER_BASE:
|
||||
viewport->base &= 0xFFFFFFFF00000000ULL;
|
||||
viewport->base |= val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_UPPER_BASE:
|
||||
viewport->base &= 0x00000000FFFFFFFFULL;
|
||||
viewport->base |= (uint64_t)val << 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
|
||||
viewport->target &= 0xFFFFFFFF00000000ULL;
|
||||
viewport->target |= val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
|
||||
viewport->target &= 0x00000000FFFFFFFFULL;
|
||||
viewport->target |= val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LIMIT:
|
||||
viewport->limit = val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_CR1:
|
||||
viewport->cr[0] = val;
|
||||
break;
|
||||
case DESIGNWARE_PCIE_ATU_CR2:
|
||||
viewport->cr[1] = val;
|
||||
designware_pcie_update_viewport(root, viewport);
|
||||
break;
|
||||
|
||||
default:
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static char *designware_pcie_viewport_name(const char *direction,
|
||||
unsigned int i,
|
||||
const char *type)
|
||||
{
|
||||
return g_strdup_printf("PCI %s Viewport %u [%s]",
|
||||
direction, i, type);
|
||||
}
|
||||
|
||||
static void designware_pcie_root_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
|
||||
DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
|
||||
MemoryRegion *address_space = &host->pci.memory;
|
||||
PCIBridge *br = PCI_BRIDGE(dev);
|
||||
DesignwarePCIEViewport *viewport;
|
||||
/*
|
||||
* Dummy values used for initial configuration of MemoryRegions
|
||||
* that belong to a given viewport
|
||||
*/
|
||||
const hwaddr dummy_offset = 0;
|
||||
const uint64_t dummy_size = 4;
|
||||
size_t i;
|
||||
|
||||
br->bus_name = "dw-pcie";
|
||||
|
||||
pci_set_word(dev->config + PCI_COMMAND,
|
||||
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
|
||||
pci_config_set_interrupt_pin(dev->config, 1);
|
||||
pci_bridge_initfn(dev, TYPE_PCIE_BUS);
|
||||
|
||||
pcie_port_init_reg(dev);
|
||||
|
||||
pcie_cap_init(dev, 0x70, PCI_EXP_TYPE_ROOT_PORT,
|
||||
0, &error_fatal);
|
||||
|
||||
msi_nonbroken = true;
|
||||
msi_init(dev, 0x50, 32, true, true, &error_fatal);
|
||||
|
||||
for (i = 0; i < DESIGNWARE_PCIE_NUM_VIEWPORTS; i++) {
|
||||
MemoryRegion *source, *destination, *mem;
|
||||
const char *direction;
|
||||
char *name;
|
||||
|
||||
viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
|
||||
viewport->inbound = true;
|
||||
viewport->base = 0x0000000000000000ULL;
|
||||
viewport->target = 0x0000000000000000ULL;
|
||||
viewport->limit = UINT32_MAX;
|
||||
viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
|
||||
|
||||
source = &host->pci.address_space_root;
|
||||
destination = get_system_memory();
|
||||
direction = "Inbound";
|
||||
|
||||
/*
|
||||
* Configure MemoryRegion implementing PCI -> CPU memory
|
||||
* access
|
||||
*/
|
||||
mem = &viewport->mem;
|
||||
name = designware_pcie_viewport_name(direction, i, "MEM");
|
||||
memory_region_init_alias(mem, OBJECT(root), name, destination,
|
||||
dummy_offset, dummy_size);
|
||||
memory_region_add_subregion_overlap(source, dummy_offset, mem, -1);
|
||||
memory_region_set_enabled(mem, false);
|
||||
g_free(name);
|
||||
|
||||
viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_OUTBOUND][i];
|
||||
viewport->root = root;
|
||||
viewport->inbound = false;
|
||||
viewport->base = 0x0000000000000000ULL;
|
||||
viewport->target = 0x0000000000000000ULL;
|
||||
viewport->limit = UINT32_MAX;
|
||||
viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
|
||||
|
||||
destination = &host->pci.memory;
|
||||
direction = "Outbound";
|
||||
source = get_system_memory();
|
||||
|
||||
/*
|
||||
* Configure MemoryRegion implementing CPU -> PCI memory
|
||||
* access
|
||||
*/
|
||||
mem = &viewport->mem;
|
||||
name = designware_pcie_viewport_name(direction, i, "MEM");
|
||||
memory_region_init_alias(mem, OBJECT(root), name, destination,
|
||||
dummy_offset, dummy_size);
|
||||
memory_region_add_subregion(source, dummy_offset, mem);
|
||||
memory_region_set_enabled(mem, false);
|
||||
g_free(name);
|
||||
|
||||
/*
|
||||
* Configure MemoryRegion implementing access to configuration
|
||||
* space
|
||||
*/
|
||||
mem = &viewport->cfg;
|
||||
name = designware_pcie_viewport_name(direction, i, "CFG");
|
||||
memory_region_init_io(&viewport->cfg, OBJECT(root),
|
||||
&designware_pci_host_conf_ops,
|
||||
viewport, name, dummy_size);
|
||||
memory_region_add_subregion(source, dummy_offset, mem);
|
||||
memory_region_set_enabled(mem, false);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* If no inbound iATU windows are configured, HW defaults to
|
||||
* letting inbound TLPs to pass in. We emulate that by exlicitly
|
||||
* configuring first inbound window to cover all of target's
|
||||
* address space.
|
||||
*
|
||||
* NOTE: This will not work correctly for the case when first
|
||||
* configured inbound window is window 0
|
||||
*/
|
||||
viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][0];
|
||||
viewport->cr[1] = DESIGNWARE_PCIE_ATU_ENABLE;
|
||||
designware_pcie_update_viewport(root, viewport);
|
||||
|
||||
memory_region_init_io(&root->msi.iomem, OBJECT(root),
|
||||
&designware_pci_host_msi_ops,
|
||||
root, "pcie-msi", 0x4);
|
||||
/*
|
||||
* We initially place MSI interrupt I/O region a adress 0 and
|
||||
* disable it. It'll be later moved to correct offset and enabled
|
||||
* in designware_pcie_root_update_msi_mapping() as a part of
|
||||
* initialization done by guest OS
|
||||
*/
|
||||
memory_region_add_subregion(address_space, dummy_offset, &root->msi.iomem);
|
||||
memory_region_set_enabled(&root->msi.iomem, false);
|
||||
}
|
||||
|
||||
static void designware_pcie_set_irq(void *opaque, int irq_num, int level)
|
||||
{
|
||||
DesignwarePCIEHost *host = DESIGNWARE_PCIE_HOST(opaque);
|
||||
|
||||
qemu_set_irq(host->pci.irqs[irq_num], level);
|
||||
}
|
||||
|
||||
static const char *
|
||||
designware_pcie_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus)
|
||||
{
|
||||
return "0000:00";
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_msi_bank = {
|
||||
.name = "designware-pcie-msi-bank",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(enable, DesignwarePCIEMSIBank),
|
||||
VMSTATE_UINT32(mask, DesignwarePCIEMSIBank),
|
||||
VMSTATE_UINT32(status, DesignwarePCIEMSIBank),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_msi = {
|
||||
.name = "designware-pcie-msi",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(base, DesignwarePCIEMSI),
|
||||
VMSTATE_STRUCT_ARRAY(intr,
|
||||
DesignwarePCIEMSI,
|
||||
DESIGNWARE_PCIE_NUM_MSI_BANKS,
|
||||
1,
|
||||
vmstate_designware_pcie_msi_bank,
|
||||
DesignwarePCIEMSIBank),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_viewport = {
|
||||
.name = "designware-pcie-viewport",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(base, DesignwarePCIEViewport),
|
||||
VMSTATE_UINT64(target, DesignwarePCIEViewport),
|
||||
VMSTATE_UINT32(limit, DesignwarePCIEViewport),
|
||||
VMSTATE_UINT32_ARRAY(cr, DesignwarePCIEViewport, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_root = {
|
||||
.name = "designware-pcie-root",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
|
||||
VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot),
|
||||
VMSTATE_STRUCT_2DARRAY(viewports,
|
||||
DesignwarePCIERoot,
|
||||
2,
|
||||
DESIGNWARE_PCIE_NUM_VIEWPORTS,
|
||||
1,
|
||||
vmstate_designware_pcie_viewport,
|
||||
DesignwarePCIEViewport),
|
||||
VMSTATE_STRUCT(msi,
|
||||
DesignwarePCIERoot,
|
||||
1,
|
||||
vmstate_designware_pcie_msi,
|
||||
DesignwarePCIEMSI),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void designware_pcie_root_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_SYNOPSYS;
|
||||
k->device_id = 0xABCD;
|
||||
k->revision = 0;
|
||||
k->class_id = PCI_CLASS_BRIDGE_PCI;
|
||||
k->is_bridge = true;
|
||||
k->exit = pci_bridge_exitfn;
|
||||
k->realize = designware_pcie_root_realize;
|
||||
k->config_read = designware_pcie_root_config_read;
|
||||
k->config_write = designware_pcie_root_config_write;
|
||||
|
||||
dc->reset = pci_bridge_reset;
|
||||
/*
|
||||
* PCI-facing part of the host bridge, not usable without the
|
||||
* host-facing part, which can't be device_add'ed, yet.
|
||||
*/
|
||||
dc->user_creatable = false;
|
||||
dc->vmsd = &vmstate_designware_pcie_root;
|
||||
}
|
||||
|
||||
static uint64_t designware_pcie_host_mmio_read(void *opaque, hwaddr addr,
|
||||
unsigned int size)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
|
||||
PCIDevice *device = pci_find_device(pci->bus, 0, 0);
|
||||
|
||||
return pci_host_config_read_common(device,
|
||||
addr,
|
||||
pci_config_size(device),
|
||||
size);
|
||||
}
|
||||
|
||||
static void designware_pcie_host_mmio_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned int size)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
|
||||
PCIDevice *device = pci_find_device(pci->bus, 0, 0);
|
||||
|
||||
return pci_host_config_write_common(device,
|
||||
addr,
|
||||
pci_config_size(device),
|
||||
val, size);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps designware_pci_mmio_ops = {
|
||||
.read = designware_pcie_host_mmio_read,
|
||||
.write = designware_pcie_host_mmio_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
/*
|
||||
* Our device would not work correctly if the guest was doing
|
||||
* unaligned access. This might not be a limitation on the real
|
||||
* device but in practice there is no reason for a guest to access
|
||||
* this device unaligned.
|
||||
*/
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
.unaligned = false,
|
||||
},
|
||||
};
|
||||
|
||||
static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque,
|
||||
int devfn)
|
||||
{
|
||||
DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(opaque);
|
||||
|
||||
return &s->pci.address_space;
|
||||
}
|
||||
|
||||
static void designware_pcie_host_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
||||
DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) {
|
||||
sysbus_init_irq(sbd, &s->pci.irqs[i]);
|
||||
}
|
||||
|
||||
memory_region_init_io(&s->mmio,
|
||||
OBJECT(s),
|
||||
&designware_pci_mmio_ops,
|
||||
s,
|
||||
"pcie.reg", 4 * 1024);
|
||||
sysbus_init_mmio(sbd, &s->mmio);
|
||||
|
||||
memory_region_init(&s->pci.io, OBJECT(s), "pcie-pio", 16);
|
||||
memory_region_init(&s->pci.memory, OBJECT(s),
|
||||
"pcie-bus-memory",
|
||||
UINT64_MAX);
|
||||
|
||||
pci->bus = pci_register_root_bus(dev, "pcie",
|
||||
designware_pcie_set_irq,
|
||||
pci_swizzle_map_irq_fn,
|
||||
s,
|
||||
&s->pci.memory,
|
||||
&s->pci.io,
|
||||
0, 4,
|
||||
TYPE_PCIE_BUS);
|
||||
|
||||
memory_region_init(&s->pci.address_space_root,
|
||||
OBJECT(s),
|
||||
"pcie-bus-address-space-root",
|
||||
UINT64_MAX);
|
||||
memory_region_add_subregion(&s->pci.address_space_root,
|
||||
0x0, &s->pci.memory);
|
||||
address_space_init(&s->pci.address_space,
|
||||
&s->pci.address_space_root,
|
||||
"pcie-bus-address-space");
|
||||
pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s);
|
||||
|
||||
qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus));
|
||||
qdev_init_nofail(DEVICE(&s->root));
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_host = {
|
||||
.name = "designware-pcie-host",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(root,
|
||||
DesignwarePCIEHost,
|
||||
1,
|
||||
vmstate_designware_pcie_root,
|
||||
DesignwarePCIERoot),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void designware_pcie_host_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
||||
|
||||
hc->root_bus_path = designware_pcie_host_root_bus_path;
|
||||
dc->realize = designware_pcie_host_realize;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->fw_name = "pci";
|
||||
dc->vmsd = &vmstate_designware_pcie_host;
|
||||
}
|
||||
|
||||
static void designware_pcie_host_init(Object *obj)
|
||||
{
|
||||
DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(obj);
|
||||
DesignwarePCIERoot *root = &s->root;
|
||||
|
||||
object_initialize(root, sizeof(*root), TYPE_DESIGNWARE_PCIE_ROOT);
|
||||
object_property_add_child(obj, "root", OBJECT(root), NULL);
|
||||
qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
|
||||
qdev_prop_set_bit(DEVICE(root), "multifunction", false);
|
||||
}
|
||||
|
||||
static const TypeInfo designware_pcie_root_info = {
|
||||
.name = TYPE_DESIGNWARE_PCIE_ROOT,
|
||||
.parent = TYPE_PCI_BRIDGE,
|
||||
.instance_size = sizeof(DesignwarePCIERoot),
|
||||
.class_init = designware_pcie_root_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static const TypeInfo designware_pcie_host_info = {
|
||||
.name = TYPE_DESIGNWARE_PCIE_HOST,
|
||||
.parent = TYPE_PCI_HOST_BRIDGE,
|
||||
.instance_size = sizeof(DesignwarePCIEHost),
|
||||
.instance_init = designware_pcie_host_init,
|
||||
.class_init = designware_pcie_host_class_init,
|
||||
};
|
||||
|
||||
static void designware_pcie_register(void)
|
||||
{
|
||||
type_register_static(&designware_pcie_root_info);
|
||||
type_register_static(&designware_pcie_host_info);
|
||||
}
|
||||
type_init(designware_pcie_register)
|
@ -1,6 +1,6 @@
|
||||
common-obj-$(CONFIG_PL181) += pl181.o
|
||||
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
|
||||
common-obj-$(CONFIG_SD) += sd.o core.o
|
||||
common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o
|
||||
common-obj-$(CONFIG_SDHCI) += sdhci.o
|
||||
|
||||
obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
|
||||
|
55
hw/sd/sd.c
55
hw/sd/sd.c
@ -120,6 +120,7 @@ struct SDState {
|
||||
qemu_irq readonly_cb;
|
||||
qemu_irq inserted_cb;
|
||||
QEMUTimer *ocr_power_timer;
|
||||
const char *proto_name;
|
||||
bool enable;
|
||||
uint8_t dat_lines;
|
||||
bool cmd_line;
|
||||
@ -866,13 +867,19 @@ static void sd_lock_command(SDState *sd)
|
||||
sd->card_status &= ~CARD_IS_LOCKED;
|
||||
}
|
||||
|
||||
static sd_rsp_type_t sd_normal_command(SDState *sd,
|
||||
SDRequest req)
|
||||
static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
|
||||
{
|
||||
uint32_t rca = 0x0000;
|
||||
uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
|
||||
|
||||
trace_sdcard_normal_command(req.cmd, req.arg, sd_state_name(sd->state));
|
||||
/* CMD55 precedes an ACMD, so we are not interested in tracing it.
|
||||
* However there is no ACMD55, so we want to trace this particular case.
|
||||
*/
|
||||
if (req.cmd != 55 || sd->expecting_acmd) {
|
||||
trace_sdcard_normal_command(sd->proto_name,
|
||||
sd_cmd_name(req.cmd), req.cmd,
|
||||
req.arg, sd_state_name(sd->state));
|
||||
}
|
||||
|
||||
/* Not interpreting this as an app command */
|
||||
sd->card_status &= ~APP_CMD;
|
||||
@ -1162,6 +1169,14 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
||||
}
|
||||
break;
|
||||
|
||||
case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
|
||||
if (sd->state == sd_transfer_state) {
|
||||
sd->state = sd_sendingdata_state;
|
||||
sd->data_offset = 0;
|
||||
return sd_r1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 23: /* CMD23: SET_BLOCK_COUNT */
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
@ -1450,7 +1465,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
||||
static sd_rsp_type_t sd_app_command(SDState *sd,
|
||||
SDRequest req)
|
||||
{
|
||||
trace_sdcard_app_command(req.cmd, req.arg);
|
||||
trace_sdcard_app_command(sd->proto_name, sd_acmd_name(req.cmd),
|
||||
req.cmd, req.arg, sd_state_name(sd->state));
|
||||
sd->card_status |= APP_CMD;
|
||||
switch (req.cmd) {
|
||||
case 6: /* ACMD6: SET_BUS_WIDTH */
|
||||
@ -1765,7 +1781,9 @@ void sd_write_data(SDState *sd, uint8_t value)
|
||||
if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
|
||||
return;
|
||||
|
||||
trace_sdcard_write_data(sd->current_cmd, value);
|
||||
trace_sdcard_write_data(sd->proto_name,
|
||||
sd_acmd_name(sd->current_cmd),
|
||||
sd->current_cmd, value);
|
||||
switch (sd->current_cmd) {
|
||||
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
|
||||
sd->data[sd->data_offset ++] = value;
|
||||
@ -1883,6 +1901,20 @@ void sd_write_data(SDState *sd, uint8_t value)
|
||||
}
|
||||
}
|
||||
|
||||
#define SD_TUNING_BLOCK_SIZE 64
|
||||
|
||||
static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = {
|
||||
/* See: Physical Layer Simplified Specification Version 3.01, Table 4-2 */
|
||||
0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc,
|
||||
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
|
||||
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
|
||||
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
|
||||
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
|
||||
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
|
||||
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
|
||||
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
|
||||
};
|
||||
|
||||
uint8_t sd_read_data(SDState *sd)
|
||||
{
|
||||
/* TODO: Append CRCs */
|
||||
@ -1903,7 +1935,9 @@ uint8_t sd_read_data(SDState *sd)
|
||||
|
||||
io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
|
||||
|
||||
trace_sdcard_read_data(sd->current_cmd, io_len);
|
||||
trace_sdcard_read_data(sd->proto_name,
|
||||
sd_acmd_name(sd->current_cmd),
|
||||
sd->current_cmd, io_len);
|
||||
switch (sd->current_cmd) {
|
||||
case 6: /* CMD6: SWITCH_FUNCTION */
|
||||
ret = sd->data[sd->data_offset ++];
|
||||
@ -1960,6 +1994,13 @@ uint8_t sd_read_data(SDState *sd)
|
||||
}
|
||||
break;
|
||||
|
||||
case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
|
||||
if (sd->data_offset >= SD_TUNING_BLOCK_SIZE - 1) {
|
||||
sd->state = sd_transfer_state;
|
||||
}
|
||||
ret = sd_tuning_block_pattern[sd->data_offset++];
|
||||
break;
|
||||
|
||||
case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
|
||||
ret = sd->data[sd->data_offset ++];
|
||||
|
||||
@ -2029,6 +2070,8 @@ static void sd_realize(DeviceState *dev, Error **errp)
|
||||
SDState *sd = SD_CARD(dev);
|
||||
int ret;
|
||||
|
||||
sd->proto_name = sd->spi ? "SPI" : "SD";
|
||||
|
||||
if (sd->blk && blk_is_read_only(sd->blk)) {
|
||||
error_setg(errp, "Cannot use read-only drive as SD card");
|
||||
return;
|
||||
|
@ -433,13 +433,13 @@ static void sdhci_read_block_from_card(SDHCIState *s)
|
||||
for (index = 0; index < blk_size; index++) {
|
||||
data = sdbus_read_data(&s->sdbus);
|
||||
if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
|
||||
/* Device is not in tunning */
|
||||
/* Device is not in tuning */
|
||||
s->fifo_buffer[index] = data;
|
||||
}
|
||||
}
|
||||
|
||||
if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
|
||||
/* Device is in tunning */
|
||||
/* Device is in tuning */
|
||||
s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK;
|
||||
s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK;
|
||||
s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ |
|
||||
|
72
hw/sd/sdmmc-internal.c
Normal file
72
hw/sd/sdmmc-internal.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* SD/MMC cards common helpers
|
||||
*
|
||||
* Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sdmmc-internal.h"
|
||||
|
||||
const char *sd_cmd_name(uint8_t cmd)
|
||||
{
|
||||
static const char *cmd_abbrev[SDMMC_CMD_MAX] = {
|
||||
[0] = "GO_IDLE_STATE",
|
||||
[2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR",
|
||||
[4] = "SET_DSR", [5] = "IO_SEND_OP_COND",
|
||||
[6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD",
|
||||
[8] = "SEND_IF_COND", [9] = "SEND_CSD",
|
||||
[10] = "SEND_CID", [11] = "VOLTAGE_SWITCH",
|
||||
[12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS",
|
||||
[15] = "GO_INACTIVE_STATE",
|
||||
[16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK",
|
||||
[18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK",
|
||||
[20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec",
|
||||
[23] = "SET_BLOCK_COUNT",
|
||||
[24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK",
|
||||
[26] = "MANUF_RSVD", [27] = "PROGRAM_CSD",
|
||||
[28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT",
|
||||
[30] = "SEND_WRITE_PROT",
|
||||
[32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END",
|
||||
[34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD",
|
||||
[36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD",
|
||||
[38] = "ERASE",
|
||||
[40] = "DPS_spec",
|
||||
[42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT",
|
||||
[44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B",
|
||||
[46] = "Q_RD_TASK", [47] = "Q_WR_TASK",
|
||||
[48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE",
|
||||
[50] = "SW_FUNC_RSVD",
|
||||
[52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED",
|
||||
[54] = "SDIO_RSVD", [55] = "APP_CMD",
|
||||
[56] = "GEN_CMD", [57] = "SW_FUNC_RSVD",
|
||||
[58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI",
|
||||
[60] = "MANUF_RSVD", [61] = "MANUF_RSVD",
|
||||
[62] = "MANUF_RSVD", [63] = "MANUF_RSVD",
|
||||
};
|
||||
return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD";
|
||||
}
|
||||
|
||||
const char *sd_acmd_name(uint8_t cmd)
|
||||
{
|
||||
static const char *acmd_abbrev[SDMMC_CMD_MAX] = {
|
||||
[6] = "SET_BUS_WIDTH",
|
||||
[13] = "SD_STATUS",
|
||||
[14] = "DPS_spec", [15] = "DPS_spec",
|
||||
[16] = "DPS_spec",
|
||||
[18] = "SECU_spec",
|
||||
[22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT",
|
||||
[41] = "SD_SEND_OP_COND",
|
||||
[42] = "SET_CLR_CARD_DETECT",
|
||||
[51] = "SEND_SCR",
|
||||
[52] = "SECU_spec", [53] = "SECU_spec",
|
||||
[54] = "SECU_spec",
|
||||
[56] = "SECU_spec", [57] = "SECU_spec",
|
||||
[58] = "SECU_spec", [59] = "SECU_spec",
|
||||
};
|
||||
|
||||
return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD";
|
||||
}
|
@ -12,4 +12,28 @@
|
||||
|
||||
#define SDMMC_CMD_MAX 64
|
||||
|
||||
/**
|
||||
* sd_cmd_name:
|
||||
* @cmd: A SD "normal" command, up to SDMMC_CMD_MAX.
|
||||
*
|
||||
* Returns a human-readable name describing the command.
|
||||
* The return value is always a static string which does not need
|
||||
* to be freed after use.
|
||||
*
|
||||
* Returns: The command name of @cmd or "UNKNOWN_CMD".
|
||||
*/
|
||||
const char *sd_cmd_name(uint8_t cmd);
|
||||
|
||||
/**
|
||||
* sd_acmd_name:
|
||||
* @cmd: A SD "Application-Specific" command, up to SDMMC_CMD_MAX.
|
||||
*
|
||||
* Returns a human-readable name describing the application command.
|
||||
* The return value is always a static string which does not need
|
||||
* to be freed after use.
|
||||
*
|
||||
* Returns: The application command name of @cmd or "UNKNOWN_ACMD".
|
||||
*/
|
||||
const char *sd_acmd_name(uint8_t cmd);
|
||||
|
||||
#endif
|
||||
|
@ -24,8 +24,8 @@ sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of
|
||||
sdhci_capareg(const char *desc, uint16_t val) "%s: %u"
|
||||
|
||||
# hw/sd/sd.c
|
||||
sdcard_normal_command(uint8_t cmd, uint32_t arg, const char *state) "CMD%d arg 0x%08x (state %s)"
|
||||
sdcard_app_command(uint8_t acmd, uint32_t arg) "ACMD%d arg 0x%08x"
|
||||
sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *state) "%s %20s/ CMD%02d arg 0x%08x (state %s)"
|
||||
sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *state) "%s %23s/ACMD%02d arg 0x%08x (state %s)"
|
||||
sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)"
|
||||
sdcard_powerup(void) ""
|
||||
sdcard_inquiry_cmd41(void) ""
|
||||
@ -39,8 +39,8 @@ sdcard_lock(void) ""
|
||||
sdcard_unlock(void) ""
|
||||
sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
|
||||
sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
|
||||
sdcard_write_data(uint8_t cmd, uint8_t value) "CMD%02d value 0x%02x"
|
||||
sdcard_read_data(uint8_t cmd, int length) "CMD%02d len %d"
|
||||
sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x"
|
||||
sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, int length) "%s %20s/ CMD%02d len %d"
|
||||
sdcard_set_voltage(uint16_t millivolts) "%u mV"
|
||||
|
||||
# hw/sd/milkymist-memcard.c
|
||||
|
222
include/hw/arm/fsl-imx7.h
Normal file
222
include/hw/arm/fsl-imx7.h
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Impinj, Inc.
|
||||
*
|
||||
* i.MX7 SoC definitions
|
||||
*
|
||||
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; 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.
|
||||
*/
|
||||
|
||||
#ifndef FSL_IMX7_H
|
||||
#define FSL_IMX7_H
|
||||
|
||||
#include "hw/arm/arm.h"
|
||||
#include "hw/cpu/a15mpcore.h"
|
||||
#include "hw/intc/imx_gpcv2.h"
|
||||
#include "hw/misc/imx7_ccm.h"
|
||||
#include "hw/misc/imx7_snvs.h"
|
||||
#include "hw/misc/imx7_gpr.h"
|
||||
#include "hw/misc/imx6_src.h"
|
||||
#include "hw/misc/imx2_wdt.h"
|
||||
#include "hw/gpio/imx_gpio.h"
|
||||
#include "hw/char/imx_serial.h"
|
||||
#include "hw/timer/imx_gpt.h"
|
||||
#include "hw/timer/imx_epit.h"
|
||||
#include "hw/i2c/imx_i2c.h"
|
||||
#include "hw/gpio/imx_gpio.h"
|
||||
#include "hw/sd/sdhci.h"
|
||||
#include "hw/ssi/imx_spi.h"
|
||||
#include "hw/net/imx_fec.h"
|
||||
#include "hw/pci-host/designware.h"
|
||||
#include "hw/usb/chipidea.h"
|
||||
#include "exec/memory.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#define TYPE_FSL_IMX7 "fsl,imx7"
|
||||
#define FSL_IMX7(obj) OBJECT_CHECK(FslIMX7State, (obj), TYPE_FSL_IMX7)
|
||||
|
||||
enum FslIMX7Configuration {
|
||||
FSL_IMX7_NUM_CPUS = 2,
|
||||
FSL_IMX7_NUM_UARTS = 7,
|
||||
FSL_IMX7_NUM_ETHS = 2,
|
||||
FSL_IMX7_ETH_NUM_TX_RINGS = 3,
|
||||
FSL_IMX7_NUM_USDHCS = 3,
|
||||
FSL_IMX7_NUM_WDTS = 4,
|
||||
FSL_IMX7_NUM_GPTS = 4,
|
||||
FSL_IMX7_NUM_IOMUXCS = 2,
|
||||
FSL_IMX7_NUM_GPIOS = 7,
|
||||
FSL_IMX7_NUM_I2CS = 4,
|
||||
FSL_IMX7_NUM_ECSPIS = 4,
|
||||
FSL_IMX7_NUM_USBS = 3,
|
||||
FSL_IMX7_NUM_ADCS = 2,
|
||||
};
|
||||
|
||||
typedef struct FslIMX7State {
|
||||
/*< private >*/
|
||||
DeviceState parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
ARMCPU cpu[FSL_IMX7_NUM_CPUS];
|
||||
A15MPPrivState a7mpcore;
|
||||
IMXGPTState gpt[FSL_IMX7_NUM_GPTS];
|
||||
IMXGPIOState gpio[FSL_IMX7_NUM_GPIOS];
|
||||
IMX7CCMState ccm;
|
||||
IMX7AnalogState analog;
|
||||
IMX7SNVSState snvs;
|
||||
IMXGPCv2State gpcv2;
|
||||
IMXSPIState spi[FSL_IMX7_NUM_ECSPIS];
|
||||
IMXI2CState i2c[FSL_IMX7_NUM_I2CS];
|
||||
IMXSerialState uart[FSL_IMX7_NUM_UARTS];
|
||||
IMXFECState eth[FSL_IMX7_NUM_ETHS];
|
||||
SDHCIState usdhc[FSL_IMX7_NUM_USDHCS];
|
||||
IMX2WdtState wdt[FSL_IMX7_NUM_WDTS];
|
||||
IMX7GPRState gpr;
|
||||
ChipideaState usb[FSL_IMX7_NUM_USBS];
|
||||
DesignwarePCIEHost pcie;
|
||||
} FslIMX7State;
|
||||
|
||||
enum FslIMX7MemoryMap {
|
||||
FSL_IMX7_MMDC_ADDR = 0x80000000,
|
||||
FSL_IMX7_MMDC_SIZE = 2 * 1024 * 1024 * 1024UL,
|
||||
|
||||
FSL_IMX7_GPIO1_ADDR = 0x30200000,
|
||||
FSL_IMX7_GPIO2_ADDR = 0x30210000,
|
||||
FSL_IMX7_GPIO3_ADDR = 0x30220000,
|
||||
FSL_IMX7_GPIO4_ADDR = 0x30230000,
|
||||
FSL_IMX7_GPIO5_ADDR = 0x30240000,
|
||||
FSL_IMX7_GPIO6_ADDR = 0x30250000,
|
||||
FSL_IMX7_GPIO7_ADDR = 0x30260000,
|
||||
|
||||
FSL_IMX7_IOMUXC_LPSR_GPR_ADDR = 0x30270000,
|
||||
|
||||
FSL_IMX7_WDOG1_ADDR = 0x30280000,
|
||||
FSL_IMX7_WDOG2_ADDR = 0x30290000,
|
||||
FSL_IMX7_WDOG3_ADDR = 0x302A0000,
|
||||
FSL_IMX7_WDOG4_ADDR = 0x302B0000,
|
||||
|
||||
FSL_IMX7_IOMUXC_LPSR_ADDR = 0x302C0000,
|
||||
|
||||
FSL_IMX7_GPT1_ADDR = 0x302D0000,
|
||||
FSL_IMX7_GPT2_ADDR = 0x302E0000,
|
||||
FSL_IMX7_GPT3_ADDR = 0x302F0000,
|
||||
FSL_IMX7_GPT4_ADDR = 0x30300000,
|
||||
|
||||
FSL_IMX7_IOMUXC_ADDR = 0x30330000,
|
||||
FSL_IMX7_IOMUXC_GPR_ADDR = 0x30340000,
|
||||
FSL_IMX7_IOMUXCn_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_ANALOG_ADDR = 0x30360000,
|
||||
FSL_IMX7_SNVS_ADDR = 0x30370000,
|
||||
FSL_IMX7_CCM_ADDR = 0x30380000,
|
||||
|
||||
FSL_IMX7_SRC_ADDR = 0x30390000,
|
||||
FSL_IMX7_SRC_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_ADC1_ADDR = 0x30610000,
|
||||
FSL_IMX7_ADC2_ADDR = 0x30620000,
|
||||
FSL_IMX7_ADCn_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_GPC_ADDR = 0x303A0000,
|
||||
|
||||
FSL_IMX7_I2C1_ADDR = 0x30A20000,
|
||||
FSL_IMX7_I2C2_ADDR = 0x30A30000,
|
||||
FSL_IMX7_I2C3_ADDR = 0x30A40000,
|
||||
FSL_IMX7_I2C4_ADDR = 0x30A50000,
|
||||
|
||||
FSL_IMX7_ECSPI1_ADDR = 0x30820000,
|
||||
FSL_IMX7_ECSPI2_ADDR = 0x30830000,
|
||||
FSL_IMX7_ECSPI3_ADDR = 0x30840000,
|
||||
FSL_IMX7_ECSPI4_ADDR = 0x30630000,
|
||||
|
||||
FSL_IMX7_LCDIF_ADDR = 0x30730000,
|
||||
FSL_IMX7_LCDIF_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_UART1_ADDR = 0x30860000,
|
||||
/*
|
||||
* Some versions of the reference manual claim that UART2 is @
|
||||
* 0x30870000, but experiments with HW + DT files in upstream
|
||||
* Linux kernel show that not to be true and that block is
|
||||
* acutally located @ 0x30890000
|
||||
*/
|
||||
FSL_IMX7_UART2_ADDR = 0x30890000,
|
||||
FSL_IMX7_UART3_ADDR = 0x30880000,
|
||||
FSL_IMX7_UART4_ADDR = 0x30A60000,
|
||||
FSL_IMX7_UART5_ADDR = 0x30A70000,
|
||||
FSL_IMX7_UART6_ADDR = 0x30A80000,
|
||||
FSL_IMX7_UART7_ADDR = 0x30A90000,
|
||||
|
||||
FSL_IMX7_ENET1_ADDR = 0x30BE0000,
|
||||
FSL_IMX7_ENET2_ADDR = 0x30BF0000,
|
||||
|
||||
FSL_IMX7_USB1_ADDR = 0x30B10000,
|
||||
FSL_IMX7_USBMISC1_ADDR = 0x30B10200,
|
||||
FSL_IMX7_USB2_ADDR = 0x30B20000,
|
||||
FSL_IMX7_USBMISC2_ADDR = 0x30B20200,
|
||||
FSL_IMX7_USB3_ADDR = 0x30B30000,
|
||||
FSL_IMX7_USBMISC3_ADDR = 0x30B30200,
|
||||
FSL_IMX7_USBMISCn_SIZE = 0x200,
|
||||
|
||||
FSL_IMX7_USDHC1_ADDR = 0x30B40000,
|
||||
FSL_IMX7_USDHC2_ADDR = 0x30B50000,
|
||||
FSL_IMX7_USDHC3_ADDR = 0x30B60000,
|
||||
|
||||
FSL_IMX7_SDMA_ADDR = 0x30BD0000,
|
||||
FSL_IMX7_SDMA_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_A7MPCORE_ADDR = 0x31000000,
|
||||
FSL_IMX7_A7MPCORE_DAP_ADDR = 0x30000000,
|
||||
|
||||
FSL_IMX7_PCIE_REG_ADDR = 0x33800000,
|
||||
FSL_IMX7_PCIE_REG_SIZE = 16 * 1024,
|
||||
|
||||
FSL_IMX7_GPR_ADDR = 0x30340000,
|
||||
};
|
||||
|
||||
enum FslIMX7IRQs {
|
||||
FSL_IMX7_USDHC1_IRQ = 22,
|
||||
FSL_IMX7_USDHC2_IRQ = 23,
|
||||
FSL_IMX7_USDHC3_IRQ = 24,
|
||||
|
||||
FSL_IMX7_UART1_IRQ = 26,
|
||||
FSL_IMX7_UART2_IRQ = 27,
|
||||
FSL_IMX7_UART3_IRQ = 28,
|
||||
FSL_IMX7_UART4_IRQ = 29,
|
||||
FSL_IMX7_UART5_IRQ = 30,
|
||||
FSL_IMX7_UART6_IRQ = 16,
|
||||
|
||||
FSL_IMX7_ECSPI1_IRQ = 31,
|
||||
FSL_IMX7_ECSPI2_IRQ = 32,
|
||||
FSL_IMX7_ECSPI3_IRQ = 33,
|
||||
FSL_IMX7_ECSPI4_IRQ = 34,
|
||||
|
||||
FSL_IMX7_I2C1_IRQ = 35,
|
||||
FSL_IMX7_I2C2_IRQ = 36,
|
||||
FSL_IMX7_I2C3_IRQ = 37,
|
||||
FSL_IMX7_I2C4_IRQ = 38,
|
||||
|
||||
FSL_IMX7_USB1_IRQ = 43,
|
||||
FSL_IMX7_USB2_IRQ = 42,
|
||||
FSL_IMX7_USB3_IRQ = 40,
|
||||
|
||||
FSL_IMX7_PCI_INTA_IRQ = 122,
|
||||
FSL_IMX7_PCI_INTB_IRQ = 123,
|
||||
FSL_IMX7_PCI_INTC_IRQ = 124,
|
||||
FSL_IMX7_PCI_INTD_IRQ = 125,
|
||||
|
||||
FSL_IMX7_UART7_IRQ = 126,
|
||||
|
||||
#define FSL_IMX7_ENET_IRQ(i, n) ((n) + ((i) ? 100 : 118))
|
||||
|
||||
FSL_IMX7_MAX_IRQ = 128,
|
||||
};
|
||||
|
||||
#endif /* FSL_IMX7_H */
|
102
include/hw/pci-host/designware.h
Normal file
102
include/hw/pci-host/designware.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Impinj, Inc.
|
||||
*
|
||||
* Designware PCIe IP block emulation
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef DESIGNWARE_H
|
||||
#define DESIGNWARE_H
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/pci/pcie_host.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
|
||||
#define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host"
|
||||
#define DESIGNWARE_PCIE_HOST(obj) \
|
||||
OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST)
|
||||
|
||||
#define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root"
|
||||
#define DESIGNWARE_PCIE_ROOT(obj) \
|
||||
OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT)
|
||||
|
||||
struct DesignwarePCIERoot;
|
||||
typedef struct DesignwarePCIERoot DesignwarePCIERoot;
|
||||
|
||||
typedef struct DesignwarePCIEViewport {
|
||||
DesignwarePCIERoot *root;
|
||||
|
||||
MemoryRegion cfg;
|
||||
MemoryRegion mem;
|
||||
|
||||
uint64_t base;
|
||||
uint64_t target;
|
||||
uint32_t limit;
|
||||
uint32_t cr[2];
|
||||
|
||||
bool inbound;
|
||||
} DesignwarePCIEViewport;
|
||||
|
||||
typedef struct DesignwarePCIEMSIBank {
|
||||
uint32_t enable;
|
||||
uint32_t mask;
|
||||
uint32_t status;
|
||||
} DesignwarePCIEMSIBank;
|
||||
|
||||
typedef struct DesignwarePCIEMSI {
|
||||
uint64_t base;
|
||||
MemoryRegion iomem;
|
||||
|
||||
#define DESIGNWARE_PCIE_NUM_MSI_BANKS 1
|
||||
|
||||
DesignwarePCIEMSIBank intr[DESIGNWARE_PCIE_NUM_MSI_BANKS];
|
||||
} DesignwarePCIEMSI;
|
||||
|
||||
struct DesignwarePCIERoot {
|
||||
PCIBridge parent_obj;
|
||||
|
||||
uint32_t atu_viewport;
|
||||
|
||||
#define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND 0
|
||||
#define DESIGNWARE_PCIE_VIEWPORT_INBOUND 1
|
||||
#define DESIGNWARE_PCIE_NUM_VIEWPORTS 4
|
||||
|
||||
DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS];
|
||||
DesignwarePCIEMSI msi;
|
||||
};
|
||||
|
||||
typedef struct DesignwarePCIEHost {
|
||||
PCIHostState parent_obj;
|
||||
|
||||
DesignwarePCIERoot root;
|
||||
|
||||
struct {
|
||||
AddressSpace address_space;
|
||||
MemoryRegion address_space_root;
|
||||
|
||||
MemoryRegion memory;
|
||||
MemoryRegion io;
|
||||
|
||||
qemu_irq irqs[4];
|
||||
} pci;
|
||||
|
||||
MemoryRegion mmio;
|
||||
} DesignwarePCIEHost;
|
||||
|
||||
#endif /* DESIGNWARE_H */
|
@ -269,4 +269,6 @@
|
||||
#define PCI_VENDOR_ID_VMWARE 0x15ad
|
||||
#define PCI_DEVICE_ID_VMWARE_PVRDMA 0x0820
|
||||
|
||||
#define PCI_VENDOR_ID_SYNOPSYS 0x16C3
|
||||
|
||||
#endif
|
||||
|
@ -19,4 +19,7 @@ struct target_pt_regs {
|
||||
#define TARGET_MLOCKALL_MCL_CURRENT 1
|
||||
#define TARGET_MLOCKALL_MCL_FUTURE 2
|
||||
|
||||
#define TARGET_PR_SVE_SET_VL 50
|
||||
#define TARGET_PR_SVE_GET_VL 51
|
||||
|
||||
#endif /* AARCH64_TARGET_SYSCALL_H */
|
||||
|
@ -1446,35 +1446,65 @@ struct target_fpsimd_context {
|
||||
uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */
|
||||
};
|
||||
|
||||
/*
|
||||
* Auxiliary context saved in the sigcontext.__reserved array. Not exported to
|
||||
* user space as it will change with the addition of new context. User space
|
||||
* should check the magic/size information.
|
||||
*/
|
||||
struct target_aux_context {
|
||||
struct target_fpsimd_context fpsimd;
|
||||
/* additional context to be added before "end" */
|
||||
struct target_aarch64_ctx end;
|
||||
#define TARGET_EXTRA_MAGIC 0x45585401
|
||||
|
||||
struct target_extra_context {
|
||||
struct target_aarch64_ctx head;
|
||||
uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */
|
||||
uint32_t size; /* size in bytes of the extra space */
|
||||
uint32_t reserved[3];
|
||||
};
|
||||
|
||||
#define TARGET_SVE_MAGIC 0x53564501
|
||||
|
||||
struct target_sve_context {
|
||||
struct target_aarch64_ctx head;
|
||||
uint16_t vl;
|
||||
uint16_t reserved[3];
|
||||
/* The actual SVE data immediately follows. It is layed out
|
||||
* according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of
|
||||
* the original struct pointer.
|
||||
*/
|
||||
};
|
||||
|
||||
#define TARGET_SVE_VQ_BYTES 16
|
||||
|
||||
#define TARGET_SVE_SIG_ZREG_SIZE(VQ) ((VQ) * TARGET_SVE_VQ_BYTES)
|
||||
#define TARGET_SVE_SIG_PREG_SIZE(VQ) ((VQ) * (TARGET_SVE_VQ_BYTES / 8))
|
||||
|
||||
#define TARGET_SVE_SIG_REGS_OFFSET \
|
||||
QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES)
|
||||
#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \
|
||||
(TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N))
|
||||
#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \
|
||||
(TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N))
|
||||
#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \
|
||||
(TARGET_SVE_SIG_PREG_OFFSET(VQ, 16))
|
||||
#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \
|
||||
(TARGET_SVE_SIG_PREG_OFFSET(VQ, 17))
|
||||
|
||||
struct target_rt_sigframe {
|
||||
struct target_siginfo info;
|
||||
struct target_ucontext uc;
|
||||
};
|
||||
|
||||
struct target_rt_frame_record {
|
||||
uint64_t fp;
|
||||
uint64_t lr;
|
||||
uint32_t tramp[2];
|
||||
};
|
||||
|
||||
static int target_setup_sigframe(struct target_rt_sigframe *sf,
|
||||
CPUARMState *env, target_sigset_t *set)
|
||||
static void target_setup_general_frame(struct target_rt_sigframe *sf,
|
||||
CPUARMState *env, target_sigset_t *set)
|
||||
{
|
||||
int i;
|
||||
struct target_aux_context *aux =
|
||||
(struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
|
||||
|
||||
/* set up the stack frame for unwinding */
|
||||
__put_user(env->xregs[29], &sf->fp);
|
||||
__put_user(env->xregs[30], &sf->lr);
|
||||
__put_user(0, &sf->uc.tuc_flags);
|
||||
__put_user(0, &sf->uc.tuc_link);
|
||||
|
||||
__put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp);
|
||||
__put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags);
|
||||
__put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size);
|
||||
|
||||
for (i = 0; i < 31; i++) {
|
||||
__put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]);
|
||||
@ -1488,39 +1518,79 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf,
|
||||
for (i = 0; i < TARGET_NSIG_WORDS; i++) {
|
||||
__put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd,
|
||||
CPUARMState *env)
|
||||
{
|
||||
int i;
|
||||
|
||||
__put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic);
|
||||
__put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size);
|
||||
__put_user(vfp_get_fpsr(env), &fpsimd->fpsr);
|
||||
__put_user(vfp_get_fpcr(env), &fpsimd->fpcr);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
uint64_t *q = aa64_vfp_qreg(env, i);
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
__put_user(q[0], &aux->fpsimd.vregs[i * 2 + 1]);
|
||||
__put_user(q[1], &aux->fpsimd.vregs[i * 2]);
|
||||
__put_user(q[0], &fpsimd->vregs[i * 2 + 1]);
|
||||
__put_user(q[1], &fpsimd->vregs[i * 2]);
|
||||
#else
|
||||
__put_user(q[0], &aux->fpsimd.vregs[i * 2]);
|
||||
__put_user(q[1], &aux->fpsimd.vregs[i * 2 + 1]);
|
||||
__put_user(q[0], &fpsimd->vregs[i * 2]);
|
||||
__put_user(q[1], &fpsimd->vregs[i * 2 + 1]);
|
||||
#endif
|
||||
}
|
||||
__put_user(vfp_get_fpsr(env), &aux->fpsimd.fpsr);
|
||||
__put_user(vfp_get_fpcr(env), &aux->fpsimd.fpcr);
|
||||
__put_user(TARGET_FPSIMD_MAGIC, &aux->fpsimd.head.magic);
|
||||
__put_user(sizeof(struct target_fpsimd_context),
|
||||
&aux->fpsimd.head.size);
|
||||
|
||||
/* set the "end" magic */
|
||||
__put_user(0, &aux->end.magic);
|
||||
__put_user(0, &aux->end.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int target_restore_sigframe(CPUARMState *env,
|
||||
struct target_rt_sigframe *sf)
|
||||
static void target_setup_extra_record(struct target_extra_context *extra,
|
||||
uint64_t datap, uint32_t extra_size)
|
||||
{
|
||||
__put_user(TARGET_EXTRA_MAGIC, &extra->head.magic);
|
||||
__put_user(sizeof(struct target_extra_context), &extra->head.size);
|
||||
__put_user(datap, &extra->datap);
|
||||
__put_user(extra_size, &extra->size);
|
||||
}
|
||||
|
||||
static void target_setup_end_record(struct target_aarch64_ctx *end)
|
||||
{
|
||||
__put_user(0, &end->magic);
|
||||
__put_user(0, &end->size);
|
||||
}
|
||||
|
||||
static void target_setup_sve_record(struct target_sve_context *sve,
|
||||
CPUARMState *env, int vq, int size)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
__put_user(TARGET_SVE_MAGIC, &sve->head.magic);
|
||||
__put_user(size, &sve->head.size);
|
||||
__put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl);
|
||||
|
||||
/* Note that SVE regs are stored as a byte stream, with each byte element
|
||||
* at a subsequent address. This corresponds to a little-endian store
|
||||
* of our 64-bit hunks.
|
||||
*/
|
||||
for (i = 0; i < 32; ++i) {
|
||||
uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
|
||||
for (j = 0; j < vq * 2; ++j) {
|
||||
__put_user_e(env->vfp.zregs[i].d[j], z + j, le);
|
||||
}
|
||||
}
|
||||
for (i = 0; i <= 16; ++i) {
|
||||
uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
|
||||
for (j = 0; j < vq; ++j) {
|
||||
uint64_t r = env->vfp.pregs[i].p[j >> 2];
|
||||
__put_user_e(r >> ((j & 3) * 16), p + j, le);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void target_restore_general_frame(CPUARMState *env,
|
||||
struct target_rt_sigframe *sf)
|
||||
{
|
||||
sigset_t set;
|
||||
int i;
|
||||
struct target_aux_context *aux =
|
||||
(struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
|
||||
uint32_t magic, size, fpsr, fpcr;
|
||||
uint64_t pstate;
|
||||
int i;
|
||||
|
||||
target_to_host_sigset(&set, &sf->uc.tuc_sigmask);
|
||||
set_sigmask(&set);
|
||||
@ -1533,34 +1603,154 @@ static int target_restore_sigframe(CPUARMState *env,
|
||||
__get_user(env->pc, &sf->uc.tuc_mcontext.pc);
|
||||
__get_user(pstate, &sf->uc.tuc_mcontext.pstate);
|
||||
pstate_write(env, pstate);
|
||||
}
|
||||
|
||||
__get_user(magic, &aux->fpsimd.head.magic);
|
||||
__get_user(size, &aux->fpsimd.head.size);
|
||||
static void target_restore_fpsimd_record(CPUARMState *env,
|
||||
struct target_fpsimd_context *fpsimd)
|
||||
{
|
||||
uint32_t fpsr, fpcr;
|
||||
int i;
|
||||
|
||||
if (magic != TARGET_FPSIMD_MAGIC
|
||||
|| size != sizeof(struct target_fpsimd_context)) {
|
||||
return 1;
|
||||
}
|
||||
__get_user(fpsr, &fpsimd->fpsr);
|
||||
vfp_set_fpsr(env, fpsr);
|
||||
__get_user(fpcr, &fpsimd->fpcr);
|
||||
vfp_set_fpcr(env, fpcr);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
uint64_t *q = aa64_vfp_qreg(env, i);
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
__get_user(q[0], &aux->fpsimd.vregs[i * 2 + 1]);
|
||||
__get_user(q[1], &aux->fpsimd.vregs[i * 2]);
|
||||
__get_user(q[0], &fpsimd->vregs[i * 2 + 1]);
|
||||
__get_user(q[1], &fpsimd->vregs[i * 2]);
|
||||
#else
|
||||
__get_user(q[0], &aux->fpsimd.vregs[i * 2]);
|
||||
__get_user(q[1], &aux->fpsimd.vregs[i * 2 + 1]);
|
||||
__get_user(q[0], &fpsimd->vregs[i * 2]);
|
||||
__get_user(q[1], &fpsimd->vregs[i * 2 + 1]);
|
||||
#endif
|
||||
}
|
||||
__get_user(fpsr, &aux->fpsimd.fpsr);
|
||||
vfp_set_fpsr(env, fpsr);
|
||||
__get_user(fpcr, &aux->fpsimd.fpcr);
|
||||
vfp_set_fpcr(env, fpcr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *env)
|
||||
static void target_restore_sve_record(CPUARMState *env,
|
||||
struct target_sve_context *sve, int vq)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* Note that SVE regs are stored as a byte stream, with each byte element
|
||||
* at a subsequent address. This corresponds to a little-endian load
|
||||
* of our 64-bit hunks.
|
||||
*/
|
||||
for (i = 0; i < 32; ++i) {
|
||||
uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
|
||||
for (j = 0; j < vq * 2; ++j) {
|
||||
__get_user_e(env->vfp.zregs[i].d[j], z + j, le);
|
||||
}
|
||||
}
|
||||
for (i = 0; i <= 16; ++i) {
|
||||
uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
|
||||
for (j = 0; j < vq; ++j) {
|
||||
uint16_t r;
|
||||
__get_user_e(r, p + j, le);
|
||||
if (j & 3) {
|
||||
env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16);
|
||||
} else {
|
||||
env->vfp.pregs[i].p[j >> 2] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int target_restore_sigframe(CPUARMState *env,
|
||||
struct target_rt_sigframe *sf)
|
||||
{
|
||||
struct target_aarch64_ctx *ctx, *extra = NULL;
|
||||
struct target_fpsimd_context *fpsimd = NULL;
|
||||
struct target_sve_context *sve = NULL;
|
||||
uint64_t extra_datap = 0;
|
||||
bool used_extra = false;
|
||||
bool err = false;
|
||||
int vq = 0, sve_size = 0;
|
||||
|
||||
target_restore_general_frame(env, sf);
|
||||
|
||||
ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved;
|
||||
while (ctx) {
|
||||
uint32_t magic, size, extra_size;
|
||||
|
||||
__get_user(magic, &ctx->magic);
|
||||
__get_user(size, &ctx->size);
|
||||
switch (magic) {
|
||||
case 0:
|
||||
if (size != 0) {
|
||||
err = true;
|
||||
goto exit;
|
||||
}
|
||||
if (used_extra) {
|
||||
ctx = NULL;
|
||||
} else {
|
||||
ctx = extra;
|
||||
used_extra = true;
|
||||
}
|
||||
continue;
|
||||
|
||||
case TARGET_FPSIMD_MAGIC:
|
||||
if (fpsimd || size != sizeof(struct target_fpsimd_context)) {
|
||||
err = true;
|
||||
goto exit;
|
||||
}
|
||||
fpsimd = (struct target_fpsimd_context *)ctx;
|
||||
break;
|
||||
|
||||
case TARGET_SVE_MAGIC:
|
||||
if (arm_feature(env, ARM_FEATURE_SVE)) {
|
||||
vq = (env->vfp.zcr_el[1] & 0xf) + 1;
|
||||
sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
|
||||
if (!sve && size == sve_size) {
|
||||
sve = (struct target_sve_context *)ctx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
err = true;
|
||||
goto exit;
|
||||
|
||||
case TARGET_EXTRA_MAGIC:
|
||||
if (extra || size != sizeof(struct target_extra_context)) {
|
||||
err = true;
|
||||
goto exit;
|
||||
}
|
||||
__get_user(extra_datap,
|
||||
&((struct target_extra_context *)ctx)->datap);
|
||||
__get_user(extra_size,
|
||||
&((struct target_extra_context *)ctx)->size);
|
||||
extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unknown record -- we certainly didn't generate it.
|
||||
* Did we in fact get out of sync?
|
||||
*/
|
||||
err = true;
|
||||
goto exit;
|
||||
}
|
||||
ctx = (void *)ctx + size;
|
||||
}
|
||||
|
||||
/* Require FPSIMD always. */
|
||||
if (fpsimd) {
|
||||
target_restore_fpsimd_record(env, fpsimd);
|
||||
} else {
|
||||
err = true;
|
||||
}
|
||||
|
||||
/* SVE data, if present, overwrites FPSIMD data. */
|
||||
if (sve) {
|
||||
target_restore_sve_record(env, sve, vq);
|
||||
}
|
||||
|
||||
exit:
|
||||
unlock_user(extra, extra_datap, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
static abi_ulong get_sigframe(struct target_sigaction *ka,
|
||||
CPUARMState *env, int size)
|
||||
{
|
||||
abi_ulong sp;
|
||||
|
||||
@ -1573,34 +1763,120 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *env)
|
||||
sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
|
||||
}
|
||||
|
||||
sp = (sp - sizeof(struct target_rt_sigframe)) & ~15;
|
||||
sp = (sp - size) & ~15;
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int total_size;
|
||||
int extra_base;
|
||||
int extra_size;
|
||||
int std_end_ofs;
|
||||
int extra_ofs;
|
||||
int extra_end_ofs;
|
||||
} target_sigframe_layout;
|
||||
|
||||
static int alloc_sigframe_space(int this_size, target_sigframe_layout *l)
|
||||
{
|
||||
/* Make sure there will always be space for the end marker. */
|
||||
const int std_size = sizeof(struct target_rt_sigframe)
|
||||
- sizeof(struct target_aarch64_ctx);
|
||||
int this_loc = l->total_size;
|
||||
|
||||
if (l->extra_base) {
|
||||
/* Once we have begun an extra space, all allocations go there. */
|
||||
l->extra_size += this_size;
|
||||
} else if (this_size + this_loc > std_size) {
|
||||
/* This allocation does not fit in the standard space. */
|
||||
/* Allocate the extra record. */
|
||||
l->extra_ofs = this_loc;
|
||||
l->total_size += sizeof(struct target_extra_context);
|
||||
|
||||
/* Allocate the standard end record. */
|
||||
l->std_end_ofs = l->total_size;
|
||||
l->total_size += sizeof(struct target_aarch64_ctx);
|
||||
|
||||
/* Allocate the requested record. */
|
||||
l->extra_base = this_loc = l->total_size;
|
||||
l->extra_size = this_size;
|
||||
}
|
||||
l->total_size += this_size;
|
||||
|
||||
return this_loc;
|
||||
}
|
||||
|
||||
static void target_setup_frame(int usig, struct target_sigaction *ka,
|
||||
target_siginfo_t *info, target_sigset_t *set,
|
||||
CPUARMState *env)
|
||||
{
|
||||
target_sigframe_layout layout = {
|
||||
/* Begin with the size pointing to the reserved space. */
|
||||
.total_size = offsetof(struct target_rt_sigframe,
|
||||
uc.tuc_mcontext.__reserved),
|
||||
};
|
||||
int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0;
|
||||
struct target_rt_sigframe *frame;
|
||||
struct target_rt_frame_record *fr;
|
||||
abi_ulong frame_addr, return_addr;
|
||||
|
||||
frame_addr = get_sigframe(ka, env);
|
||||
/* FPSIMD record is always in the standard space. */
|
||||
fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context),
|
||||
&layout);
|
||||
|
||||
/* SVE state needs saving only if it exists. */
|
||||
if (arm_feature(env, ARM_FEATURE_SVE)) {
|
||||
vq = (env->vfp.zcr_el[1] & 0xf) + 1;
|
||||
sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
|
||||
sve_ofs = alloc_sigframe_space(sve_size, &layout);
|
||||
}
|
||||
|
||||
if (layout.extra_ofs) {
|
||||
/* Reserve space for the extra end marker. The standard end marker
|
||||
* will have been allocated when we allocated the extra record.
|
||||
*/
|
||||
layout.extra_end_ofs
|
||||
= alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout);
|
||||
} else {
|
||||
/* Reserve space for the standard end marker.
|
||||
* Do not use alloc_sigframe_space because we cheat
|
||||
* std_size therein to reserve space for this.
|
||||
*/
|
||||
layout.std_end_ofs = layout.total_size;
|
||||
layout.total_size += sizeof(struct target_aarch64_ctx);
|
||||
}
|
||||
|
||||
/* Reserve space for the return code. On a real system this would
|
||||
* be within the VDSO. So, despite the name this is not a "real"
|
||||
* record within the frame.
|
||||
*/
|
||||
fr_ofs = layout.total_size;
|
||||
layout.total_size += sizeof(struct target_rt_frame_record);
|
||||
|
||||
frame_addr = get_sigframe(ka, env, layout.total_size);
|
||||
trace_user_setup_frame(env, frame_addr);
|
||||
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
|
||||
goto give_sigsegv;
|
||||
}
|
||||
|
||||
__put_user(0, &frame->uc.tuc_flags);
|
||||
__put_user(0, &frame->uc.tuc_link);
|
||||
target_setup_general_frame(frame, env, set);
|
||||
target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env);
|
||||
target_setup_end_record((void *)frame + layout.std_end_ofs);
|
||||
if (layout.extra_ofs) {
|
||||
target_setup_extra_record((void *)frame + layout.extra_ofs,
|
||||
frame_addr + layout.extra_base,
|
||||
layout.extra_size);
|
||||
target_setup_end_record((void *)frame + layout.extra_end_ofs);
|
||||
}
|
||||
if (sve_ofs) {
|
||||
target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size);
|
||||
}
|
||||
|
||||
/* Set up the stack frame for unwinding. */
|
||||
fr = (void *)frame + fr_ofs;
|
||||
__put_user(env->xregs[29], &fr->fp);
|
||||
__put_user(env->xregs[30], &fr->lr);
|
||||
|
||||
__put_user(target_sigaltstack_used.ss_sp,
|
||||
&frame->uc.tuc_stack.ss_sp);
|
||||
__put_user(sas_ss_flags(env->xregs[31]),
|
||||
&frame->uc.tuc_stack.ss_flags);
|
||||
__put_user(target_sigaltstack_used.ss_size,
|
||||
&frame->uc.tuc_stack.ss_size);
|
||||
target_setup_sigframe(frame, env, set);
|
||||
if (ka->sa_flags & TARGET_SA_RESTORER) {
|
||||
return_addr = ka->sa_restorer;
|
||||
} else {
|
||||
@ -1609,13 +1885,14 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
|
||||
* Since these are instructions they need to be put as little-endian
|
||||
* regardless of target default or current CPU endianness.
|
||||
*/
|
||||
__put_user_e(0xd2801168, &frame->tramp[0], le);
|
||||
__put_user_e(0xd4000001, &frame->tramp[1], le);
|
||||
return_addr = frame_addr + offsetof(struct target_rt_sigframe, tramp);
|
||||
__put_user_e(0xd2801168, &fr->tramp[0], le);
|
||||
__put_user_e(0xd4000001, &fr->tramp[1], le);
|
||||
return_addr = frame_addr + fr_ofs
|
||||
+ offsetof(struct target_rt_frame_record, tramp);
|
||||
}
|
||||
env->xregs[0] = usig;
|
||||
env->xregs[31] = frame_addr;
|
||||
env->xregs[29] = env->xregs[31] + offsetof(struct target_rt_sigframe, fp);
|
||||
env->xregs[29] = frame_addr + fr_ofs;
|
||||
env->pc = ka->_sa_handler;
|
||||
env->xregs[30] = return_addr;
|
||||
if (info) {
|
||||
|
@ -10672,6 +10672,33 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef TARGET_AARCH64
|
||||
case TARGET_PR_SVE_SET_VL:
|
||||
/* We cannot support either PR_SVE_SET_VL_ONEXEC
|
||||
or PR_SVE_VL_INHERIT. Therefore, anything above
|
||||
ARM_MAX_VQ results in EINVAL. */
|
||||
ret = -TARGET_EINVAL;
|
||||
if (arm_feature(cpu_env, ARM_FEATURE_SVE)
|
||||
&& arg2 >= 0 && arg2 <= ARM_MAX_VQ * 16 && !(arg2 & 15)) {
|
||||
CPUARMState *env = cpu_env;
|
||||
int old_vq = (env->vfp.zcr_el[1] & 0xf) + 1;
|
||||
int vq = MAX(arg2 / 16, 1);
|
||||
|
||||
if (vq < old_vq) {
|
||||
aarch64_sve_narrow_vq(env, vq);
|
||||
}
|
||||
env->vfp.zcr_el[1] = vq - 1;
|
||||
ret = vq * 16;
|
||||
}
|
||||
break;
|
||||
case TARGET_PR_SVE_GET_VL:
|
||||
ret = -TARGET_EINVAL;
|
||||
if (arm_feature(cpu_env, ARM_FEATURE_SVE)) {
|
||||
CPUARMState *env = cpu_env;
|
||||
ret = ((env->vfp.zcr_el[1] & 0xf) + 1) * 16;
|
||||
}
|
||||
break;
|
||||
#endif /* AARCH64 */
|
||||
case PR_GET_SECCOMP:
|
||||
case PR_SET_SECCOMP:
|
||||
/* Disable seccomp to prevent the target disabling syscalls we
|
||||
|
@ -33,6 +33,8 @@ struct arm_boot_info;
|
||||
#define ARM_CPU_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(ARMCPUClass, (obj), TYPE_ARM_CPU)
|
||||
|
||||
#define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU
|
||||
|
||||
/**
|
||||
* ARMCPUClass:
|
||||
* @parent_realize: The parent class' realize handler.
|
||||
|
103
target/arm/cpu.c
103
target/arm/cpu.c
@ -725,6 +725,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
int pagebits;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* If we needed to query the host kernel for the CPU features
|
||||
* then it's possible that might have failed in the initfn, but
|
||||
* this is the first point where we can report it.
|
||||
*/
|
||||
if (cpu->host_cpu_probe_failed) {
|
||||
if (!kvm_enabled()) {
|
||||
error_setg(errp, "The 'host' CPU type can only be used with KVM");
|
||||
} else {
|
||||
error_setg(errp, "Failed to retrieve host CPU features");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_exec_realizefn(cs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -939,6 +952,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
cs->num_ases = 1;
|
||||
}
|
||||
cpu_address_space_init(cs, ARMASIdx_NS, "cpu-memory", cs->memory);
|
||||
|
||||
/* No core_count specified, default to smp_cpus. */
|
||||
if (cpu->core_count == -1) {
|
||||
cpu->core_count = smp_cpus;
|
||||
}
|
||||
#endif
|
||||
|
||||
qemu_init_vcpu(cs);
|
||||
@ -952,9 +970,19 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model)
|
||||
ObjectClass *oc;
|
||||
char *typename;
|
||||
char **cpuname;
|
||||
const char *cpunamestr;
|
||||
|
||||
cpuname = g_strsplit(cpu_model, ",", 1);
|
||||
typename = g_strdup_printf(ARM_CPU_TYPE_NAME("%s"), cpuname[0]);
|
||||
cpunamestr = cpuname[0];
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* For backwards compatibility usermode emulation allows "-cpu any",
|
||||
* which has the same semantics as "-cpu max".
|
||||
*/
|
||||
if (!strcmp(cpunamestr, "any")) {
|
||||
cpunamestr = "max";
|
||||
}
|
||||
#endif
|
||||
typename = g_strdup_printf(ARM_CPU_TYPE_NAME("%s"), cpunamestr);
|
||||
oc = object_class_by_name(typename);
|
||||
g_strfreev(cpuname);
|
||||
g_free(typename);
|
||||
@ -1684,22 +1712,37 @@ static void pxa270c5_initfn(Object *obj)
|
||||
cpu->reset_sctlr = 0x00000078;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static void arm_any_initfn(Object *obj)
|
||||
#ifndef TARGET_AARCH64
|
||||
/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
|
||||
* otherwise, a CPU with as many features enabled as our emulation supports.
|
||||
* The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c;
|
||||
* this only needs to handle 32 bits.
|
||||
*/
|
||||
static void arm_max_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_VFP4);
|
||||
set_feature(&cpu->env, ARM_FEATURE_NEON);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_AES);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
|
||||
set_feature(&cpu->env, ARM_FEATURE_CRC);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
|
||||
cpu->midr = 0xffffffff;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
kvm_arm_set_cpu_features_from_host(cpu);
|
||||
} else {
|
||||
cortex_a15_initfn(obj);
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* We don't set these in system emulation mode for the moment,
|
||||
* since we don't correctly set the ID registers to advertise them,
|
||||
*/
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_VFP4);
|
||||
set_feature(&cpu->env, ARM_FEATURE_NEON);
|
||||
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_AES);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
|
||||
set_feature(&cpu->env, ARM_FEATURE_CRC);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1751,8 +1794,11 @@ static const ARMCPUInfo arm_cpus[] = {
|
||||
{ .name = "pxa270-b1", .initfn = pxa270b1_initfn },
|
||||
{ .name = "pxa270-c0", .initfn = pxa270c0_initfn },
|
||||
{ .name = "pxa270-c5", .initfn = pxa270c5_initfn },
|
||||
#ifndef TARGET_AARCH64
|
||||
{ .name = "max", .initfn = arm_max_initfn },
|
||||
#endif
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
{ .name = "any", .initfn = arm_any_initfn },
|
||||
{ .name = "any", .initfn = arm_max_initfn },
|
||||
#endif
|
||||
#endif
|
||||
{ .name = NULL }
|
||||
@ -1765,6 +1811,7 @@ static Property arm_cpu_properties[] = {
|
||||
DEFINE_PROP_UINT64("mp-affinity", ARMCPU,
|
||||
mp_affinity, ARM64_AFFINITY_INVALID),
|
||||
DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID),
|
||||
DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
@ -1845,6 +1892,26 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
static void arm_host_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
kvm_arm_set_cpu_features_from_host(cpu);
|
||||
}
|
||||
|
||||
static const TypeInfo host_arm_cpu_type_info = {
|
||||
.name = TYPE_ARM_HOST_CPU,
|
||||
#ifdef TARGET_AARCH64
|
||||
.parent = TYPE_AARCH64_CPU,
|
||||
#else
|
||||
.parent = TYPE_ARM_CPU,
|
||||
#endif
|
||||
.instance_init = arm_host_initfn,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static void cpu_register(const ARMCPUInfo *info)
|
||||
{
|
||||
TypeInfo type_info = {
|
||||
@ -1889,6 +1956,10 @@ static void arm_cpu_register_types(void)
|
||||
cpu_register(info);
|
||||
info++;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
type_register_static(&host_arm_cpu_type_info);
|
||||
#endif
|
||||
}
|
||||
|
||||
type_init(arm_cpu_register_types)
|
||||
|
@ -745,6 +745,16 @@ struct ARMCPU {
|
||||
/* Uniprocessor system with MP extensions */
|
||||
bool mp_is_up;
|
||||
|
||||
/* True if we tried kvm_arm_host_cpu_features() during CPU instance_init
|
||||
* and the probe failed (so we need to report the error in realize)
|
||||
*/
|
||||
bool host_cpu_probe_failed;
|
||||
|
||||
/* Specify the number of cores in this CPU cluster. Used for the L2CTLR
|
||||
* register.
|
||||
*/
|
||||
int32_t core_count;
|
||||
|
||||
/* The instance init functions for implementation-specific subclasses
|
||||
* set these fields to specify the implementation-dependent values of
|
||||
* various constant registers and reset values of non-constant
|
||||
@ -861,6 +871,7 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
|
||||
#ifdef TARGET_AARCH64
|
||||
int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||
int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||
void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq);
|
||||
#endif
|
||||
|
||||
target_ulong do_arm_semihosting(CPUARMState *env);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "hw/arm/arm.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_arm.h"
|
||||
|
||||
static inline void set_feature(CPUARMState *env, int feature)
|
||||
{
|
||||
@ -42,8 +43,10 @@ static inline void unset_feature(CPUARMState *env, int feature)
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static uint64_t a57_a53_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
/* Number of processors is in [25:24]; otherwise we RAZ */
|
||||
return (smp_cpus - 1) << 24;
|
||||
ARMCPU *cpu = arm_env_get_cpu(env);
|
||||
|
||||
/* Number of cores is in [25:24]; otherwise we RAZ */
|
||||
return (cpu->core_count - 1) << 24;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -212,31 +215,50 @@ static void aarch64_a53_initfn(Object *obj)
|
||||
define_arm_cp_regs(cpu, cortex_a57_a53_cp_reginfo);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static void aarch64_any_initfn(Object *obj)
|
||||
/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
|
||||
* otherwise, a CPU with as many features enabled as our emulation supports.
|
||||
* The version of '-cpu max' for qemu-system-arm is defined in cpu.c;
|
||||
* this only needs to handle 64 bits.
|
||||
*/
|
||||
static void aarch64_max_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_VFP4);
|
||||
set_feature(&cpu->env, ARM_FEATURE_NEON);
|
||||
set_feature(&cpu->env, ARM_FEATURE_AARCH64);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_AES);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA512);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA3);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SM3);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SM4);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
|
||||
set_feature(&cpu->env, ARM_FEATURE_CRC);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_FP16);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
|
||||
cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
|
||||
cpu->dcz_blocksize = 7; /* 512 bytes */
|
||||
}
|
||||
if (kvm_enabled()) {
|
||||
kvm_arm_set_cpu_features_from_host(cpu);
|
||||
} else {
|
||||
aarch64_a57_initfn(obj);
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* We don't set these in system emulation mode for the moment,
|
||||
* since we don't correctly set the ID registers to advertise them,
|
||||
* and in some cases they're only available in AArch64 and not AArch32,
|
||||
* whereas the architecture requires them to be present in both if
|
||||
* present in either.
|
||||
*/
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_VFP4);
|
||||
set_feature(&cpu->env, ARM_FEATURE_NEON);
|
||||
set_feature(&cpu->env, ARM_FEATURE_AARCH64);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_AES);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA512);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SHA3);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SM3);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_SM4);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
|
||||
set_feature(&cpu->env, ARM_FEATURE_CRC);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_RDM);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_FP16);
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8_FCMA);
|
||||
/* For usermode -cpu max we can use a larger and more efficient DCZ
|
||||
* blocksize since we don't have to follow what the hardware does.
|
||||
*/
|
||||
cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
|
||||
cpu->dcz_blocksize = 7; /* 512 bytes */
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct ARMCPUInfo {
|
||||
const char *name;
|
||||
@ -247,9 +269,7 @@ typedef struct ARMCPUInfo {
|
||||
static const ARMCPUInfo aarch64_cpus[] = {
|
||||
{ .name = "cortex-a57", .initfn = aarch64_a57_initfn },
|
||||
{ .name = "cortex-a53", .initfn = aarch64_a53_initfn },
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
{ .name = "any", .initfn = aarch64_any_initfn },
|
||||
#endif
|
||||
{ .name = "max", .initfn = aarch64_max_initfn },
|
||||
{ .name = NULL }
|
||||
};
|
||||
|
||||
@ -366,3 +386,44 @@ static void aarch64_cpu_register_types(void)
|
||||
}
|
||||
|
||||
type_init(aarch64_cpu_register_types)
|
||||
|
||||
/* The manual says that when SVE is enabled and VQ is widened the
|
||||
* implementation is allowed to zero the previously inaccessible
|
||||
* portion of the registers. The corollary to that is that when
|
||||
* SVE is enabled and VQ is narrowed we are also allowed to zero
|
||||
* the now inaccessible portion of the registers.
|
||||
*
|
||||
* The intent of this is that no predicate bit beyond VQ is ever set.
|
||||
* Which means that some operations on predicate registers themselves
|
||||
* may operate on full uint64_t or even unrolled across the maximum
|
||||
* uint64_t[4]. Performing 4 bits of host arithmetic unconditionally
|
||||
* may well be cheaper than conditionals to restrict the operation
|
||||
* to the relevant portion of a uint16_t[16].
|
||||
*
|
||||
* TODO: Need to call this for changes to the real system registers
|
||||
* and EL state changes.
|
||||
*/
|
||||
void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq)
|
||||
{
|
||||
int i, j;
|
||||
uint64_t pmask;
|
||||
|
||||
assert(vq >= 1 && vq <= ARM_MAX_VQ);
|
||||
|
||||
/* Zap the high bits of the zregs. */
|
||||
for (i = 0; i < 32; i++) {
|
||||
memset(&env->vfp.zregs[i].d[2 * vq], 0, 16 * (ARM_MAX_VQ - vq));
|
||||
}
|
||||
|
||||
/* Zap the high bits of the pregs and ffr. */
|
||||
pmask = 0;
|
||||
if (vq & 3) {
|
||||
pmask = ~(-1ULL << (16 * (vq & 3)));
|
||||
}
|
||||
for (j = vq / 4; j < ARM_MAX_VQ / 4; j++) {
|
||||
for (i = 0; i < 17; ++i) {
|
||||
env->vfp.pregs[i].p[j] &= pmask;
|
||||
}
|
||||
pmask = 0;
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
||||
|
||||
static bool cap_has_mp_state;
|
||||
|
||||
static ARMHostCPUFeatures arm_host_cpu_features;
|
||||
|
||||
int kvm_arm_vcpu_init(CPUState *cs)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
@ -129,43 +131,26 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray)
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data)
|
||||
void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
|
||||
{
|
||||
ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc);
|
||||
|
||||
/* All we really need to set up for the 'host' CPU
|
||||
* is the feature bits -- we rely on the fact that the
|
||||
* various ID register values in ARMCPU are only used for
|
||||
* TCG CPUs.
|
||||
*/
|
||||
if (!kvm_arm_get_host_cpu_features(ahcc)) {
|
||||
fprintf(stderr, "Failed to retrieve host CPU features!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_arm_host_cpu_initfn(Object *obj)
|
||||
{
|
||||
ARMHostCPUClass *ahcc = ARM_HOST_CPU_GET_CLASS(obj);
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
cpu->kvm_target = ahcc->target;
|
||||
cpu->dtb_compatible = ahcc->dtb_compatible;
|
||||
env->features = ahcc->features;
|
||||
}
|
||||
if (!arm_host_cpu_features.dtb_compatible) {
|
||||
if (!kvm_enabled() ||
|
||||
!kvm_arm_get_host_cpu_features(&arm_host_cpu_features)) {
|
||||
/* We can't report this error yet, so flag that we need to
|
||||
* in arm_cpu_realizefn().
|
||||
*/
|
||||
cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
|
||||
cpu->host_cpu_probe_failed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const TypeInfo host_arm_cpu_type_info = {
|
||||
.name = TYPE_ARM_HOST_CPU,
|
||||
#ifdef TARGET_AARCH64
|
||||
.parent = TYPE_AARCH64_CPU,
|
||||
#else
|
||||
.parent = TYPE_ARM_CPU,
|
||||
#endif
|
||||
.instance_init = kvm_arm_host_cpu_initfn,
|
||||
.class_init = kvm_arm_host_cpu_class_init,
|
||||
.class_size = sizeof(ARMHostCPUClass),
|
||||
};
|
||||
cpu->kvm_target = arm_host_cpu_features.target;
|
||||
cpu->dtb_compatible = arm_host_cpu_features.dtb_compatible;
|
||||
env->features = arm_host_cpu_features.features;
|
||||
}
|
||||
|
||||
int kvm_arch_init(MachineState *ms, KVMState *s)
|
||||
{
|
||||
@ -182,8 +167,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
|
||||
|
||||
cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
|
||||
|
||||
type_register_static(&host_arm_cpu_type_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ static inline void set_feature(uint64_t *features, int feature)
|
||||
*features |= 1ULL << feature;
|
||||
}
|
||||
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
|
||||
{
|
||||
/* Identify the feature bits corresponding to the host CPU, and
|
||||
* fill out the ARMHostCPUClass fields accordingly. To do this
|
||||
@ -74,13 +74,13 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
|
||||
return false;
|
||||
}
|
||||
|
||||
ahcc->target = init.target;
|
||||
ahcf->target = init.target;
|
||||
|
||||
/* This is not strictly blessed by the device tree binding docs yet,
|
||||
* but in practice the kernel does not care about this string so
|
||||
* there is no point maintaining an KVM_ARM_TARGET_* -> string table.
|
||||
*/
|
||||
ahcc->dtb_compatible = "arm,arm-v7";
|
||||
ahcf->dtb_compatible = "arm,arm-v7";
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(idregs); i++) {
|
||||
ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
|
||||
@ -132,7 +132,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
|
||||
set_feature(&features, ARM_FEATURE_VFP4);
|
||||
}
|
||||
|
||||
ahcc->features = features;
|
||||
ahcf->features = features;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -443,7 +443,7 @@ static inline void unset_feature(uint64_t *features, int feature)
|
||||
*features &= ~(1ULL << feature);
|
||||
}
|
||||
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
|
||||
{
|
||||
/* Identify the feature bits corresponding to the host CPU, and
|
||||
* fill out the ARMHostCPUClass fields accordingly. To do this
|
||||
@ -471,8 +471,8 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
|
||||
return false;
|
||||
}
|
||||
|
||||
ahcc->target = init.target;
|
||||
ahcc->dtb_compatible = "arm,arm-v8";
|
||||
ahcf->target = init.target;
|
||||
ahcf->dtb_compatible = "arm,arm-v8";
|
||||
|
||||
kvm_arm_destroy_scratch_host_vcpu(fdarray);
|
||||
|
||||
@ -486,7 +486,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
|
||||
set_feature(&features, ARM_FEATURE_AARCH64);
|
||||
set_feature(&features, ARM_FEATURE_PMU);
|
||||
|
||||
ahcc->features = features;
|
||||
ahcf->features = features;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -152,20 +152,16 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try,
|
||||
void kvm_arm_destroy_scratch_host_vcpu(int *fdarray);
|
||||
|
||||
#define TYPE_ARM_HOST_CPU "host-" TYPE_ARM_CPU
|
||||
#define ARM_HOST_CPU_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(ARMHostCPUClass, (klass), TYPE_ARM_HOST_CPU)
|
||||
#define ARM_HOST_CPU_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(ARMHostCPUClass, (obj), TYPE_ARM_HOST_CPU)
|
||||
|
||||
typedef struct ARMHostCPUClass {
|
||||
/*< private >*/
|
||||
ARMCPUClass parent_class;
|
||||
/*< public >*/
|
||||
|
||||
/**
|
||||
* ARMHostCPUFeatures: information about the host CPU (identified
|
||||
* by asking the host kernel)
|
||||
*/
|
||||
typedef struct ARMHostCPUFeatures {
|
||||
uint64_t features;
|
||||
uint32_t target;
|
||||
const char *dtb_compatible;
|
||||
} ARMHostCPUClass;
|
||||
} ARMHostCPUFeatures;
|
||||
|
||||
/**
|
||||
* kvm_arm_get_host_cpu_features:
|
||||
@ -174,8 +170,16 @@ typedef struct ARMHostCPUClass {
|
||||
* Probe the capabilities of the host kernel's preferred CPU and fill
|
||||
* in the ARMHostCPUClass struct accordingly.
|
||||
*/
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc);
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf);
|
||||
|
||||
/**
|
||||
* kvm_arm_set_cpu_features_from_host:
|
||||
* @cpu: ARMCPU to set the features for
|
||||
*
|
||||
* Set up the ARMCPU struct fields up to match the information probed
|
||||
* from the host CPU.
|
||||
*/
|
||||
void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu);
|
||||
|
||||
/**
|
||||
* kvm_arm_sync_mpstate_to_kvm
|
||||
@ -200,6 +204,15 @@ void kvm_arm_pmu_init(CPUState *cs);
|
||||
|
||||
#else
|
||||
|
||||
static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
|
||||
{
|
||||
/* This should never actually be called in the "not KVM" case,
|
||||
* but set up the fields to indicate an error anyway.
|
||||
*/
|
||||
cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
|
||||
cpu->host_cpu_probe_failed = true;
|
||||
}
|
||||
|
||||
static inline int kvm_arm_vgic_probe(void)
|
||||
{
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user