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:
Peter Maydell 2018-03-12 11:47:52 +00:00
commit 5df089564b
30 changed files with 2583 additions and 198 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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)

View File

@ -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");
}
}

View File

@ -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) {

View File

@ -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
View 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)

View File

@ -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

View File

@ -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;

View File

@ -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
View 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";
}

View File

@ -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

View File

@ -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
View 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 */

View 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 */

View File

@ -269,4 +269,6 @@
#define PCI_VENDOR_ID_VMWARE 0x15ad
#define PCI_DEVICE_ID_VMWARE_PVRDMA 0x0820
#define PCI_VENDOR_ID_SYNOPSYS 0x16C3
#endif

View File

@ -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 */

View File

@ -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) {

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;