mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20200824' into staging
target-arm queue: * hw/cpu/a9mpcore: Verify the machine use Cortex-A9 cores * hw/arm/smmuv3: Implement SMMUv3.2 range-invalidation * docs/system/arm: Document the Xilinx Versal Virt board * target/arm: Make M-profile NOCP take precedence over UNDEF * target/arm: Use correct FPST for VCMLA, VCADD on fp16 * target/arm: Various cleanups preparing for fp16 support # gpg: Signature made Mon 24 Aug 2020 10:47:14 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20200824: (27 commits) target/arm: Use correct FPST for VCMLA, VCADD on fp16 target/arm: Implement FPST_STD_F16 fpstatus target/arm: Make A32/T32 use new fpstatus_ptr() API target/arm: Replace A64 get_fpstatus_ptr() with generic fpstatus_ptr() target/arm: Delete unused ARM_FEATURE_CRC target/arm/translate.c: Delete/amend incorrect comments target/arm: Delete unused VFP_DREG macros target/arm: Remove ARCH macro target/arm: Convert T32 coprocessor insns to decodetree target/arm: Do M-profile NOCP checks early and via decodetree target/arm: Tidy up disas_arm_insn() target/arm: Convert A32 coprocessor insns to decodetree target/arm: Separate decode from handling of coproc insns target/arm: Pull handling of XScale insns out of disas_coproc_insn() docs/system/arm: Document the Xilinx Versal Virt board hw/arm/smmuv3: Advertise SMMUv3.2 range invalidation hw/arm/smmuv3: Support HAD and advertise SMMUv3.1 support hw/arm/smmuv3: Let AIDR advertise SMMUv3.0 support hw/arm/smmuv3: Fix IIDR offset hw/arm/smmuv3: Get prepared for range invalidation ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+2
-1
@@ -894,7 +894,7 @@ F: hw/misc/zynq*
|
||||
F: include/hw/misc/zynq*
|
||||
X: hw/ssi/xilinx_*
|
||||
|
||||
Xilinx ZynqMP
|
||||
Xilinx ZynqMP and Versal
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -905,6 +905,7 @@ F: include/hw/*/xlnx*.h
|
||||
F: include/hw/ssi/xilinx_spips.h
|
||||
F: hw/display/dpcd.c
|
||||
F: include/hw/display/dpcd.h
|
||||
F: docs/system/arm/xlnx-versal-virt.rst
|
||||
|
||||
ARM ACPI Subsystem
|
||||
M: Shannon Zhao <shannon.zhaosl@gmail.com>
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
Xilinx Versal Virt (``xlnx-versal-virt``)
|
||||
=========================================
|
||||
|
||||
Xilinx Versal is a family of heterogeneous multi-core SoCs
|
||||
(System on Chip) that combine traditional hardened CPUs and I/O
|
||||
peripherals in a Processing System (PS) with runtime programmable
|
||||
FPGA logic (PL) and an Artificial Intelligence Engine (AIE).
|
||||
|
||||
More details here:
|
||||
https://www.xilinx.com/products/silicon-devices/acap/versal.html
|
||||
|
||||
The family of Versal SoCs share a single architecture but come in
|
||||
different parts with different speed grades, amounts of PL and
|
||||
other differences.
|
||||
|
||||
The Xilinx Versal Virt board in QEMU is a model of a virtual board
|
||||
(does not exist in reality) with a virtual Versal SoC without I/O
|
||||
limitations. Currently, we support the following cores and devices:
|
||||
|
||||
Implemented CPU cores:
|
||||
|
||||
- 2 ACPUs (ARM Cortex-A72)
|
||||
|
||||
Implemented devices:
|
||||
|
||||
- Interrupt controller (ARM GICv3)
|
||||
- 2 UARTs (ARM PL011)
|
||||
- An RTC (Versal built-in)
|
||||
- 2 GEMs (Cadence MACB Ethernet MACs)
|
||||
- 8 ADMA (Xilinx zDMA) channels
|
||||
- 2 SD Controllers
|
||||
- OCM (256KB of On Chip Memory)
|
||||
- DDR memory
|
||||
|
||||
QEMU does not yet model any other devices, including the PL and the AI Engine.
|
||||
|
||||
Other differences between the hardware and the QEMU model:
|
||||
|
||||
- QEMU allows the amount of DDR memory provided to be specified with the
|
||||
``-m`` argument. If a DTB is provided on the command line then QEMU will
|
||||
edit it to include suitable entries describing the Versal DDR memory ranges.
|
||||
|
||||
- QEMU provides 8 virtio-mmio virtio transports; these start at
|
||||
address ``0xa0000000`` and have IRQs from 111 and upwards.
|
||||
|
||||
Running
|
||||
"""""""
|
||||
If the user provides an Operating System to be loaded, we expect users
|
||||
to use the ``-kernel`` command line option.
|
||||
|
||||
Users can load firmware or boot-loaders with the ``-device loader`` options.
|
||||
|
||||
When loading an OS, QEMU generates a DTB and selects an appropriate address
|
||||
where it gets loaded. This DTB will be passed to the kernel in register x0.
|
||||
|
||||
If there's no ``-kernel`` option, we generate a DTB and place it at 0x1000
|
||||
for boot-loaders or firmware to pick it up.
|
||||
|
||||
If users want to provide their own DTB, they can use the ``-dtb`` option.
|
||||
These DTBs will have their memory nodes modified to match QEMU's
|
||||
selected ram_size option before they get passed to the kernel or FW.
|
||||
|
||||
When loading an OS, we turn on QEMU's PSCI implementation with SMC
|
||||
as the PSCI conduit. When there's no ``-kernel`` option, we assume the user
|
||||
provides EL3 firmware to handle PSCI.
|
||||
|
||||
A few examples:
|
||||
|
||||
Direct Linux boot of a generic ARM64 upstream Linux kernel:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-aarch64 -M xlnx-versal-virt -m 2G \
|
||||
-serial mon:stdio -display none \
|
||||
-kernel arch/arm64/boot/Image \
|
||||
-nic user -nic user \
|
||||
-device virtio-rng-device,bus=virtio-mmio-bus.0 \
|
||||
-drive if=none,index=0,file=hd0.qcow2,id=hd0,snapshot \
|
||||
-drive file=qemu_sd.qcow2,if=sd,index=0,snapshot \
|
||||
-device virtio-blk-device,drive=hd0 -append root=/dev/vda
|
||||
|
||||
Direct Linux boot of PetaLinux 2019.2:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-aarch64 -M xlnx-versal-virt -m 2G \
|
||||
-serial mon:stdio -display none \
|
||||
-kernel petalinux-v2019.2/Image \
|
||||
-append "rdinit=/sbin/init console=ttyAMA0,115200n8 earlycon=pl011,mmio,0xFF000000,115200n8" \
|
||||
-net nic,model=cadence_gem,netdev=net0 -netdev user,id=net0 \
|
||||
-device virtio-rng-device,bus=virtio-mmio-bus.0,rng=rng0 \
|
||||
-object rng-random,filename=/dev/urandom,id=rng0
|
||||
|
||||
Boot PetaLinux 2019.2 via ARM Trusted Firmware (2018.3 because the 2019.2
|
||||
version of ATF tries to configure the CCI which we don't model) and U-boot:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-aarch64 -M xlnx-versal-virt -m 2G \
|
||||
-serial stdio -display none \
|
||||
-device loader,file=petalinux-v2018.3/bl31.elf,cpu-num=0 \
|
||||
-device loader,file=petalinux-v2019.2/u-boot.elf \
|
||||
-device loader,addr=0x20000000,file=petalinux-v2019.2/Image \
|
||||
-nic user -nic user \
|
||||
-device virtio-rng-device,bus=virtio-mmio-bus.0,rng=rng0 \
|
||||
-object rng-random,filename=/dev/urandom,id=rng0
|
||||
|
||||
Run the following at the U-Boot prompt:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
Versal>
|
||||
fdt addr $fdtcontroladdr
|
||||
fdt move $fdtcontroladdr 0x40000000
|
||||
fdt set /timer clock-frequency <0x3dfd240>
|
||||
setenv bootargs "rdinit=/sbin/init maxcpus=1 console=ttyAMA0,115200n8 earlycon=pl011,mmio,0xFF000000,115200n8"
|
||||
booti 20000000 - 40000000
|
||||
fdt addr $fdtcontroladdr
|
||||
|
||||
Boot Linux as DOM0 on Xen via U-Boot:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-aarch64 -M xlnx-versal-virt -m 4G \
|
||||
-serial stdio -display none \
|
||||
-device loader,file=petalinux-v2019.2/u-boot.elf,cpu-num=0 \
|
||||
-device loader,addr=0x30000000,file=linux/2018-04-24/xen \
|
||||
-device loader,addr=0x40000000,file=petalinux-v2019.2/Image \
|
||||
-nic user -nic user \
|
||||
-device virtio-rng-device,bus=virtio-mmio-bus.0,rng=rng0 \
|
||||
-object rng-random,filename=/dev/urandom,id=rng0
|
||||
|
||||
Run the following at the U-Boot prompt:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
Versal>
|
||||
fdt addr $fdtcontroladdr
|
||||
fdt move $fdtcontroladdr 0x20000000
|
||||
fdt set /timer clock-frequency <0x3dfd240>
|
||||
fdt set /chosen xen,xen-bootargs "console=dtuart dtuart=/uart@ff000000 dom0_mem=640M bootscrub=0 maxcpus=1 timer_slop=0"
|
||||
fdt set /chosen xen,dom0-bootargs "rdinit=/sbin/init clk_ignore_unused console=hvc0 maxcpus=1"
|
||||
fdt mknode /chosen dom0
|
||||
fdt set /chosen/dom0 compatible "xen,multiboot-module"
|
||||
fdt set /chosen/dom0 reg <0x00000000 0x40000000 0x0 0x03100000>
|
||||
booti 30000000 - 20000000
|
||||
|
||||
Boot Linux as Dom0 on Xen via ARM Trusted Firmware and U-Boot:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-aarch64 -M xlnx-versal-virt -m 4G \
|
||||
-serial stdio -display none \
|
||||
-device loader,file=petalinux-v2018.3/bl31.elf,cpu-num=0 \
|
||||
-device loader,file=petalinux-v2019.2/u-boot.elf \
|
||||
-device loader,addr=0x30000000,file=linux/2018-04-24/xen \
|
||||
-device loader,addr=0x40000000,file=petalinux-v2019.2/Image \
|
||||
-nic user -nic user \
|
||||
-device virtio-rng-device,bus=virtio-mmio-bus.0,rng=rng0 \
|
||||
-object rng-random,filename=/dev/urandom,id=rng0
|
||||
|
||||
Run the following at the U-Boot prompt:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
Versal>
|
||||
fdt addr $fdtcontroladdr
|
||||
fdt move $fdtcontroladdr 0x20000000
|
||||
fdt set /timer clock-frequency <0x3dfd240>
|
||||
fdt set /chosen xen,xen-bootargs "console=dtuart dtuart=/uart@ff000000 dom0_mem=640M bootscrub=0 maxcpus=1 timer_slop=0"
|
||||
fdt set /chosen xen,dom0-bootargs "rdinit=/sbin/init clk_ignore_unused console=hvc0 maxcpus=1"
|
||||
fdt mknode /chosen dom0
|
||||
fdt set /chosen/dom0 compatible "xen,multiboot-module"
|
||||
fdt set /chosen/dom0 reg <0x00000000 0x40000000 0x0 0x03100000>
|
||||
booti 30000000 - 20000000
|
||||
|
||||
@@ -93,6 +93,7 @@ undocumented; you can get a complete list by running
|
||||
arm/sx1
|
||||
arm/stellaris
|
||||
arm/virt
|
||||
arm/xlnx-versal-virt
|
||||
|
||||
Arm CPU features
|
||||
================
|
||||
|
||||
+145
-69
@@ -32,6 +32,91 @@
|
||||
|
||||
/* IOTLB Management */
|
||||
|
||||
static guint smmu_iotlb_key_hash(gconstpointer v)
|
||||
{
|
||||
SMMUIOTLBKey *key = (SMMUIOTLBKey *)v;
|
||||
uint32_t a, b, c;
|
||||
|
||||
/* Jenkins hash */
|
||||
a = b = c = JHASH_INITVAL + sizeof(*key);
|
||||
a += key->asid + key->level + key->tg;
|
||||
b += extract64(key->iova, 0, 32);
|
||||
c += extract64(key->iova, 32, 32);
|
||||
|
||||
__jhash_mix(a, b, c);
|
||||
__jhash_final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2)
|
||||
{
|
||||
SMMUIOTLBKey *k1 = (SMMUIOTLBKey *)v1, *k2 = (SMMUIOTLBKey *)v2;
|
||||
|
||||
return (k1->asid == k2->asid) && (k1->iova == k2->iova) &&
|
||||
(k1->level == k2->level) && (k1->tg == k2->tg);
|
||||
}
|
||||
|
||||
SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova,
|
||||
uint8_t tg, uint8_t level)
|
||||
{
|
||||
SMMUIOTLBKey key = {.asid = asid, .iova = iova, .tg = tg, .level = level};
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
|
||||
SMMUTransTableInfo *tt, hwaddr iova)
|
||||
{
|
||||
uint8_t tg = (tt->granule_sz - 10) / 2;
|
||||
uint8_t inputsize = 64 - tt->tsz;
|
||||
uint8_t stride = tt->granule_sz - 3;
|
||||
uint8_t level = 4 - (inputsize - 4) / stride;
|
||||
SMMUTLBEntry *entry = NULL;
|
||||
|
||||
while (level <= 3) {
|
||||
uint64_t subpage_size = 1ULL << level_shift(level, tt->granule_sz);
|
||||
uint64_t mask = subpage_size - 1;
|
||||
SMMUIOTLBKey key;
|
||||
|
||||
key = smmu_get_iotlb_key(cfg->asid, iova & ~mask, tg, level);
|
||||
entry = g_hash_table_lookup(bs->iotlb, &key);
|
||||
if (entry) {
|
||||
break;
|
||||
}
|
||||
level++;
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
cfg->iotlb_hits++;
|
||||
trace_smmu_iotlb_lookup_hit(cfg->asid, iova,
|
||||
cfg->iotlb_hits, cfg->iotlb_misses,
|
||||
100 * cfg->iotlb_hits /
|
||||
(cfg->iotlb_hits + cfg->iotlb_misses));
|
||||
} else {
|
||||
cfg->iotlb_misses++;
|
||||
trace_smmu_iotlb_lookup_miss(cfg->asid, iova,
|
||||
cfg->iotlb_hits, cfg->iotlb_misses,
|
||||
100 * cfg->iotlb_hits /
|
||||
(cfg->iotlb_hits + cfg->iotlb_misses));
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new)
|
||||
{
|
||||
SMMUIOTLBKey *key = g_new0(SMMUIOTLBKey, 1);
|
||||
uint8_t tg = (new->granule - 10) / 2;
|
||||
|
||||
if (g_hash_table_size(bs->iotlb) >= SMMU_IOTLB_MAX_SIZE) {
|
||||
smmu_iotlb_inv_all(bs);
|
||||
}
|
||||
|
||||
*key = smmu_get_iotlb_key(cfg->asid, new->entry.iova, tg, new->level);
|
||||
trace_smmu_iotlb_insert(cfg->asid, new->entry.iova, tg, new->level);
|
||||
g_hash_table_insert(bs->iotlb, key, new);
|
||||
}
|
||||
|
||||
inline void smmu_iotlb_inv_all(SMMUState *s)
|
||||
{
|
||||
trace_smmu_iotlb_inv_all();
|
||||
@@ -44,15 +129,44 @@ static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value,
|
||||
uint16_t asid = *(uint16_t *)user_data;
|
||||
SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key;
|
||||
|
||||
return iotlb_key->asid == asid;
|
||||
return SMMU_IOTLB_ASID(*iotlb_key) == asid;
|
||||
}
|
||||
|
||||
inline void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova)
|
||||
static gboolean smmu_hash_remove_by_asid_iova(gpointer key, gpointer value,
|
||||
gpointer user_data)
|
||||
{
|
||||
SMMUIOTLBKey key = {.asid = asid, .iova = iova};
|
||||
SMMUTLBEntry *iter = (SMMUTLBEntry *)value;
|
||||
IOMMUTLBEntry *entry = &iter->entry;
|
||||
SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data;
|
||||
SMMUIOTLBKey iotlb_key = *(SMMUIOTLBKey *)key;
|
||||
|
||||
trace_smmu_iotlb_inv_iova(asid, iova);
|
||||
g_hash_table_remove(s->iotlb, &key);
|
||||
if (info->asid >= 0 && info->asid != SMMU_IOTLB_ASID(iotlb_key)) {
|
||||
return false;
|
||||
}
|
||||
return ((info->iova & ~entry->addr_mask) == entry->iova) ||
|
||||
((entry->iova & ~info->mask) == info->iova);
|
||||
}
|
||||
|
||||
inline void
|
||||
smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
|
||||
uint8_t tg, uint64_t num_pages, uint8_t ttl)
|
||||
{
|
||||
if (ttl && (num_pages == 1)) {
|
||||
SMMUIOTLBKey key = smmu_get_iotlb_key(asid, iova, tg, ttl);
|
||||
|
||||
g_hash_table_remove(s->iotlb, &key);
|
||||
} else {
|
||||
/* if tg is not set we use 4KB range invalidation */
|
||||
uint8_t granule = tg ? tg * 2 + 10 : 12;
|
||||
|
||||
SMMUIOTLBPageInvInfo info = {
|
||||
.asid = asid, .iova = iova,
|
||||
.mask = (num_pages * 1 << granule) - 1};
|
||||
|
||||
g_hash_table_foreach_remove(s->iotlb,
|
||||
smmu_hash_remove_by_asid_iova,
|
||||
&info);
|
||||
}
|
||||
}
|
||||
|
||||
inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid)
|
||||
@@ -149,7 +263,7 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova)
|
||||
* @cfg: translation config
|
||||
* @iova: iova to translate
|
||||
* @perm: access type
|
||||
* @tlbe: IOMMUTLBEntry (out)
|
||||
* @tlbe: SMMUTLBEntry (out)
|
||||
* @info: handle to an error info
|
||||
*
|
||||
* Return 0 on success, < 0 on error. In case of error, @info is filled
|
||||
@@ -159,7 +273,7 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova)
|
||||
*/
|
||||
static int smmu_ptw_64(SMMUTransCfg *cfg,
|
||||
dma_addr_t iova, IOMMUAccessFlags perm,
|
||||
IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info)
|
||||
SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info)
|
||||
{
|
||||
dma_addr_t baseaddr, indexmask;
|
||||
int stage = cfg->stage;
|
||||
@@ -179,14 +293,11 @@ static int smmu_ptw_64(SMMUTransCfg *cfg,
|
||||
baseaddr = extract64(tt->ttb, 0, 48);
|
||||
baseaddr &= ~indexmask;
|
||||
|
||||
tlbe->iova = iova;
|
||||
tlbe->addr_mask = (1 << granule_sz) - 1;
|
||||
|
||||
while (level <= 3) {
|
||||
uint64_t subpage_size = 1ULL << level_shift(level, granule_sz);
|
||||
uint64_t mask = subpage_size - 1;
|
||||
uint32_t offset = iova_level_offset(iova, inputsize, level, granule_sz);
|
||||
uint64_t pte;
|
||||
uint64_t pte, gpa;
|
||||
dma_addr_t pte_addr = baseaddr + offset * sizeof(pte);
|
||||
uint8_t ap;
|
||||
|
||||
@@ -199,60 +310,50 @@ static int smmu_ptw_64(SMMUTransCfg *cfg,
|
||||
if (is_invalid_pte(pte) || is_reserved_pte(pte, level)) {
|
||||
trace_smmu_ptw_invalid_pte(stage, level, baseaddr,
|
||||
pte_addr, offset, pte);
|
||||
info->type = SMMU_PTW_ERR_TRANSLATION;
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_page_pte(pte, level)) {
|
||||
uint64_t gpa = get_page_pte_address(pte, granule_sz);
|
||||
if (is_table_pte(pte, level)) {
|
||||
ap = PTE_APTABLE(pte);
|
||||
|
||||
ap = PTE_AP(pte);
|
||||
if (is_permission_fault(ap, perm)) {
|
||||
if (is_permission_fault(ap, perm) && !tt->had) {
|
||||
info->type = SMMU_PTW_ERR_PERMISSION;
|
||||
goto error;
|
||||
}
|
||||
|
||||
tlbe->translated_addr = gpa + (iova & mask);
|
||||
tlbe->perm = PTE_AP_TO_PERM(ap);
|
||||
baseaddr = get_table_pte_address(pte, granule_sz);
|
||||
level++;
|
||||
continue;
|
||||
} else if (is_page_pte(pte, level)) {
|
||||
gpa = get_page_pte_address(pte, granule_sz);
|
||||
trace_smmu_ptw_page_pte(stage, level, iova,
|
||||
baseaddr, pte_addr, pte, gpa);
|
||||
return 0;
|
||||
}
|
||||
if (is_block_pte(pte, level)) {
|
||||
} else {
|
||||
uint64_t block_size;
|
||||
hwaddr gpa = get_block_pte_address(pte, level, granule_sz,
|
||||
&block_size);
|
||||
|
||||
ap = PTE_AP(pte);
|
||||
if (is_permission_fault(ap, perm)) {
|
||||
info->type = SMMU_PTW_ERR_PERMISSION;
|
||||
goto error;
|
||||
}
|
||||
|
||||
gpa = get_block_pte_address(pte, level, granule_sz,
|
||||
&block_size);
|
||||
trace_smmu_ptw_block_pte(stage, level, baseaddr,
|
||||
pte_addr, pte, iova, gpa,
|
||||
block_size >> 20);
|
||||
|
||||
tlbe->translated_addr = gpa + (iova & mask);
|
||||
tlbe->perm = PTE_AP_TO_PERM(ap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* table pte */
|
||||
ap = PTE_APTABLE(pte);
|
||||
|
||||
ap = PTE_AP(pte);
|
||||
if (is_permission_fault(ap, perm)) {
|
||||
info->type = SMMU_PTW_ERR_PERMISSION;
|
||||
goto error;
|
||||
}
|
||||
baseaddr = get_table_pte_address(pte, granule_sz);
|
||||
level++;
|
||||
}
|
||||
|
||||
tlbe->entry.translated_addr = gpa;
|
||||
tlbe->entry.iova = iova & ~mask;
|
||||
tlbe->entry.addr_mask = mask;
|
||||
tlbe->entry.perm = PTE_AP_TO_PERM(ap);
|
||||
tlbe->level = level;
|
||||
tlbe->granule = granule_sz;
|
||||
return 0;
|
||||
}
|
||||
info->type = SMMU_PTW_ERR_TRANSLATION;
|
||||
|
||||
error:
|
||||
tlbe->perm = IOMMU_NONE;
|
||||
tlbe->entry.perm = IOMMU_NONE;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -268,7 +369,7 @@ error:
|
||||
* return 0 on success
|
||||
*/
|
||||
inline int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm,
|
||||
IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info)
|
||||
SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info)
|
||||
{
|
||||
if (!cfg->aa64) {
|
||||
/*
|
||||
@@ -361,31 +462,6 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static guint smmu_iotlb_key_hash(gconstpointer v)
|
||||
{
|
||||
SMMUIOTLBKey *key = (SMMUIOTLBKey *)v;
|
||||
uint32_t a, b, c;
|
||||
|
||||
/* Jenkins hash */
|
||||
a = b = c = JHASH_INITVAL + sizeof(*key);
|
||||
a += key->asid;
|
||||
b += extract64(key->iova, 0, 32);
|
||||
c += extract64(key->iova, 32, 32);
|
||||
|
||||
__jhash_mix(a, b, c);
|
||||
__jhash_final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2)
|
||||
{
|
||||
const SMMUIOTLBKey *k1 = v1;
|
||||
const SMMUIOTLBKey *k2 = v2;
|
||||
|
||||
return (k1->asid == k2->asid) && (k1->iova == k2->iova);
|
||||
}
|
||||
|
||||
/* Unmap the whole notifier's range */
|
||||
static void smmu_unmap_notifier_range(IOMMUNotifier *n)
|
||||
{
|
||||
|
||||
@@ -96,4 +96,12 @@ uint64_t iova_level_offset(uint64_t iova, int inputsize,
|
||||
MAKE_64BIT_MASK(0, gsz - 3);
|
||||
}
|
||||
|
||||
#define SMMU_IOTLB_ASID(key) ((key).asid)
|
||||
|
||||
typedef struct SMMUIOTLBPageInvInfo {
|
||||
int asid;
|
||||
uint64_t iova;
|
||||
uint64_t mask;
|
||||
} SMMUIOTLBPageInvInfo;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -54,6 +54,8 @@ REG32(IDR1, 0x4)
|
||||
|
||||
REG32(IDR2, 0x8)
|
||||
REG32(IDR3, 0xc)
|
||||
FIELD(IDR3, HAD, 2, 1);
|
||||
FIELD(IDR3, RIL, 10, 1);
|
||||
REG32(IDR4, 0x10)
|
||||
REG32(IDR5, 0x14)
|
||||
FIELD(IDR5, OAS, 0, 3);
|
||||
@@ -63,7 +65,8 @@ REG32(IDR5, 0x14)
|
||||
|
||||
#define SMMU_IDR5_OAS 4
|
||||
|
||||
REG32(IIDR, 0x1c)
|
||||
REG32(IIDR, 0x18)
|
||||
REG32(AIDR, 0x1c)
|
||||
REG32(CR0, 0x20)
|
||||
FIELD(CR0, SMMU_ENABLE, 0, 1)
|
||||
FIELD(CR0, EVENTQEN, 2, 1)
|
||||
@@ -298,6 +301,8 @@ enum { /* Command completion notification */
|
||||
};
|
||||
|
||||
#define CMD_TYPE(x) extract32((x)->word[0], 0 , 8)
|
||||
#define CMD_NUM(x) extract32((x)->word[0], 12 , 5)
|
||||
#define CMD_SCALE(x) extract32((x)->word[0], 20 , 5)
|
||||
#define CMD_SSEC(x) extract32((x)->word[0], 10, 1)
|
||||
#define CMD_SSV(x) extract32((x)->word[0], 11, 1)
|
||||
#define CMD_RESUME_AC(x) extract32((x)->word[0], 12, 1)
|
||||
@@ -310,6 +315,8 @@ enum { /* Command completion notification */
|
||||
#define CMD_RESUME_STAG(x) extract32((x)->word[2], 0 , 16)
|
||||
#define CMD_RESP(x) extract32((x)->word[2], 11, 2)
|
||||
#define CMD_LEAF(x) extract32((x)->word[2], 0 , 1)
|
||||
#define CMD_TTL(x) extract32((x)->word[2], 8 , 2)
|
||||
#define CMD_TG(x) extract32((x)->word[2], 10, 2)
|
||||
#define CMD_STE_RANGE(x) extract32((x)->word[2], 0 , 5)
|
||||
#define CMD_ADDR(x) ({ \
|
||||
uint64_t high = (uint64_t)(x)->word[3]; \
|
||||
@@ -573,6 +580,7 @@ static inline int pa_range(STE *ste)
|
||||
lo = (x)->word[(sel) * 2 + 2] & ~0xfULL; \
|
||||
hi | lo; \
|
||||
})
|
||||
#define CD_HAD(x, sel) extract32((x)->word[(sel) * 2 + 2], 1, 1)
|
||||
|
||||
#define CD_TSZ(x, sel) extract32((x)->word[0], (16 * (sel)) + 0, 6)
|
||||
#define CD_TG(x, sel) extract32((x)->word[0], (16 * (sel)) + 6, 2)
|
||||
|
||||
+72
-70
@@ -254,6 +254,9 @@ static void smmuv3_init_regs(SMMUv3State *s)
|
||||
s->idr[1] = FIELD_DP32(s->idr[1], IDR1, EVENTQS, SMMU_EVENTQS);
|
||||
s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS, SMMU_CMDQS);
|
||||
|
||||
s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, 1);
|
||||
s->idr[3] = FIELD_DP32(s->idr[3], IDR3, HAD, 1);
|
||||
|
||||
/* 4K and 64K granule support */
|
||||
s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN4K, 1);
|
||||
s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN64K, 1);
|
||||
@@ -270,6 +273,7 @@ static void smmuv3_init_regs(SMMUv3State *s)
|
||||
|
||||
s->features = 0;
|
||||
s->sid_split = 0;
|
||||
s->aidr = 0x1;
|
||||
}
|
||||
|
||||
static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf,
|
||||
@@ -506,7 +510,8 @@ static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event)
|
||||
if (tt->ttb & ~(MAKE_64BIT_MASK(0, cfg->oas))) {
|
||||
goto bad_cd;
|
||||
}
|
||||
trace_smmuv3_decode_cd_tt(i, tt->tsz, tt->ttb, tt->granule_sz);
|
||||
tt->had = CD_HAD(cd, i);
|
||||
trace_smmuv3_decode_cd_tt(i, tt->tsz, tt->ttb, tt->granule_sz, tt->had);
|
||||
}
|
||||
|
||||
event->record_trans_faults = CD_R(cd);
|
||||
@@ -626,7 +631,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
|
||||
SMMUTranslationStatus status;
|
||||
SMMUState *bs = ARM_SMMU(s);
|
||||
uint64_t page_mask, aligned_addr;
|
||||
IOMMUTLBEntry *cached_entry = NULL;
|
||||
SMMUTLBEntry *cached_entry = NULL;
|
||||
SMMUTransTableInfo *tt;
|
||||
SMMUTransCfg *cfg = NULL;
|
||||
IOMMUTLBEntry entry = {
|
||||
@@ -636,7 +641,6 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
|
||||
.addr_mask = ~(hwaddr)0,
|
||||
.perm = IOMMU_NONE,
|
||||
};
|
||||
SMMUIOTLBKey key, *new_key;
|
||||
|
||||
qemu_mutex_lock(&s->mutex);
|
||||
|
||||
@@ -675,17 +679,9 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
|
||||
page_mask = (1ULL << (tt->granule_sz)) - 1;
|
||||
aligned_addr = addr & ~page_mask;
|
||||
|
||||
key.asid = cfg->asid;
|
||||
key.iova = aligned_addr;
|
||||
|
||||
cached_entry = g_hash_table_lookup(bs->iotlb, &key);
|
||||
cached_entry = smmu_iotlb_lookup(bs, cfg, tt, aligned_addr);
|
||||
if (cached_entry) {
|
||||
cfg->iotlb_hits++;
|
||||
trace_smmu_iotlb_cache_hit(cfg->asid, aligned_addr,
|
||||
cfg->iotlb_hits, cfg->iotlb_misses,
|
||||
100 * cfg->iotlb_hits /
|
||||
(cfg->iotlb_hits + cfg->iotlb_misses));
|
||||
if ((flag & IOMMU_WO) && !(cached_entry->perm & IOMMU_WO)) {
|
||||
if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) {
|
||||
status = SMMU_TRANS_ERROR;
|
||||
if (event.record_trans_faults) {
|
||||
event.type = SMMU_EVT_F_PERMISSION;
|
||||
@@ -698,17 +694,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
|
||||
goto epilogue;
|
||||
}
|
||||
|
||||
cfg->iotlb_misses++;
|
||||
trace_smmu_iotlb_cache_miss(cfg->asid, addr & ~page_mask,
|
||||
cfg->iotlb_hits, cfg->iotlb_misses,
|
||||
100 * cfg->iotlb_hits /
|
||||
(cfg->iotlb_hits + cfg->iotlb_misses));
|
||||
|
||||
if (g_hash_table_size(bs->iotlb) >= SMMU_IOTLB_MAX_SIZE) {
|
||||
smmu_iotlb_inv_all(bs);
|
||||
}
|
||||
|
||||
cached_entry = g_new0(IOMMUTLBEntry, 1);
|
||||
cached_entry = g_new0(SMMUTLBEntry, 1);
|
||||
|
||||
if (smmu_ptw(cfg, aligned_addr, flag, cached_entry, &ptw_info)) {
|
||||
g_free(cached_entry);
|
||||
@@ -753,10 +739,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
|
||||
}
|
||||
status = SMMU_TRANS_ERROR;
|
||||
} else {
|
||||
new_key = g_new0(SMMUIOTLBKey, 1);
|
||||
new_key->asid = cfg->asid;
|
||||
new_key->iova = aligned_addr;
|
||||
g_hash_table_insert(bs->iotlb, new_key, cached_entry);
|
||||
smmu_iotlb_insert(bs, cfg, cached_entry);
|
||||
status = SMMU_TRANS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -765,9 +748,9 @@ epilogue:
|
||||
switch (status) {
|
||||
case SMMU_TRANS_SUCCESS:
|
||||
entry.perm = flag;
|
||||
entry.translated_addr = cached_entry->translated_addr +
|
||||
(addr & page_mask);
|
||||
entry.addr_mask = cached_entry->addr_mask;
|
||||
entry.translated_addr = cached_entry->entry.translated_addr +
|
||||
(addr & cached_entry->entry.addr_mask);
|
||||
entry.addr_mask = cached_entry->entry.addr_mask;
|
||||
trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr,
|
||||
entry.translated_addr, entry.perm);
|
||||
break;
|
||||
@@ -807,42 +790,49 @@ epilogue:
|
||||
* @n: notifier to be called
|
||||
* @asid: address space ID or negative value if we don't care
|
||||
* @iova: iova
|
||||
* @tg: translation granule (if communicated through range invalidation)
|
||||
* @num_pages: number of @granule sized pages (if tg != 0), otherwise 1
|
||||
*/
|
||||
static void smmuv3_notify_iova(IOMMUMemoryRegion *mr,
|
||||
IOMMUNotifier *n,
|
||||
int asid,
|
||||
dma_addr_t iova)
|
||||
int asid, dma_addr_t iova,
|
||||
uint8_t tg, uint64_t num_pages)
|
||||
{
|
||||
SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
|
||||
SMMUEventInfo event = {.inval_ste_allowed = true};
|
||||
SMMUTransTableInfo *tt;
|
||||
SMMUTransCfg *cfg;
|
||||
IOMMUTLBEntry entry;
|
||||
uint8_t granule = tg;
|
||||
|
||||
cfg = smmuv3_get_config(sdev, &event);
|
||||
if (!cfg) {
|
||||
return;
|
||||
}
|
||||
if (!tg) {
|
||||
SMMUEventInfo event = {.inval_ste_allowed = true};
|
||||
SMMUTransCfg *cfg = smmuv3_get_config(sdev, &event);
|
||||
SMMUTransTableInfo *tt;
|
||||
|
||||
if (asid >= 0 && cfg->asid != asid) {
|
||||
return;
|
||||
}
|
||||
if (!cfg) {
|
||||
return;
|
||||
}
|
||||
|
||||
tt = select_tt(cfg, iova);
|
||||
if (!tt) {
|
||||
return;
|
||||
if (asid >= 0 && cfg->asid != asid) {
|
||||
return;
|
||||
}
|
||||
|
||||
tt = select_tt(cfg, iova);
|
||||
if (!tt) {
|
||||
return;
|
||||
}
|
||||
granule = tt->granule_sz;
|
||||
}
|
||||
|
||||
entry.target_as = &address_space_memory;
|
||||
entry.iova = iova;
|
||||
entry.addr_mask = (1 << tt->granule_sz) - 1;
|
||||
entry.addr_mask = num_pages * (1 << granule) - 1;
|
||||
entry.perm = IOMMU_NONE;
|
||||
|
||||
memory_region_notify_one(n, &entry);
|
||||
}
|
||||
|
||||
/* invalidate an asid/iova tuple in all mr's */
|
||||
static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova)
|
||||
/* invalidate an asid/iova range tuple in all mr's */
|
||||
static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova,
|
||||
uint8_t tg, uint64_t num_pages)
|
||||
{
|
||||
SMMUDevice *sdev;
|
||||
|
||||
@@ -850,14 +840,41 @@ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova)
|
||||
IOMMUMemoryRegion *mr = &sdev->iommu;
|
||||
IOMMUNotifier *n;
|
||||
|
||||
trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova);
|
||||
trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova,
|
||||
tg, num_pages);
|
||||
|
||||
IOMMU_NOTIFIER_FOREACH(n, mr) {
|
||||
smmuv3_notify_iova(mr, n, asid, iova);
|
||||
smmuv3_notify_iova(mr, n, asid, iova, tg, num_pages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd)
|
||||
{
|
||||
uint8_t scale = 0, num = 0, ttl = 0;
|
||||
dma_addr_t addr = CMD_ADDR(cmd);
|
||||
uint8_t type = CMD_TYPE(cmd);
|
||||
uint16_t vmid = CMD_VMID(cmd);
|
||||
bool leaf = CMD_LEAF(cmd);
|
||||
uint8_t tg = CMD_TG(cmd);
|
||||
hwaddr num_pages = 1;
|
||||
int asid = -1;
|
||||
|
||||
if (tg) {
|
||||
scale = CMD_SCALE(cmd);
|
||||
num = CMD_NUM(cmd);
|
||||
ttl = CMD_TTL(cmd);
|
||||
num_pages = (num + 1) * (1 << (scale));
|
||||
}
|
||||
|
||||
if (type == SMMU_CMD_TLBI_NH_VA) {
|
||||
asid = CMD_ASID(cmd);
|
||||
}
|
||||
trace_smmuv3_s1_range_inval(vmid, asid, addr, tg, num_pages, ttl, leaf);
|
||||
smmuv3_inv_notifiers_iova(s, asid, addr, tg, num_pages);
|
||||
smmu_iotlb_inv_iova(s, asid, addr, tg, num_pages, ttl);
|
||||
}
|
||||
|
||||
static int smmuv3_cmdq_consume(SMMUv3State *s)
|
||||
{
|
||||
SMMUState *bs = ARM_SMMU(s);
|
||||
@@ -988,27 +1005,9 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
|
||||
smmu_iotlb_inv_all(bs);
|
||||
break;
|
||||
case SMMU_CMD_TLBI_NH_VAA:
|
||||
{
|
||||
dma_addr_t addr = CMD_ADDR(&cmd);
|
||||
uint16_t vmid = CMD_VMID(&cmd);
|
||||
|
||||
trace_smmuv3_cmdq_tlbi_nh_vaa(vmid, addr);
|
||||
smmuv3_inv_notifiers_iova(bs, -1, addr);
|
||||
smmu_iotlb_inv_all(bs);
|
||||
break;
|
||||
}
|
||||
case SMMU_CMD_TLBI_NH_VA:
|
||||
{
|
||||
uint16_t asid = CMD_ASID(&cmd);
|
||||
uint16_t vmid = CMD_VMID(&cmd);
|
||||
dma_addr_t addr = CMD_ADDR(&cmd);
|
||||
bool leaf = CMD_LEAF(&cmd);
|
||||
|
||||
trace_smmuv3_cmdq_tlbi_nh_va(vmid, asid, addr, leaf);
|
||||
smmuv3_inv_notifiers_iova(bs, asid, addr);
|
||||
smmu_iotlb_inv_iova(bs, asid, addr);
|
||||
smmuv3_s1_range_inval(bs, &cmd);
|
||||
break;
|
||||
}
|
||||
case SMMU_CMD_TLBI_EL3_ALL:
|
||||
case SMMU_CMD_TLBI_EL3_VA:
|
||||
case SMMU_CMD_TLBI_EL2_ALL:
|
||||
@@ -1257,6 +1256,9 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
|
||||
case A_IIDR:
|
||||
*data = s->iidr;
|
||||
return MEMTX_OK;
|
||||
case A_AIDR:
|
||||
*data = s->aidr;
|
||||
return MEMTX_OK;
|
||||
case A_CR0:
|
||||
*data = s->cr[0];
|
||||
return MEMTX_OK;
|
||||
|
||||
+6
-6
@@ -14,6 +14,9 @@ smmu_iotlb_inv_all(void) "IOTLB invalidate all"
|
||||
smmu_iotlb_inv_asid(uint16_t asid) "IOTLB invalidate asid=%d"
|
||||
smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64
|
||||
smmu_inv_notifiers_mr(const char *name) "iommu mr=%s"
|
||||
smmu_iotlb_lookup_hit(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
|
||||
smmu_iotlb_lookup_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
|
||||
smmu_iotlb_insert(uint16_t asid, uint64_t addr, uint8_t tg, uint8_t level) "IOTLB ++ asid=%d addr=0x%"PRIx64" tg=%d level=%d"
|
||||
|
||||
# smmuv3.c
|
||||
smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)"
|
||||
@@ -36,20 +39,17 @@ smmuv3_translate_abort(const char *n, uint16_t sid, uint64_t addr, bool is_write
|
||||
smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=%d iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x"
|
||||
smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64
|
||||
smmuv3_decode_cd(uint32_t oas) "oas=%d"
|
||||
smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d"
|
||||
smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz, bool had) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d had:%d"
|
||||
smmuv3_cmdq_cfgi_ste(int streamid) "streamid =%d"
|
||||
smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%d - end=0x%d"
|
||||
smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %d"
|
||||
smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid %d (hits=%d, misses=%d, hit rate=%d)"
|
||||
smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid %d (hits=%d, misses=%d, hit rate=%d)"
|
||||
smmuv3_cmdq_tlbi_nh_va(int vmid, int asid, uint64_t addr, bool leaf) "vmid =%d asid =%d addr=0x%"PRIx64" leaf=%d"
|
||||
smmuv3_cmdq_tlbi_nh_vaa(int vmid, uint64_t addr) "vmid =%d addr=0x%"PRIx64
|
||||
smmuv3_s1_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid =%d asid =%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d"
|
||||
smmuv3_cmdq_tlbi_nh(void) ""
|
||||
smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d"
|
||||
smmu_iotlb_cache_hit(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
|
||||
smmu_iotlb_cache_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
|
||||
smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d"
|
||||
smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s"
|
||||
smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s"
|
||||
smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova) "iommu mr=%s asid=%d iova=0x%"PRIx64
|
||||
smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64
|
||||
|
||||
|
||||
+11
-1
@@ -15,6 +15,7 @@
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#define A9_GIC_NUM_PRIORITY_BITS 5
|
||||
|
||||
@@ -52,8 +53,18 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
|
||||
*wdtbusdev;
|
||||
int i;
|
||||
bool has_el3;
|
||||
CPUState *cpu0;
|
||||
Object *cpuobj;
|
||||
|
||||
cpu0 = qemu_get_cpu(0);
|
||||
cpuobj = OBJECT(cpu0);
|
||||
if (strcmp(object_get_typename(cpuobj), ARM_CPU_TYPE_NAME("cortex-a9"))) {
|
||||
/* We might allow Cortex-A5 once we model it */
|
||||
error_setg(errp,
|
||||
"Cortex-A9MPCore peripheral can only use Cortex-A9 CPU");
|
||||
return;
|
||||
}
|
||||
|
||||
scudev = DEVICE(&s->scu);
|
||||
qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) {
|
||||
@@ -70,7 +81,6 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
|
||||
/* Make the GIC's TZ support match the CPUs. We assume that
|
||||
* either all the CPUs have TZ, or none do.
|
||||
*/
|
||||
cpuobj = OBJECT(qemu_get_cpu(0));
|
||||
has_el3 = object_property_find(cpuobj, "has_el3", NULL) &&
|
||||
object_property_get_bool(cpuobj, "has_el3", &error_abort);
|
||||
qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3);
|
||||
|
||||
@@ -50,8 +50,15 @@ typedef struct SMMUTransTableInfo {
|
||||
uint64_t ttb; /* TT base address */
|
||||
uint8_t tsz; /* input range, ie. 2^(64 -tsz)*/
|
||||
uint8_t granule_sz; /* granule page shift */
|
||||
bool had; /* hierarchical attribute disable */
|
||||
} SMMUTransTableInfo;
|
||||
|
||||
typedef struct SMMUTLBEntry {
|
||||
IOMMUTLBEntry entry;
|
||||
uint8_t level;
|
||||
uint8_t granule;
|
||||
} SMMUTLBEntry;
|
||||
|
||||
/*
|
||||
* Generic structure populated by derived SMMU devices
|
||||
* after decoding the configuration information and used as
|
||||
@@ -91,6 +98,8 @@ typedef struct SMMUPciBus {
|
||||
typedef struct SMMUIOTLBKey {
|
||||
uint64_t iova;
|
||||
uint16_t asid;
|
||||
uint8_t tg;
|
||||
uint8_t level;
|
||||
} SMMUIOTLBKey;
|
||||
|
||||
typedef struct SMMUState {
|
||||
@@ -140,7 +149,7 @@ static inline uint16_t smmu_get_sid(SMMUDevice *sdev)
|
||||
* pair, according to @cfg translation config
|
||||
*/
|
||||
int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm,
|
||||
IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info);
|
||||
SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info);
|
||||
|
||||
/**
|
||||
* select_tt - compute which translation table shall be used according to
|
||||
@@ -153,9 +162,15 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid);
|
||||
|
||||
#define SMMU_IOTLB_MAX_SIZE 256
|
||||
|
||||
SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
|
||||
SMMUTransTableInfo *tt, hwaddr iova);
|
||||
void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *entry);
|
||||
SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova,
|
||||
uint8_t tg, uint8_t level);
|
||||
void smmu_iotlb_inv_all(SMMUState *s);
|
||||
void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid);
|
||||
void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova);
|
||||
void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
|
||||
uint8_t tg, uint64_t num_pages, uint8_t ttl);
|
||||
|
||||
/* Unmap the range of all the notifiers registered to any IOMMU mr */
|
||||
void smmu_inv_notifiers_all(SMMUState *s);
|
||||
|
||||
@@ -41,6 +41,7 @@ typedef struct SMMUv3State {
|
||||
|
||||
uint32_t idr[6];
|
||||
uint32_t iidr;
|
||||
uint32_t aidr;
|
||||
uint32_t cr[3];
|
||||
uint32_t cr0ack;
|
||||
uint32_t statusr;
|
||||
|
||||
@@ -47,6 +47,8 @@
|
||||
&bfi rd rn lsb msb
|
||||
&sat rd rn satimm imm sh
|
||||
&pkh rd rn rm imm tb
|
||||
&mcr cp opc1 crn crm opc2 rt
|
||||
&mcrr cp opc1 crm rt rt2
|
||||
|
||||
# Data-processing (register)
|
||||
|
||||
@@ -529,6 +531,23 @@ LDM_a32 ---- 100 b:1 i:1 u:1 w:1 1 rn:4 list:16 &ldst_block
|
||||
B .... 1010 ........................ @branch
|
||||
BL .... 1011 ........................ @branch
|
||||
|
||||
# Coprocessor instructions
|
||||
|
||||
# We decode MCR, MCR, MRRC and MCRR only, because for QEMU the
|
||||
# other coprocessor instructions always UNDEF.
|
||||
# The trans_ functions for these will ignore cp values 8..13 for v7 or
|
||||
# earlier, and 0..13 for v8 and later, because those areas of the
|
||||
# encoding space may be used for other things, such as VFP or Neon.
|
||||
|
||||
@mcr ---- .... opc1:3 . crn:4 rt:4 cp:4 opc2:3 . crm:4 &mcr
|
||||
@mcrr ---- .... .... rt2:4 rt:4 cp:4 opc1:4 crm:4 &mcrr
|
||||
|
||||
MCRR .... 1100 0100 .... .... .... .... .... @mcrr
|
||||
MRRC .... 1100 0101 .... .... .... .... .... @mcrr
|
||||
|
||||
MCR .... 1110 ... 0 .... .... .... ... 1 .... @mcr
|
||||
MRC .... 1110 ... 1 .... .... .... ... 1 .... @mcr
|
||||
|
||||
# Supervisor call
|
||||
|
||||
SVC ---- 1111 imm:24 &i
|
||||
|
||||
@@ -391,12 +391,15 @@ static void arm_cpu_reset(DeviceState *dev)
|
||||
set_flush_to_zero(1, &env->vfp.standard_fp_status);
|
||||
set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status);
|
||||
set_default_nan_mode(1, &env->vfp.standard_fp_status);
|
||||
set_default_nan_mode(1, &env->vfp.standard_fp_status_f16);
|
||||
set_float_detect_tininess(float_tininess_before_rounding,
|
||||
&env->vfp.fp_status);
|
||||
set_float_detect_tininess(float_tininess_before_rounding,
|
||||
&env->vfp.standard_fp_status);
|
||||
set_float_detect_tininess(float_tininess_before_rounding,
|
||||
&env->vfp.fp_status_f16);
|
||||
set_float_detect_tininess(float_tininess_before_rounding,
|
||||
&env->vfp.standard_fp_status_f16);
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (kvm_enabled()) {
|
||||
kvm_arm_reset_vcpu(cpu);
|
||||
|
||||
+8
-2
@@ -609,6 +609,8 @@ typedef struct CPUARMState {
|
||||
* fp_status: is the "normal" fp status.
|
||||
* fp_status_fp16: used for half-precision calculations
|
||||
* standard_fp_status : the ARM "Standard FPSCR Value"
|
||||
* standard_fp_status_fp16 : used for half-precision
|
||||
* calculations with the ARM "Standard FPSCR Value"
|
||||
*
|
||||
* Half-precision operations are governed by a separate
|
||||
* flush-to-zero control bit in FPSCR:FZ16. We pass a separate
|
||||
@@ -619,15 +621,20 @@ typedef struct CPUARMState {
|
||||
* Neon) which the architecture defines as controlled by the
|
||||
* standard FPSCR value rather than the FPSCR.
|
||||
*
|
||||
* The "standard FPSCR but for fp16 ops" is needed because
|
||||
* the "standard FPSCR" tracks the FPSCR.FZ16 bit rather than
|
||||
* using a fixed value for it.
|
||||
*
|
||||
* To avoid having to transfer exception bits around, we simply
|
||||
* say that the FPSCR cumulative exception flags are the logical
|
||||
* OR of the flags in the three fp statuses. This relies on the
|
||||
* OR of the flags in the four fp statuses. This relies on the
|
||||
* only thing which needs to read the exception flags being
|
||||
* an explicit FPSCR read.
|
||||
*/
|
||||
float_status fp_status;
|
||||
float_status fp_status_f16;
|
||||
float_status standard_fp_status;
|
||||
float_status standard_fp_status_f16;
|
||||
|
||||
/* ZCR_EL[1-3] */
|
||||
uint64_t zcr_el[4];
|
||||
@@ -1950,7 +1957,6 @@ enum arm_features {
|
||||
ARM_FEATURE_V8,
|
||||
ARM_FEATURE_AARCH64, /* supports 64 bit mode */
|
||||
ARM_FEATURE_CBAR, /* has cp15 CBAR */
|
||||
ARM_FEATURE_CRC, /* ARMv8 CRC instructions */
|
||||
ARM_FEATURE_CBAR_RO, /* has cp15 CBAR and it is read-only */
|
||||
ARM_FEATURE_EL2, /* has EL2 Virtualization support */
|
||||
ARM_FEATURE_EL3, /* has EL3 Secure monitor support */
|
||||
|
||||
@@ -8462,6 +8462,35 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu,
|
||||
assert((r->state != ARM_CP_STATE_AA32) || (r->opc0 == 0));
|
||||
/* AArch64 regs are all 64 bit so ARM_CP_64BIT is meaningless */
|
||||
assert((r->state != ARM_CP_STATE_AA64) || !(r->type & ARM_CP_64BIT));
|
||||
/*
|
||||
* This API is only for Arm's system coprocessors (14 and 15) or
|
||||
* (M-profile or v7A-and-earlier only) for implementation defined
|
||||
* coprocessors in the range 0..7. Our decode assumes this, since
|
||||
* 8..13 can be used for other insns including VFP and Neon. See
|
||||
* valid_cp() in translate.c. Assert here that we haven't tried
|
||||
* to use an invalid coprocessor number.
|
||||
*/
|
||||
switch (r->state) {
|
||||
case ARM_CP_STATE_BOTH:
|
||||
/* 0 has a special meaning, but otherwise the same rules as AA32. */
|
||||
if (r->cp == 0) {
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case ARM_CP_STATE_AA32:
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_V8) &&
|
||||
!arm_feature(&cpu->env, ARM_FEATURE_M)) {
|
||||
assert(r->cp >= 14 && r->cp <= 15);
|
||||
} else {
|
||||
assert(r->cp < 8 || (r->cp >= 14 && r->cp <= 15));
|
||||
}
|
||||
break;
|
||||
case ARM_CP_STATE_AA64:
|
||||
assert(r->cp == 0 || r->cp == CP_REG_ARM64_SYSREG_CP);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
/* The AArch64 pseudocode CheckSystemAccess() specifies that op1
|
||||
* encodes a minimum access level for the register. We roll this
|
||||
* runtime check into our general permission check code, so check
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# M-profile UserFault.NOCP exception handling
|
||||
#
|
||||
# Copyright (c) 2020 Linaro, Ltd
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#
|
||||
# This file is processed by scripts/decodetree.py
|
||||
#
|
||||
# For M-profile, the architecture specifies that NOCP UsageFaults
|
||||
# should take precedence over UNDEF faults over the whole wide
|
||||
# range of coprocessor-space encodings, with the exception of
|
||||
# VLLDM and VLSTM. (Compare v8.1M IsCPInstruction() pseudocode and
|
||||
# v8M Arm ARM rule R_QLGM.) This isn't mandatory for v8.0M but we choose
|
||||
# to behave the same as v8.1M.
|
||||
# This decode is handled before any others (and in particular before
|
||||
# decoding FP instructions which are in the coprocessor space).
|
||||
# If the coprocessor is not present or disabled then we will generate
|
||||
# the NOCP exception; otherwise we let the insn through to the main decode.
|
||||
|
||||
{
|
||||
# Special cases which do not take an early NOCP: VLLDM and VLSTM
|
||||
VLLDM_VLSTM 1110 1100 001 l:1 rn:4 0000 1010 0000 0000
|
||||
# TODO: VSCCLRM (new in v8.1M) is similar:
|
||||
#VSCCLRM 1110 1100 1-01 1111 ---- 1011 ---- ---0
|
||||
|
||||
NOCP 111- 1110 ---- ---- ---- cp:4 ---- ----
|
||||
NOCP 111- 110- ---- ---- ---- cp:4 ---- ----
|
||||
# TODO: From v8.1M onwards we will also want this range to NOCP
|
||||
#NOCP_8_1 111- 1111 ---- ---- ---- ---- ---- ---- cp=10
|
||||
}
|
||||
@@ -5,6 +5,7 @@ gen = [
|
||||
decodetree.process('neon-ls.decode', extra_args: '--static-decode=disas_neon_ls'),
|
||||
decodetree.process('vfp.decode', extra_args: '--static-decode=disas_vfp'),
|
||||
decodetree.process('vfp-uncond.decode', extra_args: '--static-decode=disas_vfp_uncond'),
|
||||
decodetree.process('m-nocp.decode', extra_args: '--static-decode=disas_m_nocp'),
|
||||
decodetree.process('a32.decode', extra_args: '--static-decode=disas_a32'),
|
||||
decodetree.process('a32-uncond.decode', extra_args: '--static-decode=disas_a32_uncond'),
|
||||
decodetree.process('t32.decode', extra_args: '--static-decode=disas_t32'),
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
&sat !extern rd rn satimm imm sh
|
||||
&pkh !extern rd rn rm imm tb
|
||||
&cps !extern mode imod M A I F
|
||||
&mcr !extern cp opc1 crn crm opc2 rt
|
||||
&mcrr !extern cp opc1 crm rt rt2
|
||||
|
||||
# Data-processing (register)
|
||||
|
||||
@@ -621,6 +623,23 @@ RFE 1110 1001 10.1 .... 1100000000000000 @rfe pu=1
|
||||
SRS 1110 1000 00.0 1101 1100 0000 000. .... @srs pu=2
|
||||
SRS 1110 1001 10.0 1101 1100 0000 000. .... @srs pu=1
|
||||
|
||||
# Coprocessor instructions
|
||||
|
||||
# We decode MCR, MCR, MRRC and MCRR only, because for QEMU the
|
||||
# other coprocessor instructions always UNDEF.
|
||||
# The trans_ functions for these will ignore cp values 8..13 for v7 or
|
||||
# earlier, and 0..13 for v8 and later, because those areas of the
|
||||
# encoding space may be used for other things, such as VFP or Neon.
|
||||
|
||||
@mcr .... .... opc1:3 . crn:4 rt:4 cp:4 opc2:3 . crm:4
|
||||
@mcrr .... .... .... rt2:4 rt:4 cp:4 opc1:4 crm:4
|
||||
|
||||
MCRR 1110 1100 0100 .... .... .... .... .... @mcrr
|
||||
MRRC 1110 1100 0101 .... .... .... .... .... @mcrr
|
||||
|
||||
MCR 1110 1110 ... 0 .... .... .... ... 1 .... @mcr
|
||||
MRC 1110 1110 ... 1 .... .... .... ... 1 .... @mcr
|
||||
|
||||
# Branches
|
||||
|
||||
%imm24 26:s1 13:1 11:1 16:10 0:11 !function=t32_branch24
|
||||
|
||||
+35
-54
@@ -609,25 +609,6 @@ static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v)
|
||||
tcg_temp_free_i64(tmp);
|
||||
}
|
||||
|
||||
TCGv_ptr get_fpstatus_ptr(bool is_f16)
|
||||
{
|
||||
TCGv_ptr statusptr = tcg_temp_new_ptr();
|
||||
int offset;
|
||||
|
||||
/* In A64 all instructions (both FP and Neon) use the FPCR; there
|
||||
* is no equivalent of the A32 Neon "standard FPSCR value".
|
||||
* However half-precision operations operate under a different
|
||||
* FZ16 flag and use vfp.fp_status_f16 instead of vfp.fp_status.
|
||||
*/
|
||||
if (is_f16) {
|
||||
offset = offsetof(CPUARMState, vfp.fp_status_f16);
|
||||
} else {
|
||||
offset = offsetof(CPUARMState, vfp.fp_status);
|
||||
}
|
||||
tcg_gen_addi_ptr(statusptr, cpu_env, offset);
|
||||
return statusptr;
|
||||
}
|
||||
|
||||
/* Expand a 2-operand AdvSIMD vector operation using an expander function. */
|
||||
static void gen_gvec_fn2(DisasContext *s, bool is_q, int rd, int rn,
|
||||
GVecGen2Fn *gvec_fn, int vece)
|
||||
@@ -689,7 +670,7 @@ static void gen_gvec_op3_fpst(DisasContext *s, bool is_q, int rd, int rn,
|
||||
int rm, bool is_fp16, int data,
|
||||
gen_helper_gvec_3_ptr *fn)
|
||||
{
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(is_fp16);
|
||||
TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR);
|
||||
tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd),
|
||||
vec_full_reg_offset(s, rn),
|
||||
vec_full_reg_offset(s, rm), fpst,
|
||||
@@ -5898,7 +5879,7 @@ static void handle_fp_compare(DisasContext *s, int size,
|
||||
bool cmp_with_zero, bool signal_all_nans)
|
||||
{
|
||||
TCGv_i64 tcg_flags = tcg_temp_new_i64();
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(size == MO_16);
|
||||
TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR);
|
||||
|
||||
if (size == MO_64) {
|
||||
TCGv_i64 tcg_vn, tcg_vm;
|
||||
@@ -6157,7 +6138,7 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn)
|
||||
tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000);
|
||||
break;
|
||||
case 0x3: /* FSQRT */
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
fpst = fpstatus_ptr(FPST_FPCR_F16);
|
||||
gen_helper_sqrt_f16(tcg_res, tcg_op, fpst);
|
||||
break;
|
||||
case 0x8: /* FRINTN */
|
||||
@@ -6167,7 +6148,7 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn)
|
||||
case 0xc: /* FRINTA */
|
||||
{
|
||||
TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7));
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
fpst = fpstatus_ptr(FPST_FPCR_F16);
|
||||
|
||||
gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
|
||||
gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst);
|
||||
@@ -6177,11 +6158,11 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn)
|
||||
break;
|
||||
}
|
||||
case 0xe: /* FRINTX */
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
fpst = fpstatus_ptr(FPST_FPCR_F16);
|
||||
gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, fpst);
|
||||
break;
|
||||
case 0xf: /* FRINTI */
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
fpst = fpstatus_ptr(FPST_FPCR_F16);
|
||||
gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst);
|
||||
break;
|
||||
default:
|
||||
@@ -6253,7 +6234,7 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn)
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
fpst = get_fpstatus_ptr(false);
|
||||
fpst = fpstatus_ptr(FPST_FPCR);
|
||||
if (rmode >= 0) {
|
||||
TCGv_i32 tcg_rmode = tcg_const_i32(rmode);
|
||||
gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
|
||||
@@ -6330,7 +6311,7 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn)
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
fpst = get_fpstatus_ptr(false);
|
||||
fpst = fpstatus_ptr(FPST_FPCR);
|
||||
if (rmode >= 0) {
|
||||
TCGv_i32 tcg_rmode = tcg_const_i32(rmode);
|
||||
gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
|
||||
@@ -6365,7 +6346,7 @@ static void handle_fp_fcvt(DisasContext *s, int opcode,
|
||||
/* Single to half */
|
||||
TCGv_i32 tcg_rd = tcg_temp_new_i32();
|
||||
TCGv_i32 ahp = get_ahp_flag();
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR);
|
||||
|
||||
gen_helper_vfp_fcvt_f32_to_f16(tcg_rd, tcg_rn, fpst, ahp);
|
||||
/* write_fp_sreg is OK here because top half of tcg_rd is zero */
|
||||
@@ -6385,7 +6366,7 @@ static void handle_fp_fcvt(DisasContext *s, int opcode,
|
||||
/* Double to single */
|
||||
gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, cpu_env);
|
||||
} else {
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR);
|
||||
TCGv_i32 ahp = get_ahp_flag();
|
||||
/* Double to half */
|
||||
gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, fpst, ahp);
|
||||
@@ -6401,7 +6382,7 @@ static void handle_fp_fcvt(DisasContext *s, int opcode,
|
||||
case 0x3:
|
||||
{
|
||||
TCGv_i32 tcg_rn = read_fp_sreg(s, rn);
|
||||
TCGv_ptr tcg_fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_FPCR);
|
||||
TCGv_i32 tcg_ahp = get_ahp_flag();
|
||||
tcg_gen_ext16u_i32(tcg_rn, tcg_rn);
|
||||
if (dtype == 0) {
|
||||
@@ -6518,7 +6499,7 @@ static void handle_fp_2src_single(DisasContext *s, int opcode,
|
||||
TCGv_ptr fpst;
|
||||
|
||||
tcg_res = tcg_temp_new_i32();
|
||||
fpst = get_fpstatus_ptr(false);
|
||||
fpst = fpstatus_ptr(FPST_FPCR);
|
||||
tcg_op1 = read_fp_sreg(s, rn);
|
||||
tcg_op2 = read_fp_sreg(s, rm);
|
||||
|
||||
@@ -6571,7 +6552,7 @@ static void handle_fp_2src_double(DisasContext *s, int opcode,
|
||||
TCGv_ptr fpst;
|
||||
|
||||
tcg_res = tcg_temp_new_i64();
|
||||
fpst = get_fpstatus_ptr(false);
|
||||
fpst = fpstatus_ptr(FPST_FPCR);
|
||||
tcg_op1 = read_fp_dreg(s, rn);
|
||||
tcg_op2 = read_fp_dreg(s, rm);
|
||||
|
||||
@@ -6624,7 +6605,7 @@ static void handle_fp_2src_half(DisasContext *s, int opcode,
|
||||
TCGv_ptr fpst;
|
||||
|
||||
tcg_res = tcg_temp_new_i32();
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
fpst = fpstatus_ptr(FPST_FPCR_F16);
|
||||
tcg_op1 = read_fp_hreg(s, rn);
|
||||
tcg_op2 = read_fp_hreg(s, rm);
|
||||
|
||||
@@ -6723,7 +6704,7 @@ static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1,
|
||||
{
|
||||
TCGv_i32 tcg_op1, tcg_op2, tcg_op3;
|
||||
TCGv_i32 tcg_res = tcg_temp_new_i32();
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR);
|
||||
|
||||
tcg_op1 = read_fp_sreg(s, rn);
|
||||
tcg_op2 = read_fp_sreg(s, rm);
|
||||
@@ -6761,7 +6742,7 @@ static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1,
|
||||
{
|
||||
TCGv_i64 tcg_op1, tcg_op2, tcg_op3;
|
||||
TCGv_i64 tcg_res = tcg_temp_new_i64();
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR);
|
||||
|
||||
tcg_op1 = read_fp_dreg(s, rn);
|
||||
tcg_op2 = read_fp_dreg(s, rm);
|
||||
@@ -6799,7 +6780,7 @@ static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1,
|
||||
{
|
||||
TCGv_i32 tcg_op1, tcg_op2, tcg_op3;
|
||||
TCGv_i32 tcg_res = tcg_temp_new_i32();
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(true);
|
||||
TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR_F16);
|
||||
|
||||
tcg_op1 = read_fp_hreg(s, rn);
|
||||
tcg_op2 = read_fp_hreg(s, rm);
|
||||
@@ -6945,7 +6926,7 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
|
||||
TCGv_i32 tcg_shift, tcg_single;
|
||||
TCGv_i64 tcg_double;
|
||||
|
||||
tcg_fpstatus = get_fpstatus_ptr(type == 3);
|
||||
tcg_fpstatus = fpstatus_ptr(type == 3 ? FPST_FPCR_F16 : FPST_FPCR);
|
||||
|
||||
tcg_shift = tcg_const_i32(64 - scale);
|
||||
|
||||
@@ -7233,7 +7214,7 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof)
|
||||
static void handle_fjcvtzs(DisasContext *s, int rd, int rn)
|
||||
{
|
||||
TCGv_i64 t = read_fp_dreg(s, rn);
|
||||
TCGv_ptr fpstatus = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpstatus = fpstatus_ptr(FPST_FPCR);
|
||||
|
||||
gen_helper_fjcvtzs(t, t, fpstatus);
|
||||
|
||||
@@ -7847,7 +7828,7 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn)
|
||||
* Note that correct NaN propagation requires that we do these
|
||||
* operations in exactly the order specified by the pseudocode.
|
||||
*/
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(size == MO_16);
|
||||
TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR);
|
||||
int fpopcode = opcode | is_min << 4 | is_u << 5;
|
||||
int vmap = (1 << elements) - 1;
|
||||
TCGv_i32 tcg_res32 = do_reduction_op(s, fpopcode, rn, esize,
|
||||
@@ -8359,7 +8340,7 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn)
|
||||
return;
|
||||
}
|
||||
|
||||
fpst = get_fpstatus_ptr(size == MO_16);
|
||||
fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR);
|
||||
break;
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
@@ -8872,7 +8853,7 @@ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn,
|
||||
int elements, int is_signed,
|
||||
int fracbits, int size)
|
||||
{
|
||||
TCGv_ptr tcg_fpst = get_fpstatus_ptr(size == MO_16);
|
||||
TCGv_ptr tcg_fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR);
|
||||
TCGv_i32 tcg_shift = NULL;
|
||||
|
||||
MemOp mop = size | (is_signed ? MO_SIGN : 0);
|
||||
@@ -9053,7 +9034,7 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar,
|
||||
assert(!(is_scalar && is_q));
|
||||
|
||||
tcg_rmode = tcg_const_i32(arm_rmode_to_sf(FPROUNDING_ZERO));
|
||||
tcg_fpstatus = get_fpstatus_ptr(size == MO_16);
|
||||
tcg_fpstatus = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR);
|
||||
gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus);
|
||||
fracbits = (16 << size) - immhb;
|
||||
tcg_shift = tcg_const_i32(fracbits);
|
||||
@@ -9392,7 +9373,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements,
|
||||
int fpopcode, int rd, int rn, int rm)
|
||||
{
|
||||
int pass;
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR);
|
||||
|
||||
for (pass = 0; pass < elements; pass++) {
|
||||
if (size) {
|
||||
@@ -9785,7 +9766,7 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s,
|
||||
return;
|
||||
}
|
||||
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
fpst = fpstatus_ptr(FPST_FPCR_F16);
|
||||
|
||||
tcg_op1 = read_fp_hreg(s, rn);
|
||||
tcg_op2 = read_fp_hreg(s, rm);
|
||||
@@ -10038,7 +10019,7 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode,
|
||||
return;
|
||||
}
|
||||
|
||||
fpst = get_fpstatus_ptr(size == MO_16);
|
||||
fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR);
|
||||
|
||||
if (is_double) {
|
||||
TCGv_i64 tcg_op = tcg_temp_new_i64();
|
||||
@@ -10168,7 +10149,7 @@ static void handle_2misc_reciprocal(DisasContext *s, int opcode,
|
||||
int size, int rn, int rd)
|
||||
{
|
||||
bool is_double = (size == 3);
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR);
|
||||
|
||||
if (is_double) {
|
||||
TCGv_i64 tcg_op = tcg_temp_new_i64();
|
||||
@@ -10309,7 +10290,7 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar,
|
||||
} else {
|
||||
TCGv_i32 tcg_lo = tcg_temp_new_i32();
|
||||
TCGv_i32 tcg_hi = tcg_temp_new_i32();
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR);
|
||||
TCGv_i32 ahp = get_ahp_flag();
|
||||
|
||||
tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, tcg_op);
|
||||
@@ -10571,7 +10552,7 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn)
|
||||
|
||||
if (is_fcvt) {
|
||||
tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode));
|
||||
tcg_fpstatus = get_fpstatus_ptr(false);
|
||||
tcg_fpstatus = fpstatus_ptr(FPST_FPCR);
|
||||
gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus);
|
||||
} else {
|
||||
tcg_rmode = NULL;
|
||||
@@ -11396,7 +11377,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode,
|
||||
|
||||
/* Floating point operations need fpst */
|
||||
if (opcode >= 0x58) {
|
||||
fpst = get_fpstatus_ptr(false);
|
||||
fpst = fpstatus_ptr(FPST_FPCR);
|
||||
} else {
|
||||
fpst = NULL;
|
||||
}
|
||||
@@ -11994,7 +11975,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn)
|
||||
break;
|
||||
}
|
||||
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
fpst = fpstatus_ptr(FPST_FPCR_F16);
|
||||
|
||||
if (pairwise) {
|
||||
int maxpass = is_q ? 8 : 4;
|
||||
@@ -12287,7 +12268,7 @@ static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q,
|
||||
/* 16 -> 32 bit fp conversion */
|
||||
int srcelt = is_q ? 4 : 0;
|
||||
TCGv_i32 tcg_res[4];
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR);
|
||||
TCGv_i32 ahp = get_ahp_flag();
|
||||
|
||||
for (pass = 0; pass < 4; pass++) {
|
||||
@@ -12759,7 +12740,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
|
||||
}
|
||||
|
||||
if (need_fpstatus || need_rmode) {
|
||||
tcg_fpstatus = get_fpstatus_ptr(false);
|
||||
tcg_fpstatus = fpstatus_ptr(FPST_FPCR);
|
||||
} else {
|
||||
tcg_fpstatus = NULL;
|
||||
}
|
||||
@@ -13149,7 +13130,7 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn)
|
||||
}
|
||||
|
||||
if (need_rmode || need_fpst) {
|
||||
tcg_fpstatus = get_fpstatus_ptr(true);
|
||||
tcg_fpstatus = fpstatus_ptr(FPST_FPCR_F16);
|
||||
}
|
||||
|
||||
if (need_rmode) {
|
||||
@@ -13458,7 +13439,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
|
||||
}
|
||||
|
||||
if (is_fp) {
|
||||
fpst = get_fpstatus_ptr(is_fp16);
|
||||
fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR);
|
||||
} else {
|
||||
fpst = NULL;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ TCGv_i64 cpu_reg_sp(DisasContext *s, int reg);
|
||||
TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf);
|
||||
TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf);
|
||||
void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v);
|
||||
TCGv_ptr get_fpstatus_ptr(bool);
|
||||
bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn,
|
||||
unsigned int imms, unsigned int immr);
|
||||
bool sve_access_check(DisasContext *s);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user