From d419890c0448281d0cf11d9384d9a30c8808f648 Mon Sep 17 00:00:00 2001 From: Amol Surati Date: Fri, 22 Jun 2018 13:28:34 +0100 Subject: [PATCH 01/28] hw/intc/arm_gicv3: fix an extra left-shift when reading IPRIORITYR When either GICD_IPRIORITYR or GICR_IPRIORITYR is read as a 32-bit register, the post left-shift operator in the for loop causes an extra shift after the least significant byte has been placed. The 32-bit value actually returned is therefore the expected value shifted left by 8 bits. Signed-off-by: Amol Surati Message-id: 20180614054857.26248-1-suratiamol@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_dist.c | 3 ++- hw/intc/arm_gicv3_redist.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 93fe936862..53c55c5729 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -441,7 +441,8 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, int i, irq = offset - GICD_IPRIORITYR; uint32_t value = 0; - for (i = irq + 3; i >= irq; i--, value <<= 8) { + for (i = irq + 3; i >= irq; i--) { + value <<= 8; value |= gicd_read_ipriorityr(s, attrs, i); } *data = value; diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 8a8684d76e..3b0ba6de1a 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -192,7 +192,8 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset, int i, irq = offset - GICR_IPRIORITYR; uint32_t value = 0; - for (i = irq + 3; i >= irq; i--, value <<= 8) { + for (i = irq + 3; i >= irq; i--) { + value <<= 8; value |= gicr_read_ipriorityr(cs, attrs, i); } *data = value; From 8297cb13e407db8a96cc7ed6b6a6c318a150759a Mon Sep 17 00:00:00 2001 From: Julia Suvorova Date: Fri, 22 Jun 2018 13:28:34 +0100 Subject: [PATCH 02/28] target/arm: Minor cleanup for ARMv6-M 32-bit instructions The arrays were made static, "if" was simplified because V7M and V8M define V6 feature. Signed-off-by: Julia Suvorova Reviewed-by: Stefan Hajnoczi Message-id: 20180618214604.6777-1-jusual@mail.ru Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index f405c82fb2..b988d379e7 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -10095,18 +10095,18 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) !arm_dc_feature(s, ARM_FEATURE_V7)) { int i; bool found = false; - const uint32_t armv6m_insn[] = {0xf3808000 /* msr */, - 0xf3b08040 /* dsb */, - 0xf3b08050 /* dmb */, - 0xf3b08060 /* isb */, - 0xf3e08000 /* mrs */, - 0xf000d000 /* bl */}; - const uint32_t armv6m_mask[] = {0xffe0d000, - 0xfff0d0f0, - 0xfff0d0f0, - 0xfff0d0f0, - 0xffe0d000, - 0xf800d000}; + static const uint32_t armv6m_insn[] = {0xf3808000 /* msr */, + 0xf3b08040 /* dsb */, + 0xf3b08050 /* dmb */, + 0xf3b08060 /* isb */, + 0xf3e08000 /* mrs */, + 0xf000d000 /* bl */}; + static const uint32_t armv6m_mask[] = {0xffe0d000, + 0xfff0d0f0, + 0xfff0d0f0, + 0xfff0d0f0, + 0xffe0d000, + 0xf800d000}; for (i = 0; i < ARRAY_SIZE(armv6m_insn); i++) { if ((insn & armv6m_mask[i]) == armv6m_insn[i]) { @@ -11039,8 +11039,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) break; case 3: /* Special control operations. */ if (!arm_dc_feature(s, ARM_FEATURE_V7) && - !(arm_dc_feature(s, ARM_FEATURE_V6) && - arm_dc_feature(s, ARM_FEATURE_M))) { + !arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; } op = (insn >> 4) & 0xf; From 5ff9aaabdc6aec367ba139bf3f2ccacd918a495f Mon Sep 17 00:00:00 2001 From: Zheng Xiang Date: Fri, 22 Jun 2018 13:28:35 +0100 Subject: [PATCH 03/28] target-arm: fix a segmentation fault due to illegal memory access The elements of kvm_devices_head list are freed in kvm_arm_machine_init_done(), but we still access these illegal memory in kvm_arm_devlistener_del(). This will cause segment fault when booting guest with MALLOC_PERTURB_=1. Signed-off-by: Zheng Xiang Message-id: 20180619075821.9884-1-zhengxiang9@huawei.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/kvm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 98f5006323..5bf41e151c 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -256,6 +256,7 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data) kvm_arm_set_device_addr(kd); } memory_region_unref(kd->mr); + QSLIST_REMOVE_HEAD(&kvm_devices_head, entries); g_free(kd); } memory_listener_unregister(&devlistener); From 77d361b13c19fdf881bff044a5bec99108cf2da2 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:35 +0100 Subject: [PATCH 04/28] linux-headers: Update to kernel mainline commit b357bf602 Update our kernel headers to mainline commit b357bf6023a948cf6a9472f07a1b0caac0e4f8e8 ("Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm") Signed-off-by: Eric Auger Message-id: 1529072910-16156-2-git-send-email-eric.auger@redhat.com [PMM: clarified commit message] Signed-off-by: Peter Maydell --- include/standard-headers/linux/pci_regs.h | 8 ++++++++ include/standard-headers/linux/virtio_gpu.h | 1 + include/standard-headers/linux/virtio_net.h | 3 +++ linux-headers/LICENSES/exceptions/Linux-syscall-note | 2 +- linux-headers/LICENSES/preferred/GPL-2.0 | 6 ++++++ linux-headers/asm-arm/kvm.h | 1 + linux-headers/asm-arm/unistd-common.h | 1 + linux-headers/asm-arm64/kvm.h | 1 + linux-headers/asm-generic/unistd.h | 4 +++- linux-headers/asm-powerpc/unistd.h | 1 + linux-headers/asm-x86/unistd_32.h | 2 ++ linux-headers/asm-x86/unistd_64.h | 2 ++ linux-headers/asm-x86/unistd_x32.h | 2 ++ linux-headers/linux/kvm.h | 5 +++-- linux-headers/linux/psp-sev.h | 12 ++++++++++++ 15 files changed, 47 insertions(+), 4 deletions(-) diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 103ba797a8..4da87e2ef8 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -506,6 +506,8 @@ #define PCI_EXP_DEVCTL_READRQ_256B 0x1000 /* 256 Bytes */ #define PCI_EXP_DEVCTL_READRQ_512B 0x2000 /* 512 Bytes */ #define PCI_EXP_DEVCTL_READRQ_1024B 0x3000 /* 1024 Bytes */ +#define PCI_EXP_DEVCTL_READRQ_2048B 0x4000 /* 2048 Bytes */ +#define PCI_EXP_DEVCTL_READRQ_4096B 0x5000 /* 4096 Bytes */ #define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ #define PCI_EXP_DEVSTA 10 /* Device Status */ #define PCI_EXP_DEVSTA_CED 0x0001 /* Correctable Error Detected */ @@ -655,6 +657,11 @@ #define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ +#define PCI_EXP_LNKCTL2_TLS 0x000f +#define PCI_EXP_LNKCTL2_TLS_2_5GT 0x0001 /* Supported Speed 2.5GT/s */ +#define PCI_EXP_LNKCTL2_TLS_5_0GT 0x0002 /* Supported Speed 5GT/s */ +#define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed 8GT/s */ +#define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ @@ -981,6 +988,7 @@ #define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */ #define PCI_EXP_DPC_CTL 6 /* DPC control */ +#define PCI_EXP_DPC_CTL_EN_FATAL 0x0001 /* Enable trigger on ERR_FATAL message */ #define PCI_EXP_DPC_CTL_EN_NONFATAL 0x0002 /* Enable trigger on ERR_NONFATAL message */ #define PCI_EXP_DPC_CTL_INT_EN 0x0008 /* DPC Interrupt Enable */ diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h index c1c8f0751d..52a830dcf8 100644 --- a/include/standard-headers/linux/virtio_gpu.h +++ b/include/standard-headers/linux/virtio_gpu.h @@ -260,6 +260,7 @@ struct virtio_gpu_cmd_submit { }; #define VIRTIO_GPU_CAPSET_VIRGL 1 +#define VIRTIO_GPU_CAPSET_VIRGL2 2 /* VIRTIO_GPU_CMD_GET_CAPSET_INFO */ struct virtio_gpu_get_capset_info { diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index e9f255ea3f..260c3681d7 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -57,6 +57,9 @@ * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ +#define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device + * with the same MAC. + */ #define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */ #ifndef VIRTIO_NET_NO_LEGACY diff --git a/linux-headers/LICENSES/exceptions/Linux-syscall-note b/linux-headers/LICENSES/exceptions/Linux-syscall-note index 6b60b61be4..9abdad71fa 100644 --- a/linux-headers/LICENSES/exceptions/Linux-syscall-note +++ b/linux-headers/LICENSES/exceptions/Linux-syscall-note @@ -1,6 +1,6 @@ SPDX-Exception-Identifier: Linux-syscall-note SPDX-URL: https://spdx.org/licenses/Linux-syscall-note.html -SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+ +SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+, GPL-2.0-only, GPL-2.0-or-later Usage-Guide: This exception is used together with one of the above SPDX-Licenses to mark user space API (uapi) header files so they can be included diff --git a/linux-headers/LICENSES/preferred/GPL-2.0 b/linux-headers/LICENSES/preferred/GPL-2.0 index b8db91d3a1..ff0812fd89 100644 --- a/linux-headers/LICENSES/preferred/GPL-2.0 +++ b/linux-headers/LICENSES/preferred/GPL-2.0 @@ -1,5 +1,7 @@ Valid-License-Identifier: GPL-2.0 +Valid-License-Identifier: GPL-2.0-only Valid-License-Identifier: GPL-2.0+ +Valid-License-Identifier: GPL-2.0-or-later SPDX-URL: https://spdx.org/licenses/GPL-2.0.html Usage-Guide: To use this license in source code, put one of the following SPDX @@ -7,8 +9,12 @@ Usage-Guide: guidelines in the licensing rules documentation. For 'GNU General Public License (GPL) version 2 only' use: SPDX-License-Identifier: GPL-2.0 + or + SPDX-License-Identifier: GPL-2.0-only For 'GNU General Public License (GPL) version 2 or any later version' use: SPDX-License-Identifier: GPL-2.0+ + or + SPDX-License-Identifier: GPL-2.0-or-later License-Text: GNU GENERAL PUBLIC LICENSE diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index 670b43c9e9..72aa226e6c 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -91,6 +91,7 @@ struct kvm_regs { #define KVM_VGIC_V3_ADDR_TYPE_DIST 2 #define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 #define KVM_VGIC_ITS_ADDR_TYPE 4 +#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5 #define KVM_VGIC_V3_DIST_SIZE SZ_64K #define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h index 8d5ceaee1a..60c2d931d0 100644 --- a/linux-headers/asm-arm/unistd-common.h +++ b/linux-headers/asm-arm/unistd-common.h @@ -354,5 +354,6 @@ #define __NR_pkey_alloc (__NR_SYSCALL_BASE + 395) #define __NR_pkey_free (__NR_SYSCALL_BASE + 396) #define __NR_statx (__NR_SYSCALL_BASE + 397) +#define __NR_rseq (__NR_SYSCALL_BASE + 398) #endif /* _ASM_ARM_UNISTD_COMMON_H */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 17315aba6a..99cb9ad14a 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -91,6 +91,7 @@ struct kvm_regs { #define KVM_VGIC_V3_ADDR_TYPE_DIST 2 #define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 #define KVM_VGIC_ITS_ADDR_TYPE 4 +#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5 #define KVM_VGIC_V3_DIST_SIZE SZ_64K #define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 8bcb186c6f..42990676a5 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -732,9 +732,11 @@ __SYSCALL(__NR_pkey_alloc, sys_pkey_alloc) __SYSCALL(__NR_pkey_free, sys_pkey_free) #define __NR_statx 291 __SYSCALL(__NR_statx, sys_statx) +#define __NR_io_pgetevents 292 +__SC_COMP(__NR_io_pgetevents, sys_io_pgetevents, compat_sys_io_pgetevents) #undef __NR_syscalls -#define __NR_syscalls 292 +#define __NR_syscalls 293 /* * 32 bit systems traditionally used different diff --git a/linux-headers/asm-powerpc/unistd.h b/linux-headers/asm-powerpc/unistd.h index 0c08edcfcd..3629858142 100644 --- a/linux-headers/asm-powerpc/unistd.h +++ b/linux-headers/asm-powerpc/unistd.h @@ -398,5 +398,6 @@ #define __NR_pkey_alloc 384 #define __NR_pkey_free 385 #define __NR_pkey_mprotect 386 +#define __NR_rseq 387 #endif /* _ASM_POWERPC_UNISTD_H_ */ diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index 8a206df454..c1b30a0cf4 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -382,5 +382,7 @@ #define __NR_pkey_free 382 #define __NR_statx 383 #define __NR_arch_prctl 384 +#define __NR_io_pgetevents 385 +#define __NR_rseq 386 #endif /* _ASM_X86_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index 336c2e4aaa..c2e464c115 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -334,5 +334,7 @@ #define __NR_pkey_alloc 330 #define __NR_pkey_free 331 #define __NR_statx 332 +#define __NR_io_pgetevents 333 +#define __NR_rseq 334 #endif /* _ASM_X86_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index cb98a52998..37229021f0 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -287,6 +287,8 @@ #define __NR_pkey_alloc (__X32_SYSCALL_BIT + 330) #define __NR_pkey_free (__X32_SYSCALL_BIT + 331) #define __NR_statx (__X32_SYSCALL_BIT + 332) +#define __NR_io_pgetevents (__X32_SYSCALL_BIT + 333) +#define __NR_rseq (__X32_SYSCALL_BIT + 334) #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) #define __NR_ioctl (__X32_SYSCALL_BIT + 514) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index cdb148e959..98f389a5a3 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -677,10 +677,10 @@ struct kvm_ioeventfd { }; #define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0) -#define KVM_X86_DISABLE_EXITS_HTL (1 << 1) +#define KVM_X86_DISABLE_EXITS_HLT (1 << 1) #define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) #define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT | \ - KVM_X86_DISABLE_EXITS_HTL | \ + KVM_X86_DISABLE_EXITS_HLT | \ KVM_X86_DISABLE_EXITS_PAUSE) /* for KVM_ENABLE_CAP */ @@ -948,6 +948,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_BPB 152 #define KVM_CAP_GET_MSR_FEATURES 153 #define KVM_CAP_HYPERV_EVENTFD 154 +#define KVM_CAP_HYPERV_TLBFLUSH 155 #ifdef KVM_CAP_IRQ_ROUTING diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h index 33e247471a..b7b933ffaa 100644 --- a/linux-headers/linux/psp-sev.h +++ b/linux-headers/linux/psp-sev.h @@ -30,6 +30,7 @@ enum { SEV_PDH_GEN, SEV_PDH_CERT_EXPORT, SEV_PEK_CERT_IMPORT, + SEV_GET_ID, SEV_MAX, }; @@ -123,6 +124,17 @@ struct sev_user_data_pdh_cert_export { __u32 cert_chain_len; /* In/Out */ } __attribute__((packed)); +/** + * struct sev_user_data_get_id - GET_ID command parameters + * + * @socket1: Buffer to pass unique ID of first socket + * @socket2: Buffer to pass unique ID of second socket + */ +struct sev_user_data_get_id { + __u8 socket1[64]; /* Out */ + __u8 socket2[64]; /* Out */ +} __attribute__((packed)); + /** * struct sev_issue_cmd - SEV ioctl parameters * From 19d1bd0b586a537805c6fe3b590f4ca8ec7a4912 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:35 +0100 Subject: [PATCH 05/28] target/arm: Allow KVM device address overwriting for KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION attribute, the attribute data pointed to by kvm_device_attr.addr is a OR of the redistributor region address and other fields such as the index of the redistributor region and the number of redistributors the region can contain. The existing machine init done notifier framework sets the address field to the actual address of the device and does not allow to OR this value with other fields. This patch extends the KVMDevice struct with a new kda_addr_ormask member. Its value is passed at registration time and OR'ed with the resolved address on kvm_arm_set_device_addr(). Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Message-id: 1529072910-16156-3-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/intc/arm_gic_kvm.c | 4 ++-- hw/intc/arm_gicv3_its_kvm.c | 2 +- hw/intc/arm_gicv3_kvm.c | 4 ++-- target/arm/kvm.c | 10 +++++++++- target/arm/kvm_arm.h | 3 ++- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 204369d0e2..86665080bd 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -558,7 +558,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) | KVM_VGIC_V2_ADDR_TYPE_DIST, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_DIST, - s->dev_fd); + s->dev_fd, 0); /* CPU interface for current core. Unlike arm_gic, we don't * provide the "interface for core #N" memory regions, because * cores with a VGIC don't have those. @@ -568,7 +568,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) | KVM_VGIC_V2_ADDR_TYPE_CPU, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_CPU, - s->dev_fd); + s->dev_fd, 0); if (kvm_has_gsi_routing()) { /* set up irq routing */ diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index eea6a73df2..271ebe461c 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -103,7 +103,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) /* register the base address */ kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd); + KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd, 0); gicv3_its_init_mmio(s, NULL); diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index d8d3b25403..95491df72a 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -807,9 +807,9 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort); kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd); + KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd, 0); kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd); + KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0); if (kvm_has_gsi_routing()) { /* set up irq routing */ diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 5bf41e151c..65f867d569 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -184,10 +184,15 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu) * We use a MemoryListener to track mapping and unmapping of * the regions during board creation, so the board models don't * need to do anything special for the KVM case. + * + * Sometimes the address must be OR'ed with some other fields + * (for example for KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION). + * @kda_addr_ormask aims at storing the value of those fields. */ typedef struct KVMDevice { struct kvm_arm_device_addr kda; struct kvm_device_attr kdattr; + uint64_t kda_addr_ormask; MemoryRegion *mr; QSLIST_ENTRY(KVMDevice) entries; int dev_fd; @@ -234,6 +239,8 @@ static void kvm_arm_set_device_addr(KVMDevice *kd) */ if (kd->dev_fd >= 0) { uint64_t addr = kd->kda.addr; + + addr |= kd->kda_addr_ormask; attr->addr = (uintptr_t)&addr; ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr); } else { @@ -267,7 +274,7 @@ static Notifier notify = { }; void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, - uint64_t attr, int dev_fd) + uint64_t attr, int dev_fd, uint64_t addr_ormask) { KVMDevice *kd; @@ -287,6 +294,7 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, kd->kdattr.group = group; kd->kdattr.attr = attr; kd->dev_fd = dev_fd; + kd->kda_addr_ormask = addr_ormask; QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries); memory_region_ref(kd->mr); } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 1e2364007d..863f205822 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -34,6 +34,7 @@ int kvm_arm_vcpu_init(CPUState *cs); * @group: device control API group for setting addresses * @attr: device control API address type * @dev_fd: device control device file descriptor (or -1 if not supported) + * @addr_ormask: value to be OR'ed with resolved address * * Remember the memory region @mr, and when it is mapped by the * machine model, tell the kernel that base address using the @@ -45,7 +46,7 @@ int kvm_arm_vcpu_init(CPUState *cs); * address at the point where machine init is complete. */ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, - uint64_t attr, int dev_fd); + uint64_t attr, int dev_fd, uint64_t addr_ormask); /** * kvm_arm_init_cpreg_list: From 1e575b66643a4311b9a6cbf0744f7f5aeba5e181 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:36 +0100 Subject: [PATCH 06/28] hw/intc/arm_gicv3: Introduce redist-region-count array property To prepare for multiple redistributor regions, we introduce an array of uint32_t properties that stores the redistributor count of each redistributor region. Non accelerated VGICv3 only supports a single redistributor region. The capacity of all redist regions is checked against the number of vcpus. Machvirt is updated to set those properties, ie. a single redistributor region with count set to the number of vcpus capped by 123. Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-4-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 11 ++++++++- hw/intc/arm_gicv3.c | 12 +++++++++- hw/intc/arm_gicv3_common.c | 38 ++++++++++++++++++++++++++---- hw/intc/arm_gicv3_kvm.c | 9 +++++-- include/hw/intc/arm_gicv3_common.h | 8 +++++-- 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 98b99cf236..de189c7296 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -523,6 +523,15 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) if (!kvm_irqchip_in_kernel()) { qdev_prop_set_bit(gicdev, "has-security-extensions", vms->secure); } + + if (type == 3) { + uint32_t redist0_capacity = + vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); + + qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1); + qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count); + } qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); @@ -1322,7 +1331,7 @@ static void machvirt_init(MachineState *machine) * many redistributors we can fit into the memory map. */ if (vms->gic_version == 3) { - virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / 0x20000; + virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; } else { virt_max_cpus = GIC_NCPU; } diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 479c66733c..7044133e2d 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -373,7 +373,17 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) return; } - gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops); + if (s->nb_redist_regions != 1) { + error_setg(errp, "VGICv3 redist region number(%d) not equal to 1", + s->nb_redist_regions); + return; + } + + gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } gicv3_init_cpuif(s); } diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 864b7c6515..ff326b374a 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -247,11 +247,22 @@ static const VMStateDescription vmstate_gicv3 = { }; void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, - const MemoryRegionOps *ops) + const MemoryRegionOps *ops, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(s); + int rdist_capacity = 0; int i; + for (i = 0; i < s->nb_redist_regions; i++) { + rdist_capacity += s->redist_region_count[i]; + } + if (rdist_capacity < s->num_cpu) { + error_setg(errp, "Capacity of the redist regions(%d) " + "is less than number of vcpus(%d)", + rdist_capacity, s->num_cpu); + return; + } + /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. * GPIO array layout is thus: * [0..N-1] spi @@ -277,11 +288,18 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, "gicv3_dist", 0x10000); - memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s, - "gicv3_redist", 0x20000 * s->num_cpu); - sysbus_init_mmio(sbd, &s->iomem_dist); - sysbus_init_mmio(sbd, &s->iomem_redist); + + s->iomem_redist = g_new0(MemoryRegion, s->nb_redist_regions); + for (i = 0; i < s->nb_redist_regions; i++) { + char *name = g_strdup_printf("gicv3_redist_region[%d]", i); + + memory_region_init_io(&s->iomem_redist[i], OBJECT(s), + ops ? &ops[1] : NULL, s, name, + s->redist_region_count[i] * GICV3_REDIST_SIZE); + sysbus_init_mmio(sbd, &s->iomem_redist[i]); + g_free(name); + } } static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) @@ -363,6 +381,13 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) } } +static void arm_gicv3_finalize(Object *obj) +{ + GICv3State *s = ARM_GICV3_COMMON(obj); + + g_free(s->redist_region_count); +} + static void arm_gicv3_common_reset(DeviceState *dev) { GICv3State *s = ARM_GICV3_COMMON(dev); @@ -467,6 +492,8 @@ static Property arm_gicv3_common_properties[] = { DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0), + DEFINE_PROP_ARRAY("redist-region-count", GICv3State, nb_redist_regions, + redist_region_count, qdev_prop_uint32, uint32_t), DEFINE_PROP_END_OF_LIST(), }; @@ -488,6 +515,7 @@ static const TypeInfo arm_gicv3_common_type = { .instance_size = sizeof(GICv3State), .class_size = sizeof(ARMGICv3CommonClass), .class_init = arm_gicv3_common_class_init, + .instance_finalize = arm_gicv3_finalize, .abstract = true, .interfaces = (InterfaceInfo []) { { TYPE_ARM_LINUX_BOOT_IF }, diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 95491df72a..2751687264 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -784,7 +784,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } - gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); + gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } for (i = 0; i < s->num_cpu; i++) { ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); @@ -808,7 +812,8 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd, 0); - kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, + kvm_arm_register_device(&s->iomem_redist[0], -1, + KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0); if (kvm_has_gsi_routing()) { diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index d75b49d558..b798486ecf 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -35,6 +35,8 @@ #define GICV3_MAXIRQ 1020 #define GICV3_MAXSPI (GICV3_MAXIRQ - GIC_INTERNAL) +#define GICV3_REDIST_SIZE 0x20000 + /* Number of SGI target-list bits */ #define GICV3_TARGETLIST_BITS 16 @@ -210,7 +212,9 @@ struct GICv3State { /*< public >*/ MemoryRegion iomem_dist; /* Distributor */ - MemoryRegion iomem_redist; /* Redistributors */ + MemoryRegion *iomem_redist; /* Redistributor Regions */ + uint32_t *redist_region_count; /* redistributor count within each region */ + uint32_t nb_redist_regions; /* number of redist regions */ uint32_t num_cpu; uint32_t num_irq; @@ -292,6 +296,6 @@ typedef struct ARMGICv3CommonClass { } ARMGICv3CommonClass; void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, - const MemoryRegionOps *ops); + const MemoryRegionOps *ops, Error **errp); #endif From 80d673338959c813770bd5b903e3061db58b1999 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:36 +0100 Subject: [PATCH 07/28] hw/intc/arm_gicv3_kvm: Get prepared to handle multiple redist regions Let's check if KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION is supported. If not, we check the number of redist region is equal to 1 and use the legacy KVM_VGIC_V3_ADDR_TYPE_REDIST attribute. Otherwise we use the new attribute and allow to register multiple regions to the KVM device. Signed-off-by: Eric Auger Reviewed-by: Peter Maydell Reviewed-by: Andrew Jones Message-id: 1529072910-16156-5-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 2751687264..1e11200fe2 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -767,6 +767,7 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { GICv3State *s = KVM_ARM_GICV3(dev); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s); + bool multiple_redist_region_allowed; Error *local_err = NULL; int i; @@ -803,6 +804,18 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } + multiple_redist_region_allowed = + kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION); + + if (!multiple_redist_region_allowed && s->nb_redist_regions > 1) { + error_setg(errp, "Multiple VGICv3 redistributor regions are not " + "supported by this host kernel"); + error_append_hint(errp, "A maximum of %d VCPUs can be used", + s->redist_region_count[0]); + return; + } + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &s->num_irq, true, &error_abort); @@ -812,9 +825,27 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd, 0); - kvm_arm_register_device(&s->iomem_redist[0], -1, - KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0); + + if (!multiple_redist_region_allowed) { + kvm_arm_register_device(&s->iomem_redist[0], -1, + KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0); + } else { + /* we register regions in reverse order as "devices" are inserted at + * the head of a QSLIST and the list is then popped from the head + * onwards by kvm_arm_machine_init_done() + */ + for (i = s->nb_redist_regions - 1; i >= 0; i--) { + /* Address mask made of the rdist region index and count */ + uint64_t addr_ormask = + i | ((uint64_t)s->redist_region_count[i] << 52); + + kvm_arm_register_device(&s->iomem_redist[i], -1, + KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, + s->dev_fd, addr_ormask); + } + } if (kvm_has_gsi_routing()) { /* set up irq routing */ From f90747c4e8fb689a8cea9c104ed6c13bd8e5086d Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:36 +0100 Subject: [PATCH 08/28] hw/arm/virt: GICv3 DT node with one or two redistributor regions This patch allows the creation of a GICv3 node with 1 or 2 redistributor regions depending on the number of smu_cpus. The second redistributor region is located just after the existing RAM region, at 256GB and contains up to up to 512 vcpus. Please refer to kernel documentation for further node details: Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-6-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 29 ++++++++++++++++++++++++----- include/hw/arm/virt.h | 14 ++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index de189c7296..e5e7606946 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -149,6 +149,8 @@ static const MemMapEntry a15memmap[] = { [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES }, + /* Additional 64 MB redist region (can contain up to 512 redistributors) */ + [VIRT_GIC_REDIST2] = { 0x4000000000ULL, 0x4000000 }, /* Second PCIe window, 512GB wide at the 512GB boundary */ [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL }, }; @@ -402,13 +404,30 @@ static void fdt_add_gic_node(VirtMachineState *vms) qemu_fdt_setprop_cell(vms->fdt, "/intc", "#size-cells", 0x2); qemu_fdt_setprop(vms->fdt, "/intc", "ranges", NULL, 0); if (vms->gic_version == 3) { + int nb_redist_regions = virt_gicv3_redist_region_count(vms); + qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", "arm,gic-v3"); - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", - 2, vms->memmap[VIRT_GIC_DIST].base, - 2, vms->memmap[VIRT_GIC_DIST].size, - 2, vms->memmap[VIRT_GIC_REDIST].base, - 2, vms->memmap[VIRT_GIC_REDIST].size); + + qemu_fdt_setprop_cell(vms->fdt, "/intc", + "#redistributor-regions", nb_redist_regions); + + if (nb_redist_regions == 1) { + qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_REDIST].base, + 2, vms->memmap[VIRT_GIC_REDIST].size); + } else { + qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_REDIST].base, + 2, vms->memmap[VIRT_GIC_REDIST].size, + 2, vms->memmap[VIRT_GIC_REDIST2].base, + 2, vms->memmap[VIRT_GIC_REDIST2].size); + } + if (vms->virt) { qemu_fdt_setprop_cells(vms->fdt, "/intc", "interrupts", GIC_FDT_IRQ_TYPE_PPI, ARCH_GICV3_MAINT_IRQ, diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 4ac7ef6a37..308156f0cd 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -35,6 +35,8 @@ #include "qemu/notify.h" #include "hw/boards.h" #include "hw/arm/arm.h" +#include "sysemu/kvm.h" +#include "hw/intc/arm_gicv3_common.h" #define NUM_GICV2M_SPIS 64 #define NUM_VIRTIO_TRANSPORTS 32 @@ -60,6 +62,7 @@ enum { VIRT_GIC_V2M, VIRT_GIC_ITS, VIRT_GIC_REDIST, + VIRT_GIC_REDIST2, VIRT_SMMU, VIRT_UART, VIRT_MMIO, @@ -130,4 +133,15 @@ typedef struct { void virt_acpi_setup(VirtMachineState *vms); +/* Return the number of used redistributor regions */ +static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) +{ + uint32_t redist0_capacity = + vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + + assert(vms->gic_version == 3); + + return vms->smp_cpus > redist0_capacity ? 2 : 1; +} + #endif /* QEMU_ARM_VIRT_H */ From a1de312f5690a1a4f3ff37319045f6ec7bdfcd31 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:36 +0100 Subject: [PATCH 09/28] hw/arm/virt-acpi-build: Advertise one or two GICR structures Depending on the number of smp_cpus we now register one or two GICR structures. Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-7-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 74f5744e87..eefd1d48f7 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -670,6 +670,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (vms->gic_version == 3) { AcpiMadtGenericTranslator *gic_its; + int nb_redist_regions = virt_gicv3_redist_region_count(vms); AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data, sizeof *gicr); @@ -678,6 +679,14 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base); gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size); + if (nb_redist_regions == 2) { + gicr = acpi_data_push(table_data, sizeof(*gicr)); + gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR; + gicr->length = sizeof(*gicr); + gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST2].base); + gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST2].size); + } + if (its_class_name() && !vmc->no_its) { gic_its = acpi_data_push(table_data, sizeof *gic_its); gic_its->type = ACPI_APIC_GENERIC_TRANSLATOR; From 03d72fa13cef2454aeaedea387a2e505c77481b4 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:37 +0100 Subject: [PATCH 10/28] hw/arm/virt: Register two redistributor regions when necessary With a VGICv3 KVM device, if the number of vcpus exceeds the capacity of the legacy redistributor region (123 redistributors), we now attempt to register a second redistributor region. Up to 512 redistributors can fit in this latter on top of the 123 allowed by the legacy redistributor region. Registering this second redistributor region is possible if the host kernel supports the following VGICv3 KVM device group/attribute: KVM_DEV_ARM_VGIC_GRP_ADDR/KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION. In case the host kernel does not support the registration of several redistributor regions and the requested number of vcpus exceeds the capacity of the legacy redistributor region, the GICv3 device initialization fails with a proper error message and qemu exits. At the moment the max number of vcpus still is capped by the virt machine class max_cpus. Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-8-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e5e7606946..9b9bc5091e 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -529,6 +529,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) SysBusDevice *gicbusdev; const char *gictype; int type = vms->gic_version, i; + uint32_t nb_redist_regions = 0; gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); @@ -548,14 +549,28 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); - qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1); + nb_redist_regions = virt_gicv3_redist_region_count(vms); + + qdev_prop_set_uint32(gicdev, "len-redist-region-count", + nb_redist_regions); qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count); + + if (nb_redist_regions == 2) { + uint32_t redist1_capacity = + vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE; + + qdev_prop_set_uint32(gicdev, "redist-region-count[1]", + MIN(smp_cpus - redist0_count, redist1_capacity)); + } } qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); if (type == 3) { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base); + if (nb_redist_regions == 2) { + sysbus_mmio_map(gicbusdev, 2, vms->memmap[VIRT_GIC_REDIST2].base); + } } else { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_CPU].base); } @@ -1351,6 +1366,7 @@ static void machvirt_init(MachineState *machine) */ if (vms->gic_version == 3) { virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + virt_max_cpus += vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE; } else { virt_max_cpus = GIC_NCPU; } From 601d626d148a6e50b4a0b5ae38f98682c5bf5e2a Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:37 +0100 Subject: [PATCH 11/28] hw/arm/virt: Add a new 256MB ECAM region This patch defines a new ECAM region located after the 256GB limit. The virt machine state is augmented with a new highmem_ecam field which guards the usage of this new ECAM region instead of the legacy 16MB one. With the highmem ECAM region, up to 256 PCIe buses can be used. Signed-off-by: Eric Auger Reviewed-by: Laszlo Ersek Reviewed-by: Andrew Jones Message-id: 1529072910-16156-9-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 21 +++++++++++++-------- hw/arm/virt.c | 12 ++++++++---- include/hw/arm/virt.h | 4 ++++ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index eefd1d48f7..6ea47e2588 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -150,16 +150,17 @@ static void acpi_dsdt_add_virtio(Aml *scope, } static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, - uint32_t irq, bool use_highmem) + uint32_t irq, bool use_highmem, bool highmem_ecam) { + int ecam_id = VIRT_ECAM_ID(highmem_ecam); Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf; int i, bus_no; hwaddr base_mmio = memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = memmap[VIRT_PCIE_MMIO].size; hwaddr base_pio = memmap[VIRT_PCIE_PIO].base; hwaddr size_pio = memmap[VIRT_PCIE_PIO].size; - hwaddr base_ecam = memmap[VIRT_PCIE_ECAM].base; - hwaddr size_ecam = memmap[VIRT_PCIE_ECAM].size; + hwaddr base_ecam = memmap[ecam_id].base; + hwaddr size_ecam = memmap[ecam_id].size; int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; Aml *dev = aml_device("%s", "PCI0"); @@ -173,7 +174,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_append(dev, aml_name_decl("_CCA", aml_int(1))); /* Declare the PCI Routing Table. */ - Aml *rt_pkg = aml_package(nr_pcie_buses * PCI_NUM_PINS); + Aml *rt_pkg = aml_varpackage(nr_pcie_buses * PCI_NUM_PINS); for (bus_no = 0; bus_no < nr_pcie_buses; bus_no++) { for (i = 0; i < PCI_NUM_PINS; i++) { int gsi = (i + bus_no) % PCI_NUM_PINS; @@ -316,7 +317,10 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, Aml *dev_res0 = aml_device("%s", "RES0"); aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(base_ecam, size_ecam, AML_READ_WRITE)); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, base_ecam, + base_ecam + size_ecam - 1, 0x0000, size_ecam)); aml_append(dev_res0, aml_name_decl("_CRS", crs)); aml_append(dev, dev_res0); aml_append(scope, dev); @@ -573,16 +577,17 @@ build_mcfg(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { AcpiTableMcfg *mcfg; const MemMapEntry *memmap = vms->memmap; + int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam); int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]); int mcfg_start = table_data->len; mcfg = acpi_data_push(table_data, len); - mcfg->allocation[0].address = cpu_to_le64(memmap[VIRT_PCIE_ECAM].base); + mcfg->allocation[0].address = cpu_to_le64(memmap[ecam_id].base); /* Only a single allocation so no need to play with segments */ mcfg->allocation[0].pci_segment = cpu_to_le16(0); mcfg->allocation[0].start_bus_number = 0; - mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size + mcfg->allocation[0].end_bus_number = (memmap[ecam_id].size / PCIE_MMCFG_SIZE_MIN) - 1; build_header(linker, table_data, (void *)(table_data->data + mcfg_start), @@ -766,7 +771,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO], (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS); acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE), - vms->highmem); + vms->highmem, vms->highmem_ecam); acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO], (irqmap[VIRT_GPIO] + ARM_SPI_BASE)); acpi_dsdt_add_power_button(scope); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 9b9bc5091e..933e60612d 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -151,6 +151,7 @@ static const MemMapEntry a15memmap[] = { [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES }, /* Additional 64 MB redist region (can contain up to 512 redistributors) */ [VIRT_GIC_REDIST2] = { 0x4000000000ULL, 0x4000000 }, + [VIRT_PCIE_ECAM_HIGH] = { 0x4010000000ULL, 0x10000000 }, /* Second PCIe window, 512GB wide at the 512GB boundary */ [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL }, }; @@ -1044,10 +1045,9 @@ static void create_pcie(VirtMachineState *vms, qemu_irq *pic) hwaddr size_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].size; hwaddr base_pio = vms->memmap[VIRT_PCIE_PIO].base; hwaddr size_pio = vms->memmap[VIRT_PCIE_PIO].size; - hwaddr base_ecam = vms->memmap[VIRT_PCIE_ECAM].base; - hwaddr size_ecam = vms->memmap[VIRT_PCIE_ECAM].size; + hwaddr base_ecam, size_ecam; hwaddr base = base_mmio; - int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; + int nr_pcie_buses; int irq = vms->irqmap[VIRT_PCIE]; MemoryRegion *mmio_alias; MemoryRegion *mmio_reg; @@ -1055,12 +1055,16 @@ static void create_pcie(VirtMachineState *vms, qemu_irq *pic) MemoryRegion *ecam_reg; DeviceState *dev; char *nodename; - int i; + int i, ecam_id; PCIHostState *pci; dev = qdev_create(NULL, TYPE_GPEX_HOST); qdev_init_nofail(dev); + ecam_id = VIRT_ECAM_ID(vms->highmem_ecam); + base_ecam = vms->memmap[ecam_id].base; + size_ecam = vms->memmap[ecam_id].size; + nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; /* Map only the first size_ecam bytes of ECAM space */ ecam_alias = g_new0(MemoryRegion, 1); ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 308156f0cd..085fdcc287 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -72,6 +72,7 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PCIE_ECAM, + VIRT_PCIE_ECAM_HIGH, VIRT_PLATFORM_BUS, VIRT_PCIE_MMIO_HIGH, VIRT_GPIO, @@ -106,6 +107,7 @@ typedef struct { FWCfgState *fw_cfg; bool secure; bool highmem; + bool highmem_ecam; bool its; bool virt; int32_t gic_version; @@ -123,6 +125,8 @@ typedef struct { int psci_conduit; } VirtMachineState; +#define VIRT_ECAM_ID(high) (high ? VIRT_PCIE_ECAM_HIGH : VIRT_PCIE_ECAM) + #define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt") #define VIRT_MACHINE(obj) \ OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE) From 8ae9a1ca5b9bb0b3be06d51914ffba7c20dd62f8 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:37 +0100 Subject: [PATCH 12/28] hw/arm/virt: Add virt-3.0 machine type Add virt-3.0 machine type. Signed-off-by: Eric Auger Reviewed-by: Laszlo Ersek Reviewed-by: Andrew Jones Message-id: 1529072910-16156-10-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 933e60612d..d8abf89e8c 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1745,7 +1745,7 @@ type_init(machvirt_machine_init); #define VIRT_COMPAT_2_12 \ HW_COMPAT_2_12 -static void virt_2_12_instance_init(Object *obj) +static void virt_3_0_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); @@ -1813,11 +1813,22 @@ static void virt_2_12_instance_init(Object *obj) vms->irqmap = a15irqmap; } +static void virt_machine_3_0_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(3, 0) + +static void virt_2_12_instance_init(Object *obj) +{ + virt_3_0_instance_init(obj); +} + static void virt_machine_2_12_options(MachineClass *mc) { + virt_machine_3_0_options(mc); SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); } -DEFINE_VIRT_MACHINE_AS_LATEST(2, 12) +DEFINE_VIRT_MACHINE(2, 12) #define VIRT_COMPAT_2_11 \ HW_COMPAT_2_11 From 17ec075a651a3f9613429c2d97018fce459ed943 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:37 +0100 Subject: [PATCH 13/28] hw/arm/virt: Use 256MB ECAM region by default With this patch, virt-3.0 machine uses a new 256MB ECAM region by default instead of the legacy 16MB one, if highmem is set (LPAE supported by the guest) and (!firmware_loaded || aarch64). Indeed aarch32 mode FW may not support this high ECAM region. Signed-off-by: Eric Auger Reviewed-by: Laszlo Ersek Reviewed-by: Andrew Jones Message-id: 1529072910-16156-11-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 10 ++++++++++ include/hw/arm/virt.h | 1 + 2 files changed, 11 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index d8abf89e8c..0f8bfa57d7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1318,6 +1318,7 @@ static void machvirt_init(MachineState *machine) int n, virt_max_cpus; MemoryRegion *ram = g_new(MemoryRegion, 1); bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); + bool aarch64 = true; /* We can probe only here because during property set * KVM is not available yet @@ -1433,6 +1434,8 @@ static void machvirt_init(MachineState *machine) numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj), &error_fatal); + aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL); + if (!vms->secure) { object_property_set_bool(cpuobj, false, "has_el3", NULL); } @@ -1491,6 +1494,8 @@ static void machvirt_init(MachineState *machine) create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hd(1)); } + vms->highmem_ecam &= vms->highmem && (!firmware_loaded || aarch64); + create_rtc(vms, pic); create_pcie(vms, pic); @@ -1788,6 +1793,8 @@ static void virt_3_0_instance_init(Object *obj) "Set GIC version. " "Valid values are 2, 3 and host", NULL); + vms->highmem_ecam = !vmc->no_highmem_ecam; + if (vmc->no_its) { vms->its = false; } else { @@ -1825,8 +1832,11 @@ static void virt_2_12_instance_init(Object *obj) static void virt_machine_2_12_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + virt_machine_3_0_options(mc); SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); + vmc->no_highmem_ecam = true; } DEFINE_VIRT_MACHINE(2, 12) diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 085fdcc287..9a870ccb6a 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -98,6 +98,7 @@ typedef struct { bool no_pmu; bool claim_edge_triggered_timers; bool smbios_old_sys_ver; + bool no_highmem_ecam; } VirtMachineClass; typedef struct { From b10fbd5363e58a9996ad9af0f8f456d89770b0a9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Fri, 22 Jun 2018 13:28:38 +0100 Subject: [PATCH 14/28] hw/arm/virt: Increase max_cpus to 512 virt 3.0 now allows up to 512 vcpus whereas for earlier machine types, max_cpus was set to 255 and any attempt to start the machine with vcpus > 255 was rejected at a very early stage, in vl.c/main level. 512 is the max supported by KVM. Anyway the actual vcpu count that can be achieved depends on other parameters such as the acceleration mode, the vgic version, the host kernel version. Those are discovered later on. Signed-off-by: Eric Auger Reviewed-by: Andrew Jones Message-id: 1529072910-16156-12-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0f8bfa57d7..742f68afca 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1706,11 +1706,11 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); mc->init = machvirt_init; - /* Start max_cpus at the maximum QEMU supports. We'll further restrict - * it later in machvirt_init, where we have more information about the + /* Start with max_cpus set to 512, which is the maximum supported by KVM. + * The value may be reduced later when we have more information about the * configuration of the particular instance. */ - mc->max_cpus = 255; + mc->max_cpus = 512; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); @@ -1837,6 +1837,7 @@ static void virt_machine_2_12_options(MachineClass *mc) virt_machine_3_0_options(mc); SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); vmc->no_highmem_ecam = true; + mc->max_cpus = 255; } DEFINE_VIRT_MACHINE(2, 12) From ebac5458c7517ed7b8ee06eb90beacc7472b295d Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 22 Jun 2018 13:28:38 +0100 Subject: [PATCH 15/28] target-arm: Add the Cortex-R5F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the Cortex-R5F with the optional FPU enabled. Reviewed-by: KONRAD Frederic Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias Message-id: 20180529124707.3025-2-edgar.iglesias@gmail.com Signed-off-by: Peter Maydell --- target/arm/cpu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e1de45e904..81c1d22b14 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1361,6 +1361,14 @@ static void cortex_r5_initfn(Object *obj) define_arm_cp_regs(cpu, cortexr5_cp_reginfo); } +static void cortex_r5f_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cortex_r5_initfn(obj); + set_feature(&cpu->env, ARM_FEATURE_VFP3); +} + static const ARMCPRegInfo cortexa8_cp_reginfo[] = { { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -1821,6 +1829,7 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "cortex-m33", .initfn = cortex_m33_initfn, .class_init = arm_v7m_class_init }, { .name = "cortex-r5", .initfn = cortex_r5_initfn }, + { .name = "cortex-r5f", .initfn = cortex_r5f_initfn }, { .name = "cortex-a7", .initfn = cortex_a7_initfn }, { .name = "cortex-a8", .initfn = cortex_a8_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn }, From eb24d4d38e0e177b2ef8189a9930eeca9277f097 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Fri, 22 Jun 2018 13:28:38 +0100 Subject: [PATCH 16/28] xlnx-zynqmp: Swap Cortex-R5 for Cortex-R5F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ZynqMP has Cortex-R5Fs with the optional FPU enabled. Reviewed-by: KONRAD Frederic Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Edgar E. Iglesias Message-id: 20180529124707.3025-3-edgar.iglesias@gmail.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zcu102.c | 2 +- hw/arm/xlnx-zynqmp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index f26fd8eb91..b6bc6a93b8 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -208,7 +208,7 @@ static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "Xilinx ZynqMP ZCU102 board with 4xA53s and 2xR5s based on " \ + mc->desc = "Xilinx ZynqMP ZCU102 board with 4xA53s and 2xR5Fs based on " \ "the value of smp"; mc->init = xlnx_zcu102_init; mc->block_default_type = IF_IDE; diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 2045b9d71e..29df35fb75 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -134,7 +134,7 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu, char *name; object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]), - "cortex-r5-" TYPE_ARM_CPU); + "cortex-r5f-" TYPE_ARM_CPU); object_property_add_child(OBJECT(s), "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]), &error_abort); From 344f4b1581f3d629954a1623736677827a0af750 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:39 +0100 Subject: [PATCH 17/28] hw/misc/tz-mpc.c: Implement the Arm TrustZone Memory Protection Controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the Arm TrustZone Memory Protection Controller, which sits in front of RAM and allows secure software to configure it to either pass through or reject transactions. We implement the MPC as a QEMU IOMMU, which will direct transactions either through to the devices and memory behind it or to a special "never works" AddressSpace if they are blocked. This initial commit implements the skeleton of the device: * it always permits accesses * it doesn't implement most of the registers * it doesn't implement the interrupt or other behaviour for blocked transactions Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Eric Auger Message-id: 20180620132032.28865-2-peter.maydell@linaro.org --- MAINTAINERS | 2 + default-configs/arm-softmmu.mak | 1 + hw/misc/Makefile.objs | 1 + hw/misc/trace-events | 7 + hw/misc/tz-mpc.c | 399 ++++++++++++++++++++++++++++++++ include/hw/misc/tz-mpc.h | 70 ++++++ 6 files changed, 480 insertions(+) create mode 100644 hw/misc/tz-mpc.c create mode 100644 include/hw/misc/tz-mpc.h diff --git a/MAINTAINERS b/MAINTAINERS index f222bf8b16..b8b4e60c06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -457,6 +457,8 @@ F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h F: hw/misc/tz-ppc.c F: include/hw/misc/tz-ppc.h +F: hw/misc/tz-mpc.c +F: include/hw/misc/tz-mpc.h ARM cores M: Peter Maydell diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 7cf73d2f27..834d45cfaf 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -108,6 +108,7 @@ CONFIG_CMSDK_APB_UART=y CONFIG_MPS2_FPGAIO=y CONFIG_MPS2_SCC=y +CONFIG_TZ_MPC=y CONFIG_TZ_PPC=y CONFIG_IOTKIT=y CONFIG_IOTKIT_SECCTL=y diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index ecd8d61098..9350900845 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -62,6 +62,7 @@ obj-$(CONFIG_MIPS_ITU) += mips_itu.o obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o obj-$(CONFIG_MPS2_SCC) += mps2-scc.o +obj-$(CONFIG_TZ_MPC) += tz-mpc.o obj-$(CONFIG_TZ_PPC) += tz-ppc.o obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o diff --git a/hw/misc/trace-events b/hw/misc/trace-events index ec5a9f0da1..72bf9d5700 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -84,6 +84,13 @@ mos6522_set_sr_int(void) "set sr_int" mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x" +# hw/misc/tz-mpc.c +tz_mpc_reg_read(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs read: offset 0x%x data 0x%" PRIx64 " size %u" +tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs write: offset 0x%x data 0x%" PRIx64 " size %u" +tz_mpc_mem_blocked_read(uint64_t addr, unsigned size, bool secure) "TZ MPC blocked read: offset 0x%" PRIx64 " size %u secure %d" +tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secure) "TZ MPC blocked write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" +tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s" + # hw/misc/tz-ppc.c tz_ppc_reset(void) "TZ PPC: reset" tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d" diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c new file mode 100644 index 0000000000..8a8617bdbd --- /dev/null +++ b/hw/misc/tz-mpc.c @@ -0,0 +1,399 @@ +/* + * ARM AHB5 TrustZone Memory Protection Controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/tz-mpc.h" + +/* Our IOMMU has two IOMMU indexes, one for secure transactions and one for + * non-secure transactions. + */ +enum { + IOMMU_IDX_S, + IOMMU_IDX_NS, + IOMMU_NUM_INDEXES, +}; + +/* Config registers */ +REG32(CTRL, 0x00) +REG32(BLK_MAX, 0x10) +REG32(BLK_CFG, 0x14) +REG32(BLK_IDX, 0x18) +REG32(BLK_LUT, 0x1c) +REG32(INT_STAT, 0x20) +REG32(INT_CLEAR, 0x24) +REG32(INT_EN, 0x28) +REG32(INT_INFO1, 0x2c) +REG32(INT_INFO2, 0x30) +REG32(INT_SET, 0x34) +REG32(PIDR4, 0xfd0) +REG32(PIDR5, 0xfd4) +REG32(PIDR6, 0xfd8) +REG32(PIDR7, 0xfdc) +REG32(PIDR0, 0xfe0) +REG32(PIDR1, 0xfe4) +REG32(PIDR2, 0xfe8) +REG32(PIDR3, 0xfec) +REG32(CIDR0, 0xff0) +REG32(CIDR1, 0xff4) +REG32(CIDR2, 0xff8) +REG32(CIDR3, 0xffc) + +static const uint8_t tz_mpc_idregs[] = { + 0x04, 0x00, 0x00, 0x00, + 0x60, 0xb8, 0x1b, 0x00, + 0x0d, 0xf0, 0x05, 0xb1, +}; + +static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, + uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + uint64_t r; + uint32_t offset = addr & ~0x3; + + if (!attrs.secure && offset < A_PIDR4) { + /* NS accesses can only see the ID registers */ + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register read: NS access to offset 0x%x\n", + offset); + r = 0; + goto read_out; + } + + switch (offset) { + case A_PIDR4: + case A_PIDR5: + case A_PIDR6: + case A_PIDR7: + case A_PIDR0: + case A_PIDR1: + case A_PIDR2: + case A_PIDR3: + case A_CIDR0: + case A_CIDR1: + case A_CIDR2: + case A_CIDR3: + r = tz_mpc_idregs[(offset - A_PIDR4) / 4]; + break; + case A_INT_CLEAR: + case A_INT_SET: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register read: write-only offset 0x%x\n", + offset); + r = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register read: bad offset 0x%x\n", offset); + r = 0; + break; + } + + if (size != 4) { + /* None of our registers are read-sensitive (except BLK_LUT, + * which can special case the "size not 4" case), so just + * pull the right bytes out of the word read result. + */ + r = extract32(r, (addr & 3) * 8, size * 8); + } + +read_out: + trace_tz_mpc_reg_read(addr, r, size); + *pdata = r; + return MEMTX_OK; +} + +static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, + uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + uint32_t offset = addr & ~0x3; + + trace_tz_mpc_reg_write(addr, value, size); + + if (!attrs.secure && offset < A_PIDR4) { + /* NS accesses can only see the ID registers */ + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register write: NS access to offset 0x%x\n", + offset); + return MEMTX_OK; + } + + if (size != 4) { + /* Expand the byte or halfword write to a full word size. + * In most cases we can do this with zeroes; the exceptions + * are CTRL, BLK_IDX and BLK_LUT. + */ + uint32_t oldval; + + switch (offset) { + /* As we add support for registers which need expansions + * other than zeroes we'll fill in cases here. + */ + default: + oldval = 0; + break; + } + value = deposit32(oldval, (addr & 3) * 8, size * 8, value); + } + + switch (offset) { + case A_PIDR4: + case A_PIDR5: + case A_PIDR6: + case A_PIDR7: + case A_PIDR0: + case A_PIDR1: + case A_PIDR2: + case A_PIDR3: + case A_CIDR0: + case A_CIDR1: + case A_CIDR2: + case A_CIDR3: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register write: read-only offset 0x%x\n", offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register write: bad offset 0x%x\n", offset); + break; + } + + return MEMTX_OK; +} + +static const MemoryRegionOps tz_mpc_reg_ops = { + .read_with_attrs = tz_mpc_reg_read, + .write_with_attrs = tz_mpc_reg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, +}; + +/* Accesses only reach these read and write functions if the MPC is + * blocking them; non-blocked accesses go directly to the downstream + * memory region without passing through this code. + */ +static MemTxResult tz_mpc_mem_blocked_read(void *opaque, hwaddr addr, + uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + trace_tz_mpc_mem_blocked_read(addr, size, attrs.secure); + + *pdata = 0; + return MEMTX_OK; +} + +static MemTxResult tz_mpc_mem_blocked_write(void *opaque, hwaddr addr, + uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + trace_tz_mpc_mem_blocked_write(addr, value, size, attrs.secure); + + return MEMTX_OK; +} + +static const MemoryRegionOps tz_mpc_mem_blocked_ops = { + .read_with_attrs = tz_mpc_mem_blocked_read, + .write_with_attrs = tz_mpc_mem_blocked_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .impl.min_access_size = 1, + .impl.max_access_size = 8, +}; + +static IOMMUTLBEntry tz_mpc_translate(IOMMUMemoryRegion *iommu, + hwaddr addr, IOMMUAccessFlags flags, + int iommu_idx) +{ + TZMPC *s = TZ_MPC(container_of(iommu, TZMPC, upstream)); + bool ok; + + IOMMUTLBEntry ret = { + .iova = addr & ~(s->blocksize - 1), + .translated_addr = addr & ~(s->blocksize - 1), + .addr_mask = s->blocksize - 1, + .perm = IOMMU_RW, + }; + + /* Look at the per-block configuration for this address, and + * return a TLB entry directing the transaction at either + * downstream_as or blocked_io_as, as appropriate. + * For the moment, always permit accesses. + */ + ok = true; + + trace_tz_mpc_translate(addr, flags, + iommu_idx == IOMMU_IDX_S ? "S" : "NS", + ok ? "pass" : "block"); + + ret.target_as = ok ? &s->downstream_as : &s->blocked_io_as; + return ret; +} + +static int tz_mpc_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs) +{ + /* We treat unspecified attributes like secure. Transactions with + * unspecified attributes come from places like + * cpu_physical_memory_write_rom() for initial image load, and we want + * those to pass through the from-reset "everything is secure" config. + * All the real during-emulation transactions from the CPU will + * specify attributes. + */ + return (attrs.unspecified || attrs.secure) ? IOMMU_IDX_S : IOMMU_IDX_NS; +} + +static int tz_mpc_num_indexes(IOMMUMemoryRegion *iommu) +{ + return IOMMU_NUM_INDEXES; +} + +static void tz_mpc_reset(DeviceState *dev) +{ +} + +static void tz_mpc_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + TZMPC *s = TZ_MPC(obj); + + qdev_init_gpio_out_named(dev, &s->irq, "irq", 1); +} + +static void tz_mpc_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + TZMPC *s = TZ_MPC(dev); + uint64_t size; + + /* We can't create the upstream end of the port until realize, + * as we don't know the size of the MR used as the downstream until then. + * We insist on having a downstream, to avoid complicating the code + * with handling the "don't know how big this is" case. It's easy + * enough for the user to create an unimplemented_device as downstream + * if they have nothing else to plug into this. + */ + if (!s->downstream) { + error_setg(errp, "MPC 'downstream' link not set"); + return; + } + + size = memory_region_size(s->downstream); + + memory_region_init_iommu(&s->upstream, sizeof(s->upstream), + TYPE_TZ_MPC_IOMMU_MEMORY_REGION, + obj, "tz-mpc-upstream", size); + + /* In real hardware the block size is configurable. In QEMU we could + * make it configurable but will need it to be at least as big as the + * target page size so we can execute out of the resulting MRs. Guest + * software is supposed to check the block size using the BLK_CFG + * register, so make it fixed at the page size. + */ + s->blocksize = memory_region_iommu_get_min_page_size(&s->upstream); + if (size % s->blocksize != 0) { + error_setg(errp, + "MPC 'downstream' size %" PRId64 + " is not a multiple of %" HWADDR_PRIx " bytes", + size, s->blocksize); + object_unref(OBJECT(&s->upstream)); + return; + } + + /* BLK_MAX is the max value of BLK_IDX, which indexes an array of 32-bit + * words, each bit of which indicates one block. + */ + s->blk_max = DIV_ROUND_UP(size / s->blocksize, 32); + + memory_region_init_io(&s->regmr, obj, &tz_mpc_reg_ops, + s, "tz-mpc-regs", 0x1000); + sysbus_init_mmio(sbd, &s->regmr); + + sysbus_init_mmio(sbd, MEMORY_REGION(&s->upstream)); + + /* This memory region is not exposed to users of this device as a + * sysbus MMIO region, but is instead used internally as something + * that our IOMMU translate function might direct accesses to. + */ + memory_region_init_io(&s->blocked_io, obj, &tz_mpc_mem_blocked_ops, + s, "tz-mpc-blocked-io", size); + + address_space_init(&s->downstream_as, s->downstream, + "tz-mpc-downstream"); + address_space_init(&s->blocked_io_as, &s->blocked_io, + "tz-mpc-blocked-io"); +} + +static const VMStateDescription tz_mpc_vmstate = { + .name = "tz-mpc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +static Property tz_mpc_properties[] = { + DEFINE_PROP_LINK("downstream", TZMPC, downstream, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tz_mpc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = tz_mpc_realize; + dc->vmsd = &tz_mpc_vmstate; + dc->reset = tz_mpc_reset; + dc->props = tz_mpc_properties; +} + +static const TypeInfo tz_mpc_info = { + .name = TYPE_TZ_MPC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TZMPC), + .instance_init = tz_mpc_init, + .class_init = tz_mpc_class_init, +}; + +static void tz_mpc_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = tz_mpc_translate; + imrc->attrs_to_index = tz_mpc_attrs_to_index; + imrc->num_indexes = tz_mpc_num_indexes; +} + +static const TypeInfo tz_mpc_iommu_memory_region_info = { + .name = TYPE_TZ_MPC_IOMMU_MEMORY_REGION, + .parent = TYPE_IOMMU_MEMORY_REGION, + .class_init = tz_mpc_iommu_memory_region_class_init, +}; + +static void tz_mpc_register_types(void) +{ + type_register_static(&tz_mpc_info); + type_register_static(&tz_mpc_iommu_memory_region_info); +} + +type_init(tz_mpc_register_types); diff --git a/include/hw/misc/tz-mpc.h b/include/hw/misc/tz-mpc.h new file mode 100644 index 0000000000..d1a65fd9a3 --- /dev/null +++ b/include/hw/misc/tz-mpc.h @@ -0,0 +1,70 @@ +/* + * ARM AHB5 TrustZone Memory Protection Controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the TrustZone memory protection controller (MPC). + * It is documented in the ARM CoreLink SIE-200 System IP for Embedded TRM + * (DDI 0571G): + * https://developer.arm.com/products/architecture/m-profile/docs/ddi0571/g + * + * The MPC sits in front of memory and allows secure software to + * configure it to either pass through or reject transactions. + * Rejected transactions may be configured to either be aborted, or to + * behave as RAZ/WI. An interrupt can be signalled for a rejected transaction. + * + * The MPC has a register interface which the guest uses to configure it. + * + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion for the MPC's config registers + * + sysbus MMIO region 1: MemoryRegion for the upstream end of the MPC + * + Property "downstream": MemoryRegion defining the downstream memory + * + Named GPIO output "irq": set for a transaction-failed interrupt + */ + +#ifndef TZ_MPC_H +#define TZ_MPC_H + +#include "hw/sysbus.h" + +#define TYPE_TZ_MPC "tz-mpc" +#define TZ_MPC(obj) OBJECT_CHECK(TZMPC, (obj), TYPE_TZ_MPC) + +#define TZ_NUM_PORTS 16 + +#define TYPE_TZ_MPC_IOMMU_MEMORY_REGION "tz-mpc-iommu-memory-region" + +typedef struct TZMPC TZMPC; + +struct TZMPC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + + qemu_irq irq; + + /* Properties */ + MemoryRegion *downstream; + + hwaddr blocksize; + uint32_t blk_max; + + /* MemoryRegions exposed to user */ + MemoryRegion regmr; + IOMMUMemoryRegion upstream; + + /* MemoryRegion used internally */ + MemoryRegion blocked_io; + + AddressSpace downstream_as; + AddressSpace blocked_io_as; +}; + +#endif From cdb6099818a78e80b307432f6f9c143452135c59 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:39 +0100 Subject: [PATCH 18/28] hw/misc/tz-mpc.c: Implement registers Implement the missing registers for the TZ MPC. Signed-off-by: Peter Maydell Reviewed-by: Eric Auger Message-id: 20180620132032.28865-3-peter.maydell@linaro.org --- hw/misc/tz-mpc.c | 140 ++++++++++++++++++++++++++++++++++++++- include/hw/misc/tz-mpc.h | 10 +++ 2 files changed, 147 insertions(+), 3 deletions(-) diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 8a8617bdbd..e5b91bf81a 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -28,16 +28,23 @@ enum { /* Config registers */ REG32(CTRL, 0x00) + FIELD(CTRL, SEC_RESP, 4, 1) + FIELD(CTRL, AUTOINC, 8, 1) + FIELD(CTRL, LOCKDOWN, 31, 1) REG32(BLK_MAX, 0x10) REG32(BLK_CFG, 0x14) REG32(BLK_IDX, 0x18) REG32(BLK_LUT, 0x1c) REG32(INT_STAT, 0x20) + FIELD(INT_STAT, IRQ, 0, 1) REG32(INT_CLEAR, 0x24) + FIELD(INT_CLEAR, IRQ, 0, 1) REG32(INT_EN, 0x28) + FIELD(INT_EN, IRQ, 0, 1) REG32(INT_INFO1, 0x2c) REG32(INT_INFO2, 0x30) REG32(INT_SET, 0x34) + FIELD(INT_SET, IRQ, 0, 1) REG32(PIDR4, 0xfd0) REG32(PIDR5, 0xfd4) REG32(PIDR6, 0xfd8) @@ -57,10 +64,25 @@ static const uint8_t tz_mpc_idregs[] = { 0x0d, 0xf0, 0x05, 0xb1, }; +static void tz_mpc_irq_update(TZMPC *s) +{ + qemu_set_irq(s->irq, s->int_stat && s->int_en); +} + +static void tz_mpc_autoinc_idx(TZMPC *s, unsigned access_size) +{ + /* Auto-increment BLK_IDX if necessary */ + if (access_size == 4 && (s->ctrl & R_CTRL_AUTOINC_MASK)) { + s->blk_idx++; + s->blk_idx %= s->blk_max; + } +} + static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, uint64_t *pdata, unsigned size, MemTxAttrs attrs) { + TZMPC *s = TZ_MPC(opaque); uint64_t r; uint32_t offset = addr & ~0x3; @@ -74,6 +96,38 @@ static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, } switch (offset) { + case A_CTRL: + r = s->ctrl; + break; + case A_BLK_MAX: + r = s->blk_max; + break; + case A_BLK_CFG: + /* We are never in "init in progress state", so this just indicates + * the block size. s->blocksize == (1 << BLK_CFG + 5), so + * BLK_CFG == ctz32(s->blocksize) - 5 + */ + r = ctz32(s->blocksize) - 5; + break; + case A_BLK_IDX: + r = s->blk_idx; + break; + case A_BLK_LUT: + r = s->blk_lut[s->blk_idx]; + tz_mpc_autoinc_idx(s, size); + break; + case A_INT_STAT: + r = s->int_stat; + break; + case A_INT_EN: + r = s->int_en; + break; + case A_INT_INFO1: + r = s->int_info1; + break; + case A_INT_INFO2: + r = s->int_info2; + break; case A_PIDR4: case A_PIDR5: case A_PIDR6: @@ -120,6 +174,7 @@ static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) { + TZMPC *s = TZ_MPC(opaque); uint32_t offset = addr & ~0x3; trace_tz_mpc_reg_write(addr, value, size); @@ -140,9 +195,15 @@ static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, uint32_t oldval; switch (offset) { - /* As we add support for registers which need expansions - * other than zeroes we'll fill in cases here. - */ + case A_CTRL: + oldval = s->ctrl; + break; + case A_BLK_IDX: + oldval = s->blk_idx; + break; + case A_BLK_LUT: + oldval = s->blk_lut[s->blk_idx]; + break; default: oldval = 0; break; @@ -150,7 +211,48 @@ static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, value = deposit32(oldval, (addr & 3) * 8, size * 8, value); } + if ((s->ctrl & R_CTRL_LOCKDOWN_MASK) && + (offset == A_CTRL || offset == A_BLK_LUT || offset == A_INT_EN)) { + /* Lockdown mode makes these three registers read-only, and + * the only way out of it is to reset the device. + */ + qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write to offset 0x%x " + "while MPC is in lockdown mode\n", offset); + return MEMTX_OK; + } + switch (offset) { + case A_CTRL: + /* We don't implement the 'data gating' feature so all other bits + * are reserved and we make them RAZ/WI. + */ + s->ctrl = value & (R_CTRL_SEC_RESP_MASK | + R_CTRL_AUTOINC_MASK | + R_CTRL_LOCKDOWN_MASK); + break; + case A_BLK_IDX: + s->blk_idx = value % s->blk_max; + break; + case A_BLK_LUT: + s->blk_lut[s->blk_idx] = value; + tz_mpc_autoinc_idx(s, size); + break; + case A_INT_CLEAR: + if (value & R_INT_CLEAR_IRQ_MASK) { + s->int_stat = 0; + tz_mpc_irq_update(s); + } + break; + case A_INT_EN: + s->int_en = value & R_INT_EN_IRQ_MASK; + tz_mpc_irq_update(s); + break; + case A_INT_SET: + if (value & R_INT_SET_IRQ_MASK) { + s->int_stat = R_INT_STAT_IRQ_MASK; + tz_mpc_irq_update(s); + } + break; case A_PIDR4: case A_PIDR5: case A_PIDR6: @@ -266,6 +368,16 @@ static int tz_mpc_num_indexes(IOMMUMemoryRegion *iommu) static void tz_mpc_reset(DeviceState *dev) { + TZMPC *s = TZ_MPC(dev); + + s->ctrl = 0x00000100; + s->blk_idx = 0; + s->int_stat = 0; + s->int_en = 1; + s->int_info1 = 0; + s->int_info2 = 0; + + memset(s->blk_lut, 0, s->blk_max * sizeof(uint32_t)); } static void tz_mpc_init(Object *obj) @@ -339,13 +451,35 @@ static void tz_mpc_realize(DeviceState *dev, Error **errp) "tz-mpc-downstream"); address_space_init(&s->blocked_io_as, &s->blocked_io, "tz-mpc-blocked-io"); + + s->blk_lut = g_new(uint32_t, s->blk_max); +} + +static int tz_mpc_post_load(void *opaque, int version_id) +{ + TZMPC *s = TZ_MPC(opaque); + + /* Check the incoming data doesn't point blk_idx off the end of blk_lut. */ + if (s->blk_idx >= s->blk_max) { + return -1; + } + return 0; } static const VMStateDescription tz_mpc_vmstate = { .name = "tz-mpc", .version_id = 1, .minimum_version_id = 1, + .post_load = tz_mpc_post_load, .fields = (VMStateField[]) { + VMSTATE_UINT32(ctrl, TZMPC), + VMSTATE_UINT32(blk_idx, TZMPC), + VMSTATE_UINT32(int_stat, TZMPC), + VMSTATE_UINT32(int_en, TZMPC), + VMSTATE_UINT32(int_info1, TZMPC), + VMSTATE_UINT32(int_info2, TZMPC), + VMSTATE_VARRAY_UINT32(blk_lut, TZMPC, blk_max, + 0, vmstate_info_uint32, uint32_t), VMSTATE_END_OF_LIST() } }; diff --git a/include/hw/misc/tz-mpc.h b/include/hw/misc/tz-mpc.h index d1a65fd9a3..6f15945410 100644 --- a/include/hw/misc/tz-mpc.h +++ b/include/hw/misc/tz-mpc.h @@ -48,6 +48,16 @@ struct TZMPC { /*< public >*/ + /* State */ + uint32_t ctrl; + uint32_t blk_idx; + uint32_t int_stat; + uint32_t int_en; + uint32_t int_info1; + uint32_t int_info2; + + uint32_t *blk_lut; + qemu_irq irq; /* Properties */ From 57c49a6e8773bcce0d407ff4d9b2de7a904f1b0d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 19/28] hw/misc/tz-mpc.c: Implement correct blocked-access behaviour The MPC is guest-configurable for whether blocked accesses: * should be RAZ/WI or cause a bus error * should generate an interrupt or not Implement this behaviour in the blocked-access handlers. Signed-off-by: Peter Maydell Reviewed-by: Eric Auger Message-id: 20180620132032.28865-4-peter.maydell@linaro.org --- hw/misc/tz-mpc.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index e5b91bf81a..fded5922a2 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -43,6 +43,9 @@ REG32(INT_EN, 0x28) FIELD(INT_EN, IRQ, 0, 1) REG32(INT_INFO1, 0x2c) REG32(INT_INFO2, 0x30) + FIELD(INT_INFO2, HMASTER, 0, 16) + FIELD(INT_INFO2, HNONSEC, 16, 1) + FIELD(INT_INFO2, CFG_NS, 17, 1) REG32(INT_SET, 0x34) FIELD(INT_SET, IRQ, 0, 1) REG32(PIDR4, 0xfd0) @@ -287,6 +290,45 @@ static const MemoryRegionOps tz_mpc_reg_ops = { .impl.max_access_size = 4, }; +static inline bool tz_mpc_cfg_ns(TZMPC *s, hwaddr addr) +{ + /* Return the cfg_ns bit from the LUT for the specified address */ + hwaddr blknum = addr / s->blocksize; + hwaddr blkword = blknum / 32; + uint32_t blkbit = 1U << (blknum % 32); + + /* This would imply the address was larger than the size we + * defined this memory region to be, so it can't happen. + */ + assert(blkword < s->blk_max); + return s->blk_lut[blkword] & blkbit; +} + +static MemTxResult tz_mpc_handle_block(TZMPC *s, hwaddr addr, MemTxAttrs attrs) +{ + /* Handle a blocked transaction: raise IRQ, capture info, etc */ + if (!s->int_stat) { + /* First blocked transfer: capture information into INT_INFO1 and + * INT_INFO2. Subsequent transfers are still blocked but don't + * capture information until the guest clears the interrupt. + */ + + s->int_info1 = addr; + s->int_info2 = 0; + s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HMASTER, + attrs.requester_id & 0xffff); + s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HNONSEC, + ~attrs.secure); + s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, CFG_NS, + tz_mpc_cfg_ns(s, addr)); + s->int_stat |= R_INT_STAT_IRQ_MASK; + tz_mpc_irq_update(s); + } + + /* Generate bus error if desired; otherwise RAZ/WI */ + return (s->ctrl & R_CTRL_SEC_RESP_MASK) ? MEMTX_ERROR : MEMTX_OK; +} + /* Accesses only reach these read and write functions if the MPC is * blocking them; non-blocked accesses go directly to the downstream * memory region without passing through this code. @@ -295,19 +337,23 @@ static MemTxResult tz_mpc_mem_blocked_read(void *opaque, hwaddr addr, uint64_t *pdata, unsigned size, MemTxAttrs attrs) { + TZMPC *s = TZ_MPC(opaque); + trace_tz_mpc_mem_blocked_read(addr, size, attrs.secure); *pdata = 0; - return MEMTX_OK; + return tz_mpc_handle_block(s, addr, attrs); } static MemTxResult tz_mpc_mem_blocked_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) { + TZMPC *s = TZ_MPC(opaque); + trace_tz_mpc_mem_blocked_write(addr, value, size, attrs.secure); - return MEMTX_OK; + return tz_mpc_handle_block(s, addr, attrs); } static const MemoryRegionOps tz_mpc_mem_blocked_ops = { From dd29d0687d472be0c92b554383d2d8549fe37584 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 20/28] hw/misc/tz_mpc.c: Honour the BLK_LUT settings in translate The final part of the Memory Protection Controller we need to implement is actually using the BLK_LUT data programmed by the guest to determine whether to block the transaction or not. Since this means we now change transaction mappings when the guest writes to BLK_LUT, we must also call the IOMMU notifiers at that point. Signed-off-by: Peter Maydell Reviewed-by: Eric Auger Message-id: 20180620132032.28865-5-peter.maydell@linaro.org --- hw/misc/trace-events | 1 + hw/misc/tz-mpc.c | 53 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 72bf9d5700..c956e1419b 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -90,6 +90,7 @@ tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs wri tz_mpc_mem_blocked_read(uint64_t addr, unsigned size, bool secure) "TZ MPC blocked read: offset 0x%" PRIx64 " size %u secure %d" tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secure) "TZ MPC blocked write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s" +tz_mpc_iommu_notify(uint64_t addr) "TZ MPC iommu: notifying UNMAP/MAP for 0x%" PRIx64 # hw/misc/tz-ppc.c tz_ppc_reset(void) "TZ PPC: reset" diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index fded5922a2..8316079b4b 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -72,6 +72,53 @@ static void tz_mpc_irq_update(TZMPC *s) qemu_set_irq(s->irq, s->int_stat && s->int_en); } +static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx, + uint32_t oldlut, uint32_t newlut) +{ + /* Called when the LUT word at lutidx has changed from oldlut to newlut; + * must call the IOMMU notifiers for the changed blocks. + */ + IOMMUTLBEntry entry = { + .addr_mask = s->blocksize - 1, + }; + hwaddr addr = lutidx * s->blocksize * 32; + int i; + + for (i = 0; i < 32; i++, addr += s->blocksize) { + bool block_is_ns; + + if (!((oldlut ^ newlut) & (1 << i))) { + continue; + } + /* This changes the mappings for both the S and the NS space, + * so we need to do four notifies: an UNMAP then a MAP for each. + */ + block_is_ns = newlut & (1 << i); + + trace_tz_mpc_iommu_notify(addr); + entry.iova = addr; + entry.translated_addr = addr; + + entry.perm = IOMMU_NONE; + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); + + entry.perm = IOMMU_RW; + if (block_is_ns) { + entry.target_as = &s->blocked_io_as; + } else { + entry.target_as = &s->downstream_as; + } + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); + if (block_is_ns) { + entry.target_as = &s->downstream_as; + } else { + entry.target_as = &s->blocked_io_as; + } + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); + } +} + static void tz_mpc_autoinc_idx(TZMPC *s, unsigned access_size) { /* Auto-increment BLK_IDX if necessary */ @@ -237,6 +284,7 @@ static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, s->blk_idx = value % s->blk_max; break; case A_BLK_LUT: + tz_mpc_iommu_notify(s, s->blk_idx, s->blk_lut[s->blk_idx], value); s->blk_lut[s->blk_idx] = value; tz_mpc_autoinc_idx(s, size); break; @@ -383,9 +431,10 @@ static IOMMUTLBEntry tz_mpc_translate(IOMMUMemoryRegion *iommu, /* Look at the per-block configuration for this address, and * return a TLB entry directing the transaction at either * downstream_as or blocked_io_as, as appropriate. - * For the moment, always permit accesses. + * If the LUT cfg_ns bit is 1, only non-secure transactions + * may pass. If the bit is 0, only secure transactions may pass. */ - ok = true; + ok = tz_mpc_cfg_ns(s, addr) == (iommu_idx == IOMMU_IDX_NS); trace_tz_mpc_translate(addr, flags, iommu_idx == IOMMU_IDX_S ? "S" : "NS", From 3fd3cb2f6f1b3ada95a728e2bf0a0cfa4c84a8e1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 21/28] hw/misc/iotkit-secctl.c: Implement SECMPCINTSTATUS Implement the SECMPCINTSTATUS register. This is the only register in the security controller that deals with Memory Protection Controllers, and it simply provides a read-only view of the interrupt lines from the various MPCs in the system. Signed-off-by: Peter Maydell Message-id: 20180620132032.28865-6-peter.maydell@linaro.org --- hw/misc/iotkit-secctl.c | 38 +++++++++++++++++++++++++++++++-- include/hw/misc/iotkit-secctl.h | 8 +++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index ddd1584d34..de4fd8e36d 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -139,6 +139,9 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, case A_NSCCFG: r = s->nsccfg; break; + case A_SECMPCINTSTATUS: + r = s->mpcintstatus; + break; case A_SECPPCINTSTAT: r = s->secppcintstat; break; @@ -186,7 +189,6 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, case A_APBSPPPCEXP3: r = s->apbexp[offset_to_ppc_idx(offset)].sp; break; - case A_SECMPCINTSTATUS: case A_SECMSCINTSTAT: case A_SECMSCINTEN: case A_NSMSCEXP: @@ -572,6 +574,20 @@ static void iotkit_secctl_reset(DeviceState *dev) foreach_ppc(s, iotkit_secctl_reset_ppc); } +static void iotkit_secctl_mpc_status(void *opaque, int n, int level) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + s->mpcintstatus = deposit32(s->mpcintstatus, 0, 1, !!level); +} + +static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level); +} + static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level) { IoTKitSecCtlPPC *ppc = opaque; @@ -640,6 +656,10 @@ static void iotkit_secctl_init(Object *obj) qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1); + qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", 1); + qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status, + "mpcexp_status", IOTS_NUM_EXP_MPC); + memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops, s, "iotkit-secctl-s-regs", 0x1000); memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops, @@ -660,6 +680,16 @@ static const VMStateDescription iotkit_secctl_ppc_vmstate = { } }; +static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = { + .name = "iotkit-secctl-mpcintstatus", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(mpcintstatus, IoTKitSecCtl), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription iotkit_secctl_vmstate = { .name = "iotkit-secctl", .version_id = 1, @@ -677,7 +707,11 @@ static const VMStateDescription iotkit_secctl_vmstate = { VMSTATE_STRUCT_ARRAY(ahbexp, IoTKitSecCtl, IOTS_NUM_AHB_EXP_PPC, 1, iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), VMSTATE_END_OF_LIST() - } + }, + .subsections = (const VMStateDescription*[]) { + &iotkit_secctl_mpcintstatus_vmstate, + NULL + }, }; static void iotkit_secctl_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/misc/iotkit-secctl.h b/include/hw/misc/iotkit-secctl.h index faad0c9190..082c14c925 100644 --- a/include/hw/misc/iotkit-secctl.h +++ b/include/hw/misc/iotkit-secctl.h @@ -39,6 +39,11 @@ * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + * Controlling the MPC in the IoTKit: + * + named GPIO input mpc_status + * Controlling each of the 16 expansion MPCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mpcexp_status[0..15] */ #ifndef IOTKIT_SECCTL_H @@ -55,6 +60,8 @@ #define IOTS_NUM_APB_PPC 2 #define IOTS_NUM_APB_EXP_PPC 4 #define IOTS_NUM_AHB_EXP_PPC 4 +#define IOTS_NUM_EXP_MPC 16 +#define IOTS_NUM_MPC 1 typedef struct IoTKitSecCtl IoTKitSecCtl; @@ -94,6 +101,7 @@ struct IoTKitSecCtl { uint32_t secrespcfg; uint32_t nsccfg; uint32_t brginten; + uint32_t mpcintstatus; IoTKitSecCtlPPC apb[IOTS_NUM_APB_PPC]; IoTKitSecCtlPPC apbexp[IOTS_NUM_APB_EXP_PPC]; From af60b29183208123d9aab25acf47e44d54d12a9d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 22/28] hw/arm/iotkit: Instantiate MPC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up the one MPC that is part of the IoTKit itself. For the moment we don't wire up its interrupt line. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180620132032.28865-7-peter.maydell@linaro.org --- hw/arm/iotkit.c | 38 +++++++++++++++++++++++++++----------- include/hw/arm/iotkit.h | 2 ++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 234185e8f7..160e40c744 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -130,6 +130,7 @@ static void iotkit_init(Object *obj) TYPE_TZ_PPC); init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), TYPE_TZ_PPC); + init_sysbus_child(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0), TYPE_CMSDK_APB_TIMER); init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1), @@ -266,15 +267,6 @@ static void iotkit_realize(DeviceState *dev, Error **errp) */ make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); - /* This RAM should be behind a Memory Protection Controller, but we - * don't implement that yet. - */ - memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); - if (err) { - error_propagate(errp, err); - return; - } - memory_region_add_subregion(&s->container, 0x20000000, &s->sram0); /* Security controller */ object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); @@ -310,6 +302,32 @@ static void iotkit_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, qdev_get_gpio_in(dev_splitter, 0)); + /* This RAM lives behind the Memory Protection Controller */ + memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_link(OBJECT(&s->mpc), OBJECT(&s->sram0), + "downstream", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* Map the upstream end of the MPC into the right place... */ + memory_region_add_subregion(&s->container, 0x20000000, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), + 1)); + /* ...and its register interface */ + memory_region_add_subregion(&s->container, 0x50083000, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), + 0)); + /* Devices behind APB PPC0: * 0x40000000: timer0 * 0x40001000: timer1 @@ -473,8 +491,6 @@ static void iotkit_realize(DeviceState *dev, Error **errp) create_unimplemented_device("NS watchdog", 0x40081000, 0x1000); create_unimplemented_device("S watchdog", 0x50081000, 0x1000); - create_unimplemented_device("SRAM0 MPC", 0x50083000, 0x1000); - for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h index c6129d926b..b21cf1ab9d 100644 --- a/include/hw/arm/iotkit.h +++ b/include/hw/arm/iotkit.h @@ -51,6 +51,7 @@ #include "hw/arm/armv7m.h" #include "hw/misc/iotkit-secctl.h" #include "hw/misc/tz-ppc.h" +#include "hw/misc/tz-mpc.h" #include "hw/timer/cmsdk-apb-timer.h" #include "hw/misc/unimp.h" #include "hw/or-irq.h" @@ -74,6 +75,7 @@ typedef struct IoTKit { IoTKitSecCtl secctl; TZPPC apb_ppc0; TZPPC apb_ppc1; + TZMPC mpc; CMSDKAPBTIMER timer0; CMSDKAPBTIMER timer1; qemu_or_irq ppc_irq_orgate; From bb75e16d5e67a764e008d0b73b4ec9f905e5b408 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:40 +0100 Subject: [PATCH 23/28] hw/arm/iotkit: Wire up MPC interrupt lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interrupt outputs from the MPC in the IoTKit and the expansion MPCs in the board must be wired up to the security controller, and also all ORed together to produce a single line to the NVIC. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180620132032.28865-8-peter.maydell@linaro.org --- hw/arm/iotkit.c | 74 +++++++++++++++++++++++++++++++++++++++++ include/hw/arm/iotkit.h | 6 ++++ 2 files changed, 80 insertions(+) diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 160e40c744..133d5bb34f 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -131,6 +131,18 @@ static void iotkit_init(Object *obj) init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), TYPE_TZ_PPC); init_sysbus_child(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); + object_initialize(&s->mpc_irq_orgate, sizeof(s->mpc_irq_orgate), + TYPE_OR_IRQ); + object_property_add_child(obj, "mpc-irq-orgate", + OBJECT(&s->mpc_irq_orgate), &error_abort); + for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) { + char *name = g_strdup_printf("mpc-irq-splitter-%d", i); + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + + object_initialize(splitter, sizeof(*splitter), TYPE_SPLIT_IRQ); + object_property_add_child(obj, name, OBJECT(splitter), &error_abort); + g_free(name); + } init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0), TYPE_CMSDK_APB_TIMER); init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1), @@ -163,6 +175,12 @@ static void iotkit_exp_irq(void *opaque, int n, int level) qemu_set_irq(s->exp_irqs[n], level); } +static void iotkit_mpcexp_status(void *opaque, int n, int level) +{ + IoTKit *s = IOTKIT(opaque); + qemu_set_irq(s->mpcexp_status_in[n], level); +} + static void iotkit_realize(DeviceState *dev, Error **errp) { IoTKit *s = IOTKIT(dev); @@ -328,6 +346,22 @@ static void iotkit_realize(DeviceState *dev, Error **errp) sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), 0)); + /* We must OR together lines from the MPC splitters to go to the NVIC */ + object_property_set_int(OBJECT(&s->mpc_irq_orgate), + IOTS_NUM_EXP_MPC + IOTS_NUM_MPC, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 9)); + /* Devices behind APB PPC0: * 0x40000000: timer0 * 0x40001000: timer1 @@ -536,6 +570,46 @@ static void iotkit_realize(DeviceState *dev, Error **errp) g_free(gpioname); } + /* Wire up the splitters for the MPC IRQs */ + for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) { + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + DeviceState *dev_splitter = DEVICE(splitter); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(splitter), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (i < IOTS_NUM_EXP_MPC) { + /* Splitter input is from GPIO input line */ + s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpcexp_status", i)); + } else { + /* Splitter input is from our own MPC */ + qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0, + qdev_get_gpio_in(dev_splitter, 0)); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpc_status", 0)); + } + + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); + } + /* Create GPIO inputs which will pass the line state for our + * mpcexp_irq inputs to the correct splitter devices. + */ + qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status", + IOTS_NUM_EXP_MPC); + iotkit_forward_sec_resp_cfg(s); system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h index b21cf1ab9d..2cddde55dd 100644 --- a/include/hw/arm/iotkit.h +++ b/include/hw/arm/iotkit.h @@ -42,6 +42,9 @@ * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + * Controlling each of the 16 expansion MPCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mpcexp_status[0..15] */ #ifndef IOTKIT_H @@ -81,6 +84,8 @@ typedef struct IoTKit { qemu_or_irq ppc_irq_orgate; SplitIRQ sec_resp_splitter; SplitIRQ ppc_irq_splitter[NUM_PPCS]; + SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; + qemu_or_irq mpc_irq_orgate; UnimplementedDeviceState dualtimer; UnimplementedDeviceState s32ktimer; @@ -99,6 +104,7 @@ typedef struct IoTKit { qemu_irq nsc_cfg_in; qemu_irq irq_status_in[NUM_EXTERNAL_PPCS]; + qemu_irq mpcexp_status_in[IOTS_NUM_EXP_MPC]; uint32_t nsccfg; From 665670aa3ae85444e70888654e7af1eda19598b9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:41 +0100 Subject: [PATCH 24/28] hw/arm/mps2-tz.c: Instantiate MPCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instantiate and wire up the Memory Protection Controllers in the MPS2 board itself. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20180620132032.28865-9-peter.maydell@linaro.org --- hw/arm/mps2-tz.c | 71 ++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index c5ef95e4cc..22180c56fb 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -44,6 +44,7 @@ #include "hw/timer/cmsdk-apb-timer.h" #include "hw/misc/mps2-scc.h" #include "hw/misc/mps2-fpgaio.h" +#include "hw/misc/tz-mpc.h" #include "hw/arm/iotkit.h" #include "hw/devices.h" #include "net/net.h" @@ -64,13 +65,12 @@ typedef struct { IoTKit iotkit; MemoryRegion psram; - MemoryRegion ssram1; + MemoryRegion ssram[3]; MemoryRegion ssram1_m; - MemoryRegion ssram23; MPS2SCC scc; MPS2FPGAIO fpgaio; TZPPC ppc[5]; - UnimplementedDeviceState ssram_mpc[3]; + TZMPC ssram_mpc[3]; UnimplementedDeviceState spi[5]; UnimplementedDeviceState i2c[4]; UnimplementedDeviceState i2s_audio; @@ -96,16 +96,6 @@ typedef struct { /* Main SYSCLK frequency in Hz */ #define SYSCLK_FRQ 20000000 -/* Initialize the auxiliary RAM region @mr and map it into - * the memory map at @base. - */ -static void make_ram(MemoryRegion *mr, const char *name, - hwaddr base, hwaddr size) -{ - memory_region_init_ram(mr, NULL, name, size, &error_fatal); - memory_region_add_subregion(get_system_memory(), base, mr); -} - /* Create an alias of an entire original MemoryRegion @orig * located at @base in the memory map. */ @@ -245,6 +235,44 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(s, 0); } +static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + TZMPC *mpc = opaque; + int i = mpc - &mms->ssram_mpc[0]; + MemoryRegion *ssram = &mms->ssram[i]; + MemoryRegion *upstream; + char *mpcname = g_strdup_printf("%s-mpc", name); + static uint32_t ramsize[] = { 0x00400000, 0x00200000, 0x00200000 }; + static uint32_t rambase[] = { 0x00000000, 0x28000000, 0x28200000 }; + + memory_region_init_ram(ssram, NULL, name, ramsize[i], &error_fatal); + + init_sysbus_child(OBJECT(mms), mpcname, mpc, + sizeof(mms->ssram_mpc[0]), TYPE_TZ_MPC); + object_property_set_link(OBJECT(mpc), OBJECT(ssram), + "downstream", &error_fatal); + object_property_set_bool(OBJECT(mpc), true, "realized", &error_fatal); + /* Map the upstream end of the MPC into system memory */ + upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 1); + memory_region_add_subregion(get_system_memory(), rambase[i], upstream); + /* and connect its interrupt to the IoTKit */ + qdev_connect_gpio_out_named(DEVICE(mpc), "irq", 0, + qdev_get_gpio_in_named(DEVICE(&mms->iotkit), + "mpcexp_status", i)); + + /* The first SSRAM is a special case as it has an alias; accesses to + * the alias region at 0x00400000 must also go to the MPC upstream. + */ + if (i == 0) { + make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", upstream, 0x00400000); + } + + g_free(mpcname); + /* Return the register interface MR for our caller to map behind the PPC */ + return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -306,14 +334,6 @@ static void mps2tz_common_init(MachineState *machine) NULL, "mps.ram", 0x01000000); memory_region_add_subregion(system_memory, 0x80000000, &mms->psram); - /* The SSRAM memories should all be behind Memory Protection Controllers, - * but we don't implement that yet. - */ - make_ram(&mms->ssram1, "mps.ssram1", 0x00000000, 0x00400000); - make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x00400000); - - make_ram(&mms->ssram23, "mps.ssram23", 0x28000000, 0x00400000); - /* The overflow IRQs for all UARTs are ORed together. * Tx, Rx and "combined" IRQs are sent to the NVIC separately. * Create the OR gate for this. @@ -343,12 +363,9 @@ static void mps2tz_common_init(MachineState *machine) const PPCInfo ppcs[] = { { .name = "apb_ppcexp0", .ports = { - { "ssram-mpc0", make_unimp_dev, &mms->ssram_mpc[0], - 0x58007000, 0x1000 }, - { "ssram-mpc1", make_unimp_dev, &mms->ssram_mpc[1], - 0x58008000, 0x1000 }, - { "ssram-mpc2", make_unimp_dev, &mms->ssram_mpc[2], - 0x58009000, 0x1000 }, + { "ssram-0", make_mpc, &mms->ssram_mpc[0], 0x58007000, 0x1000 }, + { "ssram-1", make_mpc, &mms->ssram_mpc[1], 0x58008000, 0x1000 }, + { "ssram-2", make_mpc, &mms->ssram_mpc[2], 0x58009000, 0x1000 }, }, }, { .name = "apb_ppcexp1", From cc2ae7c9de14efd72c6205825eb7cd980ac09c11 Mon Sep 17 00:00:00 2001 From: Julia Suvorova Date: Fri, 22 Jun 2018 13:28:41 +0100 Subject: [PATCH 25/28] target/arm: Introduce ARM_FEATURE_M_MAIN This feature is intended to distinguish ARMv8-M variants: Baseline and Mainline. ARMv7-M compatibility requires the Main Extension. ARMv6-M compatibility is provided by all ARMv8-M implementations. Signed-off-by: Julia Suvorova Message-id: 20180622080138.17702-2-jusual@mail.ru Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu.c | 3 +++ target/arm/cpu.h | 1 + 2 files changed, 4 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 81c1d22b14..2ae4fffafb 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1238,6 +1238,7 @@ static void cortex_m3_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); cpu->midr = 0x410fc231; cpu->pmsav7_dregion = 8; cpu->id_pfr0 = 0x00000030; @@ -1262,6 +1263,7 @@ static void cortex_m4_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); cpu->midr = 0x410fc240; /* r0p0 */ cpu->pmsav7_dregion = 8; @@ -1287,6 +1289,7 @@ static void cortex_m33_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); cpu->midr = 0x410fd213; /* r0p3 */ diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 8488273c5b..a4507a2d6f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1482,6 +1482,7 @@ enum arm_features { ARM_FEATURE_V8_RDM, /* implements v8.1 simd round multiply */ ARM_FEATURE_V8_FP16, /* implements v8.2 half-precision float */ ARM_FEATURE_V8_FCMA, /* has complex number part of v8.3 extensions. */ + ARM_FEATURE_M_MAIN, /* M profile Main Extension */ }; static inline int arm_feature(CPUARMState *env, int feature) From 2aeba0d007d33efa12a6339bb140aa634e0d52eb Mon Sep 17 00:00:00 2001 From: Julia Suvorova Date: Fri, 22 Jun 2018 13:28:41 +0100 Subject: [PATCH 26/28] target/arm: Strict alignment for ARMv6-M and ARMv8-M Baseline Unlike ARMv7-M, ARMv6-M and ARMv8-M Baseline only supports naturally aligned memory accesses for load/store instructions. Signed-off-by: Julia Suvorova Message-id: 20180622080138.17702-3-jusual@mail.ru Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/translate.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index b988d379e7..2a3e4f5d4c 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -1100,7 +1100,14 @@ static inline TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, TCGMemOp op) static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, int index, TCGMemOp opc) { - TCGv addr = gen_aa32_addr(s, a32, opc); + TCGv addr; + + if (arm_dc_feature(s, ARM_FEATURE_M) && + !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) { + opc |= MO_ALIGN; + } + + addr = gen_aa32_addr(s, a32, opc); tcg_gen_qemu_ld_i32(val, addr, index, opc); tcg_temp_free(addr); } @@ -1108,7 +1115,14 @@ static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, static void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, int index, TCGMemOp opc) { - TCGv addr = gen_aa32_addr(s, a32, opc); + TCGv addr; + + if (arm_dc_feature(s, ARM_FEATURE_M) && + !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) { + opc |= MO_ALIGN; + } + + addr = gen_aa32_addr(s, a32, opc); tcg_gen_qemu_st_i32(val, addr, index, opc); tcg_temp_free(addr); } From b8d880ba6dcca098dc3a5d345a13bebdcce89c90 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:42 +0100 Subject: [PATCH 27/28] vl.c: Don't zero-initialize statics for serial_hds checkpatch reminds us that statics shouldn't be zero-initialized: ERROR: do not initialise statics to 0 or NULL #35: FILE: vl.c:157: +static int num_serial_hds = 0; ERROR: do not initialise statics to 0 or NULL #36: FILE: vl.c:158: +static Chardev **serial_hds = NULL; I forgot to fix this in 6af2692e86f9fdfb3d; do so now. Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Message-id: 20180426140253.3918-1-peter.maydell@linaro.org --- vl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vl.c b/vl.c index b3426e03d0..d451f45dc5 100644 --- a/vl.c +++ b/vl.c @@ -151,8 +151,8 @@ QEMUClockType rtc_clock; int vga_interface_type = VGA_NONE; static DisplayOptions dpy; int no_frame; -static int num_serial_hds = 0; -static Chardev **serial_hds = NULL; +static int num_serial_hds; +static Chardev **serial_hds; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES]; int win2k_install_hack = 0; From 6dad8260e82b69bd278685ee25209f5824360455 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 22 Jun 2018 13:28:42 +0100 Subject: [PATCH 28/28] xen: Don't use memory_region_init_ram_nomigrate() in pci_assign_dev_load_option_rom() The xen pci_assign_dev_load_option_rom() currently creates a RAM memory region with memory_region_init_ram_nomigrate(), and then manually registers it with vmstate_register_ram(). In fact for its only callsite, the 'owner' pointer we use for the init call and the '&dev->qdev' pointer we use for the vmstate_register_ram() call refer to the same object. Simplify the function to only take a pointer to the device once instead of twice, and use memory_region_init_ram() which automatically does the vmstate register for us. Acked-by: Anthony PERARD Signed-off-by: Peter Maydell --- hw/xen/xen_pt.h | 2 +- hw/xen/xen_pt_graphics.c | 2 +- hw/xen/xen_pt_load_rom.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index aa39a9aa5f..dbee3308fd 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -319,7 +319,7 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) } extern void *pci_assign_dev_load_option_rom(PCIDevice *dev, - struct Object *owner, int *size, + int *size, unsigned int domain, unsigned int bus, unsigned int slot, unsigned int function); diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index 0f4c8d77e2..135c8df1e7 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -132,7 +132,7 @@ int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) static void *get_vgabios(XenPCIPassthroughState *s, int *size, XenHostPCIDevice *dev) { - return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size, + return pci_assign_dev_load_option_rom(&s->dev, size, dev->domain, dev->bus, dev->dev, dev->func); } diff --git a/hw/xen/xen_pt_load_rom.c b/hw/xen/xen_pt_load_rom.c index 71063c4d79..e6a86ca818 100644 --- a/hw/xen/xen_pt_load_rom.c +++ b/hw/xen/xen_pt_load_rom.c @@ -19,7 +19,7 @@ * load the corresponding ROM data to RAM. If an error occurs while loading an * option ROM, we just ignore that option ROM and continue with the next one. */ -void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, +void *pci_assign_dev_load_option_rom(PCIDevice *dev, int *size, unsigned int domain, unsigned int bus, unsigned int slot, unsigned int function) @@ -29,6 +29,7 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, uint8_t val; struct stat st; void *ptr = NULL; + Object *owner = OBJECT(dev); /* If loading ROM from file, pci handles it */ if (dev->romfile || !dev->rom_bar) { @@ -59,8 +60,7 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, fseek(fp, 0, SEEK_SET); snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner)); - memory_region_init_ram_nomigrate(&dev->rom, owner, name, st.st_size, &error_abort); - vmstate_register_ram(&dev->rom, &dev->qdev); + memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort); ptr = memory_region_get_ram_ptr(&dev->rom); memset(ptr, 0xff, st.st_size);