From a674db604db3fc4ef5267243dc991852f1f1bebc Mon Sep 17 00:00:00 2001 From: JianChunfu Date: Fri, 7 Mar 2025 10:08:18 +0000 Subject: [PATCH 01/21] hw/arm/smmu-common: Remove the repeated ttb field SMMUTransCfg->ttb is never used in QEMU, TT base address can be accessed by SMMUTransCfg->tt[i]->ttb. Signed-off-by: JianChunfu Reviewed-by: Eric Auger Message-id: 20250221031034.69822-1-jansef.jian@hj-micro.com Signed-off-by: Peter Maydell --- include/hw/arm/smmu-common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index d1a4a64551..e5ad55bbae 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -110,7 +110,6 @@ typedef struct SMMUTransCfg { /* Used by stage-1 only. */ bool aa64; /* arch64 or aarch32 translation table */ bool record_faults; /* record fault events */ - uint64_t ttb; /* TT base address */ uint8_t oas; /* output address width */ uint8_t tbi; /* Top Byte Ignore */ int asid; From 3b2e22c0bbe2ce07123d93961d52f17644562cd7 Mon Sep 17 00:00:00 2001 From: Patrick Venture Date: Fri, 7 Mar 2025 10:08:19 +0000 Subject: [PATCH 02/21] hw/gpio: npcm7xx: fixup out-of-bounds access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reg isn't validated to be a possible register before it's dereferenced for one case. The mmio space registered for the gpio device is 4KiB but there aren't that many registers in the struct. Cc: qemu-stable@nongnu.org Fixes: 526dbbe0874 ("hw/gpio: Add GPIO model for Nuvoton NPCM7xx") Signed-off-by: Patrick Venture Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250226024603.493148-1-venture@google.com Signed-off-by: Peter Maydell --- hw/gpio/npcm7xx_gpio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c index 23e67424c9..2916056fae 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -220,8 +220,6 @@ static void npcm7xx_gpio_regs_write(void *opaque, hwaddr addr, uint64_t v, return; } - diff = s->regs[reg] ^ value; - switch (reg) { case NPCM7XX_GPIO_TLOCK1: case NPCM7XX_GPIO_TLOCK2: @@ -242,6 +240,7 @@ static void npcm7xx_gpio_regs_write(void *opaque, hwaddr addr, uint64_t v, case NPCM7XX_GPIO_PU: case NPCM7XX_GPIO_PD: case NPCM7XX_GPIO_IEM: + diff = s->regs[reg] ^ value; s->regs[reg] = value; npcm7xx_gpio_update_pins(s, diff); break; From 5f6b9b0564b69bd9548860419a70e79579d64aeb Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 7 Mar 2025 10:08:19 +0000 Subject: [PATCH 03/21] tests/functional/test_arm_sx1: Check whether the serial console is working MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel that is used in the sx1 test prints the usual Linux log onto the serial console, but this test currently ignores it. To make sure that the serial device is working properly, let's check for some strings in the output here. While we're at it, also add the test to the corresponding section in the MAINTAINERS file. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250226104833.1176253-1-thuth@redhat.com Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + tests/functional/test_arm_sx1.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5df6020ed5..699cf59e0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2010,6 +2010,7 @@ S: Maintained F: hw/*/omap* F: include/hw/arm/omap.h F: docs/system/arm/sx1.rst +F: tests/functional/test_arm_sx1.py IPack M: Alberto Garcia diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/test_arm_sx1.py index 4dd1e1859f..25800b388c 100755 --- a/tests/functional/test_arm_sx1.py +++ b/tests/functional/test_arm_sx1.py @@ -43,7 +43,8 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-append', f'kunit.enable=0 rdinit=/sbin/init {self.CONSOLE_ARGS}') self.vm.add_args('-no-reboot') self.launch_kernel(zimage_path, - initrd=initrd_path) + initrd=initrd_path, + wait_for='Boot successful') self.vm.wait(timeout=120) def test_arm_sx1_sd(self): @@ -54,7 +55,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-no-reboot') self.vm.add_args('-snapshot') self.vm.add_args('-drive', f'format=raw,if=sd,file={sd_fs_path}') - self.launch_kernel(zimage_path) + self.launch_kernel(zimage_path, wait_for='Boot successful') self.vm.wait(timeout=120) def test_arm_sx1_flash(self): @@ -65,7 +66,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-no-reboot') self.vm.add_args('-snapshot') self.vm.add_args('-drive', f'format=raw,if=pflash,file={flash_path}') - self.launch_kernel(zimage_path) + self.launch_kernel(zimage_path, wait_for='Boot successful') self.vm.wait(timeout=120) if __name__ == '__main__': From db6c2192839ee0282d38f6f6666a87e0629fcd13 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:19 +0000 Subject: [PATCH 04/21] target/arm: Apply correct timer offset when calculating deadlines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we are calculating timer deadlines, the correct definition of whether or not to apply an offset to the physical count is described in the Arm ARM DDI4087 rev L.a section D12.2.4.1. This is different from when the offset should be applied for a direct read of the counter sysreg. We got this right for the EL1 physical timer and for the EL1 virtual timer, but got all the rest wrong: they should be using a zero offset always. Factor the offset calculation out into a function that has a comment documenting exactly which offset it is calculating and which gets the HYP, SEC, and HYPVIRT cases right. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-2-peter.maydell@linaro.org --- target/arm/helper.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 71dead7241..7f341d753c 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2460,6 +2460,32 @@ static uint64_t gt_phys_cnt_offset(CPUARMState *env) return gt_phys_raw_cnt_offset(env); } +static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx) +{ + /* + * Return the timer offset to use for indirect accesses to the timer. + * This is the Offset value as defined in D12.2.4.1 "Operation of the + * CompareValue views of the timers". + * + * The condition here is not always the same as the condition for + * whether to apply an offset register when doing a direct read of + * the counter sysreg; those conditions are described in the + * access pseudocode for each counter register. + */ + switch (timeridx) { + case GTIMER_PHYS: + return gt_phys_raw_cnt_offset(env); + case GTIMER_VIRT: + return env->cp15.cntvoff_el2; + case GTIMER_HYP: + case GTIMER_SEC: + case GTIMER_HYPVIRT: + return 0; + default: + g_assert_not_reached(); + } +} + static void gt_recalc_timer(ARMCPU *cpu, int timeridx) { ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx]; @@ -2469,8 +2495,7 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx) * Timer enabled: calculate and set current ISTATUS, irq, and * reset timer to when ISTATUS next has to change */ - uint64_t offset = timeridx == GTIMER_VIRT ? - cpu->env.cp15.cntvoff_el2 : gt_phys_raw_cnt_offset(&cpu->env); + uint64_t offset = gt_indirect_access_timer_offset(&cpu->env, timeridx); uint64_t count = gt_get_countervalue(&cpu->env); /* Note that this must be unsigned 64 bit arithmetic: */ int istatus = count - offset >= gt->cval; From 5709038aa8b4d58b8c201ed53c327074173a35c6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:20 +0000 Subject: [PATCH 05/21] target/arm: Don't apply CNTVOFF_EL2 for EL2_VIRT timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CNTVOFF_EL2 offset register should only be applied for accessses to CNTVCT_EL0 and for the EL1 virtual timer (CNTV_*). We were incorrectly applying it for the EL2 virtual timer (CNTHV_*). Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-3-peter.maydell@linaro.org --- target/arm/helper.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 7f341d753c..5729b313f8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2604,7 +2604,6 @@ static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, switch (timeridx) { case GTIMER_VIRT: - case GTIMER_HYPVIRT: offset = gt_virt_cnt_offset(env); break; case GTIMER_PHYS: @@ -2624,7 +2623,6 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, switch (timeridx) { case GTIMER_VIRT: - case GTIMER_HYPVIRT: offset = gt_virt_cnt_offset(env); break; case GTIMER_PHYS: From bdd641541fbef0a27bf9f60e7eba6f8a31d4706c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:20 +0000 Subject: [PATCH 06/21] target/arm: Make CNTPS_* UNDEF from Secure EL1 when Secure EL2 is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we added Secure EL2 support, we missed that this needs an update to the access code for the EL3 physical timer registers. These are supposed to UNDEF from Secure EL1 when Secure EL2 is enabled. (Note for stable backporting: for backports to branches where CP_ACCESS_UNDEFINED is not defined, the old name to use instead is CP_ACCESS_TRAP_UNCATEGORIZED.) Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-4-peter.maydell@linaro.org --- target/arm/helper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5729b313f8..5b6de446ac 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2387,6 +2387,9 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, if (!arm_is_secure(env)) { return CP_ACCESS_UNDEFINED; } + if (arm_is_el2_enabled(env)) { + return CP_ACCESS_UNDEFINED; + } if (!(env->cp15.scr_el3 & SCR_ST)) { return CP_ACCESS_TRAP_EL3; } From 4aecd4b442d7abb4355896d878ffc9b028625b01 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:20 +0000 Subject: [PATCH 07/21] target/arm: Always apply CNTVOFF_EL2 for CNTV_TVAL_EL02 accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we handle CNTV_TVAL_EL02 by calling gt_tval_read() for the EL1 virt timer. This is almost correct, but the underlying CNTV_TVAL_EL0 register behaves slightly differently. CNTV_TVAL_EL02 always applies the CNTVOFF_EL2 offset; CNTV_TVAL_EL0 doesn't do so if we're at EL2 and HCR_EL2.E2H is 1. We were getting this wrong, because we ended up in gt_virt_cnt_offset() and did the E2H check. Factor out the tval read/write calculation from the selection of the offset, so that we can special case gt_virt_tval_read() and gt_virt_tval_write() to unconditionally pass CNTVOFF_EL2. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-5-peter.maydell@linaro.org --- target/arm/helper.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5b6de446ac..acf77793c7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2600,6 +2600,12 @@ static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_recalc_timer(env_archcpu(env), timeridx); } +static uint64_t do_tval_read(CPUARMState *env, int timeridx, uint64_t offset) +{ + return (uint32_t)(env->cp15.c14_timer[timeridx].cval - + (gt_get_countervalue(env) - offset)); +} + static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx) { @@ -2614,8 +2620,16 @@ static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, break; } - return (uint32_t)(env->cp15.c14_timer[timeridx].cval - - (gt_get_countervalue(env) - offset)); + return do_tval_read(env, timeridx, offset); +} + +static void do_tval_write(CPUARMState *env, int timeridx, uint64_t value, + uint64_t offset) +{ + trace_arm_gt_tval_write(timeridx, value); + env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset + + sextract64(value, 0, 32); + gt_recalc_timer(env_archcpu(env), timeridx); } static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2632,11 +2646,7 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, offset = gt_phys_cnt_offset(env); break; } - - trace_arm_gt_tval_write(timeridx, value); - env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset + - sextract64(value, 0, 32); - gt_recalc_timer(env_archcpu(env), timeridx); + do_tval_write(env, timeridx, value, offset); } static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2768,13 +2778,21 @@ static void gt_virt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t gt_virt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_tval_read(env, ri, GTIMER_VIRT); + /* + * This is CNTV_TVAL_EL02; unlike the underlying CNTV_TVAL_EL0 + * we always apply CNTVOFF_EL2. Special case that here rather + * than going into the generic gt_tval_read() and then having + * to re-detect that it's this register. + * Note that the accessfn/perms mean we know we're at EL2 or EL3 here. + */ + return do_tval_read(env, GTIMER_VIRT, env->cp15.cntvoff_el2); } static void gt_virt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - gt_tval_write(env, ri, GTIMER_VIRT, value); + /* Similarly for writes to CNTV_TVAL_EL02 */ + do_tval_write(env, GTIMER_VIRT, value, env->cp15.cntvoff_el2); } static void gt_virt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, From 02c648a0a103a1a7b2c077ec5a81da9907f45544 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 08/21] target/arm: Refactor handling of timer offset for direct register accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When reading or writing the timer registers, sometimes we need to apply one of the timer offsets. Specifically, this happens for direct reads of the counter registers CNTPCT_EL0 and CNTVCT_EL0 (and their self-synchronized variants CNTVCTSS_EL0 and CNTPCTSS_EL0). It also applies for direct reads and writes of the CNT*_TVAL_EL* registers that provide the 32-bit downcounting view of each timer. We currently do this with duplicated code in gt_tval_read() and gt_tval_write() and a special-case in gt_virt_cnt_read() and gt_cnt_read(). Refactor this so that we handle it all in a single function gt_direct_access_timer_offset(), to parallel how we handle the offset for indirect accesses. The call in the WFIT helper previously to gt_virt_cnt_offset() is now to gt_direct_access_timer_offset(); this is the correct behaviour, but it's not immediately obvious that it shouldn't be considered an indirect access, so we add an explanatory comment. This commit should make no behavioural changes. (Cc to stable because the following bugfix commit will depend on this one.) Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250204125009.2281315-6-peter.maydell@linaro.org --- target/arm/helper.c | 103 +++++++++++++++++++------------------ target/arm/internals.h | 5 +- target/arm/tcg/op_helper.c | 8 ++- 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index acf77793c7..54147d9761 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2455,14 +2455,6 @@ static uint64_t gt_phys_raw_cnt_offset(CPUARMState *env) return 0; } -static uint64_t gt_phys_cnt_offset(CPUARMState *env) -{ - if (arm_current_el(env) >= 2) { - return 0; - } - return gt_phys_raw_cnt_offset(env); -} - static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx) { /* @@ -2489,6 +2481,52 @@ static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx) } } +uint64_t gt_direct_access_timer_offset(CPUARMState *env, int timeridx) +{ + /* + * Return the timer offset to use for direct accesses to the + * counter registers CNTPCT and CNTVCT, and for direct accesses + * to the CNT*_TVAL registers. + * + * This isn't exactly the same as the indirect-access offset, + * because here we also care about what EL the register access + * is being made from. + * + * This corresponds to the access pseudocode for the registers. + */ + uint64_t hcr; + + switch (timeridx) { + case GTIMER_PHYS: + if (arm_current_el(env) >= 2) { + return 0; + } + return gt_phys_raw_cnt_offset(env); + case GTIMER_VIRT: + switch (arm_current_el(env)) { + case 2: + hcr = arm_hcr_el2_eff(env); + if (hcr & HCR_E2H) { + return 0; + } + break; + case 0: + hcr = arm_hcr_el2_eff(env); + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + return 0; + } + break; + } + return env->cp15.cntvoff_el2; + case GTIMER_HYP: + case GTIMER_SEC: + case GTIMER_HYPVIRT: + return 0; + default: + g_assert_not_reached(); + } +} + static void gt_recalc_timer(ARMCPU *cpu, int timeridx) { ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx]; @@ -2561,34 +2599,14 @@ static void gt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_get_countervalue(env) - gt_phys_cnt_offset(env); -} - -uint64_t gt_virt_cnt_offset(CPUARMState *env) -{ - uint64_t hcr; - - switch (arm_current_el(env)) { - case 2: - hcr = arm_hcr_el2_eff(env); - if (hcr & HCR_E2H) { - return 0; - } - break; - case 0: - hcr = arm_hcr_el2_eff(env); - if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { - return 0; - } - break; - } - - return env->cp15.cntvoff_el2; + uint64_t offset = gt_direct_access_timer_offset(env, GTIMER_PHYS); + return gt_get_countervalue(env) - offset; } static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_get_countervalue(env) - gt_virt_cnt_offset(env); + uint64_t offset = gt_direct_access_timer_offset(env, GTIMER_VIRT); + return gt_get_countervalue(env) - offset; } static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2609,16 +2627,7 @@ static uint64_t do_tval_read(CPUARMState *env, int timeridx, uint64_t offset) static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx) { - uint64_t offset = 0; - - switch (timeridx) { - case GTIMER_VIRT: - offset = gt_virt_cnt_offset(env); - break; - case GTIMER_PHYS: - offset = gt_phys_cnt_offset(env); - break; - } + uint64_t offset = gt_direct_access_timer_offset(env, timeridx); return do_tval_read(env, timeridx, offset); } @@ -2636,16 +2645,8 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx, uint64_t value) { - uint64_t offset = 0; + uint64_t offset = gt_direct_access_timer_offset(env, timeridx); - switch (timeridx) { - case GTIMER_VIRT: - offset = gt_virt_cnt_offset(env); - break; - case GTIMER_PHYS: - offset = gt_phys_cnt_offset(env); - break; - } do_tval_write(env, timeridx, value, offset); } diff --git a/target/arm/internals.h b/target/arm/internals.h index a6ff228f9f..bb96238919 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1819,9 +1819,10 @@ int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type); uint64_t gt_get_countervalue(CPUARMState *env); /* * Return the currently applicable offset between the system counter - * and CNTVCT_EL0 (this will be either 0 or the value of CNTVOFF_EL2). + * and the counter for the specified timer, as used for direct register + * accesses. */ -uint64_t gt_virt_cnt_offset(CPUARMState *env); +uint64_t gt_direct_access_timer_offset(CPUARMState *env, int timeridx); /* * Return mask of ARMMMUIdxBit values corresponding to an "invalidate diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 02c375d196..30786fd1ff 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -427,7 +427,13 @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout) int target_el = check_wfx_trap(env, false, &excp); /* The WFIT should time out when CNTVCT_EL0 >= the specified value. */ uint64_t cntval = gt_get_countervalue(env); - uint64_t offset = gt_virt_cnt_offset(env); + /* + * We want the value that we would get if we read CNTVCT_EL0 from + * the current exception level, so the direct_access offset, not + * the indirect_access one. Compare the pseudocode LocalTimeoutEvent(), + * which calls VirtualCounterTimer(). + */ + uint64_t offset = gt_direct_access_timer_offset(env, GTIMER_VIRT); uint64_t cntvct = cntval - offset; uint64_t nexttick; From f9f99d7ca522339c1de2292f132bb8ddc3471c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 09/21] target/arm: Implement SEL2 physical and virtual timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When FEAT_SEL2 was implemented the SEL2 timers were missed. This shows up when building the latest Hafnium with SPMC_AT_EL=2. The actual implementation utilises the same logic as the rest of the timers so all we need to do is: - define the timers and their access functions - conditionally add the correct system registers - create a new accessfn as the rules are subtly different to the existing secure timer Fixes: e9152ee91c (target/arm: add ARMv8.4-SEL2 system registers) Signed-off-by: Alex Bennée Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell Message-id: 20250204125009.2281315-7-peter.maydell@linaro.org Cc: qemu-stable@nongnu.org Cc: Andrei Homescu Cc: Arve Hjønnevåg Cc: Rémi Denis-Courmont [PMM: CP_ACCESS_TRAP_UNCATEGORIZED -> CP_ACCESS_UNDEFINED; offset logic now in gt_{indirect,direct}_access_timer_offset() ] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- include/hw/arm/bsa.h | 2 + target/arm/cpu.c | 4 ++ target/arm/cpu.h | 2 + target/arm/gtimer.h | 4 +- target/arm/helper.c | 163 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 1 deletion(-) diff --git a/include/hw/arm/bsa.h b/include/hw/arm/bsa.h index 8eaab603c0..13ed2d2ac1 100644 --- a/include/hw/arm/bsa.h +++ b/include/hw/arm/bsa.h @@ -22,6 +22,8 @@ #define QEMU_ARM_BSA_H /* These are architectural INTID values */ +#define ARCH_TIMER_S_EL2_VIRT_IRQ 19 +#define ARCH_TIMER_S_EL2_IRQ 20 #define VIRTUAL_PMU_IRQ 23 #define ARCH_GIC_MAINT_IRQ 25 #define ARCH_TIMER_NS_EL2_IRQ 26 diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 948defa3f5..cacbbc615a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2069,6 +2069,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) arm_gt_stimer_cb, cpu); cpu->gt_timer[GTIMER_HYPVIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale, arm_gt_hvtimer_cb, cpu); + cpu->gt_timer[GTIMER_S_EL2_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + arm_gt_sel2timer_cb, cpu); + cpu->gt_timer[GTIMER_S_EL2_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + arm_gt_sel2vtimer_cb, cpu); } #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 215845c7e2..8f52380c88 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1171,6 +1171,8 @@ void arm_gt_vtimer_cb(void *opaque); void arm_gt_htimer_cb(void *opaque); void arm_gt_stimer_cb(void *opaque); void arm_gt_hvtimer_cb(void *opaque); +void arm_gt_sel2timer_cb(void *opaque); +void arm_gt_sel2vtimer_cb(void *opaque); unsigned int gt_cntfrq_period_ns(ARMCPU *cpu); void gt_rme_post_el_change(ARMCPU *cpu, void *opaque); diff --git a/target/arm/gtimer.h b/target/arm/gtimer.h index b992941bef..0e89b8e58d 100644 --- a/target/arm/gtimer.h +++ b/target/arm/gtimer.h @@ -15,7 +15,9 @@ enum { GTIMER_HYP = 2, GTIMER_SEC = 3, GTIMER_HYPVIRT = 4, -#define NUM_GTIMERS 5 + GTIMER_S_EL2_PHYS = 5, /* CNTHPS_* ; only if FEAT_SEL2 */ + GTIMER_S_EL2_VIRT = 6, /* CNTHVS_* ; only if FEAT_SEL2 */ +#define NUM_GTIMERS 7 }; #endif diff --git a/target/arm/helper.c b/target/arm/helper.c index 54147d9761..407efe9427 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2404,6 +2404,45 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, } } +static CPAccessResult gt_sel2timer_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* + * The AArch64 register view of the secure EL2 timers are mostly + * accessible from EL3 and EL2 although can also be trapped to EL2 + * from EL1 depending on nested virt config. + */ + switch (arm_current_el(env)) { + case 0: /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + case 1: + if (!arm_is_secure(env)) { + /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + } else if (arm_hcr_el2_eff(env) & HCR_NV) { + /* Aarch64.SystemAccessTrap(EL2, 0x18) */ + return CP_ACCESS_TRAP_EL2; + } + /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + case 2: + if (!arm_is_secure(env)) { + /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + } + return CP_ACCESS_OK; + case 3: + if (env->cp15.scr_el3 & SCR_EEL2) { + return CP_ACCESS_OK; + } else { + return CP_ACCESS_UNDEFINED; + } + default: + g_assert_not_reached(); + } +} + uint64_t gt_get_countervalue(CPUARMState *env) { ARMCPU *cpu = env_archcpu(env); @@ -2475,6 +2514,8 @@ static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx) case GTIMER_HYP: case GTIMER_SEC: case GTIMER_HYPVIRT: + case GTIMER_S_EL2_PHYS: + case GTIMER_S_EL2_VIRT: return 0; default: g_assert_not_reached(); @@ -2521,6 +2562,8 @@ uint64_t gt_direct_access_timer_offset(CPUARMState *env, int timeridx) case GTIMER_HYP: case GTIMER_SEC: case GTIMER_HYPVIRT: + case GTIMER_S_EL2_PHYS: + case GTIMER_S_EL2_VIRT: return 0; default: g_assert_not_reached(); @@ -2953,6 +2996,62 @@ static void gt_sec_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_ctl_write(env, ri, GTIMER_SEC, value); } +static void gt_sec_pel2_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_S_EL2_PHYS); +} + +static void gt_sec_pel2_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_S_EL2_PHYS, value); +} + +static uint64_t gt_sec_pel2_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_S_EL2_PHYS); +} + +static void gt_sec_pel2_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_S_EL2_PHYS, value); +} + +static void gt_sec_pel2_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_S_EL2_PHYS, value); +} + +static void gt_sec_vel2_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_S_EL2_VIRT); +} + +static void gt_sec_vel2_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_S_EL2_VIRT, value); +} + +static uint64_t gt_sec_vel2_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_S_EL2_VIRT); +} + +static void gt_sec_vel2_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_S_EL2_VIRT, value); +} + +static void gt_sec_vel2_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_S_EL2_VIRT, value); +} + static void gt_hv_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) { gt_timer_reset(env, ri, GTIMER_HYPVIRT); @@ -3009,6 +3108,20 @@ void arm_gt_stimer_cb(void *opaque) gt_recalc_timer(cpu, GTIMER_SEC); } +void arm_gt_sel2timer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_S_EL2_PHYS); +} + +void arm_gt_sel2vtimer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_S_EL2_VIRT); +} + void arm_gt_hvtimer_cb(void *opaque) { ARMCPU *cpu = opaque; @@ -5733,6 +5846,56 @@ static const ARMCPRegInfo el2_sec_cp_reginfo[] = { .access = PL2_RW, .accessfn = sel2_access, .nv2_redirect_offset = 0x48, .fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) }, +#ifndef CONFIG_USER_ONLY + /* Secure EL2 Physical Timer */ + { .name = "CNTHPS_TVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .readfn = gt_sec_pel2_tval_read, + .writefn = gt_sec_pel2_tval_write, + .resetfn = gt_sec_pel2_timer_reset, + }, + { .name = "CNTHPS_CTL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 1, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_PHYS].ctl), + .resetvalue = 0, + .writefn = gt_sec_pel2_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTHPS_CVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 2, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_PHYS].cval), + .writefn = gt_sec_pel2_cval_write, .raw_writefn = raw_write, + }, + /* Secure EL2 Virtual Timer */ + { .name = "CNTHVS_TVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .readfn = gt_sec_vel2_tval_read, + .writefn = gt_sec_vel2_tval_write, + .resetfn = gt_sec_vel2_timer_reset, + }, + { .name = "CNTHVS_CTL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 1, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_VIRT].ctl), + .resetvalue = 0, + .writefn = gt_sec_vel2_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTHVS_CVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 2, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_VIRT].cval), + .writefn = gt_sec_vel2_cval_write, .raw_writefn = raw_write, + }, +#endif }; static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, From 47e2c5510f8c13310bfe738ebaa913bb52feca2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 10/21] target/arm: Document the architectural names of our GTIMERs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we are about to add more physical and virtual timers let's make it clear what each timer does. Signed-off-by: Alex Bennée Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell Message-id: 20250204125009.2281315-8-peter.maydell@linaro.org [PMM: Add timer register name prefix to each comment] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/gtimer.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/arm/gtimer.h b/target/arm/gtimer.h index 0e89b8e58d..d49c63cbf8 100644 --- a/target/arm/gtimer.h +++ b/target/arm/gtimer.h @@ -10,11 +10,11 @@ #define TARGET_ARM_GTIMER_H enum { - GTIMER_PHYS = 0, - GTIMER_VIRT = 1, - GTIMER_HYP = 2, - GTIMER_SEC = 3, - GTIMER_HYPVIRT = 4, + GTIMER_PHYS = 0, /* CNTP_* ; EL1 physical timer */ + GTIMER_VIRT = 1, /* CNTV_* ; EL1 virtual timer */ + GTIMER_HYP = 2, /* CNTHP_* ; EL2 physical timer */ + GTIMER_SEC = 3, /* CNTPS_* ; EL3 physical timer */ + GTIMER_HYPVIRT = 4, /* CNTHV_* ; EL2 virtual timer ; only if FEAT_VHE */ GTIMER_S_EL2_PHYS = 5, /* CNTHPS_* ; only if FEAT_SEL2 */ GTIMER_S_EL2_VIRT = 6, /* CNTHVS_* ; only if FEAT_SEL2 */ #define NUM_GTIMERS 7 From 5dcaea8bcd82972add29eef350547f922fb4caa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 11/21] hw/arm: enable secure EL2 timers for virt machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Signed-off-by: Peter Maydell Reviewed-by: Peter Maydell Message-id: 20250204125009.2281315-9-peter.maydell@linaro.org Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 904c698b14..a96452f17a 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -882,6 +882,8 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, [GTIMER_HYPVIRT] = ARCH_TIMER_NS_EL2_VIRT_IRQ, + [GTIMER_S_EL2_PHYS] = ARCH_TIMER_S_EL2_IRQ, + [GTIMER_S_EL2_VIRT] = ARCH_TIMER_S_EL2_VIRT_IRQ, }; for (unsigned irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { From 9a9d9e82093efa22e3e2bdaac0f24c823f8786f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 12/21] hw/arm: enable secure EL2 timers for sbsa machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-id: 20250204125009.2281315-10-peter.maydell@linaro.org Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell --- hw/arm/sbsa-ref.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index e720de3064..aa09d7a091 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -484,6 +484,8 @@ static void create_gic(SBSAMachineState *sms, MemoryRegion *mem) [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, [GTIMER_HYPVIRT] = ARCH_TIMER_NS_EL2_VIRT_IRQ, + [GTIMER_S_EL2_PHYS] = ARCH_TIMER_S_EL2_IRQ, + [GTIMER_S_EL2_VIRT] = ARCH_TIMER_S_EL2_VIRT_IRQ, }; for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { From cde3247651dc998da5dc1005148302a90d72f21f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 13/21] target/arm: Correct LDRD atomicity and fault behaviour Our LDRD implementation is wrong in two respects: * if the address is 4-aligned and the load crosses a page boundary and the second load faults and the first load was to the base register (as in cases like "ldrd r2, r3, [r2]", then we must not update the base register before taking the fault * if the address is 8-aligned the access must be a 64-bit single-copy atomic access, not two 32-bit accesses Rewrite the handling of the loads in LDRD to use a single tcg_gen_qemu_ld_i64() and split the result into the destination registers. This allows us to get the atomicity requirements right, and also implicitly means that we won't update the base register too early for the page-crossing case. Note that because we no longer increment 'addr' by 4 in the course of performing the LDRD we must change the adjustment value we pass to op_addr_ri_post() and op_addr_rr_post(): it no longer needs to subtract 4 to get the correct value to use if doing base register writeback. STRD has the same problem with not getting the atomicity right; we will deal with that in the following commit. Cc: qemu-stable@nongnu.org Reported-by: Stu Grossman Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250227142746.1698904-2-peter.maydell@linaro.org --- target/arm/tcg/translate.c | 70 +++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index d8225b77c8..93772da39a 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -5003,10 +5003,49 @@ static bool op_store_rr(DisasContext *s, arg_ldst_rr *a, return true; } +static void do_ldrd_load(DisasContext *s, TCGv_i32 addr, int rt, int rt2) +{ + /* + * LDRD is required to be an atomic 64-bit access if the + * address is 8-aligned, two atomic 32-bit accesses if + * it's only 4-aligned, and to give an alignment fault + * if it's not 4-aligned. This is MO_ALIGN_4 | MO_ATOM_SUBALIGN. + * Rt is always the word from the lower address, and Rt2 the + * data from the higher address, regardless of endianness. + * So (like gen_load_exclusive) we avoid gen_aa32_ld_i64() + * so we don't get its SCTLR_B check, and instead do a 64-bit access + * using MO_BE if appropriate and then split the two halves. + * + * For M-profile, and for A-profile before LPAE, the 64-bit + * atomicity is not required. We could model that using + * the looser MO_ATOM_IFALIGN_PAIR, but providing a higher + * level of atomicity than required is harmless (we would not + * currently generate better code for IFALIGN_PAIR here). + * + * This also gives us the correct behaviour of not updating + * rt if the load of rt2 faults; this is required for cases + * like "ldrd r2, r3, [r2]" where rt is also the base register. + */ + int mem_idx = get_mem_index(s); + MemOp opc = MO_64 | MO_ALIGN_4 | MO_ATOM_SUBALIGN | s->be_data; + TCGv taddr = gen_aa32_addr(s, addr, opc); + TCGv_i64 t64 = tcg_temp_new_i64(); + TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 tmp2 = tcg_temp_new_i32(); + + tcg_gen_qemu_ld_i64(t64, taddr, mem_idx, opc); + if (s->be_data == MO_BE) { + tcg_gen_extr_i64_i32(tmp2, tmp, t64); + } else { + tcg_gen_extr_i64_i32(tmp, tmp2, t64); + } + store_reg(s, rt, tmp); + store_reg(s, rt2, tmp2); +} + static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) { - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + TCGv_i32 addr; if (!ENABLE_ARCH_5TE) { return false; @@ -5017,18 +5056,10 @@ static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) } addr = op_addr_rr_pre(s, a); - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, a->rt, tmp); - - tcg_gen_addi_i32(addr, addr, 4); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, a->rt + 1, tmp); + do_ldrd_load(s, addr, a->rt, a->rt + 1); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_rr_post(s, a, addr, -4); + op_addr_rr_post(s, a, addr, 0); return true; } @@ -5152,23 +5183,14 @@ static bool op_store_ri(DisasContext *s, arg_ldst_ri *a, static bool op_ldrd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) { - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + TCGv_i32 addr; addr = op_addr_ri_pre(s, a); - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, a->rt, tmp); - - tcg_gen_addi_i32(addr, addr, 4); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, rt2, tmp); + do_ldrd_load(s, addr, a->rt, rt2); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_ri_post(s, a, addr, -4); + op_addr_ri_post(s, a, addr, 0); return true; } From ee786ca115045a2b7e86ac3073b0761cb99e0d49 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:21 +0000 Subject: [PATCH 14/21] target/arm: Correct STRD atomicity Our STRD implementation doesn't correctly implement the requirement: * if the address is 8-aligned the access must be a 64-bit single-copy atomic access, not two 32-bit accesses Rewrite the handling of STRD to use a single tcg_gen_qemu_st_i64() of a value produced by concatenating the two 32 bit source registers. This allows us to get the atomicity right. As with the LDRD change, now that we don't update 'addr' in the course of performing the store we need to adjust the offset we pass to op_addr_ri_post() and op_addr_rr_post(). Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250227142746.1698904-3-peter.maydell@linaro.org --- target/arm/tcg/translate.c | 59 +++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 93772da39a..404a254678 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -5063,10 +5063,42 @@ static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) return true; } +static void do_strd_store(DisasContext *s, TCGv_i32 addr, int rt, int rt2) +{ + /* + * STRD is required to be an atomic 64-bit access if the + * address is 8-aligned, two atomic 32-bit accesses if + * it's only 4-aligned, and to give an alignment fault + * if it's not 4-aligned. + * Rt is always the word from the lower address, and Rt2 the + * data from the higher address, regardless of endianness. + * So (like gen_store_exclusive) we avoid gen_aa32_ld_i64() + * so we don't get its SCTLR_B check, and instead do a 64-bit access + * using MO_BE if appropriate, using a value constructed + * by putting the two halves together in the right order. + * + * As with LDRD, the 64-bit atomicity is not required for + * M-profile, or for A-profile before LPAE, and we provide + * the higher guarantee always for simplicity. + */ + int mem_idx = get_mem_index(s); + MemOp opc = MO_64 | MO_ALIGN_4 | MO_ATOM_SUBALIGN | s->be_data; + TCGv taddr = gen_aa32_addr(s, addr, opc); + TCGv_i32 t1 = load_reg(s, rt); + TCGv_i32 t2 = load_reg(s, rt2); + TCGv_i64 t64 = tcg_temp_new_i64(); + + if (s->be_data == MO_BE) { + tcg_gen_concat_i32_i64(t64, t2, t1); + } else { + tcg_gen_concat_i32_i64(t64, t1, t2); + } + tcg_gen_qemu_st_i64(t64, taddr, mem_idx, opc); +} + static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) { - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + TCGv_i32 addr; if (!ENABLE_ARCH_5TE) { return false; @@ -5077,15 +5109,9 @@ static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) } addr = op_addr_rr_pre(s, a); - tmp = load_reg(s, a->rt); - gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + do_strd_store(s, addr, a->rt, a->rt + 1); - tcg_gen_addi_i32(addr, addr, 4); - - tmp = load_reg(s, a->rt + 1); - gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - - op_addr_rr_post(s, a, addr, -4); + op_addr_rr_post(s, a, addr, 0); return true; } @@ -5213,20 +5239,13 @@ static bool trans_LDRD_ri_t32(DisasContext *s, arg_ldst_ri2 *a) static bool op_strd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) { - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + TCGv_i32 addr; addr = op_addr_ri_pre(s, a); - tmp = load_reg(s, a->rt); - gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + do_strd_store(s, addr, a->rt, rt2); - tcg_gen_addi_i32(addr, addr, 4); - - tmp = load_reg(s, rt2); - gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - - op_addr_ri_post(s, a, addr, -4); + op_addr_ri_post(s, a, addr, 0); return true; } From 5be4419c573e78c21be953a4c31947f3087931a5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 7 Mar 2025 10:08:22 +0000 Subject: [PATCH 15/21] target/arm: Drop unused address_offset from op_addr_{rr, ri}_post() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the callers of op_addr_rr_post() and op_addr_ri_post() now pass in zero for the address_offset, so we can remove that argument. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250227142746.1698904-4-peter.maydell@linaro.org --- target/arm/tcg/translate.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 404a254678..d280018138 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -4941,7 +4941,7 @@ static TCGv_i32 op_addr_rr_pre(DisasContext *s, arg_ldst_rr *a) } static void op_addr_rr_post(DisasContext *s, arg_ldst_rr *a, - TCGv_i32 addr, int address_offset) + TCGv_i32 addr) { if (!a->p) { TCGv_i32 ofs = load_reg(s, a->rm); @@ -4954,7 +4954,6 @@ static void op_addr_rr_post(DisasContext *s, arg_ldst_rr *a, } else if (!a->w) { return; } - tcg_gen_addi_i32(addr, addr, address_offset); store_reg(s, a->rn, addr); } @@ -4974,7 +4973,7 @@ static bool op_load_rr(DisasContext *s, arg_ldst_rr *a, * Perform base writeback before the loaded value to * ensure correct behavior with overlapping index registers. */ - op_addr_rr_post(s, a, addr, 0); + op_addr_rr_post(s, a, addr); store_reg_from_load(s, a->rt, tmp); return true; } @@ -4999,7 +4998,7 @@ static bool op_store_rr(DisasContext *s, arg_ldst_rr *a, gen_aa32_st_i32(s, tmp, addr, mem_idx, mop); disas_set_da_iss(s, mop, issinfo); - op_addr_rr_post(s, a, addr, 0); + op_addr_rr_post(s, a, addr); return true; } @@ -5059,7 +5058,7 @@ static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) do_ldrd_load(s, addr, a->rt, a->rt + 1); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_rr_post(s, a, addr, 0); + op_addr_rr_post(s, a, addr); return true; } @@ -5111,7 +5110,7 @@ static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) do_strd_store(s, addr, a->rt, a->rt + 1); - op_addr_rr_post(s, a, addr, 0); + op_addr_rr_post(s, a, addr); return true; } @@ -5147,13 +5146,14 @@ static TCGv_i32 op_addr_ri_pre(DisasContext *s, arg_ldst_ri *a) } static void op_addr_ri_post(DisasContext *s, arg_ldst_ri *a, - TCGv_i32 addr, int address_offset) + TCGv_i32 addr) { + int address_offset = 0; if (!a->p) { if (a->u) { - address_offset += a->imm; + address_offset = a->imm; } else { - address_offset -= a->imm; + address_offset = -a->imm; } } else if (!a->w) { return; @@ -5178,7 +5178,7 @@ static bool op_load_ri(DisasContext *s, arg_ldst_ri *a, * Perform base writeback before the loaded value to * ensure correct behavior with overlapping index registers. */ - op_addr_ri_post(s, a, addr, 0); + op_addr_ri_post(s, a, addr); store_reg_from_load(s, a->rt, tmp); return true; } @@ -5203,7 +5203,7 @@ static bool op_store_ri(DisasContext *s, arg_ldst_ri *a, gen_aa32_st_i32(s, tmp, addr, mem_idx, mop); disas_set_da_iss(s, mop, issinfo); - op_addr_ri_post(s, a, addr, 0); + op_addr_ri_post(s, a, addr); return true; } @@ -5216,7 +5216,7 @@ static bool op_ldrd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) do_ldrd_load(s, addr, a->rt, rt2); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_ri_post(s, a, addr, 0); + op_addr_ri_post(s, a, addr); return true; } @@ -5245,7 +5245,7 @@ static bool op_strd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) do_strd_store(s, addr, a->rt, rt2); - op_addr_ri_post(s, a, addr, 0); + op_addr_ri_post(s, a, addr); return true; } From cc503abf4ba30ed34bbf18b3fd8eaa8046fae48b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 28 Feb 2025 16:24:24 +0000 Subject: [PATCH 16/21] target/arm: Make dummy debug registers RAZ, not NOP In debug_helper.c we provide a few dummy versions of debug registers: * DBGVCR (AArch32 only): enable bits for vector-catch debug events * MDCCINT_EL1: interrupt enable bits for the DCC debug communications channel * DBGVCR32_EL2: the AArch64 accessor for the state in DBGVCR We implemented these only to stop Linux crashing on startup, but we chose to implement them as ARM_CP_NOP. This worked for Linux where it only cares about trying to write to these registers, but is very confusing behaviour for anything that wants to read the registers (perhaps for context state switches), because the destination register will be left with whatever random value it happened to have before the read. Model these registers instead as RAZ. Fixes: 5e8b12ffbb8c68 ("target-arm: Implement minimal DBGVCR, OSDLR_EL1, MDCCSR_EL0") Fixes: 5dbdc4342f479d ("target-arm: Implement dummy MDCCINT_EL1") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2708 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250228162424.1917269-1-peter.maydell@linaro.org --- target/arm/debug_helper.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 36bffde74e..a9a619ba6b 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -1037,7 +1037,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .name = "DBGVCR", .cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, .access = PL1_RW, .accessfn = access_tda, - .type = ARM_CP_NOP }, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* * Dummy MDCCINT_EL1, since we don't implement the Debug Communications * Channel but Linux may try to access this register. The 32-bit @@ -1046,7 +1046,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .name = "MDCCINT_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, .access = PL1_RW, .accessfn = access_tdcc, - .type = ARM_CP_NOP }, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* * Dummy DBGCLAIM registers. * "The architecture does not define any functionality for the CLAIM tag bits.", @@ -1075,7 +1075,8 @@ static const ARMCPRegInfo debug_aa32_el1_reginfo[] = { { .name = "DBGVCR32_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 4, .crn = 0, .crm = 7, .opc2 = 0, .access = PL2_RW, .accessfn = access_dbgvcr32, - .type = ARM_CP_NOP | ARM_CP_EL3_NO_EL2_KEEP }, + .type = ARM_CP_CONST | ARM_CP_EL3_NO_EL2_KEEP, + .resetvalue = 0 }, }; static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { From 02ae315467cee589d02dfb89e13a2a6a8de09fc5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 10 Feb 2025 13:58:04 +0000 Subject: [PATCH 17/21] util/qemu-timer.c: Don't warp timer from timerlist_rearm() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we call icount_start_warp_timer() from timerlist_rearm(). This produces incorrect behaviour, because timerlist_rearm() is called, for instance, when a timer callback modifies its timer. We cannot decide here to warp the timer forwards to the next timer deadline merely because all_cpu_threads_idle() is true, because the timer callback we were called from (or some other callback later in the list of callbacks being invoked) may be about to raise a CPU interrupt and move a CPU from idle to ready. The only valid place to choose to warp the timer forward is from the main loop, when we know we have no outstanding IO or timer callbacks that might be about to wake up a CPU. For Arm guests, this bug was mostly latent until the refactoring commit f6fc36deef6abc ("target/arm/helper: Implement CNTHCTL_EL2.CNT[VP]MASK"), which exposed it because it refactored a timer callback so that it happened to call timer_mod() first and raise the interrupt second, when it had previously raised the interrupt first and called timer_mod() afterwards. This call seems to have originally derived from the pre-record-and-replay icount code, which (as of e.g. commit db1a49726c3c in 2010) in this location did a call to qemu_notify_event(), necessary to get the icount code in the vCPU round-robin thread to stop and recalculate the icount deadline when a timer was reprogrammed from the IO thread. In current QEMU, everything is done on the vCPU thread when we are in icount mode, so there's no need to try to notify another thread here. I suspect that the other reason why this call was doing icount timer warping is that it pre-dates commit efab87cf79077a from 2015, which added a call to icount_start_warp_timer() to main_loop_wait(). Once the call in timerlist_rearm() has been removed, if the timer callbacks don't cause any CPU to be woken up then we will end up calling icount_start_warp_timer() from main_loop_wait() when the rr main loop code calls rr_wait_io_event(). Remove the incorrect call from timerlist_rearm(). Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2703 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 20250210135804.3526943-1-peter.maydell@linaro.org --- util/qemu-timer.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 3243d2c515..788466fe22 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -409,10 +409,6 @@ static bool timer_mod_ns_locked(QEMUTimerList *timer_list, static void timerlist_rearm(QEMUTimerList *timer_list) { - /* Interrupt execution to force deadline recalculation. */ - if (icount_enabled() && timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { - icount_start_warp_timer(); - } timerlist_notify(timer_list); } From 84e5ce68c0c2d52d97a66ff2f53697f9ced190a8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 28 Feb 2025 10:32:22 +0000 Subject: [PATCH 18/21] include/exec/memop.h: Expand comment for MO_ATOM_SUBALIGN Expand the example in the comment documenting MO_ATOM_SUBALIGN, to be clearer about the atomicity guarantees it represents. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250228103222.1838913-1-peter.maydell@linaro.org --- include/exec/memop.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/exec/memop.h b/include/exec/memop.h index acdb40a9b3..407a47d82c 100644 --- a/include/exec/memop.h +++ b/include/exec/memop.h @@ -91,8 +91,12 @@ typedef enum MemOp { * Depending on alignment, one or both will be single-copy atomic. * This is the atomicity e.g. of Arm FEAT_LSE2 LDP. * MO_ATOM_SUBALIGN: the operation is single-copy atomic by parts - * by the alignment. E.g. if the address is 0 mod 4, then each - * 4-byte subobject is single-copy atomic. + * by the alignment. E.g. if an 8-byte value is accessed at an + * address which is 0 mod 8, then the whole 8-byte access is + * single-copy atomic; otherwise, if it is accessed at 0 mod 4 + * then each 4-byte subobject is single-copy atomic; otherwise + * if it is accessed at 0 mod 2 then the four 2-byte subobjects + * are single-copy atomic. * This is the atomicity e.g. of IBM Power. * MO_ATOM_NONE: the operation has no atomicity requirements. * From 8881b691d2d3613e9d7ff596a46a451b393377a5 Mon Sep 17 00:00:00 2001 From: JianChunfu Date: Fri, 28 Feb 2025 11:14:38 +0800 Subject: [PATCH 19/21] hw/arm/smmu: Introduce smmu_configs_inv_sid_range() helper Use a similar terminology smmu_hash_remove_by_sid_range() as the one being used for other hash table matching functions since smmuv3_invalidate_ste() name is not self explanatory, and introduce a helper that invokes the g_hash_table_foreach_remove. No functional change intended. Signed-off-by: JianChunfu Reviewed-by: Eric Auger Message-id: 20250228031438.3916-1-jansef.jian@hj-micro.com Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 21 +++++++++++++++++++++ hw/arm/smmu-internal.h | 5 ----- hw/arm/smmuv3.c | 19 ++----------------- hw/arm/trace-events | 3 ++- include/hw/arm/smmu-common.h | 6 ++++++ 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 8c1b407b82..6e720e1b9a 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -225,6 +225,27 @@ static gboolean smmu_hash_remove_by_vmid_ipa(gpointer key, gpointer value, ((entry->iova & ~info->mask) == info->iova); } +static gboolean +smmu_hash_remove_by_sid_range(gpointer key, gpointer value, gpointer user_data) +{ + SMMUDevice *sdev = (SMMUDevice *)key; + uint32_t sid = smmu_get_sid(sdev); + SMMUSIDRange *sid_range = (SMMUSIDRange *)user_data; + + if (sid < sid_range->start || sid > sid_range->end) { + return false; + } + trace_smmu_config_cache_inv(sid); + return true; +} + +void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range) +{ + trace_smmu_configs_inv_sid_range(sid_range.start, sid_range.end); + g_hash_table_foreach_remove(s->configs, smmu_hash_remove_by_sid_range, + &sid_range); +} + void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl) { diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h index 843bebb185..d143d296f3 100644 --- a/hw/arm/smmu-internal.h +++ b/hw/arm/smmu-internal.h @@ -141,9 +141,4 @@ typedef struct SMMUIOTLBPageInvInfo { uint64_t mask; } SMMUIOTLBPageInvInfo; -typedef struct SMMUSIDRange { - uint32_t start; - uint32_t end; -} SMMUSIDRange; - #endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index b40acbe024..1a96287ba9 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -903,7 +903,7 @@ static void smmuv3_flush_config(SMMUDevice *sdev) SMMUv3State *s = sdev->smmu; SMMUState *bc = &s->smmu_state; - trace_smmuv3_config_cache_inv(smmu_get_sid(sdev)); + trace_smmu_config_cache_inv(smmu_get_sid(sdev)); g_hash_table_remove(bc->configs, sdev); } @@ -1277,20 +1277,6 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage) } } -static gboolean -smmuv3_invalidate_ste(gpointer key, gpointer value, gpointer user_data) -{ - SMMUDevice *sdev = (SMMUDevice *)key; - uint32_t sid = smmu_get_sid(sdev); - SMMUSIDRange *sid_range = (SMMUSIDRange *)user_data; - - if (sid < sid_range->start || sid > sid_range->end) { - return false; - } - trace_smmuv3_config_cache_inv(sid); - return true; -} - static int smmuv3_cmdq_consume(SMMUv3State *s) { SMMUState *bs = ARM_SMMU(s); @@ -1373,8 +1359,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) sid_range.end = sid_range.start + mask; trace_smmuv3_cmdq_cfgi_ste_range(sid_range.start, sid_range.end); - g_hash_table_foreach_remove(bs->configs, smmuv3_invalidate_ste, - &sid_range); + smmu_configs_inv_sid_range(bs, sid_range); break; } case SMMU_CMD_CFGI_CD: diff --git a/hw/arm/trace-events b/hw/arm/trace-events index f49cae1656..f3386bd7ae 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -22,6 +22,8 @@ smmu_iotlb_inv_asid_vmid(int asid, int vmid) "IOTLB invalidate asid=%d vmid=%d" smmu_iotlb_inv_vmid(int vmid) "IOTLB invalidate vmid=%d" smmu_iotlb_inv_vmid_s1(int vmid) "IOTLB invalidate vmid=%d" smmu_iotlb_inv_iova(int asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 +smmu_configs_inv_sid_range(uint32_t start, uint32_t end) "Config cache INV SID range from 0x%x to 0x%x" +smmu_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" smmu_iotlb_lookup_hit(int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" smmu_iotlb_lookup_miss(int asid, int vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" @@ -59,7 +61,6 @@ smmuv3_cmdq_tlbi_nh(int vmid) "vmid=%d" smmuv3_cmdq_tlbi_nsnh(void) "" smmuv3_cmdq_tlbi_nh_asid(int asid) "asid=%d" smmuv3_cmdq_tlbi_s12_vmid(int vmid) "vmid=%d" -smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" 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, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d" diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index e5ad55bbae..e5e2d09294 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -142,6 +142,11 @@ typedef struct SMMUIOTLBKey { uint8_t level; } SMMUIOTLBKey; +typedef struct SMMUSIDRange { + uint32_t start; + uint32_t end; +} SMMUSIDRange; + struct SMMUState { /* */ SysBusDevice dev; @@ -219,6 +224,7 @@ void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl); void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg, uint64_t num_pages, uint8_t ttl); +void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range); /* Unmap the range of all the notifiers registered to any IOMMU mr */ void smmu_inv_notifiers_all(SMMUState *s); From 7610317f45159b620475aafc7a94c93b66eda7e2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 18 Feb 2025 13:21:00 -0800 Subject: [PATCH 20/21] target/rx: Set exception vector base to 0xffffff80 The documentation says the vector is at 0xffffff80, instead of the previous value of 0xffffffc0. That value must have been a bug because the standard vector values (20, 21, 23, 25, 30) were all past the end of the array. Signed-off-by: Keith Packard Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/rx/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/rx/helper.c b/target/rx/helper.c index 7f28e72989..e8aabf40ff 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -88,7 +88,7 @@ void rx_cpu_do_interrupt(CPUState *cs) cpu_stl_data(env, env->isp, env->pc); if (vec < 0x100) { - env->pc = cpu_ldl_data(env, 0xffffffc0 + vec * 4); + env->pc = cpu_ldl_data(env, 0xffffff80 + vec * 4); } else { env->pc = cpu_ldl_data(env, env->intb + (vec & 0xff) * 4); } From 0ce0739d46983e5e88fa9c149cb305689c9d8c6f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 18 Feb 2025 13:21:01 -0800 Subject: [PATCH 21/21] target/rx: Remove TCG_CALL_NO_WG from helpers which write env Functions which modify TCG globals must not be marked TCG_CALL_NO_WG, as that tells the optimizer that TCG global values already loaded in machine registers are still valid, and so any changes which these helpers make to the CPU state may be ignored. The target/rx code chooses to put (among other things) all the PSW bits and also ACC into globals, so the NO_WG flag on various functions that touch the PSW or ACC is incorrect and must be removed. This includes all the floating point helper functions, because update_fpsw() will update PSW Z and S. Signed-off-by: Keith Packard [PMM: Clarified commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/rx/helper.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/target/rx/helper.h b/target/rx/helper.h index ebb4739474..8cc38b0cb7 100644 --- a/target/rx/helper.h +++ b/target/rx/helper.h @@ -4,27 +4,27 @@ DEF_HELPER_1(raise_privilege_violation, noreturn, env) DEF_HELPER_1(wait, noreturn, env) DEF_HELPER_2(rxint, noreturn, env, i32) DEF_HELPER_1(rxbrk, noreturn, env) -DEF_HELPER_FLAGS_3(fadd, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fsub, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fmul, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fdiv, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_WG, void, env, f32, f32) -DEF_HELPER_FLAGS_2(ftoi, TCG_CALL_NO_WG, i32, env, f32) -DEF_HELPER_FLAGS_2(round, TCG_CALL_NO_WG, i32, env, f32) -DEF_HELPER_FLAGS_2(itof, TCG_CALL_NO_WG, f32, env, i32) +DEF_HELPER_3(fadd, f32, env, f32, f32) +DEF_HELPER_3(fsub, f32, env, f32, f32) +DEF_HELPER_3(fmul, f32, env, f32, f32) +DEF_HELPER_3(fdiv, f32, env, f32, f32) +DEF_HELPER_3(fcmp, void, env, f32, f32) +DEF_HELPER_2(ftoi, i32, env, f32) +DEF_HELPER_2(round, i32, env, f32) +DEF_HELPER_2(itof, f32, env, i32) DEF_HELPER_2(set_fpsw, void, env, i32) -DEF_HELPER_FLAGS_2(racw, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(set_psw_rte, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(set_psw, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_2(racw, void, env, i32) +DEF_HELPER_2(set_psw_rte, void, env, i32) +DEF_HELPER_2(set_psw, void, env, i32) DEF_HELPER_1(pack_psw, i32, env) -DEF_HELPER_FLAGS_3(div, TCG_CALL_NO_WG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) -DEF_HELPER_FLAGS_1(scmpu, TCG_CALL_NO_WG, void, env) +DEF_HELPER_3(div, i32, env, i32, i32) +DEF_HELPER_3(divu, i32, env, i32, i32) +DEF_HELPER_1(scmpu, void, env) DEF_HELPER_1(smovu, void, env) DEF_HELPER_1(smovf, void, env) DEF_HELPER_1(smovb, void, env) DEF_HELPER_2(sstr, void, env, i32) -DEF_HELPER_FLAGS_2(swhile, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(suntil, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(rmpa, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_2(swhile, void, env, i32) +DEF_HELPER_2(suntil, void, env, i32) +DEF_HELPER_2(rmpa, void, env, i32) DEF_HELPER_1(satr, void, env)