* [PULL 01/54] hw/timer/imx_epit: Replace DPRINTF with trace events
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 02/54] target/arm: Enable SCTLR_EL1.EnFPM for user-only Peter Maydell
` (53 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: jack wang <163wangjack@gmail.com>
Clean up the codebase by removing the outdated DEBUG_IMX_EPIT
and DPRINTF macros, replacing them with modern QEMU trace events.
This also removes an empty and meaningless DPRINTF("\n") in the
imx_epit_realize function.
Signed-off-by: jack wang <163wangjack@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Message-id: 20260617161406.14705-1-163wangjack@gmail.com
[PMM: remove stray blank lines from bottom of trace-events file]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/timer/imx_epit.c | 25 +++++--------------------
hw/timer/trace-events | 6 ++++++
2 files changed, 11 insertions(+), 20 deletions(-)
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index c67a39f10c..374ec7d324 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -20,18 +20,7 @@
#include "hw/misc/imx_ccm.h"
#include "qemu/module.h"
#include "qemu/log.h"
-
-#ifndef DEBUG_IMX_EPIT
-#define DEBUG_IMX_EPIT 0
-#endif
-
-#define DPRINTF(fmt, args...) \
- do { \
- if (DEBUG_IMX_EPIT) { \
- fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_EPIT, \
- __func__, ##args); \
- } \
- } while (0)
+#include "trace.h"
static const char *imx_epit_reg_name(uint32_t reg)
{
@@ -80,7 +69,7 @@ static uint32_t imx_epit_get_freq(IMXEPITState *s)
uint32_t prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, CR_PRESCALE_BITS);
uint32_t f_in = imx_ccm_get_clock_frequency(s->ccm, imx_epit_clocks[clksrc]);
uint32_t freq = f_in / prescaler;
- DPRINTF("ptimer frequency is %u\n", freq);
+ trace_imx_epit_get_freq(freq);
return freq;
}
@@ -146,8 +135,7 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size)
HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset);
break;
}
-
- DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(offset >> 2), reg_value);
+ trace_imx_epit_read(imx_epit_reg_name(offset >> 2), reg_value);
return reg_value;
}
@@ -328,8 +316,7 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
{
IMXEPITState *s = IMX_EPIT(opaque);
- DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2),
- (uint32_t)value);
+ trace_imx_epit_write(imx_epit_reg_name(offset >> 2), value);
switch (offset >> 2) {
case 0: /* CR */
@@ -362,7 +349,7 @@ static void imx_epit_cmp(void *opaque)
/* The cmp ptimer can't be running when the peripheral is disabled */
assert(s->cr & CR_EN);
- DPRINTF("sr was %d\n", s->sr);
+ trace_imx_epit_cmp(s->sr);
/* Set interrupt status bit SR.OCIF and update the interrupt state */
s->sr |= SR_OCIF;
imx_epit_update_int(s);
@@ -399,8 +386,6 @@ static void imx_epit_realize(DeviceState *dev, Error **errp)
IMXEPITState *s = IMX_EPIT(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- DPRINTF("\n");
-
sysbus_init_irq(sbd, &s->irq);
memory_region_init_io(&s->iomem, OBJECT(s), &imx_epit_ops, s, TYPE_IMX_EPIT,
0x00001000);
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index ac5afe84e8..634ba1da27 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -122,3 +122,9 @@ hpet_ram_write_tn_cmp(uint8_t reg_off) "hpet_ram_writel HPET_TN_CMP + %" PRIu8
hpet_ram_write_invalid_tn_cmp(void) "invalid HPET_TN_CMP + 4 write"
hpet_ram_write_invalid(void) "invalid hpet_ram_writel"
hpet_ram_write_counter_write_while_enabled(void) "Writing counter while HPET enabled!"
+
+# imx_epit.c
+imx_epit_get_freq(uint32_t freq) "ptimer frequency is %u"
+imx_epit_read(const char *name, uint32_t value) "(%s) = 0x%08x"
+imx_epit_write(const char *name, uint64_t value) "(%s, value = 0x%08" PRIx64 ")"
+imx_epit_cmp(uint32_t sr) "sr was %d"
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 02/54] target/arm: Enable SCTLR_EL1.EnFPM for user-only
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
2026-06-29 11:59 ` [PULL 01/54] hw/timer/imx_epit: Replace DPRINTF with trace events Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 03/54] target/arm: Implement EXPAND Peter Maydell
` (52 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
We advertised linux-user support for FPMR without actually
enabling access to the register as the kernel must do.
Fixes: 2430d49a17c9 ("target/arm: Enable FEAT_FPMR for -cpu max")
Reported-by: Dillon Sharlet <dsharlet@google.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260618040718.572950-2-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/cpu.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 86aae36ae5..128c78f6cf 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -421,6 +421,10 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type)
env->cp15.mdscr_el1 |= 1 << 12;
/* Enable FEAT_MOPS */
env->cp15.sctlr_el[1] |= SCTLR_MSCEN;
+ /* Enable FEAT_FPMR */
+ if (cpu_isar_feature(aa64_fpmr, cpu)) {
+ env->cp15.sctlr_el[1] |= SCTLR_EnFPM;
+ }
/* For Linux, GCSPR_EL0 is always readable. */
if (cpu_isar_feature(aa64_gcs, cpu)) {
env->cp15.gcscr_el[0] = GCSCRE0_NTR;
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 03/54] target/arm: Implement EXPAND
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
2026-06-29 11:59 ` [PULL 01/54] hw/timer/imx_epit: Replace DPRINTF with trace events Peter Maydell
2026-06-29 11:59 ` [PULL 02/54] target/arm: Enable SCTLR_EL1.EnFPM for user-only Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 04/54] target/arm: Generalize sve_compact_[sd] Peter Maydell
` (51 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618040718.572950-3-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/helper-sve-defs.h | 5 +++++
target/arm/tcg/sve.decode | 2 ++
target/arm/tcg/sve_helper.c | 26 ++++++++++++++++++++++++++
target/arm/tcg/translate-sve.c | 7 +++++++
4 files changed, 40 insertions(+)
diff --git a/target/arm/tcg/helper-sve-defs.h b/target/arm/tcg/helper-sve-defs.h
index de2254bb19..1d9066dcbb 100644
--- a/target/arm/tcg/helper-sve-defs.h
+++ b/target/arm/tcg/helper-sve-defs.h
@@ -3203,3 +3203,8 @@ DEF_HELPER_FLAGS_6(sve2_famin_s, TCG_CALL_NO_RWG,
void, ptr, ptr, ptr, ptr, fpst, i32)
DEF_HELPER_FLAGS_6(sve2_famin_d, TCG_CALL_NO_RWG,
void, ptr, ptr, ptr, ptr, fpst, i32)
+
+DEF_HELPER_FLAGS_4(sve_expand_b, TCG_CALL_NO_WG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(sve_expand_h, TCG_CALL_NO_WG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(sve_expand_s, TCG_CALL_NO_WG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(sve_expand_d, TCG_CALL_NO_WG, void, ptr, ptr, ptr, i32)
diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode
index 2795c2ec7f..5ec1d420c2 100644
--- a/target/arm/tcg/sve.decode
+++ b/target/arm/tcg/sve.decode
@@ -742,6 +742,8 @@ SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm
# SVE2 vector splice (predicated, constructive)
SPLICE_sve2 00000101 .. 101 101 100 ... ..... ..... @rd_pg_rn
+EXPAND 00000101 .. 110 001 100 ... ..... ..... @rd_pg_rn
+
### SVE Select Vectors Group
# SVE select vector elements (predicated)
diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c
index a0a189eb1e..2517362189 100644
--- a/target/arm/tcg/sve_helper.c
+++ b/target/arm/tcg/sve_helper.c
@@ -3671,6 +3671,32 @@ void HELPER(sve_compact_d)(void *vd, void *vn, void *vg, uint32_t desc)
}
}
+#define DO_EXPAND(NAME, TYPE, H) \
+void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \
+{ \
+ intptr_t oprsz = simd_oprsz(desc); \
+ ARMVectorReg tmp_n = *(ARMVectorReg *)vn; \
+ for (intptr_t i = 0, j = 0; i < oprsz; ) { \
+ uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \
+ do { \
+ TYPE nn = 0; \
+ if (pg & 1) { \
+ nn = *(TYPE *)((void *)&tmp_n + H(j)); \
+ j += sizeof(TYPE); \
+ } \
+ *(TYPE *)(vd + H(i)) = nn; \
+ i += sizeof(TYPE); \
+ pg >>= sizeof(TYPE); \
+ } while (i & 15); \
+ } \
+}
+
+DO_EXPAND(sve_expand_b, uint8_t, H1)
+DO_EXPAND(sve_expand_h, uint16_t, H1_2)
+DO_EXPAND(sve_expand_s, uint32_t, H1_4)
+DO_EXPAND(sve_expand_d, uint64_t, H1_8)
+
+#undef DO_EXPAND
/* Similar to the ARM LastActiveElement pseudocode function, except the
* result is multiplied by the element size. This includes the not found
* indication; e.g. not found for esz=3 is -8.
diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
index 9a72e03513..57c21a13e8 100644
--- a/target/arm/tcg/translate-sve.c
+++ b/target/arm/tcg/translate-sve.c
@@ -6956,6 +6956,13 @@ static gen_helper_gvec_3 * const sqneg_fns[4] = {
TRANS_FEAT(SQNEG_m, aa64_sme_or_sve2, gen_gvec_ool_arg_zpz, sqneg_fns[a->esz], a, 0)
TRANS_FEAT(SQNEG_z, aa64_sme2p2_or_sve2p2, gen_gvec_ool_arg_zpz, sqneg_fns[a->esz], a, 1)
+static gen_helper_gvec_3 * const expand_fns[4] = {
+ gen_helper_sve_expand_b, gen_helper_sve_expand_h,
+ gen_helper_sve_expand_s, gen_helper_sve_expand_d,
+};
+TRANS_FEAT_STREAMING_IF(EXPAND, aa64_sme2p2_or_sve2p2, aa64_sme2p2,
+ gen_gvec_ool_arg_zpz, expand_fns[a->esz], a, 0)
+
DO_ZPZZ(SQSHL, aa64_sme_or_sve2, sve2_sqshl)
DO_ZPZZ(SQRSHL, aa64_sme_or_sve2, sve2_sqrshl)
DO_ZPZZ(SRSHL, aa64_sme_or_sve2, sve2_srshl)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 04/54] target/arm: Generalize sve_compact_[sd]
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (2 preceding siblings ...)
2026-06-29 11:59 ` [PULL 03/54] target/arm: Implement EXPAND Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 05/54] target/arm: Implement COMPACT for byte/halfword Peter Maydell
` (50 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Implement with a DO_COMPACT macro and general purpose
predicate handling.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260618040718.572950-4-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sve_helper.c | 51 +++++++++++++++----------------------
1 file changed, 21 insertions(+), 30 deletions(-)
diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c
index 2517362189..9b45b0d331 100644
--- a/target/arm/tcg/sve_helper.c
+++ b/target/arm/tcg/sve_helper.c
@@ -3637,39 +3637,30 @@ DO_TRN(sve2_trn_q, Int128, )
#undef DO_UZP
#undef DO_TRN
-void HELPER(sve_compact_s)(void *vd, void *vn, void *vg, uint32_t desc)
-{
- intptr_t i, j, opr_sz = simd_oprsz(desc) / 4;
- uint32_t *d = vd, *n = vn;
- uint8_t *pg = vg;
-
- for (i = j = 0; i < opr_sz; i++) {
- if (pg[H1(i / 2)] & (i & 1 ? 0x10 : 0x01)) {
- d[H4(j)] = n[H4(i)];
- j++;
- }
- }
- for (; j < opr_sz; j++) {
- d[H4(j)] = 0;
- }
+#define DO_COMPACT(NAME, TYPE, H) \
+void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \
+{ \
+ intptr_t j = 0, oprsz = simd_oprsz(desc); \
+ for (intptr_t i = 0; i < oprsz; ) { \
+ uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \
+ do { \
+ if (pg & 1) { \
+ *(TYPE *)(vd + H(j)) = *(TYPE *)(vn + H(i)); \
+ j += sizeof(TYPE); \
+ } \
+ i += sizeof(TYPE); \
+ pg >>= sizeof(TYPE); \
+ } while (i & 15); \
+ } \
+ for (; j < oprsz; j += sizeof(TYPE)) { \
+ *(TYPE *)(vd + H(j)) = 0; \
+ } \
}
-void HELPER(sve_compact_d)(void *vd, void *vn, void *vg, uint32_t desc)
-{
- intptr_t i, j, opr_sz = simd_oprsz(desc) / 8;
- uint64_t *d = vd, *n = vn;
- uint8_t *pg = vg;
+DO_COMPACT(sve_compact_s, uint32_t, H1_4)
+DO_COMPACT(sve_compact_d, uint64_t, H1_8)
- for (i = j = 0; i < opr_sz; i++) {
- if (pg[H1(i)] & 1) {
- d[j] = n[i];
- j++;
- }
- }
- for (; j < opr_sz; j++) {
- d[j] = 0;
- }
-}
+#undef DO_COMPACT
#define DO_EXPAND(NAME, TYPE, H) \
void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 05/54] target/arm: Implement COMPACT for byte/halfword
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (3 preceding siblings ...)
2026-06-29 11:59 ` [PULL 04/54] target/arm: Generalize sve_compact_[sd] Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 06/54] target/arm: Implement FIRSTP, LASTP Peter Maydell
` (49 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618040718.572950-5-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/helper-sve-defs.h | 2 ++
target/arm/tcg/sve.decode | 1 -
target/arm/tcg/sve_helper.c | 2 ++
target/arm/tcg/translate-sve.c | 22 +++++++++++++++++-----
4 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/target/arm/tcg/helper-sve-defs.h b/target/arm/tcg/helper-sve-defs.h
index 1d9066dcbb..b64a0faa19 100644
--- a/target/arm/tcg/helper-sve-defs.h
+++ b/target/arm/tcg/helper-sve-defs.h
@@ -733,6 +733,8 @@ DEF_HELPER_FLAGS_4(sve_trn_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(sve_trn_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(sve2_trn_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(sve_compact_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(sve_compact_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(sve_compact_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(sve_compact_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode
index 5ec1d420c2..a3eefd60e5 100644
--- a/target/arm/tcg/sve.decode
+++ b/target/arm/tcg/sve.decode
@@ -693,7 +693,6 @@ TBXQ 00000101 .. 1 ..... 001 101 ..... ..... @rd_rn_rm
### SVE Permute - Predicated Group
# SVE compress active elements
-# Note esz >= 2
COMPACT 00000101 .. 100001 100 ... ..... ..... @rd_pg_rn
# SVE conditionally broadcast element to vector
diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c
index 9b45b0d331..bd8fa2da7e 100644
--- a/target/arm/tcg/sve_helper.c
+++ b/target/arm/tcg/sve_helper.c
@@ -3657,6 +3657,8 @@ void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \
} \
}
+DO_COMPACT(sve_compact_b, uint8_t, H1)
+DO_COMPACT(sve_compact_h, uint16_t, H1_2)
DO_COMPACT(sve_compact_s, uint32_t, H1_4)
DO_COMPACT(sve_compact_d, uint64_t, H1_8)
diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
index 57c21a13e8..01199dbd78 100644
--- a/target/arm/tcg/translate-sve.c
+++ b/target/arm/tcg/translate-sve.c
@@ -2769,11 +2769,23 @@ TRANS_FEAT_NONSTREAMING(TRN2_q, aa64_sve_f64mm, do_interleave_q,
*** SVE Permute Vector - Predicated Group
*/
-static gen_helper_gvec_3 * const compact_fns[4] = {
- NULL, NULL, gen_helper_sve_compact_s, gen_helper_sve_compact_d
-};
-TRANS_FEAT_NONSTREAMING(COMPACT, aa64_sve, gen_gvec_ool_arg_zpz,
- compact_fns[a->esz], a, 0)
+static bool trans_COMPACT(DisasContext *s, arg_COMPACT *a)
+{
+ static gen_helper_gvec_3 * const fns[4] = {
+ gen_helper_sve_compact_b, gen_helper_sve_compact_h,
+ gen_helper_sve_compact_s, gen_helper_sve_compact_d
+ };
+
+ if (!dc_isar_feature(aa64_sme2p2, s)) {
+ if (!(a->esz >= MO_32
+ ? dc_isar_feature(aa64_sve, s)
+ : dc_isar_feature(aa64_sve2p2, s))) {
+ return false;
+ }
+ s->is_nonstreaming = true;
+ }
+ return gen_gvec_ool_arg_zpz(s, fns[a->esz], a, 0);
+}
/* Call the helper that computes the ARM LastActiveElement pseudocode
* function, scaled by the element size. This includes the not found
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 06/54] target/arm: Implement FIRSTP, LASTP
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (4 preceding siblings ...)
2026-06-29 11:59 ` [PULL 05/54] target/arm: Implement COMPACT for byte/halfword Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 07/54] target/arm: Rename isar_feature_aa64_sve_aes Peter Maydell
` (48 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260618040718.572950-6-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/helper-sve-defs.h | 3 ++
target/arm/tcg/sve.decode | 3 ++
target/arm/tcg/sve_helper.c | 30 +++++++++++++++++
target/arm/tcg/translate-sve.c | 56 ++++++++++++++++++++++++++++++++
4 files changed, 92 insertions(+)
diff --git a/target/arm/tcg/helper-sve-defs.h b/target/arm/tcg/helper-sve-defs.h
index b64a0faa19..19bd936f6e 100644
--- a/target/arm/tcg/helper-sve-defs.h
+++ b/target/arm/tcg/helper-sve-defs.h
@@ -961,6 +961,9 @@ DEF_HELPER_FLAGS_4(sve_brkns, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(sve_cntp, TCG_CALL_NO_RWG, i64, ptr, ptr, i32)
DEF_HELPER_FLAGS_2(sve2p1_cntp_c, TCG_CALL_NO_RWG_SE, i64, i32, i32)
+DEF_HELPER_FLAGS_3(sve_firstp, TCG_CALL_NO_RWG_SE, i64, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(sve_lastp, TCG_CALL_NO_RWG_SE, i64, ptr, ptr, i32)
+
DEF_HELPER_FLAGS_3(sve_whilel, TCG_CALL_NO_RWG, i32, ptr, i32, i32)
DEF_HELPER_FLAGS_3(sve_whileg, TCG_CALL_NO_RWG, i32, ptr, i32, i32)
diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode
index a3eefd60e5..bdc2b7f0c0 100644
--- a/target/arm/tcg/sve.decode
+++ b/target/arm/tcg/sve.decode
@@ -852,6 +852,9 @@ BRKN 00100101 0. 01100001 .... 0 .... 0 .... @pd_pg_pn_s
CNTP 00100101 .. 100 000 10 .... 0 .... ..... @rd_pg4_pn
CNTP_c 00100101 esz:2 100 000 10 000 vl:1 1 rn:4 rd:5
+FIRSTP 00100101 .. 100 001 10 .... 0 .... ..... @rd_pg4_pn
+LASTP 00100101 .. 100 010 10 .... 0 .... ..... @rd_pg4_pn
+
# SVE inc/dec register by predicate count
INCDECP_r 00100101 .. 10110 d:1 10001 00 .... ..... @incdec_pred u=1
diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c
index bd8fa2da7e..b2875a559b 100644
--- a/target/arm/tcg/sve_helper.c
+++ b/target/arm/tcg/sve_helper.c
@@ -4328,6 +4328,36 @@ uint64_t HELPER(sve2p1_cntp_c)(uint32_t png, uint32_t desc)
return count >> p.lg2_stride;
}
+uint64_t HELPER(sve_firstp)(void *vn, void *vg, uint32_t pred_desc)
+{
+ intptr_t words = DIV_ROUND_UP(FIELD_EX32(pred_desc, PREDDESC, OPRSZ), 8);
+ intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ);
+ uint64_t *n = vn, *g = vg, mask = pred_esz_masks[esz];
+
+ for (intptr_t i = 0; i < words; ++i) {
+ uint64_t t = n[i] & g[i] & mask;
+ if (t) {
+ return (i * 64 + ctz64(t)) >> esz;
+ }
+ }
+ return -1;
+}
+
+uint64_t HELPER(sve_lastp)(void *vn, void *vg, uint32_t pred_desc)
+{
+ intptr_t words = DIV_ROUND_UP(FIELD_EX32(pred_desc, PREDDESC, OPRSZ), 8);
+ intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ);
+ uint64_t *n = vn, *g = vg, mask = pred_esz_masks[esz];
+
+ for (intptr_t i = words - 1; i >= 0; --i) {
+ uint64_t t = n[i] & g[i] & mask;
+ if (t) {
+ return (i * 64 + (63 - clz64(t))) >> esz;
+ }
+ }
+ return -1;
+}
+
/* C.f. Arm pseudocode EncodePredCount */
static uint64_t encode_pred_count(uint32_t elements, uint32_t count,
uint32_t esz, bool invert)
diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
index 01199dbd78..63f64796e5 100644
--- a/target/arm/tcg/translate-sve.c
+++ b/target/arm/tcg/translate-sve.c
@@ -3481,6 +3481,62 @@ static bool trans_SINCDECP_z(DisasContext *s, arg_incdec2_pred *a)
return true;
}
+static bool do_firstp_lastp(DisasContext *s, arg_rpr_esz *a, bool firstp)
+{
+ if (sve_access_check(s)) {
+ unsigned psz = pred_full_reg_size(s);
+ TCGv_i64 v = cpu_reg(s, a->rd);
+
+ if (psz <= 8) {
+ uint64_t psz_mask;
+
+ tcg_gen_ld_i64(v, tcg_env, pred_full_reg_offset(s, a->rn));
+ if (a->rn != a->pg) {
+ TCGv_i64 g = tcg_temp_new_i64();
+ tcg_gen_ld_i64(g, tcg_env, pred_full_reg_offset(s, a->pg));
+ tcg_gen_and_i64(v, v, g);
+ }
+
+ /*
+ * Reduce the pred_esz_masks value simply to reduce the
+ * size of the code generated here.
+ */
+ psz_mask = MAKE_64BIT_MASK(0, psz * 8);
+ tcg_gen_andi_i64(v, v, pred_esz_masks[a->esz] & psz_mask);
+
+ if (firstp) {
+ tcg_gen_ctzi_i64(v, v, -1);
+ } else {
+ tcg_gen_clzi_i64(v, v, 64);
+ tcg_gen_subfi_i64(v, 63, v);
+ }
+ tcg_gen_sari_i64(v, v, a->esz);
+ } else {
+ TCGv_ptr t_pn = tcg_temp_new_ptr();
+ TCGv_ptr t_pg = tcg_temp_new_ptr();
+ unsigned desc = 0;
+ TCGv_i32 t_desc;
+
+ desc = FIELD_DP32(desc, PREDDESC, OPRSZ, psz);
+ desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz);
+
+ tcg_gen_addi_ptr(t_pn, tcg_env, pred_full_reg_offset(s, a->rn));
+ tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, a->pg));
+ t_desc = tcg_constant_i32(desc);
+
+ if (firstp) {
+ gen_helper_sve_firstp(v, t_pn, t_pg, t_desc);
+ } else {
+ gen_helper_sve_lastp(v, t_pn, t_pg, t_desc);
+ }
+ }
+ }
+ return true;
+}
+
+TRANS_FEAT(FIRSTP, aa64_sme2p2_or_sve2p2, do_firstp_lastp, a, true)
+TRANS_FEAT(LASTP, aa64_sme2p2_or_sve2p2, do_firstp_lastp, a, false)
+
/*
*** SVE Integer Compare Scalars Group
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 07/54] target/arm: Rename isar_feature_aa64_sve_aes
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (5 preceding siblings ...)
2026-06-29 11:59 ` [PULL 06/54] target/arm: Implement FIRSTP, LASTP Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 08/54] target/arm: Implement FEAT_SSVE_AES Peter Maydell
` (47 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Rename from isar_feature_aa64_sve2_aes to match
the feature name: FEAT_SVE_AES.
Suggested-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260618040718.572950-7-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
bsd-user/aarch64/target_arch_elf.h | 2 +-
linux-user/aarch64/elfload.c | 2 +-
target/arm/cpu-features.h | 2 +-
target/arm/tcg/translate-sve.c | 8 ++++----
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/bsd-user/aarch64/target_arch_elf.h b/bsd-user/aarch64/target_arch_elf.h
index 5ea5232a57..2fb7dcd778 100644
--- a/bsd-user/aarch64/target_arch_elf.h
+++ b/bsd-user/aarch64/target_arch_elf.h
@@ -126,7 +126,7 @@ static uint32_t get_elf_hwcap2(void)
GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP);
GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2);
- GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES);
+ GET_FEATURE_ID(aa64_sve_aes, ARM_HWCAP2_A64_SVEAES);
GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL);
GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM);
GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3);
diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c
index 4887a3a7b7..3479e61a12 100644
--- a/linux-user/aarch64/elfload.c
+++ b/linux-user/aarch64/elfload.c
@@ -184,7 +184,7 @@ abi_ulong get_elf_hwcap2(CPUState *cs)
GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP);
GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2);
- GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES);
+ GET_FEATURE_ID(aa64_sve_aes, ARM_HWCAP2_A64_SVEAES);
GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL);
GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM);
GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3);
diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h
index a80b251589..af843b32d3 100644
--- a/target/arm/cpu-features.h
+++ b/target/arm/cpu-features.h
@@ -1524,7 +1524,7 @@ static inline bool isar_feature_aa64_sve2p2(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64ZFR0, SVEVER) >= 3;
}
-static inline bool isar_feature_aa64_sve2_aes(const ARMISARegisters *id)
+static inline bool isar_feature_aa64_sve_aes(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64ZFR0, AES) != 0;
}
diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
index 63f64796e5..9f0f0e0e12 100644
--- a/target/arm/tcg/translate-sve.c
+++ b/target/arm/tcg/translate-sve.c
@@ -8174,14 +8174,14 @@ TRANS_FEAT(SDOT_zzzz_2s, aa64_sme2_or_sve2p1, gen_gvec_ool_arg_zzzz,
TRANS_FEAT(UDOT_zzzz_2s, aa64_sme2_or_sve2p1, gen_gvec_ool_arg_zzzz,
gen_helper_gvec_udot_2h, a, 0)
-TRANS_FEAT_NONSTREAMING(AESMC, aa64_sve2_aes, gen_gvec_ool_zz,
+TRANS_FEAT_NONSTREAMING(AESMC, aa64_sve_aes, gen_gvec_ool_zz,
gen_helper_crypto_aesmc, a->rd, a->rd, 0)
-TRANS_FEAT_NONSTREAMING(AESIMC, aa64_sve2_aes, gen_gvec_ool_zz,
+TRANS_FEAT_NONSTREAMING(AESIMC, aa64_sve_aes, gen_gvec_ool_zz,
gen_helper_crypto_aesimc, a->rd, a->rd, 0)
-TRANS_FEAT_NONSTREAMING(AESE, aa64_sve2_aes, gen_gvec_ool_arg_zzz,
+TRANS_FEAT_NONSTREAMING(AESE, aa64_sve_aes, gen_gvec_ool_arg_zzz,
gen_helper_crypto_aese, a, 0)
-TRANS_FEAT_NONSTREAMING(AESD, aa64_sve2_aes, gen_gvec_ool_arg_zzz,
+TRANS_FEAT_NONSTREAMING(AESD, aa64_sve_aes, gen_gvec_ool_arg_zzz,
gen_helper_crypto_aesd, a, 0)
TRANS_FEAT_NONSTREAMING(SM4E, aa64_sve2_sm4, gen_gvec_ool_arg_zzz,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 08/54] target/arm: Implement FEAT_SSVE_AES
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (6 preceding siblings ...)
2026-06-29 11:59 ` [PULL 07/54] target/arm: Rename isar_feature_aa64_sve_aes Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 09/54] target/arm: Use expand_pred_N for helper_sve_not_zpz_N Peter Maydell
` (46 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Enable FEAT_SVE_AES instructions in streaming mode.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618040718.572950-8-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
docs/system/arm/emulation.rst | 1 +
linux-user/aarch64/elfload.c | 1 +
target/arm/cpu-features.h | 5 +++++
target/arm/tcg/cpu64.c | 1 +
target/arm/tcg/translate-sve.c | 20 +++++++++++---------
5 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst
index a8072ddb67..0b4623139a 100644
--- a/docs/system/arm/emulation.rst
+++ b/docs/system/arm/emulation.rst
@@ -171,6 +171,7 @@ the following architecture extensions:
- FEAT_SME_F64F64 (Double-precision floating-point outer product instructions)
- FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions)
- FEAT_SME_LUTv2 (Lookup table instructions with 4-bit indices and 8-bit elements)
+- FEAT_SSVE_AES (Streaming SVE Mode Advanced Encryption Standard and 128-bit polynomial multiply long instructions)
- FEAT_SSVE_FP8DOT2 (SVE2 FP8 2-way dot product to half-precision instructions in Streaming SVE mode)
- FEAT_SSVE_FP8DOT4 (SVE2 FP8 4-way dot product to single-precision instructions in Streaming SVE mode)
- FEAT_SSVE_FP8FMA (SVE2 FP8 multiply-accumulate to half-precision and single-precision instructions in Streaming SVE mode)
diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c
index 3479e61a12..0f96bbe70b 100644
--- a/linux-user/aarch64/elfload.c
+++ b/linux-user/aarch64/elfload.c
@@ -173,6 +173,7 @@ abi_ulong get_elf_hwcap(CPUState *cs)
GET_FEATURE_ID(aa64_cmpbr, ARM_HWCAP_A64_CMPBR);
GET_FEATURE_ID(aa64_f8mm8, ARM_HWCAP_A64_F8MM8);
GET_FEATURE_ID(aa64_f8mm4, ARM_HWCAP_A64_F8MM4);
+ GET_FEATURE_ID(aa64_ssve_aes, ARM_HWCAP_A64_SME_AES);
return hwcaps;
}
diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h
index af843b32d3..c07bcb0a73 100644
--- a/target/arm/cpu-features.h
+++ b/target/arm/cpu-features.h
@@ -1575,6 +1575,11 @@ static inline bool isar_feature_aa64_sve_b16b16(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64ZFR0, B16B16);
}
+static inline bool isar_feature_aa64_ssve_aes(const ARMISARegisters *id)
+{
+ return FIELD_EX64_IDREG(id, ID_AA64SMFR0, AES);
+}
+
static inline bool isar_feature_aa64_ssve_f8fma(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64SMFR0, SF8FMA);
diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c
index f7a920a202..e4a8d7a2f6 100644
--- a/target/arm/tcg/cpu64.c
+++ b/target/arm/tcg/cpu64.c
@@ -1383,6 +1383,7 @@ void aarch64_max_tcg_initfn(Object *obj)
SET_IDREG(isar, ID_AA64DFR0, t);
t = GET_IDREG(isar, ID_AA64SMFR0);
+ t = FIELD_DP64(t, ID_AA64SMFR0, AES, 1); /* FEAT_SSVE_AES */
t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP2, 1); /* FEAT_SSVE_FP8DOT2 */
t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP4, 1); /* FEAT_SSVE_FP8DOT4 */
t = FIELD_DP64(t, ID_AA64SMFR0, SF8FMA, 1); /* FEAT_SSVE_FP8FMA */
diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
index 9f0f0e0e12..79e8e16b24 100644
--- a/target/arm/tcg/translate-sve.c
+++ b/target/arm/tcg/translate-sve.c
@@ -7169,7 +7169,7 @@ static bool do_trans_pmull(DisasContext *s, arg_rrr_esz *a, bool sel)
if (!dc_isar_feature(aa64_sve2_pmull128, s)) {
return false;
}
- s->is_nonstreaming = true;
+ s->is_nonstreaming = !dc_isar_feature(aa64_ssve_aes, s);
}
return gen_gvec_ool_arg_zzz(s, fns[a->esz], a, sel);
}
@@ -8174,15 +8174,17 @@ TRANS_FEAT(SDOT_zzzz_2s, aa64_sme2_or_sve2p1, gen_gvec_ool_arg_zzzz,
TRANS_FEAT(UDOT_zzzz_2s, aa64_sme2_or_sve2p1, gen_gvec_ool_arg_zzzz,
gen_helper_gvec_udot_2h, a, 0)
-TRANS_FEAT_NONSTREAMING(AESMC, aa64_sve_aes, gen_gvec_ool_zz,
- gen_helper_crypto_aesmc, a->rd, a->rd, 0)
-TRANS_FEAT_NONSTREAMING(AESIMC, aa64_sve_aes, gen_gvec_ool_zz,
- gen_helper_crypto_aesimc, a->rd, a->rd, 0)
+TRANS_FEAT_STREAMING_IF(AESMC, aa64_sve_aes, aa64_ssve_aes,
+ gen_gvec_ool_zz, gen_helper_crypto_aesmc,
+ a->rd, a->rd, 0)
+TRANS_FEAT_STREAMING_IF(AESIMC, aa64_sve_aes, aa64_ssve_aes,
+ gen_gvec_ool_zz, gen_helper_crypto_aesimc,
+ a->rd, a->rd, 0)
-TRANS_FEAT_NONSTREAMING(AESE, aa64_sve_aes, gen_gvec_ool_arg_zzz,
- gen_helper_crypto_aese, a, 0)
-TRANS_FEAT_NONSTREAMING(AESD, aa64_sve_aes, gen_gvec_ool_arg_zzz,
- gen_helper_crypto_aesd, a, 0)
+TRANS_FEAT_STREAMING_IF(AESE, aa64_sve_aes, aa64_ssve_aes,
+ gen_gvec_ool_arg_zzz, gen_helper_crypto_aese, a, 0)
+TRANS_FEAT_STREAMING_IF(AESD, aa64_sve_aes, aa64_ssve_aes,
+ gen_gvec_ool_arg_zzz, gen_helper_crypto_aesd, a, 0)
TRANS_FEAT_NONSTREAMING(SM4E, aa64_sve2_sm4, gen_gvec_ool_arg_zzz,
gen_helper_crypto_sm4e, a, 0)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 09/54] target/arm: Use expand_pred_N for helper_sve_not_zpz_N
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (7 preceding siblings ...)
2026-06-29 11:59 ` [PULL 08/54] target/arm: Implement FEAT_SSVE_AES Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 10/54] target/arm: Move tile_vslice_{index, offset} to vec_internal.h Peter Maydell
` (45 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
These is a bitwise operation; with expand_pred_N,
we can always operate on uint64_t.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618040718.572950-9-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sve_helper.c | 43 ++++++++++++++++++++++++++++++++-----
1 file changed, 38 insertions(+), 5 deletions(-)
diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c
index b2875a559b..ac96b18784 100644
--- a/target/arm/tcg/sve_helper.c
+++ b/target/arm/tcg/sve_helper.c
@@ -917,12 +917,45 @@ DO_ZPZ(sve_ah_fneg_h, uint16_t, H1_2, DO_AH_FNEG_H)
DO_ZPZ(sve_ah_fneg_s, uint32_t, H1_4, DO_AH_FNEG_S)
DO_ZPZ_D(sve_ah_fneg_d, uint64_t, DO_AH_FNEG_D)
-#define DO_NOT(N) (~N)
+static inline void
+sve_not_zpz(uint64_t *d, uint64_t *n, uint8_t *pg, uint32_t desc,
+ uint64_t (*expand)(uint8_t))
+{
+ intptr_t opr_sz = simd_oprsz(desc) / 8;
+ bool zeroing = simd_data(desc) & 1;
-DO_ZPZ(sve_not_zpz_b, uint8_t, H1, DO_NOT)
-DO_ZPZ(sve_not_zpz_h, uint16_t, H1_2, DO_NOT)
-DO_ZPZ(sve_not_zpz_s, uint32_t, H1_4, DO_NOT)
-DO_ZPZ_D(sve_not_zpz_d, uint64_t, DO_NOT)
+ if (zeroing) {
+ for (intptr_t i = 0; i < opr_sz; ++i) {
+ uint64_t p = expand(pg[H1(i)]);
+ d[i] = ~n[i] & p;
+ }
+ } else {
+ for (intptr_t i = 0; i < opr_sz; ++i) {
+ uint64_t p = expand(pg[H1(i)]);
+ d[i] = (~n[i] & p) | (d[i] & ~p);
+ }
+ }
+}
+
+void HELPER(sve_not_zpz_b)(void *vd, void *vn, void *pg, uint32_t desc)
+{
+ sve_not_zpz(vd, vn, pg, desc, expand_pred_b);
+}
+
+void HELPER(sve_not_zpz_h)(void *vd, void *vn, void *pg, uint32_t desc)
+{
+ sve_not_zpz(vd, vn, pg, desc, expand_pred_h);
+}
+
+void HELPER(sve_not_zpz_s)(void *vd, void *vn, void *pg, uint32_t desc)
+{
+ sve_not_zpz(vd, vn, pg, desc, expand_pred_s);
+}
+
+void HELPER(sve_not_zpz_d)(void *vd, void *vn, void *pg, uint32_t desc)
+{
+ sve_not_zpz(vd, vn, pg, desc, expand_pred_d);
+}
#define DO_SXTB(N) ((int8_t)N)
#define DO_SXTH(N) ((int16_t)N)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 10/54] target/arm: Move tile_vslice_{index, offset} to vec_internal.h
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (8 preceding siblings ...)
2026-06-29 11:59 ` [PULL 09/54] target/arm: Use expand_pred_N for helper_sve_not_zpz_N Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 11/54] target/arm: Implement FMOPA (widening, 4-way fp8 to fp32) Peter Maydell
` (44 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Move in preparation for being used outside sme_helper.c.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260618041517.573469-2-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme_helper.c | 33 ---------------------------------
target/arm/tcg/vec_internal.h | 31 +++++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 33 deletions(-)
diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c
index 8e8e6c00ca..685b6b46be 100644
--- a/target/arm/tcg/sme_helper.c
+++ b/target/arm/tcg/sme_helper.c
@@ -64,39 +64,6 @@ void helper_sme_zero(CPUARMState *env, uint32_t imm, uint32_t svl)
}
}
-
-/*
- * When considering the ZA storage as an array of elements of
- * type T, the index within that array of the Nth element of
- * a vertical slice of a tile can be calculated like this,
- * regardless of the size of type T. This is because the tiles
- * are interleaved, so if type T is size N bytes then row 1 of
- * the tile is N rows away from row 0. The division by N to
- * convert a byte offset into an array index and the multiplication
- * by N to convert from vslice-index-within-the-tile to
- * the index within the ZA storage cancel out.
- */
-#define tile_vslice_index(i) ((i) * sizeof(ARMVectorReg))
-
-/*
- * When doing byte arithmetic on the ZA storage, the element
- * byteoff bytes away in a tile vertical slice is always this
- * many bytes away in the ZA storage, regardless of the
- * size of the tile element, assuming that byteoff is a multiple
- * of the element size. Again this is because of the interleaving
- * of the tiles. For instance if we have 1 byte per element then
- * each row of the ZA storage has one byte of the vslice data,
- * and (counting from 0) byte 8 goes in row 8 of the storage
- * at offset (8 * row-size-in-bytes).
- * If we have 8 bytes per element then each row of the ZA storage
- * has 8 bytes of the data, but there are 8 interleaved tiles and
- * so byte 8 of the data goes into row 1 of the tile,
- * which is again row 8 of the storage, so the offset is still
- * (8 * row-size-in-bytes). Similarly for other element sizes.
- */
-#define tile_vslice_offset(byteoff) ((byteoff) * sizeof(ARMVectorReg))
-
-
/*
* Move Zreg vector to ZArray column.
*/
diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h
index 06fef2a209..77e5b01550 100644
--- a/target/arm/tcg/vec_internal.h
+++ b/target/arm/tcg/vec_internal.h
@@ -55,6 +55,37 @@ typedef struct CPUArchState CPUARMState;
#define H8(x) (x)
#define H1_8(x) (x)
+/*
+ * When considering the ZA storage as an array of elements of
+ * type T, the index within that array of the Nth element of
+ * a vertical slice of a tile can be calculated like this,
+ * regardless of the size of type T. This is because the tiles
+ * are interleaved, so if type T is size N bytes then row 1 of
+ * the tile is N rows away from row 0. The division by N to
+ * convert a byte offset into an array index and the multiplication
+ * by N to convert from vslice-index-within-the-tile to
+ * the index within the ZA storage cancel out.
+ */
+#define tile_vslice_index(i) ((i) * sizeof(ARMVectorReg))
+
+/*
+ * When doing byte arithmetic on the ZA storage, the element
+ * byteoff bytes away in a tile vertical slice is always this
+ * many bytes away in the ZA storage, regardless of the
+ * size of the tile element, assuming that byteoff is a multiple
+ * of the element size. Again this is because of the interleaving
+ * of the tiles. For instance if we have 1 byte per element then
+ * each row of the ZA storage has one byte of the vslice data,
+ * and (counting from 0) byte 8 goes in row 8 of the storage
+ * at offset (8 * row-size-in-bytes).
+ * If we have 8 bytes per element then each row of the ZA storage
+ * has 8 bytes of the data, but there are 8 interleaved tiles and
+ * so byte 8 of the data goes into row 1 of the tile,
+ * which is again row 8 of the storage, so the offset is still
+ * (8 * row-size-in-bytes). Similarly for other element sizes.
+ */
+#define tile_vslice_offset(byteoff) ((byteoff) * sizeof(ARMVectorReg))
+
/*
* Expand active predicate bits to bytes, for byte elements.
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 11/54] target/arm: Implement FMOPA (widening, 4-way fp8 to fp32)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (9 preceding siblings ...)
2026-06-29 11:59 ` [PULL 10/54] target/arm: Move tile_vslice_{index, offset} to vec_internal.h Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 12/54] target/arm: Implement FMLALL (multiple and single vector) Peter Maydell
` (43 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260618041517.573469-3-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/cpu-features.h | 5 +++++
target/arm/tcg/fp8_helper.c | 35 ++++++++++++++++++++++++++++++++
target/arm/tcg/helper-fp8-defs.h | 2 ++
target/arm/tcg/sme.decode | 2 ++
target/arm/tcg/translate-sme.c | 19 +++++++++++++++++
5 files changed, 63 insertions(+)
diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h
index c07bcb0a73..45213f71ab 100644
--- a/target/arm/cpu-features.h
+++ b/target/arm/cpu-features.h
@@ -1600,6 +1600,11 @@ static inline bool isar_feature_aa64_sme_b16b16(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64SMFR0, B16B16);
}
+static inline bool isar_feature_aa64_sme_f8f32(const ARMISARegisters *id)
+{
+ return FIELD_EX64_IDREG(id, ID_AA64SMFR0, F8F32);
+}
+
static inline bool isar_feature_aa64_sme_f16f16(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64SMFR0, F16F16);
diff --git a/target/arm/tcg/fp8_helper.c b/target/arm/tcg/fp8_helper.c
index bd0877071b..08e04364fa 100644
--- a/target/arm/tcg/fp8_helper.c
+++ b/target/arm/tcg/fp8_helper.c
@@ -857,3 +857,38 @@ void HELPER(gvec_fmmla_hb)(void *vd, void *vn, void *vm,
clear_tail(vd, oprsz, simd_maxsz(desc));
}
+
+void HELPER(sme_fmopa_sb)(void *vza, void *vzn, void *vzm, void *vpn,
+ void *vpm, CPUARMState *env, uint32_t desc)
+{
+ FP8MulContext ctx = fp8_mul_start(env, -1);
+ intptr_t oprsz = simd_maxsz(desc);
+ uint16_t *pn = vpn, *pm = vpm;
+
+ for (intptr_t row = 0; row < oprsz; ) {
+ uint16_t prow = pn[H2(row >> 4)];
+ do {
+ void *vza_row = vza + tile_vslice_offset(row);
+ uint32_t n = *(uint32_t *)(vzn + H1_4(row));
+
+ n &= expand_pred_b(prow & 0xf);
+
+ for (intptr_t col = 0; col < oprsz; ) {
+ uint16_t pcol = pm[H2(col >> 4)];
+ do {
+ if (prow & pcol & 0xf) {
+ uint32_t *a = vza_row + H1_4(col);
+ uint32_t m = *(uint32_t *)(vzm + H1_4(col));
+
+ m &= expand_pred_b(pcol & 0xf);
+ *a = f8dotadd_s(n, m, 4, *a, &ctx);
+ }
+ col += 4;
+ pcol >>= 4;
+ } while (col & 15);
+ }
+ row += 4;
+ prow >>= 4;
+ } while (row & 15);
+ }
+}
diff --git a/target/arm/tcg/helper-fp8-defs.h b/target/arm/tcg/helper-fp8-defs.h
index e942308af4..7c090ccad6 100644
--- a/target/arm/tcg/helper-fp8-defs.h
+++ b/target/arm/tcg/helper-fp8-defs.h
@@ -38,3 +38,5 @@ DEF_HELPER_FLAGS_5(gvec_fdot_idx_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env,
DEF_HELPER_FLAGS_5(gvec_fmmla_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32)
DEF_HELPER_FLAGS_5(gvec_fmmla_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32)
+
+DEF_HELPER_FLAGS_7(sme_fmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 495330aed7..e668293efd 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -198,6 +198,8 @@ BFMOPA 10000001 101 ..... ... ... ..... . 100 . @op_16
BFMOPA_w 10000001 100 ..... ... ... ..... . 00 .. @op_32
FMOPA_w_h 10000001 101 ..... ... ... ..... . 00 .. @op_32
+FMOPA_sb 10000000 101 zm:5 pm:3 pn:3 zn:5 0 00 zad:2 &op sub=0
+
SMOPA_s 1010000 0 10 0 ..... ... ... ..... . 00 .. @op_32
SUMOPA_s 1010000 0 10 1 ..... ... ... ..... . 00 .. @op_32
USMOPA_s 1010000 1 10 0 ..... ... ... ..... . 00 .. @op_32
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 98d3d18791..63c6e2c9b1 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -616,6 +616,25 @@ TRANS_FEAT(BFMOPA_w, aa64_sme, do_outprod_env, a, MO_32,
: !s->fpcr_ah ? gen_helper_sme_bfmops_w
: gen_helper_sme_ah_bfmops_w)
+static bool trans_FMOPA_sb(DisasContext *s, arg_op *a)
+{
+ if (!dc_isar_feature(aa64_sme_f8f32, s)) {
+ return false;
+ }
+ if (fpmr_access_check(s) && sme_smza_enabled_check(s)) {
+ int svl = streaming_vec_reg_size(s);
+ uint32_t desc = simd_desc(svl, svl, 0);
+
+ gen_helper_sme_fmopa_sb(get_tile(s, MO_32, a->zad),
+ vec_full_reg_ptr(s, a->zn),
+ vec_full_reg_ptr(s, a->zm),
+ pred_full_reg_ptr(s, a->pn),
+ pred_full_reg_ptr(s, a->pm),
+ tcg_env, tcg_constant_i32(desc));
+ }
+ return true;
+}
+
TRANS_FEAT(SMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_smopa_s)
TRANS_FEAT(UMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_umopa_s)
TRANS_FEAT(SUMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_sumopa_s)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 12/54] target/arm: Implement FMLALL (multiple and single vector)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (10 preceding siblings ...)
2026-06-29 11:59 ` [PULL 11/54] target/arm: Implement FMOPA (widening, 4-way fp8 to fp32) Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 13/54] target/arm: Implement FMLALL (multiple vector) Peter Maydell
` (42 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260618041517.573469-4-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 4 +++
target/arm/tcg/translate-sme.c | 49 ++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index e668293efd..4956995924 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -445,6 +445,10 @@ FMLS_n1_h 11000001 001 1 .... 0 .. 111 ..... 01 ... @azz_nx1_o3 n=4
FMLS_n1_s 11000001 001 1 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=4
FMLS_n1_d 11000001 011 1 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=4
+FMLALL_n1_b 11000001 001 1 .... 0 .. 001 ..... 000 .. @azz_nx1_o2x4 n=1
+FMLALL_n1_b 11000001 001 0 .... 0 .. 000 ..... 0001 . @azz_nx1_o1x4 n=2
+FMLALL_n1_b 11000001 001 1 .... 0 .. 000 ..... 0001 . @azz_nx1_o1x4 n=4
+
### SME2 Multi-vector Multiple Array Vectors
%zn_ax2 6:4 !function=times_2
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 63c6e2c9b1..30487c34a0 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1059,6 +1059,47 @@ static bool do_azz_acc_fp(DisasContext *s, int nreg, int nsel,
return true;
}
+static bool do_azz_acc_fp8(DisasContext *s, int nreg, int nsel,
+ int rv, int off, int zn, int zm,
+ int data, int shsel, bool multi,
+ gen_helper_gvec_3_ptr *fn)
+{
+ /*
+ * TODO: Could plausibly reuse do_azz_acc_fp, after the fpmr check,
+ * but the fp8 helpers were written without a separate addend operand.
+ */
+ if (fpmr_access_check(s) && sme_smza_enabled_check(s)) {
+ int svl = streaming_vec_reg_size(s);
+ int vstride = svl / nreg;
+ TCGv_ptr t_za = get_zarray(s, rv, off, nreg, nsel);
+ TCGv_ptr t;
+
+ t = tcg_temp_new_ptr();
+
+ for (int r = 0; r < nreg; ++r) {
+ TCGv_ptr t_zn = vec_full_reg_ptr(s, zn);
+ TCGv_ptr t_zm = vec_full_reg_ptr(s, zm);
+
+ for (int i = 0; i < nsel; ++i) {
+ int o_za = (r * vstride + i) * sizeof(ARMVectorReg);
+ int desc = simd_desc(svl, svl, data | (i << shsel));
+
+ tcg_gen_addi_ptr(t, t_za, o_za);
+ fn(t, t_zn, t_zm, tcg_env, tcg_constant_i32(desc));
+ }
+
+ /*
+ * For multiple-and-single vectors, Zn may wrap.
+ * For multiple vectors, both Zn and Zm are aligned.
+ */
+ zn = (zn + 1) % 32;
+ zm += multi;
+ }
+ }
+ return true;
+}
+
+
static bool do_fmlal(DisasContext *s, arg_azz_n *a, bool sub, bool multi)
{
return do_azz_acc_fp(s, a->n, 2, a->rv, a->off, a->zn, a->zm,
@@ -1071,6 +1112,14 @@ TRANS_FEAT(FMLSL_n1, aa64_sme2, do_fmlal, a, true, false)
TRANS_FEAT(FMLAL_nn, aa64_sme2, do_fmlal, a, false, true)
TRANS_FEAT(FMLSL_nn, aa64_sme2, do_fmlal, a, true, true)
+static bool do_fmlall_fp8(DisasContext *s, arg_azz_n *a, bool multi)
+{
+ return do_azz_acc_fp8(s, a->n, 4, a->rv, a->off, a->zn, a->zm,
+ 0, 0, multi, gen_helper_gvec_fmla_sb);
+}
+
+TRANS_FEAT(FMLALL_n1_b, aa64_sme_f8f32, do_fmlall_fp8, a, false)
+
static bool do_fmlal_nx(DisasContext *s, arg_azx_n *a, bool sub)
{
return do_azz_acc_fp(s, a->n, 2, a->rv, a->off, a->zn, a->zm,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 13/54] target/arm: Implement FMLALL (multiple vector)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (11 preceding siblings ...)
2026-06-29 11:59 ` [PULL 12/54] target/arm: Implement FMLALL (multiple and single vector) Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 14/54] target/arm: Implement FMLALL (multiple and indexed vector) Peter Maydell
` (41 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260618041517.573469-5-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 3 +++
target/arm/tcg/translate-sme.c | 1 +
2 files changed, 4 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 4956995924..ccc33463c2 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -569,6 +569,9 @@ FMLS_nn_h 11000001 101 ...01 0 .. 100 ...00 11 ... @azz_4x4_o3
FMLS_nn_s 11000001 101 ...01 0 .. 110 ...00 01 ... @azz_4x4_o3
FMLS_nn_d 11000001 111 ...01 0 .. 110 ...00 01 ... @azz_4x4_o3
+FMLALL_nn_b 11000001 101 ....0 0 .. 000 ....1 0000 . @azz_2x2_o1x4
+FMLALL_nn_b 11000001 101 ...01 0 .. 000 ...01 0000 . @azz_4x4_o1x4
+
&az_n n off rv zm
@az_2x2_o3 ........ ... ..... . .. ... ..... .. off:3 \
&az_n n=2 rv=%mova_rv zm=%zn_ax2
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 30487c34a0..ae1dbd0528 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1119,6 +1119,7 @@ static bool do_fmlall_fp8(DisasContext *s, arg_azz_n *a, bool multi)
}
TRANS_FEAT(FMLALL_n1_b, aa64_sme_f8f32, do_fmlall_fp8, a, false)
+TRANS_FEAT(FMLALL_nn_b, aa64_sme_f8f32, do_fmlall_fp8, a, true)
static bool do_fmlal_nx(DisasContext *s, arg_azx_n *a, bool sub)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 14/54] target/arm: Implement FMLALL (multiple and indexed vector)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (12 preceding siblings ...)
2026-06-29 11:59 ` [PULL 13/54] target/arm: Implement FMLALL (multiple vector) Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 15/54] target/arm: Implement FDOT (multiple and single vector) Peter Maydell
` (40 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260618041517.573469-6-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 4 ++++
target/arm/tcg/translate-sme.c | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index ccc33463c2..80ad674cf2 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -771,6 +771,10 @@ FMLS_nx_h 11000001 0001 .... 1 .. 1.. ...00 1 .... @azx_4x1_i3_o3
FMLS_nx_s 11000001 0101 .... 1 .. 0.. ...00 10 ... @azx_4x1_i2_o3
FMLS_nx_d 11000001 1101 .... 1 .. 00. ...00 10 ... @azx_4x1_i1_o3
+FMLALL_nx_b 11000001 0100 .... . .. ... ..... 000 .. @azx_1x1_i4_o2
+FMLALL_nx_b 11000001 1001 .... 0 .. 0.. ....1 00.. . @azx_2x1_i4_o1
+FMLALL_nx_b 11000001 0001 .... 1 .. 0.. ...10 00.. . @azx_4x1_i4_o1
+
### SME2 Add / Sub array accumulators
ADD_aaz_s 11000001 101 000000 .. 111 ....0 10 ... @az_2x2_o3
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index ae1dbd0528..cc85c6d6e0 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1157,6 +1157,10 @@ static bool do_bfmlal_nx(DisasContext *s, arg_azx_n *a, bool sub)
TRANS_FEAT(BFMLAL_nx, aa64_sme2, do_bfmlal_nx, a, false)
TRANS_FEAT(BFMLSL_nx, aa64_sme2, do_bfmlal_nx, a, true)
+TRANS_FEAT(FMLALL_nx_b, aa64_sme_f8f32, do_azz_acc_fp8,
+ a->n, 4, a->rv, a->off, a->zn, a->zm,
+ a->idx << 2, 0, false, gen_helper_gvec_fmla_idx_sb)
+
static bool do_fdot(DisasContext *s, arg_azz_n *a, bool multi)
{
return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm, 1, 0,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 15/54] target/arm: Implement FDOT (multiple and single vector)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (13 preceding siblings ...)
2026-06-29 11:59 ` [PULL 14/54] target/arm: Implement FMLALL (multiple and indexed vector) Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 16/54] target/arm: Implement FDOT (multiple vector) Peter Maydell
` (39 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618041517.573469-7-richard.henderson@linaro.org
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 3 +++
target/arm/tcg/translate-sme.c | 8 ++++++++
2 files changed, 11 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 80ad674cf2..3c7caf8bdb 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -449,6 +449,9 @@ FMLALL_n1_b 11000001 001 1 .... 0 .. 001 ..... 000 .. @azz_nx1_o2x4 n=1
FMLALL_n1_b 11000001 001 0 .... 0 .. 000 ..... 0001 . @azz_nx1_o1x4 n=2
FMLALL_n1_b 11000001 001 1 .... 0 .. 000 ..... 0001 . @azz_nx1_o1x4 n=4
+FDOT_n1_sb 11000001 001 0 .... 0 .. 100 ..... 11 ... @azz_nx1_o3 n=2
+FDOT_n1_sb 11000001 001 1 .... 0 .. 100 ..... 11 ... @azz_nx1_o3 n=4
+
### SME2 Multi-vector Multiple Array Vectors
%zn_ax2 6:4 !function=times_2
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index cc85c6d6e0..8fea451089 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1170,6 +1170,14 @@ static bool do_fdot(DisasContext *s, arg_azz_n *a, bool multi)
TRANS_FEAT(FDOT_n1, aa64_sme2, do_fdot, a, false)
TRANS_FEAT(FDOT_nn, aa64_sme2, do_fdot, a, true)
+static bool do_fdot_fp8(DisasContext *s, arg_azz_n *a, bool multi)
+{
+ return do_azz_acc_fp8(s, a->n, 1, a->rv, a->off, a->zn, a->zm,
+ 0, 0, multi, gen_helper_gvec_fdot_sb);
+}
+
+TRANS_FEAT(FDOT_n1_sb, aa64_sme_f8f32, do_fdot_fp8, a, false)
+
static bool do_fdot_nx(DisasContext *s, arg_azx_n *a)
{
return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 16/54] target/arm: Implement FDOT (multiple vector)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (14 preceding siblings ...)
2026-06-29 11:59 ` [PULL 15/54] target/arm: Implement FDOT (multiple and single vector) Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 17/54] target/arm: Implement FDOT (multiple and indexed vector) Peter Maydell
` (38 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618041517.573469-8-richard.henderson@linaro.org
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 3 +++
target/arm/tcg/translate-sme.c | 1 +
2 files changed, 4 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 3c7caf8bdb..04dd0e6df0 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -575,6 +575,9 @@ FMLS_nn_d 11000001 111 ...01 0 .. 110 ...00 01 ... @azz_4x4_o3
FMLALL_nn_b 11000001 101 ....0 0 .. 000 ....1 0000 . @azz_2x2_o1x4
FMLALL_nn_b 11000001 101 ...01 0 .. 000 ...01 0000 . @azz_4x4_o1x4
+FDOT_nn_sb 11000001 101 ....0 0 .. 100 ....1 10 ... @azz_2x2_o3
+FDOT_nn_sb 11000001 101 ...01 0 .. 100 ...01 10 ... @azz_4x4_o3
+
&az_n n off rv zm
@az_2x2_o3 ........ ... ..... . .. ... ..... .. off:3 \
&az_n n=2 rv=%mova_rv zm=%zn_ax2
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 8fea451089..467b44e832 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1177,6 +1177,7 @@ static bool do_fdot_fp8(DisasContext *s, arg_azz_n *a, bool multi)
}
TRANS_FEAT(FDOT_n1_sb, aa64_sme_f8f32, do_fdot_fp8, a, false)
+TRANS_FEAT(FDOT_nn_sb, aa64_sme_f8f32, do_fdot_fp8, a, true)
static bool do_fdot_nx(DisasContext *s, arg_azx_n *a)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 17/54] target/arm: Implement FDOT (multiple and indexed vector)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (15 preceding siblings ...)
2026-06-29 11:59 ` [PULL 16/54] target/arm: Implement FDOT (multiple vector) Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 18/54] target/arm: Implement FVDOTB, FVDOTT Peter Maydell
` (37 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618041517.573469-9-richard.henderson@linaro.org
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 3 +++
target/arm/tcg/translate-sme.c | 4 ++++
2 files changed, 7 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 04dd0e6df0..7324ccddac 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -781,6 +781,9 @@ FMLALL_nx_b 11000001 0100 .... . .. ... ..... 000 .. @azx_1x1_i4_o2
FMLALL_nx_b 11000001 1001 .... 0 .. 0.. ....1 00.. . @azx_2x1_i4_o1
FMLALL_nx_b 11000001 0001 .... 1 .. 0.. ...10 00.. . @azx_4x1_i4_o1
+FDOT_nx_b 11000001 0101 .... 0 .. 0.. ....1 11 ... @azx_2x1_i2_o3
+FDOT_nx_b 11000001 0101 .... 1 .. 0.. ...00 01 ... @azx_4x1_i2_o3
+
### SME2 Add / Sub array accumulators
ADD_aaz_s 11000001 101 000000 .. 111 ....0 10 ... @az_2x2_o3
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 467b44e832..a9adaa02bd 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1161,6 +1161,10 @@ TRANS_FEAT(FMLALL_nx_b, aa64_sme_f8f32, do_azz_acc_fp8,
a->n, 4, a->rv, a->off, a->zn, a->zm,
a->idx << 2, 0, false, gen_helper_gvec_fmla_idx_sb)
+TRANS_FEAT(FDOT_nx_b, aa64_sme_f8f32, do_azz_acc_fp8,
+ a->n, 1, a->rv, a->off, a->zn, a->zm,
+ a->idx, 0, false, gen_helper_gvec_fdot_idx_sb)
+
static bool do_fdot(DisasContext *s, arg_azz_n *a, bool multi)
{
return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm, 1, 0,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 18/54] target/arm: Implement FVDOTB, FVDOTT
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (16 preceding siblings ...)
2026-06-29 11:59 ` [PULL 17/54] target/arm: Implement FDOT (multiple and indexed vector) Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 19/54] target/arm: Enable FEAT_SME_F8F32 for -cpu max Peter Maydell
` (36 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618041517.573469-10-richard.henderson@linaro.org
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/fp8_helper.c | 23 +++++++++++++++++++++++
target/arm/tcg/helper-fp8-defs.h | 2 ++
target/arm/tcg/sme.decode | 7 +++++++
target/arm/tcg/translate-sme.c | 10 ++++++++++
4 files changed, 42 insertions(+)
diff --git a/target/arm/tcg/fp8_helper.c b/target/arm/tcg/fp8_helper.c
index 08e04364fa..3c2d959099 100644
--- a/target/arm/tcg/fp8_helper.c
+++ b/target/arm/tcg/fp8_helper.c
@@ -892,3 +892,26 @@ void HELPER(sme_fmopa_sb)(void *vza, void *vzn, void *vzm, void *vpn,
} while (row & 15);
}
}
+
+void HELPER(sme_fvdot_idx_sb)(void *vd, void *vn, void *vm,
+ CPUARMState *env, uint32_t desc)
+{
+ FP8MulContext ctx = fp8_mul_start(env, -1);
+ intptr_t oprsz = simd_maxsz(desc);
+ intptr_t elements = oprsz / sizeof(float32);
+ int idx_n = extract32(desc, SIMD_DATA_SHIFT, 2);
+ int idx_m = extract32(desc, SIMD_DATA_SHIFT + 2, 3);
+ float32 *d = vd;
+ uint8_t *n0 = vn;
+ uint8_t *n1 = vn + sizeof(ARMVectorReg);
+ uint16_t *m = vm;
+ intptr_t i = 0;
+
+ do {
+ uint16_t mm = m[H2(2 * i + idx_m)];
+ do {
+ uint16_t nn = n0[H1(4 * i + idx_n)] | (n1[H1(4 * i + idx_n)] << 8);
+ d[H4(i)] = f8dotadd_s(nn, mm, 2, d[H4(i)], &ctx);
+ } while (++i & 3);
+ } while (i < elements);
+}
diff --git a/target/arm/tcg/helper-fp8-defs.h b/target/arm/tcg/helper-fp8-defs.h
index 7c090ccad6..ef1375fea7 100644
--- a/target/arm/tcg/helper-fp8-defs.h
+++ b/target/arm/tcg/helper-fp8-defs.h
@@ -40,3 +40,5 @@ DEF_HELPER_FLAGS_5(gvec_fmmla_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32
DEF_HELPER_FLAGS_5(gvec_fmmla_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32)
DEF_HELPER_FLAGS_7(sme_fmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32)
+
+DEF_HELPER_FLAGS_5(sme_fvdot_idx_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 7324ccddac..1de5f341ef 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -784,6 +784,13 @@ FMLALL_nx_b 11000001 0001 .... 1 .. 0.. ...10 00.. . @azx_4x1_i4_o1
FDOT_nx_b 11000001 0101 .... 0 .. 0.. ....1 11 ... @azx_2x1_i2_o3
FDOT_nx_b 11000001 0101 .... 1 .. 0.. ...00 01 ... @azx_4x1_i2_o3
+%idx2_10_3 10:1 3:1
+@azx_4x2_i2_o3 ........ .... zm:4 . .. ... .... ... off:3 \
+ &azx_n n=4 rv=%mova_rv zn=%zn_ax2 idx=%idx2_10_3
+
+FVDOTB_sb 11000001 1101 .... 0 .. 01. ....0 0. ... @azx_4x2_i2_o3
+FVDOTT_sb 11000001 1101 .... 0 .. 01. ....0 1. ... @azx_4x2_i2_o3
+
### SME2 Add / Sub array accumulators
ADD_aaz_s 11000001 101 000000 .. 111 ....0 10 ... @az_2x2_o3
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index a9adaa02bd..a79b0a9b80 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1233,6 +1233,16 @@ static bool do_vdot(DisasContext *s, arg_azx_n *a, gen_helper_gvec_4_ptr *fn)
TRANS_FEAT(FVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_fvdot_idx_h)
TRANS_FEAT(BFVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_bfvdot_idx)
+static bool do_fvdot_sb(DisasContext *s, arg_azx_n *a, bool top)
+{
+ return do_azz_acc_fp8(s, a->n, 1, a->rv, a->off, a->zn, a->zm,
+ (2 * a->idx + top) << 2, 0, false,
+ gen_helper_sme_fvdot_idx_sb);
+}
+
+TRANS_FEAT(FVDOTB_sb, aa64_sme_f8f32, do_fvdot_sb, a, false)
+TRANS_FEAT(FVDOTT_sb, aa64_sme_f8f32, do_fvdot_sb, a, true)
+
static bool do_fmla(DisasContext *s, arg_azz_n *a, bool multi,
ARMFPStatusFlavour fpst, gen_helper_gvec_3_ptr *fn)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 19/54] target/arm: Enable FEAT_SME_F8F32 for -cpu max
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (17 preceding siblings ...)
2026-06-29 11:59 ` [PULL 18/54] target/arm: Implement FVDOTB, FVDOTT Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 20/54] arm/gicv5: Add URL to GICv5 specification Peter Maydell
` (35 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618041517.573469-11-richard.henderson@linaro.org
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
docs/system/arm/emulation.rst | 1 +
linux-user/aarch64/elfload.c | 1 +
target/arm/tcg/cpu64.c | 1 +
3 files changed, 3 insertions(+)
diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst
index 0b4623139a..a3a1607ff9 100644
--- a/docs/system/arm/emulation.rst
+++ b/docs/system/arm/emulation.rst
@@ -169,6 +169,7 @@ the following architecture extensions:
- FEAT_SME_FA64 (Full A64 instruction set in Streaming SVE mode)
- FEAT_SME_F16F16 (Non-widening half-precision FP16 arithmetic for SME2)
- FEAT_SME_F64F64 (Double-precision floating-point outer product instructions)
+- FEAT_SME_F8F32 (SME2 ZA-targeting FP8 multiply-accumulate, dot product, and outer product to single-precision instructions)
- FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions)
- FEAT_SME_LUTv2 (Lookup table instructions with 4-bit indices and 8-bit elements)
- FEAT_SSVE_AES (Streaming SVE Mode Advanced Encryption Standard and 128-bit polynomial multiply long instructions)
diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c
index 0f96bbe70b..20b9838520 100644
--- a/linux-user/aarch64/elfload.c
+++ b/linux-user/aarch64/elfload.c
@@ -235,6 +235,7 @@ abi_ulong get_elf_hwcap2(CPUState *cs)
GET_FEATURE_ID(aa64_ssve_f8fma, ARM_HWCAP2_A64_SME_SF8FMA);
GET_FEATURE_ID(aa64_ssve_f8dp4, ARM_HWCAP2_A64_SME_SF8DP4);
GET_FEATURE_ID(aa64_ssve_f8dp2, ARM_HWCAP2_A64_SME_SF8DP2);
+ GET_FEATURE_ID(aa64_sme_f8f32, ARM_HWCAP2_A64_SME_F8F32);
return hwcaps;
}
diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c
index e4a8d7a2f6..e9eda8fbf1 100644
--- a/target/arm/tcg/cpu64.c
+++ b/target/arm/tcg/cpu64.c
@@ -1392,6 +1392,7 @@ void aarch64_max_tcg_initfn(Object *obj)
t = FIELD_DP64(t, ID_AA64SMFR0, B16F32, 1); /* FEAT_SME */
t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* FEAT_SME */
t = FIELD_DP64(t, ID_AA64SMFR0, I8I32, 0xf); /* FEAT_SME */
+ t = FIELD_DP64(t, ID_AA64SMFR0, F8F32, 1); /* FEAT_SME_F8F32 */
t = FIELD_DP64(t, ID_AA64SMFR0, F16F16, 1); /* FEAT_SME_F16F16 */
t = FIELD_DP64(t, ID_AA64SMFR0, B16B16, 1); /* FEAT_SME_B16B16 */
t = FIELD_DP64(t, ID_AA64SMFR0, I16I32, 5); /* FEAT_SME2 */
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 20/54] arm/gicv5: Add URL to GICv5 specification
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (18 preceding siblings ...)
2026-06-29 11:59 ` [PULL 19/54] target/arm: Enable FEAT_SME_F8F32 for -cpu max Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 11:59 ` [PULL 21/54] target/arm: GICv5 cpuif: Recalculate IRQ/FIQ on ICC_PCR_EL1 write Peter Maydell
` (34 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
The GICv5 specification has now been assigned its final document ID
for a rev A.a spec version. This seems like a good point to add a
pointer to the spec to the main source files.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Tested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Message-id: 20260615105029.2898872-2-peter.maydell@linaro.org
---
hw/intc/arm_gicv5.c | 5 +++++
target/arm/tcg/gicv5-cpuif.c | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index 6b1dd04991..0fa3d6b601 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -4,6 +4,11 @@
* Copyright (c) 2025 Linaro Limited
*
* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * The IRS is defined in IHI 111701
+ * (ARM Generic Interrupt Controller Architecture Specification,
+ * GIC architecture version 5):
+ * https://developer.arm.com/documentation/111701/latest
*/
#include "qemu/osdep.h"
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index dd2f696511..891cb2d3b7 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -4,6 +4,11 @@
* Copyright (c) 2025 Linaro Limited
*
* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * The cpu interface is defined in IHI 111701
+ * (ARM Generic Interrupt Controller Architecture Specification,
+ * GIC architecture version 5):
+ * https://developer.arm.com/documentation/111701/latest
*/
#include "qemu/osdep.h"
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 21/54] target/arm: GICv5 cpuif: Recalculate IRQ/FIQ on ICC_PCR_EL1 write
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (19 preceding siblings ...)
2026-06-29 11:59 ` [PULL 20/54] arm/gicv5: Add URL to GICv5 specification Peter Maydell
@ 2026-06-29 11:59 ` Peter Maydell
2026-06-29 12:00 ` [PULL 22/54] target/arm: GICv5 cpuif: Tag ICC_PPI_PRIORITYR<n> regs as ARM_CP_NO_RAW Peter Maydell
` (33 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 11:59 UTC (permalink / raw)
To: qemu-devel
When the guest writes ICC_PCR_EL1 we should recheck whether we should
be signalling IRQ or FIQ, because we use this priority mask value in
determining whether the highest priority interrupt should be
signalled or not.
Fixes: 9bd90bddb79b ("target/arm: GICv5 cpuif: Signal IRQ or FIQ")
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Message-id: 20260615105029.2898872-3-peter.maydell@linaro.org
---
target/arm/tcg/gicv5-cpuif.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index 891cb2d3b7..09c3fdda61 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -565,6 +565,7 @@ static void gic_icc_pcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
value &= R_ICC_PCR_PRIORITY_MASK;
env->gicv5_cpuif.icc_pcr[domain] = value;
+ gicv5_update_irq_fiq(env);
}
static void gic_icc_pcr_el1_reset(CPUARMState *env, const ARMCPRegInfo *ri)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 22/54] target/arm: GICv5 cpuif: Tag ICC_PPI_PRIORITYR<n> regs as ARM_CP_NO_RAW
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (20 preceding siblings ...)
2026-06-29 11:59 ` [PULL 21/54] target/arm: GICv5 cpuif: Recalculate IRQ/FIQ on ICC_PCR_EL1 write Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 23/54] target/arm: GICv5 cpuif: Remove ICC_HAPR_EL1 Peter Maydell
` (32 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
The GICv5 system registers are all tagged with ARM_CP_NO_RAW, because
we don't want them to use the standard mechanisms for migration or
KVM state synchronization. (GICv5 handling of both migration and KVM
support is a "will be implemented later" feature.) We missed this tag
on the ICC_PPI_PRIORITYR<n>_EL1 registers; add it.
Fixes: ef540c1d301 ("target/arm: GICv5 cpuif: Implement PPI priority registers")
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Message-id: 20260615105029.2898872-4-peter.maydell@linaro.org
---
target/arm/tcg/gicv5-cpuif.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index 09c3fdda61..1aa4bde799 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -950,7 +950,7 @@ void define_gicv5_cpuif_regs(ARMCPU *cpu)
.name = name, .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 12,
.crm = 14 + (i >> 3), .opc2 = i & 7,
- .access = PL1_RW, .type = ARM_CP_IO,
+ .access = PL1_RW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.fieldoffset = offsetof(CPUARMState, gicv5_cpuif.ppi_priority[i]),
.writefn = gic_ppi_priority_write, .raw_writefn = raw_write,
};
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 23/54] target/arm: GICv5 cpuif: Remove ICC_HAPR_EL1
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (21 preceding siblings ...)
2026-06-29 12:00 ` [PULL 22/54] target/arm: GICv5 cpuif: Tag ICC_PPI_PRIORITYR<n> regs as ARM_CP_NO_RAW Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 24/54] hw/intc/gicv5: Define and use GICV5_PENDING_IRQ_NONE Peter Maydell
` (31 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
The ICC_HAPR_EL1 register reported the current running priority of
the CPU. However this can be easily calculated by reading the
APR register, so it has been removed in the newly released A.a
version of the GICv5 spec (IHI 111701).
Remove QEMU's implementation of this register. (This is OK for
us to do without warning because our GICv5 support is listed
as "experimental"; we anticipated that this kind of spec change
might happen.)
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Message-id: 20260615105029.2898872-5-peter.maydell@linaro.org
---
target/arm/tcg/gicv5-cpuif.c | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index 1aa4bde799..a868adca66 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -511,15 +511,6 @@ static void gic_icc_apr_el1_reset(CPUARMState *env, const ARMCPRegInfo *ri)
}
}
-static uint64_t gic_icc_hapr_el1_read(CPUARMState *env, const ARMCPRegInfo *ri)
-{
- /*
- * ICC_HAPR_EL1 reports the current running priority, which can be
- * calculated from the APR register.
- */
- return gic_running_prio(env, gicv5_current_phys_domain(env));
-}
-
/* ICC_CR0_EL1 is also banked */
static uint64_t gic_icc_cr0_el1_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
@@ -928,11 +919,6 @@ static const ARMCPRegInfo gicv5_cpuif_reginfo[] = {
.writefn = gic_icc_pcr_el1_write,
.resetfn = gic_icc_pcr_el1_reset,
},
- { .name = "ICC_HAPR_EL1", .state = ARM_CP_STATE_AA64,
- .opc0 = 3, .opc1 = 1, .crn = 12, .crm = 0, .opc2 = 3,
- .access = PL1_R, .type = ARM_CP_IO | ARM_CP_NO_RAW,
- .readfn = gic_icc_hapr_el1_read, .raw_writefn = arm_cp_write_ignore,
- },
};
void define_gicv5_cpuif_regs(ARMCPU *cpu)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 24/54] hw/intc/gicv5: Define and use GICV5_PENDING_IRQ_NONE
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (22 preceding siblings ...)
2026-06-29 12:00 ` [PULL 23/54] target/arm: GICv5 cpuif: Remove ICC_HAPR_EL1 Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 25/54] target/arm/tcg/cpu64.c: Extra test for GPC3 Peter Maydell
` (30 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
The GICv5PendingIrq struct representation of "there is no pending
interrupt" sets the prio field to PRIO_IDLE, and generally to avoid
confusion we also set the intid to 0. We want to return this value
or initialize some variable to it in several places in the GICv5
implementation, and the support for the virtual interrupt domain
introduces more.
Define a convenience macro for this special-case struct value, and
use it instead of opencoding either the structure initializer or
explicit assignment to the two fields.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Message-id: 20260615105029.2898872-6-peter.maydell@linaro.org
---
hw/intc/arm_gicv5.c | 6 ++----
include/hw/intc/arm_gicv5_types.h | 4 ++++
target/arm/tcg/gicv5-cpuif.c | 7 +++----
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index 0fa3d6b601..08878193df 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -429,8 +429,7 @@ static void irs_recalc_hppi(GICv5 *s, GICv5Domain domain, uint32_t iaffid)
ARMCPU *cpu = cpuidx >= 0 ? cs->cpus[cpuidx] : NULL;
GICv5PendingIrq best;
- best.intid = 0;
- best.prio = PRIO_IDLE;
+ best = GICV5_PENDING_IRQ_NONE;
if (!cpu) {
/* Nothing happens for iaffids targeting nonexistent CPUs */
@@ -526,8 +525,7 @@ static void irs_recall_hppis(GICv5 *s, GICv5Domain domain)
GICv5Common *cs = ARM_GICV5_COMMON(s);
for (int i = 0; i < cs->num_cpus; i++) {
- s->hppi[domain][i].intid = 0;
- s->hppi[domain][i].prio = PRIO_IDLE;
+ s->hppi[domain][i] = GICV5_PENDING_IRQ_NONE;
gicv5_forward_interrupt(cs->cpus[i], domain);
}
}
diff --git a/include/hw/intc/arm_gicv5_types.h b/include/hw/intc/arm_gicv5_types.h
index de4f78a149..851286b1b5 100644
--- a/include/hw/intc/arm_gicv5_types.h
+++ b/include/hw/intc/arm_gicv5_types.h
@@ -105,6 +105,10 @@ typedef struct GICv5PendingIrq {
uint8_t prio;
} GICv5PendingIrq;
+/* A GICv5PendingIrq struct initializer for "no pending interrupt" */
+#define GICV5_PENDING_IRQ_NONE \
+ ((GICv5PendingIrq) { .intid = 0, .prio = PRIO_IDLE })
+
/* Fields in a generic 32-bit INTID, per R_TJPHS */
FIELD(INTID, ID, 0, 24)
FIELD(INTID, TYPE, 29, 3)
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index a868adca66..1cdd4103d0 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -144,7 +144,7 @@ static GICv5PendingIrq gic_hppi(CPUARMState *env, GICv5Domain domain)
if (!(env->gicv5_cpuif.icc_cr0[domain] & R_ICC_CR0_EN_MASK)) {
/* If cpuif is disabled there is no HPPI */
- return (GICv5PendingIrq) { .intid = 0, .prio = PRIO_IDLE };
+ return GICV5_PENDING_IRQ_NONE;
}
irs_hppi = gicv5_get_hppi(gic, domain, env->gicv5_iaffid);
@@ -168,7 +168,7 @@ static GICv5PendingIrq gic_hppi(CPUARMState *env, GICv5Domain domain)
if (best.prio == PRIO_IDLE ||
best.prio > env->gicv5_cpuif.icc_pcr[domain] ||
best.prio >= gic_running_prio(env, domain)) {
- return (GICv5PendingIrq) { .intid = 0, .prio = PRIO_IDLE };
+ return GICV5_PENDING_IRQ_NONE;
}
return best;
}
@@ -258,8 +258,7 @@ static void gic_recalc_ppi_hppi(CPUARMState *env)
* enabled, pending and not active.
*/
for (int i = 0; i < ARRAY_SIZE(env->gicv5_cpuif.ppi_hppi); i++) {
- env->gicv5_cpuif.ppi_hppi[i].intid = 0;
- env->gicv5_cpuif.ppi_hppi[i].prio = PRIO_IDLE;
+ env->gicv5_cpuif.ppi_hppi[i] = GICV5_PENDING_IRQ_NONE;
};
for (int i = 0; i < ARRAY_SIZE(env->gicv5_cpuif.ppi_active); i++) {
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 25/54] target/arm/tcg/cpu64.c: Extra test for GPC3.
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (23 preceding siblings ...)
2026-06-29 12:00 ` [PULL 24/54] hw/intc/gicv5: Define and use GICV5_PENDING_IRQ_NONE Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 26/54] target/arm: Setup new registers " Peter Maydell
` (29 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Jim MacArthur <jim.macarthur@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org>
Message-id: 20260618-jmac-gpc3b-v3-1-353e546067e7@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/cpu-features.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h
index 45213f71ab..2e4f22f193 100644
--- a/target/arm/cpu-features.h
+++ b/target/arm/cpu-features.h
@@ -1159,6 +1159,11 @@ static inline bool isar_feature_aa64_rme_gpc2(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64PFR0, RME) >= 2;
}
+static inline bool isar_feature_aa64_rme_gpc3(const ARMISARegisters *id)
+{
+ return FIELD_EX64_IDREG(id, ID_AA64PFR0, RME) >= 3;
+}
+
static inline bool isar_feature_aa64_dit(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64PFR0, DIT) != 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 26/54] target/arm: Setup new registers for GPC3
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (24 preceding siblings ...)
2026-06-29 12:00 ` [PULL 25/54] target/arm/tcg/cpu64.c: Extra test for GPC3 Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 27/54] target/arm/ptw.c: Add Granule Bypass Windows Peter Maydell
` (28 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Jim MacArthur <jim.macarthur@linaro.org>
Adds GPCBW_EL3. A custom write function is necessary to flush TLBs
when this register is written. Also allows write to the GPCBW bit of
GPCCR_EL3.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org>
Message-id: 20260618-jmac-gpc3b-v3-2-353e546067e7@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/cpu.h | 7 +++++++
target/arm/helper.c | 19 +++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 31a5567c95..84d33e87dc 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -559,6 +559,7 @@ typedef struct CPUArchState {
/* RME registers */
uint64_t gpccr_el3;
uint64_t gptbr_el3;
+ uint64_t gpcbw_el3;
uint64_t mfar_el3;
/* NV2 register */
@@ -1244,6 +1245,8 @@ void arm_v7m_cpu_do_interrupt(CPUState *cpu);
typedef struct ARMGranuleProtectionConfig {
/* GPCCR_EL3 */
uint64_t gpccr;
+ /* GPCBW_EL3 */
+ uint64_t gpcbw;
/* GPTBR_EL3 */
uint64_t gptbr;
/* ID_AA64MMFR0_EL1.PARange */
@@ -2116,6 +2119,10 @@ FIELD(GPCCR, NA6, 27, 1)
FIELD(GPCCR, NA7, 28, 1)
FIELD(GPCCR, GPCBW, 29, 1)
+FIELD(GPCBW, BWSIZE, 37, 2)
+FIELD(GPCBW, BWSTRIDE, 32, 5)
+FIELD(GPCBW, BWADDR, 0, 25)
+
FIELD(MFAR, FPA, 12, 40)
FIELD(MFAR, NSE, 62, 1)
FIELD(MFAR, NS, 63, 1)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index a234aa031c..ddca634538 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -5001,6 +5001,10 @@ static void gpccr_write(CPUARMState *env, const ARMCPRegInfo *ri,
R_GPCCR_SPAD_MASK | R_GPCCR_NSPAD_MASK | R_GPCCR_RLPAD_MASK;
}
+ if (cpu_isar_feature(aa64_rme_gpc3, env_archcpu(env))) {
+ rw_mask |= R_GPCCR_GPCBW_MASK;
+ }
+
env->cp15.gpccr_el3 = (value & rw_mask) | (env->cp15.gpccr_el3 & ~rw_mask);
}
@@ -5010,11 +5014,26 @@ static void gpccr_reset(CPUARMState *env, const ARMCPRegInfo *ri)
env_archcpu(env)->reset_l0gptsz);
}
+static void gpcbw_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ uint64_t rw_mask = R_GPCBW_BWADDR_MASK | R_GPCBW_BWSTRIDE_MASK |
+ R_GPCBW_BWSIZE_MASK;
+ ARMCPU *cpu = env_archcpu(env);
+
+ tlb_flush(CPU(cpu));
+ env->cp15.gpcbw_el3 = (value & rw_mask);
+}
+
static const ARMCPRegInfo rme_reginfo[] = {
{ .name = "GPCCR_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .crn = 2, .crm = 1, .opc2 = 6,
.access = PL3_RW, .writefn = gpccr_write, .resetfn = gpccr_reset,
.fieldoffset = offsetof(CPUARMState, cp15.gpccr_el3) },
+ { .name = "GPCBW_EL3", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 1, .opc2 = 5,
+ .access = PL3_RW, .writefn = gpcbw_write,
+ .fieldoffset = offsetof(CPUARMState, cp15.gpcbw_el3) },
{ .name = "GPTBR_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .crn = 2, .crm = 1, .opc2 = 4,
.access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.gptbr_el3) },
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 27/54] target/arm/ptw.c: Add Granule Bypass Windows
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (25 preceding siblings ...)
2026-06-29 12:00 ` [PULL 26/54] target/arm: Setup new registers " Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 28/54] target/arm/cpu-features.h: x-rme now means GPC3 Peter Maydell
` (27 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Jim MacArthur <jim.macarthur@linaro.org>
Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618-jmac-gpc3b-v3-3-353e546067e7@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/ptw.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 77 insertions(+)
diff --git a/target/arm/ptw.c b/target/arm/ptw.c
index 1470de3010..99295954a1 100644
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -343,11 +343,21 @@ bool arm_granule_protection_check(ARMGranuleProtectionConfig config,
.space = ARMSS_Root,
};
const uint64_t gpccr = config.gpccr;
+ const uint64_t gpcbw = config.gpcbw;
unsigned pps, pgs, l0gptsz, level = 0;
uint64_t tableaddr, pps_mask, align, entry, index;
MemTxResult result;
int gpi;
+ const uint64_t BW_ADDR_SHIFT = 30;
+ const uint64_t BW_SIZE_SHIFT = 30;
+ const uint64_t BW_STRIDE_SHIFT = 40;
+
+ uint64_t bw_size_field = FIELD_EX64(gpcbw, GPCBW, BWSIZE);
+ uint64_t bw_stride_field = FIELD_EX64(gpcbw, GPCBW, BWSTRIDE);
+ uint64_t bw_addr = FIELD_EX64(gpcbw, GPCBW, BWADDR) << BW_ADDR_SHIFT;
+ uint64_t bw_mask = 0;
+
/*
* We assume Granule Protection Check is enabled when
* calling this function (GPCCR.GPC == 1).
@@ -399,6 +409,58 @@ bool arm_granule_protection_check(ARMGranuleProtectionConfig config,
goto fault_walk;
}
+ /* At this point, GPCCR_EL3 is valid */
+
+ /*
+ * GPC Priority 1 (R_GMGRR):
+ * If GPCCR_EL3.GPCBW is 1 and the configuration GPCBW
+ * is invalid, the access fails as GPT walk fault at level 0.
+ */
+ if (FIELD_EX64(gpccr, GPCCR, GPCBW)) {
+ uint64_t bw_size = 0;
+ uint64_t bw_stride = 0;
+
+ /* BWSIZE, BWSTRIDE have a limited number of acceptable values. */
+ switch (bw_size_field) {
+ case 0b000:
+ case 0b001:
+ case 0b010:
+ case 0b100:
+ case 0b110:
+ bw_size = 1ULL << (bw_size_field + BW_SIZE_SHIFT);
+ break;
+ default: /* Reserved value */
+ goto fault_walk;
+ }
+ switch (bw_stride_field) {
+ case 0b00000:
+ case 0b00010:
+ case 0b00100:
+ case 0b00110:
+ case 0b00111:
+ case 0b01000:
+ case 0b01001:
+ case 0b01010:
+ case 0b10000:
+ bw_stride = 1ULL << (bw_stride_field + BW_STRIDE_SHIFT);
+ break;
+ default: /* Reserved value */
+ goto fault_walk;
+ }
+ /*
+ * GPCBW is invalid if the base address is:
+ * not aligned to the size programmed in BWSIZE, or
+ * greater than or equal to the stride value configured by BWSTRIDE.
+ * We can make bw_mask which marks exactly which bits in bw_addr may
+ * be set (gpcbwu:gpcbwl).
+ */
+ bw_mask = bw_stride - bw_size;
+
+ if (bw_addr & ~bw_mask) {
+ goto fault_walk;
+ }
+ }
+
/* Note this field is read-only and fixed at reset. */
l0gptsz = 30 + FIELD_EX64(gpccr, GPCCR, L0GPTSZ);
@@ -433,6 +495,20 @@ bool arm_granule_protection_check(ARMGranuleProtectionConfig config,
goto fault_fail;
}
+ /*
+ * Bypass window check.
+ * I_JJLRM: Granule Protection Table (GPT) lookups can be skipped
+ * in portions of the memory map by using GPC bypass windows.
+ * I_XNHTX: The GPC bypass window check (...) is performed
+ * immediately after priority 3.
+ * bw_mask from earlier makes this check for us.
+ */
+ if (FIELD_EX64(gpccr, GPCCR, GPCBW)) {
+ if ((paddress & bw_mask) == bw_addr) {
+ return true;
+ }
+ }
+
/* GPC Priority 4: the base address of GPTBR_EL3 exceeds PPS. */
tableaddr = config.gptbr << 12;
if (tableaddr & ~pps_mask) {
@@ -3879,6 +3955,7 @@ static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw,
};
struct ARMGranuleProtectionConfig config = {
.gpccr = env->cp15.gpccr_el3,
+ .gpcbw = env->cp15.gpcbw_el3,
.gptbr = env->cp15.gptbr_el3,
.parange = FIELD_EX64_IDREG(&cpu->isar, ID_AA64MMFR0, PARANGE),
.support_sel2 = cpu_isar_feature(aa64_sel2, cpu),
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 28/54] target/arm/cpu-features.h: x-rme now means GPC3
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (26 preceding siblings ...)
2026-06-29 12:00 ` [PULL 27/54] target/arm/ptw.c: Add Granule Bypass Windows Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 29/54] tests/tcg/aarch64/system: Alternative boot object for exception logging Peter Maydell
` (26 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Jim MacArthur <jim.macarthur@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org>
Message-id: 20260618-jmac-gpc3b-v3-4-353e546067e7@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/cpu64.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c
index e9eda8fbf1..a5734446dd 100644
--- a/target/arm/tcg/cpu64.c
+++ b/target/arm/tcg/cpu64.c
@@ -159,8 +159,8 @@ static void cpu_arm_set_rme(Object *obj, bool value, Error **errp)
{
ARMCPU *cpu = ARM_CPU(obj);
- /* Enable FEAT_RME_GPC2 */
- FIELD_DP64_IDREG(&cpu->isar, ID_AA64PFR0, RME, value ? 2 : 0);
+ /* Enable FEAT_RME_GPC3 */
+ FIELD_DP64_IDREG(&cpu->isar, ID_AA64PFR0, RME, value ? 3 : 0);
}
static void cpu_max_set_l0gptsz(Object *obj, Visitor *v, const char *name,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 29/54] tests/tcg/aarch64/system: Alternative boot object for exception logging
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (27 preceding siblings ...)
2026-06-29 12:00 ` [PULL 28/54] target/arm/cpu-features.h: x-rme now means GPC3 Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 30/54] tests/tcg/aarch64/system/gpc-test.c: Basic test for granule protection check Peter Maydell
` (25 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Jim MacArthur <jim.macarthur@linaro.org>
This allows us to record information about exceptions using a small
area of memory, and continue with the test so it can verify exceptions
have been taken where expected.
LOGGING_VECTOR_TABLE is added to switch this on, and vec_logging_boot.o
is built from boot.S with this flag. Since boot.o is created for multiple
test targets, we have to build a separate object and selectively link this
new vec_logging_boot.o into particular test binaries.
Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260618-jmac-gpc3b-v3-5-353e546067e7@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
tests/tcg/aarch64/Makefile.softmmu-target | 11 ++--
tests/tcg/aarch64/system/boot.S | 65 +++++++++++++++++++++++
tests/tcg/aarch64/system/boot.h | 14 +++++
3 files changed, 86 insertions(+), 4 deletions(-)
create mode 100644 tests/tcg/aarch64/system/boot.h
diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target
index f7a7d2b800..c353b7f8d6 100644
--- a/tests/tcg/aarch64/Makefile.softmmu-target
+++ b/tests/tcg/aarch64/Makefile.softmmu-target
@@ -25,7 +25,7 @@ LDFLAGS=-Wl,-T$(LINK_SCRIPT)
TESTS+=$(AARCH64_TESTS) $(MULTIARCH_TESTS)
EXTRA_RUNS+=$(MULTIARCH_RUNS)
CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC)
-LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc
+LDFLAGS+=-static -nostdlib $(MINILIB_OBJS) -lgcc
config-cc.mak: Makefile
$(quiet-@)( \
@@ -36,17 +36,20 @@ config-cc.mak: Makefile
# building head blobs
.PRECIOUS: $(CRT_OBJS)
+vector_log_boot.o: $(CRT_PATH)/boot.S
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DLOGGING_VECTOR_TABLE -x assembler-with-cpp -Wa,--noexecstack -c $< -o $@
+
%.o: $(CRT_PATH)/%.S
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -Wa,--noexecstack -c $< -o $@
# Build and link the tests
%: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS)
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) boot.o
memory: CFLAGS+=-DCHECK_UNALIGNED=1
memory-sve: memory.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS)
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) boot.o
memory-sve: CFLAGS+=-DCHECK_UNALIGNED=1 -march=armv8.1-a+sve -O3
@@ -107,7 +110,7 @@ QEMU_MTE_ENABLED_MACHINE=-M virt,mte=on -cpu max -display none
QEMU_OPTS_WITH_MTE_ON = $(QEMU_MTE_ENABLED_MACHINE) $(QEMU_BASE_ARGS) -kernel
mte: CFLAGS+=-march=armv8.5-a+memtag
mte: mte.S $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS)
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) boot.o
run-mte: QEMU_OPTS=$(QEMU_OPTS_WITH_MTE_ON)
run-mte: mte
diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S
index 8bfa4e4efc..c6e5ad889b 100644
--- a/tests/tcg/aarch64/system/boot.S
+++ b/tests/tcg/aarch64/system/boot.S
@@ -60,6 +60,40 @@ curr_sp0_irq:
curr_sp0_fiq:
curr_sp0_serror:
curr_spx_sync:
+#ifdef LOGGING_VECTOR_TABLE
+ sub sp, sp, #16
+ stp x0, x1, [sp, #0]
+ mrs x0, ESR_EL3
+ lsr x0, x0, #26
+ and x0, x0, #0x3f
+ cmp x0, #37
+ beq data_fault
+ cmp x0, #30
+ beq gpc_fault
+ b generic_exception
+
+data_fault:
+ mrs x0, FAR_EL3
+ adrp x1, exception_log
+ str x0, [x1]
+ ldr x0, =0x1001
+ str x0, [x1, #8]
+ b skip_return
+gpc_fault:
+ mrs x0, FAR_EL3
+ adrp x1, exception_log
+ str x0, [x1]
+ ldr x0, =0x1002
+ str x0, [x1, #8]
+ /* Fall through */
+skip_return:
+ mrs x0, ELR_EL3
+ add x0, x0, #4 /* Skip faulting instruction */
+ msr ELR_EL3, x0
+ ldp x0, x1, [sp, #0]
+ add sp, sp, #16
+ eret
+#endif
curr_spx_irq:
curr_spx_fiq:
curr_spx_serror:
@@ -71,6 +105,7 @@ lower_a32_sync:
lower_a32_irq:
lower_a32_fiq:
lower_a32_serror:
+generic_exception:
adr x1, .unexp_excp
exit_msg:
mov x0, SYS_WRITE0
@@ -404,6 +439,36 @@ ttb_stage2:
.space 4096, 0
.align 12
+ .global realms_gpt0
+ /* GPT stage 0 table */
+realms_gpt0:
+ .space 4096, 0
+ .align 17
+ .global realms_gpt1
+ /* GPT stage 1 table, initialised to all 0xFF (full access) */
+realms_gpt1:
+ .space 524288, 0xFF
+#ifdef LOGGING_VECTOR_TABLE
+ .align 12
+ .global exception_fault_address
+ .type exception_fault_address, @object
+ .size exception_fault_address, 8
+ .global exception_type_code
+ .type exception_type_code, @object
+ .size exception_type_code, 8
+ /*
+ * These fields record details of the last exception, if
+ * LOGGING_VECTOR_TABLE is defined.
+ */
+exception_log:
+exception_fault_address:
+ /* The contents of FAR_EL3 when an exception is taken. */
+ .space 8, 0
+exception_type_code:
+ /* A generic code indicating what type of exception occurred. */
+ .space 8, 0
+#endif
+ .align 12
system_stack:
.space 4096, 0
system_stack_end:
diff --git a/tests/tcg/aarch64/system/boot.h b/tests/tcg/aarch64/system/boot.h
new file mode 100644
index 0000000000..cb9ab6c1b9
--- /dev/null
+++ b/tests/tcg/aarch64/system/boot.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ *
+ * Copyright (c) 2026 Linaro Ltd
+ *
+ */
+
+
+/* Global variables exported in boot.S */
+extern volatile uint64_t exception_fault_address; /* Updated by ISR */
+extern volatile uint64_t exception_type_code; /* Updated by ISR */
+extern uint64_t realms_gpt0[];
+extern uint64_t realms_gpt1[];
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 30/54] tests/tcg/aarch64/system/gpc-test.c: Basic test for granule protection check
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (28 preceding siblings ...)
2026-06-29 12:00 ` [PULL 29/54] tests/tcg/aarch64/system: Alternative boot object for exception logging Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 31/54] hw/arm/sabrelite: Open code DEFINE_MACHINE_ARM Peter Maydell
` (24 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Jim MacArthur <jim.macarthur@linaro.org>
* Sets up granule protection tables
* Enables GPC and bypass windows
* Performs memory accesses in the protected region to
check for allowed and disallowed reads.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org>
Message-id: 20260618-jmac-gpc3b-v3-6-353e546067e7@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
tests/tcg/aarch64/Makefile.softmmu-target | 11 +-
tests/tcg/aarch64/system/gpc-test.c | 165 ++++++++++++++++++++++
2 files changed, 175 insertions(+), 1 deletion(-)
create mode 100644 tests/tcg/aarch64/system/gpc-test.c
diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target
index c353b7f8d6..3f30be6c5d 100644
--- a/tests/tcg/aarch64/Makefile.softmmu-target
+++ b/tests/tcg/aarch64/Makefile.softmmu-target
@@ -53,7 +53,10 @@ memory-sve: memory.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
memory-sve: CFLAGS+=-DCHECK_UNALIGNED=1 -march=armv8.1-a+sve -O3
-TESTS+=memory-sve
+gpc-test: gpc-test.c $(LINK_SCRIPT) vector_log_boot.o $(MINILIB_OBJS)
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) vector_log_boot.o
+
+TESTS+=memory-sve gpc-test
# Running
QEMU_BASE_MACHINE=-M virt -cpu max -display none
@@ -74,6 +77,12 @@ QEMU_EL2_MACHINE=-machine virt,virtualization=on,gic-version=2 -cpu cortex-a57 -
QEMU_EL2_BASE_ARGS=-semihosting-config enable=on,target=native,chardev=output,arg="2"
run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_EL2_BASE_ARGS) -kernel
+# gpc tests need EL3 and RME
+QEMU_EL3_MACHINE=-machine virt,virtualization=on,secure=on,gic-version=3 -cpu max,x-rme=on
+QEMU_EL3_BASE_ARGS=-semihosting-config enable=on,target=native,chardev=output,arg="3"
+run-gpc-test: QEMU_OPTS=$(QEMU_EL3_MACHINE) $(QEMU_EL3_BASE_ARGS) -kernel
+run-gpc3-test: QEMU_OPTS=$(QEMU_EL3_MACHINE) $(QEMU_EL3_BASE_ARGS) -kernel
+
# Simple Record/Replay Test
.PHONY: memory-record
run-memory-record: memory-record memory
diff --git a/tests/tcg/aarch64/system/gpc-test.c b/tests/tcg/aarch64/system/gpc-test.c
new file mode 100644
index 0000000000..f26b23efaf
--- /dev/null
+++ b/tests/tcg/aarch64/system/gpc-test.c
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ *
+ * Copyright (c) 2026 Linaro Ltd
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <minilib.h>
+#include "boot.h"
+
+#define ID_AA64PFR0_EL1 "S3_0_C0_C4_0"
+
+#define GPTBR_EL3 "S3_6_C2_C1_4"
+#define GPCBW_EL3 "S3_6_C2_C1_5"
+#define GPCCR_EL3 "S3_6_C2_C1_6"
+#define VBAR_EL3 "S3_6_C12_C0_0"
+
+#define get_sys_reg(register_name, dest) \
+ asm("mrs %[reg], " register_name "\n\t" : [reg] "=r" (dest))
+#define set_sys_reg(register_name, value) \
+ asm("msr " register_name ", %[reg]\n\r" : : [reg] "r" (value))
+
+const uint32_t gpc_granule_size = 4096;
+const uint32_t gpis_per_64_bits = 16;
+
+int main(uint64_t sp)
+{
+ uint64_t out;
+ uint64_t pfr0;
+ uint64_t gpt_base;
+ uint64_t rme_status;
+ uint64_t currentel_raw;
+ uint64_t currentel;
+ uint64_t gpcbw;
+ uint64_t gpt_table0_addr = (uint64_t) realms_gpt0;
+ uint64_t gpt_table1_addr = (uint64_t) realms_gpt1;
+
+ /* Mask is FNG1, FNG0, and A2 */
+ const uint64_t feature_mask = (1ULL << 18 | 1ULL << 17 | 1ULL << 16);
+ const uint64_t in = feature_mask;
+
+ get_sys_reg("CurrentEL", currentel_raw);
+ currentel = (currentel_raw >> 2) & 0x3;
+
+ if (currentel < 3) {
+ ml_printf("FAIL: Test must be run at EL3 (it is %d)\n", currentel);
+ return 1;
+ }
+
+ get_sys_reg(ID_AA64PFR0_EL1, pfr0);
+
+ /* rme_status is 1 for RME, 2 for RME + GPC2, 3 for RME+GPC3 */
+ rme_status = (pfr0 >> 52) & 0xF;
+ if (rme_status < 2) {
+ ml_printf("SKIP: System does not support RME (RME=%ld)\n", rme_status);
+ return 0;
+ }
+
+ /* Configure the level 0 table for the first 4GB of memory */
+ realms_gpt0[0] = gpt_table1_addr | 0x3; /* Covers GB 0; table descriptor */
+ realms_gpt0[1] = 0xf1; /* Covers GB 1; full access */
+ realms_gpt0[2] = 0xf1; /* Covers GB 2; full access */
+ realms_gpt0[3] = 0xf1; /* Covers GB 3; full access */
+
+ /* Pick an artibtrary location to read inside the first 1GB. */
+ uint64_t fault_location = 0x10202008;
+ uint32_t gpi_index = fault_location / gpc_granule_size;
+ realms_gpt1[gpi_index / gpis_per_64_bits] = 0;
+
+ gpt_base = gpt_table0_addr >> 12;
+ set_sys_reg(GPTBR_EL3, gpt_base);
+
+ /*
+ * Default values:
+ * PPS=0: GPC table 0 protects 4GB.
+ * RLPAD=0: Realm physical address spaces are normal
+ * NSPAD=0: Non-secure physical address spaces are normal
+ * SPAD=0: Secure physical address spaces are normal
+ * IRGN=0: Inner non-cacheable
+ * ORGN=0: Outer non-cacheable
+ * PGS=0: Physical granule size is 4KB.
+ * GPCP=0: All GPC faults reported
+ * TBGPCP=0: Trace buffer rejects trace
+ * L0GPTSZ=0: Each entry in table 0 protects 1GB.
+ * APPSAA=0: Accesses above 4GB must be to Non-secure PAs
+ * GPCBW=0: Bypass windows disabled.
+ * NA6, NA7, NSP, SA, NSO are all reserved values for GPI.
+ */
+ uint64_t gpccr = 0;
+
+ /* Switch on granule protection check */
+ gpccr |= 1 << 16; /* GPC enabled. */
+ gpccr |= 0b10 << 12; /* SH = Outer shareable */
+ set_sys_reg(GPCCR_EL3, gpccr);
+
+ /* Access some memory outside the GPC forbidden region */
+ uint64_t x = *(unsigned int *) (fault_location + 4096 * 16);
+ ml_printf("Fault address: %lx\n", exception_fault_address);
+ if (exception_fault_address != 0) {
+ ml_printf("FAIL: Memory access was blocked by GPC, "
+ "and should not have been\n");
+ return 1;
+ }
+
+ /* Access the GPC forbidden region */
+ x = *(unsigned int *) fault_location;
+
+ ml_printf("Fault address: %lx\n", exception_fault_address);
+ if (exception_fault_address != fault_location) {
+ ml_printf("FAIL: Memory access was not blocked by GPC, "
+ "and should have been\n");
+ return 1;
+ }
+
+ rme_status = (pfr0 >> 52) & 0xF;
+ if (rme_status < 3) {
+ ml_printf("SKIP: System does not support GPC3 (RME=%ld)\n", rme_status);
+ return 0;
+ }
+
+ /* Clear the exception record */
+ exception_fault_address = 0;
+
+ /* Enable bypass windows */
+ gpccr |= 1 << 29; /* GPC Bypass windows enabled */
+ set_sys_reg(GPCCR_EL3, gpccr);
+
+ gpcbw = 0; /* Base 0GB, Size 1GB, Stride 1TB */
+ set_sys_reg(GPCBW_EL3, gpcbw);
+ ml_printf("GPCBW configured\n");
+
+ /* Access the GPC forbidden region again */
+ x = *(unsigned int *) fault_location;
+
+ ml_printf("Fault address: %lx\n", exception_fault_address);
+ if (exception_fault_address != 0) {
+ ml_printf("FAIL: Memory access was blocked by GPC, "
+ "and should have been allowed by bypass window. code=%lx\n",
+ exception_type_code);
+ return 1;
+ }
+
+ /* Clear the exception record */
+ exception_fault_address = 0;
+ /* Reconfigure GPCBW to 1GB start */
+ gpcbw = 1; /* Base 1GB, Size 1GB, Stride 1TB */
+ set_sys_reg(GPCBW_EL3, gpcbw);
+ ml_printf("GPCBW reconfigured for 1GB start\n");
+
+ /* Access the GPC forbidden region again */
+ x = *(unsigned int *) fault_location;
+
+ ml_printf("Fault address: %lx\n", exception_fault_address);
+ if (exception_fault_address != fault_location) {
+ ml_printf("FAIL: Memory access was allowed by GPC, "
+ "and should not have been allowed by bypass window. code=%lx\n",
+ exception_type_code);
+ return 1;
+ }
+
+ return 0;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 31/54] hw/arm/sabrelite: Open code DEFINE_MACHINE_ARM
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (29 preceding siblings ...)
2026-06-29 12:00 ` [PULL 30/54] tests/tcg/aarch64/system/gpc-test.c: Basic test for granule protection check Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 32/54] hw/arm/sabrelite: Introduce class SabreliteMachineState Peter Maydell
` (23 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Matyáš Bobek <matyas.bobek@gmail.com>
Open code the DEFINE_MACHINE_ARM macro in preparation of creating
SabreliteMachineState class.
Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com>
Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
Reviewed-by: Bernhard Beschow <shentey@gmail.com>
Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz>
Message-id: 629d00a1d8712dbf293fb13ac1a02bf80f6334ca.1782140438.git.matyas.bobek@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/sabrelite.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c
index db5669c5c2..04484aaa14 100644
--- a/hw/arm/sabrelite.c
+++ b/hw/arm/sabrelite.c
@@ -104,8 +104,10 @@ static void sabrelite_init(MachineState *machine)
}
}
-static void sabrelite_machine_init(MachineClass *mc)
+static void sabrelite_machine_class_init(ObjectClass *oc, const void *data)
{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
mc->desc = "Freescale i.MX6 Quad SABRE Lite Board (Cortex-A9)";
mc->init = sabrelite_init;
mc->max_cpus = FSL_IMX6_NUM_CPUS;
@@ -114,4 +116,18 @@ static void sabrelite_machine_init(MachineClass *mc)
mc->auto_create_sdcard = true;
}
-DEFINE_MACHINE_ARM("sabrelite", sabrelite_machine_init)
+static const TypeInfo sabrelite_machine_init_typeinfo = {
+ .name = MACHINE_TYPE_NAME("sabrelite"),
+ .parent = TYPE_MACHINE,
+ .class_init = sabrelite_machine_class_init,
+ .instance_size = sizeof(MachineState),
+ .abstract = false,
+ .interfaces = arm_machine_interfaces,
+};
+
+static void sabrelite_machine_init_register_types(void)
+{
+ type_register_static(&sabrelite_machine_init_typeinfo);
+}
+
+type_init(sabrelite_machine_init_register_types)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 32/54] hw/arm/sabrelite: Introduce class SabreliteMachineState
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (30 preceding siblings ...)
2026-06-29 12:00 ` [PULL 31/54] hw/arm/sabrelite: Open code DEFINE_MACHINE_ARM Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 33/54] hw/misc/imx6_ccm: Add PLL3 and CAN clock Peter Maydell
` (22 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Matyáš Bobek <matyas.bobek@gmail.com>
Create full class SabreliteMachineState extending MachineState.
Previously, Sabrelite board MachineState was declared indirectly by the
DEFINE_MACHINE_ARM macro. FslIMX6State was only instantiated in the
sabrelite_init function. For FlexCAN device, machine properties
will be used to select QEMU CAN buses. A custom class is therefore
required, so the CanBusState "connections" can then be added as fields.
Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com>
Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
Reviewed-by: Bernhard Beschow <shentey@gmail.com>
Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz>
Message-id: 115740db83cf2e5a5e7bb349130c9706934ec1f3.1782140438.git.matyas.bobek@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/sabrelite.c | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c
index 04484aaa14..7e036e6388 100644
--- a/hw/arm/sabrelite.c
+++ b/hw/arm/sabrelite.c
@@ -20,6 +20,15 @@
#include "qemu/error-report.h"
#include "system/qtest.h"
+struct SabreliteMachineState {
+ MachineState parent_obj;
+
+ FslIMX6State soc;
+};
+
+#define TYPE_SABRELITE_MACHINE MACHINE_TYPE_NAME("sabrelite")
+OBJECT_DECLARE_SIMPLE_TYPE(SabreliteMachineState, SABRELITE_MACHINE)
+
static struct arm_boot_info sabrelite_binfo = {
/* DDR memory start */
.loader_start = FSL_IMX6_MMDC_ADDR,
@@ -41,7 +50,7 @@ static void sabrelite_reset_secondary(ARMCPU *cpu,
static void sabrelite_init(MachineState *machine)
{
- FslIMX6State *s;
+ SabreliteMachineState *s = SABRELITE_MACHINE(machine);
/* Check the amount of memory is compatible with the SOC */
if (machine->ram_size > FSL_IMX6_MMDC_SIZE) {
@@ -50,13 +59,12 @@ static void sabrelite_init(MachineState *machine)
exit(1);
}
- s = FSL_IMX6(object_new(TYPE_FSL_IMX6));
- object_property_add_child(OBJECT(machine), "soc", OBJECT(s));
+ object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_FSL_IMX6);
/* Ethernet PHY address is 6 */
- object_property_set_int(OBJECT(s), "fec-phy-num", 6, &error_fatal);
+ object_property_set_int(OBJECT(&s->soc), "fec-phy-num", 6, &error_fatal);
- qdev_realize(DEVICE(s), NULL, &error_fatal);
+ qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
memory_region_add_subregion(get_system_memory(), FSL_IMX6_MMDC_ADDR,
machine->ram);
@@ -70,7 +78,7 @@ static void sabrelite_init(MachineState *machine)
/* Add the sst25vf016b NOR FLASH memory to first SPI */
Object *spi_dev;
- spi_dev = object_resolve_path_component(OBJECT(s), "spi1");
+ spi_dev = object_resolve_path_component(OBJECT(&s->soc), "spi1");
if (spi_dev) {
SSIBus *spi_bus;
@@ -89,7 +97,7 @@ static void sabrelite_init(MachineState *machine)
qdev_realize_and_unref(flash_dev, BUS(spi_bus), &error_fatal);
cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
- qdev_connect_gpio_out(DEVICE(&s->gpio[2]), 19, cs_line);
+ qdev_connect_gpio_out(DEVICE(&s->soc.gpio[2]), 19, cs_line);
}
}
}
@@ -100,7 +108,7 @@ static void sabrelite_init(MachineState *machine)
sabrelite_binfo.secondary_cpu_reset_hook = sabrelite_reset_secondary;
if (!qtest_enabled()) {
- arm_load_kernel(&s->cpu[0], machine, &sabrelite_binfo);
+ arm_load_kernel(&s->soc.cpu[0], machine, &sabrelite_binfo);
}
}
@@ -117,10 +125,10 @@ static void sabrelite_machine_class_init(ObjectClass *oc, const void *data)
}
static const TypeInfo sabrelite_machine_init_typeinfo = {
- .name = MACHINE_TYPE_NAME("sabrelite"),
+ .name = TYPE_SABRELITE_MACHINE,
.parent = TYPE_MACHINE,
.class_init = sabrelite_machine_class_init,
- .instance_size = sizeof(MachineState),
+ .instance_size = sizeof(SabreliteMachineState),
.abstract = false,
.interfaces = arm_machine_interfaces,
};
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 33/54] hw/misc/imx6_ccm: Add PLL3 and CAN clock
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (31 preceding siblings ...)
2026-06-29 12:00 ` [PULL 32/54] hw/arm/sabrelite: Introduce class SabreliteMachineState Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 34/54] hw/net/can/flexcan: NXP FlexCAN core emulation Peter Maydell
` (21 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Matyáš Bobek <matyas.bobek@gmail.com>
Add fixed frequency (480 MHz) PLL3, of which the FlexCAN
clock is derived, and compute FlexCAN frequency based on
the divider configuration (CCM_CSCMR2).
The clock frequency will be used for computing timestamps
in FlexCAN emulator.
Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com>
Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
Reviewed-by: Bernhard Beschow <shentey@gmail.com>
Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz>
Message-id: 95da33074f8b79842566d61e4e04448b1e622080.1782140438.git.matyas.bobek@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/misc/imx6_ccm.c | 24 ++++++++++++++++++++++++
hw/misc/trace-events | 2 ++
include/hw/misc/imx6_ccm.h | 4 ++++
include/hw/misc/imx_ccm.h | 1 +
4 files changed, 31 insertions(+)
diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c
index a10b67d396..45fdd0d5a8 100644
--- a/hw/misc/imx6_ccm.c
+++ b/hw/misc/imx6_ccm.c
@@ -257,6 +257,15 @@ static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev)
return freq;
}
+static uint64_t imx6_analog_get_pll3_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 480000000;
+
+ trace_imx6_analog_get_pll3_clk(freq);
+
+ return freq;
+}
+
static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev)
{
uint64_t freq = 0;
@@ -344,6 +353,18 @@ static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev)
return freq;
}
+static uint64_t imx6_ccm_get_can_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_pll3_clk(dev) / 8;
+ freq /= (1 + EXTRACT(dev->ccm[CCM_CSCMR2], CAN_CLK_PODF));
+
+ trace_imx6_ccm_get_can_clk(freq);
+
+ return freq;
+}
+
static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
{
uint32_t freq = 0;
@@ -358,6 +379,9 @@ static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
case CLK_IPG_HIGH:
freq = imx6_ccm_get_per_clk(s);
break;
+ case CLK_CAN:
+ freq = imx6_ccm_get_can_clk(s);
+ break;
case CLK_32k:
freq = CKIL_FREQ;
break;
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index b88accc437..99910fc068 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -242,11 +242,13 @@ imx6_analog_get_periph_clk(uint32_t freq) "freq = %u Hz"
imx6_analog_get_pll2_clk(uint32_t freq) "freq = %u Hz"
imx6_analog_get_pll2_pfd0_clk(uint32_t freq) "freq = %u Hz"
imx6_analog_get_pll2_pfd2_clk(uint32_t freq) "freq = %u Hz"
+imx6_analog_get_pll3_clk(uint32_t freq) "freq = %u Hz"
imx6_analog_read(const char *reg, uint32_t value) "reg[%s] => 0x%" PRIx32
imx6_analog_write(const char *reg, uint32_t value) "reg[%s] <= 0x%" PRIx32
imx6_ccm_get_ahb_clk(uint32_t freq) "freq = %u Hz"
imx6_ccm_get_ipg_clk(uint32_t freq) "freq = %u Hz"
imx6_ccm_get_per_clk(uint32_t freq) "freq = %u Hz"
+imx6_ccm_get_can_clk(uint32_t freq) "freq = %u Hz"
imx6_ccm_get_clock_frequency(unsigned clock, uint32_t freq) "(Clock = %d) = %u"
imx6_ccm_read(const char *reg, uint32_t value) "reg[%s] => 0x%" PRIx32
imx6_ccm_reset(void) ""
diff --git a/include/hw/misc/imx6_ccm.h b/include/hw/misc/imx6_ccm.h
index ccf46d7353..a54b940686 100644
--- a/include/hw/misc/imx6_ccm.h
+++ b/include/hw/misc/imx6_ccm.h
@@ -164,6 +164,10 @@
#define PERCLK_PODF_SHIFT (0)
#define PERCLK_PODF_LENGTH (6)
+/* CCM_CSCMR2 */
+#define CAN_CLK_PODF_SHIFT (2)
+#define CAN_CLK_PODF_LENGTH (6)
+
/* CCM_ANALOG_PFD_528 */
#define PFD0_FRAC_SHIFT (0)
#define PFD0_FRAC_LENGTH (6)
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index c4212d04ea..6839716ea3 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -46,6 +46,7 @@ typedef enum {
CLK_EXT,
CLK_HIGH_DIV,
CLK_HIGH,
+ CLK_CAN,
} IMXClk;
struct IMXCCMClass {
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 34/54] hw/net/can/flexcan: NXP FlexCAN core emulation
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (32 preceding siblings ...)
2026-06-29 12:00 ` [PULL 33/54] hw/misc/imx6_ccm: Add PLL3 and CAN clock Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-07-02 12:37 ` Philippe Mathieu-Daudé
2026-07-03 11:34 ` Peter Maydell
2026-06-29 12:00 ` [PULL 35/54] hw/arm: Plug FlexCAN into FSL_IMX6 and Sabrelite Peter Maydell
` (20 subsequent siblings)
54 siblings, 2 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Matyáš Bobek <matyas.bobek@gmail.com>
Added the FlexCAN2 emulator implementation core, with
CAN_FLEXCAN Kconfig flag and MAINTAINERS entry.
FlexCAN2 version can be found in i.MX6 SoCs and others.
More information about the implementation can be found in [1].
Some macro and struct defintions were borrowed from the Linux kernel.
The original authors agreed with relicensing them to GPL-2.0-or-later on
the qemu-devel mailing list.
[1] http://dspace.cvut.cz/bitstream/handle/10467/122654/F3-BP-2025-Bobek-Matyas-BP_Bobek_FlexCAN_final_4.pdf
Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com>
Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
Reviewed-by: Bernhard Beschow <shentey@gmail.com>
Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz>
Message-id: 03dc62ff8013bb946aab8f64e51638b810629529.1782140438.git.matyas.bobek@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
MAINTAINERS | 8 +
hw/net/Kconfig | 5 +
hw/net/can/flexcan.c | 1396 +++++++++++++++++++++++++++++++++++++
hw/net/can/flexcan_regs.h | 197 ++++++
hw/net/can/meson.build | 1 +
hw/net/can/trace-events | 18 +
include/hw/net/flexcan.h | 145 ++++
7 files changed, 1770 insertions(+)
create mode 100644 hw/net/can/flexcan.c
create mode 100644 hw/net/can/flexcan_regs.h
create mode 100644 include/hw/net/flexcan.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 72357df7c4..91317818bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2107,6 +2107,14 @@ F: hw/net/can/xlnx-*
F: include/hw/net/xlnx-*
F: tests/qtest/xlnx-can*-test*
+FlexCAN
+M: Matyas Bobek <matyas.bobek@gmail.com>
+M: Pavel Pisa <pisa@cmp.felk.cvut.cz>
+S: Maintained
+F: hw/net/can/flexcan.c
+F: hw/net/can/flexcan_regs.h
+F: include/hw/net/flexcan.h
+
EDU
M: Jiri Slaby <jslaby@suse.cz>
S: Maintained
diff --git a/hw/net/Kconfig b/hw/net/Kconfig
index f9a1dfb80d..b56a173eed 100644
--- a/hw/net/Kconfig
+++ b/hw/net/Kconfig
@@ -157,3 +157,8 @@ config CAN_CTUCANFD_PCI
default y if PCI_DEVICES
depends on PCI && CAN_CTUCANFD
select CAN_BUS
+
+config CAN_FLEXCAN
+ bool
+ depends on IMX
+ select CAN_BUS
diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c
new file mode 100644
index 0000000000..1ea459d9f6
--- /dev/null
+++ b/hw/net/can/flexcan.c
@@ -0,0 +1,1396 @@
+/*
+ * QEMU model of the NXP FLEXCAN device.
+ *
+ * This implementation is based on the following reference manual:
+ * i.MX 6Dual/6Quad Applications Processor Reference Manual
+ * Document Number: IMX6DQRM, Rev. 6, 05/2020
+ *
+ * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
+ *
+ * Based on CTU CAN FD emulation implemented by Jan Charvat.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/core/sysbus.h"
+#include "qapi/error.h"
+#include "hw/core/irq.h"
+#include "migration/vmstate.h"
+#include "net/can_emu.h"
+#include "hw/core/qdev-properties.h"
+#include "trace.h"
+
+#include "hw/net/flexcan.h"
+#include "flexcan_regs.h"
+#include "qemu/timer.h"
+
+/*
+ * Indicates MB w/ received frame has not been serviced yet
+ * This is an emulator-only flag in position of unused (reserved) bit
+ * of message buffer control register
+ */
+#define FLEXCAN_MB_CNT_NOT_SRV BIT(23)
+/**
+ * if no MB is locked, FlexcanState.locked_mb
+ * is set to FLEXCAN_NO_MB_LOCKED
+ */
+#define FLEXCAN_NO_MB_LOCKED -1
+/**
+ * if no frame is waiting in the SMB, FlexcanState.smb_target_mbid
+ * is set to FLEXCAN_SMB_EMPTY
+ */
+#define FLEXCAN_SMB_EMPTY -1
+/**
+ * When the module is disabled or in freeze mode,
+ * the timer is not running. That is indicated by setting
+ * FlexcanState.timer_start to FLEXCAN_TIMER_STOPPED.
+ */
+#define FLEXCAN_TIMER_STOPPED -1
+
+/* These constants are returned by flexcan_fifo_rx() and flexcan_mb_rx(), */
+enum FlexcanRx {
+/* Retry the other receiving mechanism (ie. message bufer or mailbox). */
+ FLEXCAN_RX_SEARCH_RETRY,
+/* The frame was received and stored. */
+ FLEXCAN_RX_SEARCH_ACCEPT,
+/* The frame was filtered out and dropped. */
+ FLEXCAN_RX_SEARCH_DROPPED,
+};
+
+/*
+ * These constants are returned by flexcan_mb_rx_check_mb().
+ * See flexcan_mb_rx_check_mb() kerneldoc for details.
+ */
+enum FlexcanCheck {
+ FLEXCAN_CHECK_MB_NIL = 0,
+ FLEXCAN_CHECK_MB_MATCH = 3,
+ FLEXCAN_CHECK_MB_MATCH_NON_FREE = 1,
+ FLEXCAN_CHECK_MB_MATCH_LOCKED = 5,
+};
+
+static const FlexcanRegs flexcan_regs_write_mask = {
+ .mcr = 0xF6EB337F,
+ .ctrl = 0xFFFFFFFF,
+ .timer = 0xFFFFFFFF,
+ .tcr = 0xFFFFFFFF,
+ .rxmgmask = 0xFFFFFFFF,
+ .rx14mask = 0xFFFFFFFF,
+ .rx15mask = 0xFFFFFFFF,
+ .ecr = 0xFFFFFFFF,
+ .esr = 0xFFFFFFFF,
+ .imask2 = 0xFFFFFFFF,
+ .imask1 = 0xFFFFFFFF,
+ .iflag2 = 0,
+ .iflag1 = 0,
+ .ctrl2 = 0xFFFFFFFF,
+ .esr2 = 0,
+ .imeur = 0,
+ .lrfr = 0,
+ .crcr = 0,
+ .rxfgmask = 0xFFFFFFFF,
+ .rxfir = 0,
+ .cbt = 0,
+ ._reserved2 = 0,
+ .dbg1 = 0,
+ .dbg2 = 0,
+ .mbs = { [0 ... 63] = {
+ .can_ctrl = 0xFFFFFFFF & ~FLEXCAN_MB_CNT_NOT_SRV,
+ .can_id = 0xFFFFFFFF,
+ .data = { 0xFFFFFFFF, 0xFFFFFFFF },
+ } },
+ ._reserved4 = {0},
+ .rximr = { [0 ... 63] = 0xFFFFFFFF },
+ ._reserved5 = {0},
+ .gfwr_mx6 = 0xFFFFFFFF,
+ ._reserved6 = {0},
+ ._reserved8 = {0},
+ .rx_smb0_raw = {0, 0, 0, 0},
+ .rx_smb1 = {0, 0, 0, 0},
+};
+static const FlexcanRegs flexcan_regs_reset_mask = {
+ .mcr = 0x80000000,
+ .ctrl = 0xFFFFFFFF,
+ .timer = 0,
+ .tcr = 0,
+ .rxmgmask = 0xFFFFFFFF,
+ .rx14mask = 0xFFFFFFFF,
+ .rx15mask = 0xFFFFFFFF,
+ .ecr = 0,
+ .esr = 0,
+ .imask2 = 0,
+ .imask1 = 0,
+ .iflag2 = 0,
+ .iflag1 = 0,
+ .ctrl2 = 0xFFFFFFFF,
+ .esr2 = 0,
+ .imeur = 0,
+ .lrfr = 0,
+ .crcr = 0,
+ .rxfgmask = 0xFFFFFFFF,
+ .rxfir = 0xFFFFFFFF,
+ .cbt = 0,
+ ._reserved2 = 0,
+ .dbg1 = 0,
+ .dbg2 = 0,
+ .mb = {0xFFFFFFFF},
+ ._reserved4 = {0},
+ .rximr = {0xFFFFFFFF},
+ ._reserved5 = {0},
+ .gfwr_mx6 = 0,
+ ._reserved6 = {0},
+ ._reserved8 = {0},
+ .rx_smb0_raw = {0, 0, 0, 0},
+ .rx_smb1 = {0, 0, 0, 0},
+};
+
+/* length of buffer used to format register names in trace output */
+#define FLEXCAN_DBG_BUF_LEN 16
+
+/**
+ * flexcan_dbg_mb_code_strs - Readable names for CODE field codes
+ *
+ * Readable names for possible values of CODE field in message buffer
+ * control word.
+ */
+static const char *flexcan_dbg_mb_code_strs[16] = {
+ "INACTIVE_RX",
+ "FULL",
+ "EMPTY",
+ "OVERRUN",
+ "INACTIVE_TX",
+ "RANSWER",
+ "DATA",
+ "TANSWER"
+};
+
+/**
+ * flexcan_dbg_mb_code() - Get the string representation of a mailbox code
+ * @mb_ctrl: The mailbox control register value
+ * @buf: The buffer to store the string representation
+ *
+ * Return: Either constant string or string formatted into @buf
+ */
+static const char *flexcan_dbg_mb_code(uint32_t mb_ctrl, char *buf)
+{
+ uint32_t code = mb_ctrl & FLEXCAN_MB_CODE_MASK;
+ uint32_t code_idx = code >> 24;
+ if (code == FLEXCAN_MB_CODE_TX_ABORT) {
+ return "ABORT";
+ } else {
+ const char *code_str = flexcan_dbg_mb_code_strs[code_idx >> 1];
+ if (code_idx & 1) {
+ g_snprintf(buf, FLEXCAN_DBG_BUF_LEN, "%s+BUSY", code_str);
+ return buf;
+ }
+
+ return code_str;
+ }
+}
+
+static const char *flexcan_dbg_reg_name_fixed(hwaddr addr)
+{
+ switch (addr) {
+ case offsetof(FlexcanRegs, mcr):
+ return "MCR";
+ case offsetof(FlexcanRegs, ctrl):
+ return "CTRL";
+ case offsetof(FlexcanRegs, timer):
+ return "TIMER";
+ case offsetof(FlexcanRegs, esr):
+ return "ESR";
+ case offsetof(FlexcanRegs, rxmgmask):
+ return "RXMGMASK";
+ case offsetof(FlexcanRegs, rx14mask):
+ return "RX14MASK";
+ case offsetof(FlexcanRegs, rx15mask):
+ return "RX15MASK";
+ case offsetof(FlexcanRegs, rxfgmask):
+ return "RXFGMASK";
+ case offsetof(FlexcanRegs, ecr):
+ return "ECR";
+ case offsetof(FlexcanRegs, ctrl2):
+ return "CTRL2";
+ case offsetof(FlexcanRegs, imask2):
+ return "IMASK2";
+ case offsetof(FlexcanRegs, imask1):
+ return "IMASK1";
+ case offsetof(FlexcanRegs, iflag2):
+ return "IFLAG2";
+ case offsetof(FlexcanRegs, iflag1):
+ return "IFLAG1";
+ }
+ return NULL;
+}
+
+static inline void flexcan_trace_mem_op(FlexcanState *s, hwaddr addr,
+ uint32_t value, int size, bool is_wr)
+{
+ if (trace_event_get_state_backends(TRACE_FLEXCAN_MEM_OP)) {
+ const char *reg_name = "unknown";
+ char reg_name_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };
+ const char *reg_name_fixed = flexcan_dbg_reg_name_fixed(addr);
+ const char *op_string = is_wr ? "write" : "read";
+
+ if (reg_name_fixed) {
+ reg_name = reg_name_fixed;
+ } else if (addr >= 0x80 && addr < 0x480) {
+ int mbidx = (addr - 0x80) / 16;
+ g_snprintf(reg_name_buf, sizeof(reg_name_buf), "MB%i", mbidx);
+ reg_name = reg_name_buf;
+ } else if (addr >= 0x880 && addr < 0x9e0) {
+ int id = (addr - 0x880) / 4;
+ g_snprintf(reg_name_buf, sizeof(reg_name_buf), "RXIMR%i", id);
+ reg_name = reg_name_buf;
+ }
+
+ trace_flexcan_mem_op(DEVICE(s)->canonical_path, op_string, value, addr,
+ reg_name, size);
+ }
+}
+
+static enum FlexcanRx flexcan_mb_rx(FlexcanState *s,
+ const qemu_can_frame *frame);
+static void flexcan_mb_unlock(FlexcanState *s);
+
+/* ========== Mailbox Utils ========== */
+
+/**
+ * flexcan_mailbox_count() - Get number of enabled mailboxes
+ * @s: FlexCAN device pointer
+ *
+ * Count is based on MCR[MAXMB] field. Note that some of those mailboxes
+ * might be part of queue or queue ID filters or ordinary message buffers.
+ */
+static inline int flexcan_enabled_mailbox_count(const FlexcanState *s)
+{
+ return MIN((s->regs.mcr & FLEXCAN_MCR_MAXMB(UINT32_MAX)) + 1,
+ FLEXCAN_MAILBOX_COUNT);
+}
+
+/**
+ * flexcan_get_first_message_buffer() - Get pointer to first message buffer
+ * @s: FlexCAN device pointer
+ *
+ * In context of this function, message buffer means a mailbox which is not
+ * a queue element nor a queue filter. Note this function does not take
+ * MCR[MAXMB] into account, meaning that the returned mailbox
+ * might be disabled.
+ */
+static FlexcanRegsMessageBuffer *flexcan_get_first_message_buffer(
+ FlexcanState *s)
+{
+ if (s->regs.mcr & FLEXCAN_MCR_FEN) {
+ int rffn = (s->regs.ctrl2 & FLEXCAN_CTRL2_RFFN(UINT32_MAX)) >> 24;
+ return s->regs.mbs + 8 + 2 * rffn;
+ }
+
+ return s->regs.mbs;
+}
+
+/**
+ * flexcan_get_last_enabled_mailbox() - Get pointer to last enabled mailbox.
+ * @s: FlexCAN device pointer
+ *
+ * When used with flexcan_get_first_message_buffer(), all mailboxes *ptr in
+ * range `first_message_buffer() <= ptr <= last_enabled_mailbox` are valid
+ * message buffer mailboxes.
+ *
+ * Return: Last enabled mailbox in MCR[MAXMB] sense. The mailbox might be
+ * of any type.
+ */
+static inline FlexcanRegsMessageBuffer *flexcan_get_last_enabled_mailbox(
+ FlexcanState *s)
+{
+ return s->regs.mbs + flexcan_enabled_mailbox_count(s);
+}
+
+/* ========== Free-running Timer ========== */
+static inline int64_t flexcan_get_time(void)
+{
+ return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+/**
+ * flexcan_get_bitrate() - Calculate CAN bitrate (in Hz)
+ * @s: FlexCAN device pointer
+ *
+ * The bitrate is determined by FlexCAN configuration in CTRL1 register,
+ * and CCM co
+ */
+static uint32_t flexcan_get_bitrate(FlexcanState *s)
+{
+ uint32_t conf_presdiv = (s->regs.ctrl & FLEXCAN_CTRL_PRESDIV_MASK) >> 24;
+ uint32_t conf_pseg1 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG1_MASK) >> 19;
+ uint32_t conf_pseg2 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG2_MASK) >> 16;
+ uint32_t conf_propseg = s->regs.ctrl & FLEXCAN_CTRL_PROPSEG_MASK;
+
+ /* N of time quanta for segments */
+ uint32_t tseg1 = 2 + conf_pseg1 + conf_propseg;
+ uint32_t tseg2 = 1 + conf_pseg2;
+ uint32_t total_qpb = 1 + tseg1 + tseg2;
+
+ uint32_t pe_freq, s_freq, bitrate;
+
+ assert(s->ccm);
+
+ /* s_freq: CAN clock from CCM divided by the prescaler */
+ pe_freq = imx_ccm_get_clock_frequency(s->ccm, CLK_CAN);
+ s_freq = pe_freq / (1 + conf_presdiv);
+ bitrate = s_freq / total_qpb;
+
+ trace_flexcan_get_bitrate(DEVICE(s)->canonical_path, pe_freq,
+ 1 + conf_presdiv, s_freq, tseg1, tseg2, total_qpb,
+ bitrate);
+ return bitrate;
+}
+
+/**
+ * int128_mul_6464() - Multiply two 64-bit integers into a 128-bit one
+ */
+static Int128 int128_muls_6464(int64_t ai, int64_t bi)
+{
+ uint64_t l, h;
+
+ muls64(&l, &h, ai, bi);
+ return int128_make128(l, h);
+}
+
+/**
+ * flexcan_get_timestamp() - Get current value of the 16-bit free-running timer
+ * @s: FlexCAN device pointer
+ * @mk_unique: if true, make the timestamp unique by incrementing it if needed
+ */
+static uint32_t flexcan_get_timestamp(FlexcanState *s, bool mk_unique)
+{
+ const Int128 nanoseconds_in_second = int128_makes64((int64_t)1e9);
+ Int128 ncycles, cycles128;
+ int64_t current_time, elapsed_time_ns;
+ uint64_t cycles;
+ uint32_t rv, shift = 0;
+
+ if (s->timer_start == FLEXCAN_TIMER_STOPPED) {
+ /* timer is not running, return last value */
+ trace_flexcan_get_timestamp(DEVICE(s)->canonical_path, -1, 0, 0, 0,
+ s->regs.timer);
+ return s->regs.timer;
+ }
+
+ current_time = flexcan_get_time();
+ elapsed_time_ns = current_time - s->timer_start;
+ if (elapsed_time_ns < 0) {
+ trace_flexcan_timer_overflow(DEVICE(s)->canonical_path, current_time,
+ s->timer_start, elapsed_time_ns);
+ return 0xFFFF;
+ }
+
+ ncycles = int128_muls_6464(s->timer_freq, elapsed_time_ns);
+ cycles128 = int128_divs(ncycles, nanoseconds_in_second);
+ /* 64 bits hold for over 50k years at 10MHz */
+ cycles = int128_getlo(cycles128);
+
+ if (mk_unique && cycles <= s->last_rx_timer_cycles) {
+ shift = 1;
+ cycles = s->last_rx_timer_cycles + shift;
+ }
+
+ s->last_rx_timer_cycles = cycles;
+ rv = (uint32_t)cycles & 0xFFFF;
+
+ trace_flexcan_get_timestamp(DEVICE(s)->canonical_path,
+ elapsed_time_ns / (uint32_t)1e6,
+ s->timer_freq, cycles, shift, rv);
+ return rv;
+}
+
+/**
+ * flexcan_timer_start() - Start the free-running timer
+ * @s: FlexCAN device pointer
+ *
+ * This should be called when the module leaves freeze mode.
+ */
+static void flexcan_timer_start(FlexcanState *s)
+{
+ s->timer_freq = flexcan_get_bitrate(s);
+ s->timer_start = flexcan_get_time();
+ s->last_rx_timer_cycles = 0;
+
+ trace_flexcan_timer_start(DEVICE(s)->canonical_path, s->timer_freq,
+ s->regs.timer);
+}
+
+/**
+ * flexcan_timer_stop() - Stop the free-running timer
+ * @s: FlexCAN device pointer
+ *
+ * This should be called when the module enters freeze mode.
+ * Stores the current timestamp in the TIMER register.
+ */
+static void flexcan_timer_stop(FlexcanState *s)
+{
+ s->regs.timer = flexcan_get_timestamp(s, false);
+ s->timer_start = FLEXCAN_TIMER_STOPPED;
+
+ trace_flexcan_timer_stop(DEVICE(s)->canonical_path, s->timer_freq,
+ s->regs.timer);
+}
+
+/* ========== IRQ handling ========== */
+/**
+ * flexcan_irq_update() - Update qemu_irq line based on interrupt registers
+ * @s: FlexCAN device pointer
+ */
+static void flexcan_irq_update(FlexcanState *s)
+{
+ uint32_t mb_irqs[2];
+ int irq_pending;
+ /* these are all interrupt sources from FlexCAN */
+ /* mailbox interrupt sources */
+ mb_irqs[0] = s->regs.iflag1 & s->regs.imask1;
+ mb_irqs[1] = s->regs.iflag2 & s->regs.imask2;
+
+ /**
+ * these interrupts aren't currently used and they can never be raised
+ *
+ * bool irq_wake_up = (s->regs.mcr & FLEXCAN_MCR_WAK_MSK) &&
+ * (s->regs.ecr & FLEXCAN_ESR_WAK_INT);
+ * bool irq_bus_off = (s->regs.ctrl & FLEXCAN_CTRL_BOFF_MSK) &&
+ * (s->regs.ecr & FLEXCAN_ESR_BOFF_INT);
+ * bool irq_error = (s->regs.ctrl & FLEXCAN_CTRL_ERR_MSK) &&
+ * (s->regs.ecr & FLEXCAN_ESR_ERR_INT);
+ * bool irq_tx_warn = (s->regs.ctrl & FLEXCAN_CTRL_TWRN_MSK) &&
+ * (s->regs.ecr & FLEXCAN_ESR_TWRN_INT);
+ * bool irq_rx_warn = (s->regs.ctrl & FLEXCAN_CTRL_RWRN_MSK) &&
+ * (s->regs.ecr & FLEXCAN_ESR_RWRN_INT);
+ */
+
+ irq_pending = (mb_irqs[0] || mb_irqs[1]) ? 1 : 0;
+ trace_flexcan_irq_update(DEVICE(s)->canonical_path, mb_irqs[0], mb_irqs[1],
+ irq_pending);
+
+ qemu_set_irq(s->irq, irq_pending);
+}
+
+/**
+ * flexcan_irq_iflag_set() - Set IFLAG bit corresponding to MB mbidx
+ * @s: FlexCAN device pointer
+ * @mbidx: mailbox index
+ */
+static void flexcan_irq_iflag_set(FlexcanState *s, int mbidx)
+{
+ if (mbidx < 32) {
+ s->regs.iflag1 |= BIT(mbidx);
+ } else {
+ s->regs.iflag2 |= BIT(mbidx - 32);
+ }
+}
+
+/**
+ * flexcan_irq_iflag_clear() - Clear IFLAG bit corresponding to MB mbidx
+ * @s: FlexCAN device pointer
+ * @mbidx: mailbox index
+ */
+static void flexcan_irq_iflag_clear(FlexcanState *s, int mbidx)
+{
+ if (mbidx < 32) {
+ s->regs.iflag1 &= ~BIT(mbidx);
+ } else {
+ s->regs.iflag2 &= ~BIT(mbidx - 32);
+ }
+}
+
+/* ========== RESET ========== */
+static void flexcan_reset_local_state(FlexcanState *s)
+{
+ uint32_t *reset_mask = (uint32_t *)&flexcan_regs_reset_mask;
+ for (int i = 0; i < (sizeof(FlexcanRegs) / 4); i++) {
+ s->regs_raw[i] &= reset_mask[i];
+ }
+
+ s->regs.mcr |= 0x5980000F;
+ s->locked_mbidx = FLEXCAN_NO_MB_LOCKED;
+ s->smb_target_mbidx = FLEXCAN_SMB_EMPTY;
+ s->timer_start = FLEXCAN_TIMER_STOPPED;
+
+ trace_flexcan_reset(DEVICE(s)->canonical_path);
+}
+
+static void flexcan_reset_enter(Object *obj, ResetType type)
+{
+ FlexcanState *s = CAN_FLEXCAN(obj);
+
+ memset(&s->regs, 0, sizeof(s->regs));
+ flexcan_reset_local_state(s);
+}
+
+static void flexcan_reset_hold(Object *obj, ResetType type)
+{
+ FlexcanState *s = CAN_FLEXCAN(obj);
+
+ flexcan_irq_update(s);
+}
+
+
+/* ========== Operation mode control ========== */
+/**
+ * flexcan_update_esr() - Update ESR based on mode and CAN bus connection state
+ * @s: FlexCAN device pointer
+ */
+static void flexcan_update_esr(FlexcanState *s)
+{
+ bool is_running = (s->regs.mcr & FLEXCAN_MCR_NOT_RDY) == 0;
+ /* potentially, there could be other influences on ESR[SYNCH] */
+
+ if (is_running && s->canbus) {
+ s->regs.esr |= FLEXCAN_ESR_SYNCH | FLEXCAN_ESR_IDLE;
+ } else {
+ s->regs.esr &= ~(FLEXCAN_ESR_SYNCH | FLEXCAN_ESR_IDLE);
+ }
+}
+
+/**
+ * flexcan_update_esr() - Process MCR write
+ * @s: FlexCAN device pointer
+ * @pv: previously set MCR value
+ *
+ * This function expects the new MCR value to be already written in s->regs.mcr.
+ */
+static void flexcan_set_mcr(FlexcanState *s, const uint32_t pv)
+{
+ uint32_t cv = s->regs.mcr;
+
+ /* -- module disable mode -- */
+ if (!(pv & FLEXCAN_MCR_MDIS) && (cv & FLEXCAN_MCR_MDIS)) {
+ /* transition to Module Disable mode */
+ cv |= FLEXCAN_MCR_LPM_ACK;
+ } else if ((pv & FLEXCAN_MCR_MDIS) && !(cv & FLEXCAN_MCR_MDIS)) {
+ /* transition from Module Disable mode */
+ cv &= ~FLEXCAN_MCR_LPM_ACK;
+ }
+
+ /* -- soft reset -- */
+ if (!(cv & FLEXCAN_MCR_LPM_ACK) && (cv & FLEXCAN_MCR_SOFTRST)) {
+ if (s->regs.mcr & FLEXCAN_MCR_LPM_ACK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid soft reset request in low-power mode",
+ DEVICE(s)->canonical_path);
+ }
+
+ flexcan_reset_local_state(s);
+ cv = s->regs.mcr;
+ }
+
+ /* -- freeze mode -- */
+ if (!(cv & FLEXCAN_MCR_LPM_ACK) &&
+ (cv & FLEXCAN_MCR_FRZ) &&
+ (cv & FLEXCAN_MCR_HALT)) {
+ cv |= FLEXCAN_MCR_FRZ_ACK;
+ } else {
+ cv &= ~FLEXCAN_MCR_FRZ_ACK;
+ }
+
+ /* -- fifo mode -- */
+ if (
+ ((pv & FLEXCAN_MCR_FEN) && !(cv & FLEXCAN_MCR_FEN)) ||
+ (!(pv & FLEXCAN_MCR_FEN) && (cv & FLEXCAN_MCR_FEN))
+ ) {
+ /* clear iflags used by fifo */
+ s->regs.iflag1 &= ~(
+ FLEXCAN_IFLAG_RX_FIFO_AVAILABLE |
+ FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
+ FLEXCAN_IFLAG_RX_FIFO_WARN
+ );
+ }
+ if (!(pv & FLEXCAN_MCR_FEN) && (cv & FLEXCAN_MCR_FEN)) {
+ /* zero out fifo region, we rely on zeroed can_ctrl for empty slots */
+ memset(s->regs.mbs, 0,
+ FLEXCAN_FIFO_DEPTH * sizeof(FlexcanRegsMessageBuffer));
+ }
+
+ /*
+ * assert NOT_RDY bit if in disable,
+ * stop (not implemented) or freeze mode
+ */
+ if ((cv & FLEXCAN_MCR_LPM_ACK) || (cv & FLEXCAN_MCR_FRZ_ACK)) {
+ cv |= FLEXCAN_MCR_NOT_RDY;
+ } else {
+ cv &= ~FLEXCAN_MCR_NOT_RDY;
+ }
+
+ if ((pv & FLEXCAN_MCR_NOT_RDY) && !(cv & FLEXCAN_MCR_NOT_RDY)) {
+ /* module went up, start the timer */
+ flexcan_timer_start(s);
+ } else if (!(pv & FLEXCAN_MCR_NOT_RDY) && (cv & FLEXCAN_MCR_NOT_RDY)) {
+ /* module went down, store the current timer value */
+ flexcan_timer_stop(s);
+ }
+
+ s->regs.mcr = cv;
+ flexcan_update_esr(s);
+ trace_flexcan_set_mcr(
+ DEVICE(s)->canonical_path,
+ cv & FLEXCAN_MCR_LPM_ACK ? "DISABLED" : "ENABLED",
+ (cv & FLEXCAN_MCR_FRZ_ACK || cv & FLEXCAN_MCR_LPM_ACK) ?
+ "FROZEN" : "RUNNING",
+ cv & FLEXCAN_MCR_FEN ? "FIFO" : "MAILBOX",
+ cv & FLEXCAN_MCR_NOT_RDY ? "NOT_RDY" : "RDY",
+ s->regs.esr & FLEXCAN_ESR_SYNCH ? "SYNC" : "NOSYNC"
+ );
+}
+
+/* ========== TX ========== */
+static void flexcan_transmit(FlexcanState *s, int mbidx)
+{
+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbidx];
+ qemu_can_frame frame = {
+ .flags = 0,
+ };
+ uint32_t *frame_data = (uint32_t *)&frame.data;
+ uint32_t timestamp = flexcan_get_timestamp(s, true);
+
+ if ((s->regs.ctrl & FLEXCAN_CTRL_LOM) ||
+ (s->regs.mcr & FLEXCAN_MCR_NOT_RDY)) {
+ /* no transmiting in listen-only, freeze or low-power mode */
+ return;
+ }
+
+ if (mb->can_ctrl & FLEXCAN_MB_CNT_IDE) {
+ /* 29b ID stored in bits [0, 29) */
+ uint32_t id = mb->can_id & 0x1FFFFFFF;
+ frame.can_id = id | QEMU_CAN_EFF_FLAG;
+ } else {
+ /* 11b ID stored in bits [18, 29) */
+ uint32_t id = (mb->can_id & (0x7FF << 18)) >> 18;
+ frame.can_id = id;
+ }
+
+ frame.can_dlc = (mb->can_ctrl & (0xF << 16)) >> 16;
+
+ for (int i = 0; i < 2; i++) {
+ stl_be_p(&frame_data[i], mb->data[i]);
+ }
+
+ if (!(s->regs.mcr & FLEXCAN_MCR_SRX_DIS)) {
+ /* self-reception */
+ flexcan_mb_rx(s, &frame);
+ }
+ if (!(s->regs.ctrl & FLEXCAN_CTRL_LPB)) {
+ /* send to bus if not in loopback mode */
+ if (s->canbus) {
+ can_bus_client_send(&s->bus_client, &frame, 1);
+ } else {
+ /* todo: raise error (no ack) */
+ }
+ }
+
+ mb->can_ctrl &= ~(FLEXCAN_MB_CODE_MASK | FLEXCAN_MB_CNT_TIMESTAMP_MASK);
+ mb->can_ctrl |= FLEXCAN_MB_CODE_TX_INACTIVE |
+ FLEXCAN_MB_CNT_TIMESTAMP(timestamp);
+
+ /* todo: compute the CRC */
+ s->regs.crcr = FLEXCAN_CRCR_TXCRC(0) | FLEXCAN_CRCR_MBCRC(mbidx);
+
+ flexcan_irq_iflag_set(s, mbidx);
+}
+
+static void flexcan_mb_write(FlexcanState *s, int mbid)
+{
+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbid];
+
+ bool is_mailbox = (mb <= flexcan_get_last_enabled_mailbox(s)) &&
+ (mb >= flexcan_get_first_message_buffer(s));
+
+ if (trace_event_get_state_backends(TRACE_FLEXCAN_MB_WRITE)) {
+ char code_str_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };
+ const char *code_str = flexcan_dbg_mb_code(mb->can_ctrl, code_str_buf);
+ trace_flexcan_mb_write(DEVICE(s)->canonical_path, mbid, code_str,
+ is_mailbox, mb->can_ctrl, mb->can_id);
+ }
+
+ if (!is_mailbox) {
+ /**
+ * Disabled mailbox or mailbox in region of queue filters
+ * was updated. Either way there is nothing to do.
+ */
+ return;
+ }
+
+ /* any write to message buffer clears the not_serviced flag */
+ mb->can_ctrl &= ~FLEXCAN_MB_CNT_NOT_SRV;
+
+ /**
+ * todo: search for active tx mbs on transition from freeze/disable mode
+ */
+ switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
+ case FLEXCAN_MB_CODE_TX_INACTIVE:
+ QEMU_FALLTHROUGH;
+ case FLEXCAN_MB_CODE_RX_INACTIVE:
+ QEMU_FALLTHROUGH;
+ case FLEXCAN_MB_CODE_RX_EMPTY:
+ QEMU_FALLTHROUGH;
+ case FLEXCAN_MB_CODE_RX_FULL:
+ QEMU_FALLTHROUGH;
+ case FLEXCAN_MB_CODE_RX_RANSWER:
+ break;
+
+ case FLEXCAN_MB_CODE_TX_DATA:
+ flexcan_transmit(s, mbid);
+ break;
+ case FLEXCAN_MB_CODE_TX_ABORT:
+ /*
+ * as transmission is instant, it can never be aborted
+ * we need to set CODE in C/S back to the previous code
+ */
+ mb->can_ctrl &= ~FLEXCAN_MB_CODE(1);
+ break;
+ case FLEXCAN_MB_CODE_TX_TANSWER:
+ break;
+ default:
+ /* prevent setting the busy bit */
+ mb->can_ctrl &= ~FLEXCAN_MB_CODE_RX_BUSY_BIT;
+ break;
+ }
+
+}
+
+/* ========== RX ========== */
+static void flexcan_mb_move_in(FlexcanState *s, const qemu_can_frame *frame,
+ FlexcanRegsMessageBuffer *target_mb)
+{
+ uint32_t frame_len = frame->can_dlc;
+ uint32_t *frame_data = (uint32_t *)&frame->data;
+ int timestamp = flexcan_get_timestamp(s, true);
+ uint32_t new_code = 0;
+
+ memset(target_mb, 0, sizeof(FlexcanRegsMessageBuffer));
+
+ if (frame_len > 8) {
+ frame_len = 8;
+ }
+ for (int i = 0; i < 2; i++) {
+ target_mb->data[i] = ldl_be_p(&frame_data[i]);
+ }
+
+ switch (target_mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
+ case FLEXCAN_MB_CODE_RX_FULL:
+ case FLEXCAN_MB_CODE_RX_OVERRUN:
+ if (target_mb->can_ctrl & FLEXCAN_MB_CNT_NOT_SRV) {
+ new_code = FLEXCAN_MB_CODE_RX_OVERRUN;
+ } else {
+ new_code = FLEXCAN_MB_CODE_RX_FULL;
+ }
+ break;
+ case FLEXCAN_MB_CODE_RX_RANSWER:
+ assert(s->regs.ctrl2 & FLEXCAN_CTRL2_RRS);
+ new_code = FLEXCAN_MB_CODE_TX_TANSWER;
+ break;
+ default:
+ new_code = FLEXCAN_MB_CODE_RX_FULL;
+ }
+
+ target_mb->can_ctrl = new_code
+ | FLEXCAN_MB_CNT_TIMESTAMP(timestamp)
+ | FLEXCAN_MB_CNT_LENGTH(frame_len)
+ | FLEXCAN_MB_CNT_NOT_SRV
+ | FLEXCAN_MB_CNT_SRR; /* always set for received frames */
+ if (frame->can_id & QEMU_CAN_RTR_FLAG) {
+ target_mb->can_ctrl |= FLEXCAN_MB_CNT_RTR;
+ }
+
+ if (frame->can_id & QEMU_CAN_EFF_FLAG) {
+ target_mb->can_ctrl |= FLEXCAN_MB_CNT_IDE;
+ target_mb->can_id |= frame->can_id & QEMU_CAN_EFF_MASK;
+ } else {
+ target_mb->can_id |= (frame->can_id & QEMU_CAN_SFF_MASK) << 18;
+ }
+}
+static void flexcan_mb_lock(FlexcanState *s, int mbidx)
+{
+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbidx];
+ if ((mb > flexcan_get_last_enabled_mailbox(s)) ||
+ (mb < flexcan_get_first_message_buffer(s))) {
+ return;
+ }
+ switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
+ case FLEXCAN_MB_CODE_RX_FULL:
+ QEMU_FALLTHROUGH;
+ case FLEXCAN_MB_CODE_RX_OVERRUN:
+ QEMU_FALLTHROUGH;
+ case FLEXCAN_MB_CODE_RX_RANSWER:
+ /* continue */
+ trace_flexcan_mb_lock(DEVICE(s)->canonical_path, mbidx, 1);
+ break;
+ default:
+ trace_flexcan_mb_lock(DEVICE(s)->canonical_path, mbidx, 0);
+ return;
+ }
+
+ s->locked_mbidx = mbidx;
+}
+
+static void flexcan_mb_unlock(FlexcanState *s)
+{
+ int locked_mbidx = s->locked_mbidx;
+ bool has_pending_frame = locked_mbidx == s->smb_target_mbidx;
+
+ if (s->locked_mbidx == FLEXCAN_NO_MB_LOCKED) {
+ return;
+ }
+
+ assert(locked_mbidx >= 0 && locked_mbidx < FLEXCAN_MAILBOX_COUNT);
+ FlexcanRegsMessageBuffer *locked_mb = &s->regs.mbs[locked_mbidx];
+ s->locked_mbidx = FLEXCAN_NO_MB_LOCKED;
+
+ if (locked_mb >= flexcan_get_first_message_buffer(s) &&
+ locked_mb <= flexcan_get_last_enabled_mailbox(s)
+ ) {
+ /* mark the message buffer as serviced */
+ locked_mb->can_ctrl &= ~FLEXCAN_MB_CNT_NOT_SRV;
+ }
+
+ /* try move in from SMB */
+ trace_flexcan_mb_unlock(DEVICE(s)->canonical_path, locked_mbidx,
+ has_pending_frame ? " PENDING FRAME IN SMB" : "");
+
+ /* todo: in low-power modes, this should be postponed until exit */
+ if (has_pending_frame) {
+ FlexcanRegsMessageBuffer *target_mb = &s->regs.mbs[locked_mbidx];
+ memcpy(target_mb, &s->regs.rx_smb0, sizeof(FlexcanRegsMessageBuffer));
+
+ memset(&s->regs.rx_smb0, 0, sizeof(FlexcanRegsMessageBuffer));
+ s->locked_mbidx = FLEXCAN_SMB_EMPTY;
+
+ flexcan_irq_iflag_set(s, locked_mbidx);
+ }
+}
+
+static bool flexcan_can_receive(CanBusClientState *client)
+{
+ FlexcanState *s = container_of(client, FlexcanState, bus_client);
+ return !(s->regs.mcr & FLEXCAN_MCR_NOT_RDY);
+}
+
+/* --------- RX FIFO ---------- */
+
+/**
+ * flexcan_fifo_pop() - Pop message from FIFO and update IRQs
+ * @s: FlexCAN device pointer
+ *
+ * Does not require the queue to be non-empty.
+ */
+static void flexcan_fifo_pop(FlexcanState *s)
+{
+ if (s->regs.fifo.mb_back.can_ctrl != 0) {
+ /* move queue elements forward */
+ memmove(&s->regs.fifo.mb_back, &s->regs.fifo.mbs_queue[0],
+ sizeof(s->regs.fifo.mbs_queue));
+
+ /* clear the first-in slot */
+ memset(&s->regs.mbs[FLEXCAN_FIFO_DEPTH - 1], 0,
+ sizeof(FlexcanRegsMessageBuffer));
+
+ trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 1,
+ s->regs.fifo.mb_back.can_ctrl != 0);
+ } else {
+ trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 0, 0);
+ }
+
+ if (s->regs.fifo.mb_back.can_ctrl != 0) {
+ flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE);
+ } else {
+ flexcan_irq_iflag_clear(s, I_FIFO_AVAILABLE);
+ }
+}
+
+/**
+ * flexcan_fifo_find_free_slot() - Find the first free slot in the FIFO
+ * @s: FlexCAN device pointer
+ *
+ * Return: Pointer to the first free slot in the FIFO,
+ * or NULL if the queue is full.
+ */
+static FlexcanRegsMessageBuffer *flexcan_fifo_find_free_slot(FlexcanState *s)
+{
+ for (int i = 0; i < FLEXCAN_FIFO_DEPTH; i++) {
+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[i];
+ if (mb->can_ctrl == 0) {
+ return mb;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * flexcan_fifo_push() - Update FIFO IRQs after frame move-in
+ * @s: FlexCAN device pointer
+ * @slot: Target FIFO slot
+ *
+ * The usage is as follows:
+ * 1. Get free slot pointer using flexcan_fifo_find_free_slot()
+ * 2. Move the frame in if not NULL
+ * 3. Call flexcan_fifo_push() regardless of the NULL pointer
+ */
+static void flexcan_fifo_push(FlexcanState *s, FlexcanRegsMessageBuffer *slot)
+{
+ if (slot) {
+ int n_occupied = slot - s->regs.mbs;
+ if (n_occupied == 4) { /* 4 means the 5th slot was filled in */
+ /*
+ * fifo occupancy increased from 4 to 5,
+ * raising FIFO_WARN interrupt
+ */
+ flexcan_irq_iflag_set(s, I_FIFO_WARN);
+ }
+ flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE);
+
+ trace_flexcan_fifo_push(DEVICE(s)->canonical_path, n_occupied);
+ } else {
+ flexcan_irq_iflag_set(s, I_FIFO_OVERFLOW);
+
+ trace_flexcan_fifo_push(DEVICE(s)->canonical_path, -1);
+ }
+}
+
+static enum FlexcanRx flexcan_fifo_rx(FlexcanState *s,
+ const qemu_can_frame *buf)
+{
+ /* todo: filtering. return FLEXCAN_FIFO_RX_RETRY if filtered out */
+ if ((s->regs.mcr & FLEXCAN_MCR_IDAM_MASK) == FLEXCAN_MCR_IDAM_D) {
+ /* all frames rejected */
+ return FLEXCAN_RX_SEARCH_RETRY;
+ } else {
+ /* push message to queue if not full */
+ FlexcanRegsMessageBuffer *slot = flexcan_fifo_find_free_slot(s);
+ if (slot) {
+ flexcan_mb_move_in(s, buf, slot);
+ }
+ flexcan_fifo_push(s, slot);
+
+ return slot ? FLEXCAN_RX_SEARCH_ACCEPT : FLEXCAN_RX_SEARCH_DROPPED;
+ }
+}
+
+/* --------- RX message buffer ---------- */
+
+/**
+ * flexcan_mb_rx_check_mb() - Check if a mb matches a received frame
+ * @s: FlexCAN device pointer
+ * @buf: Frame to be received from CAN subsystem
+ * @mbid: Target mailbox index. The mailbox must be a valid message buffer.
+ *
+ * Return: FLEXCAN_CHECK_MB_NIL if the message buffer does not match.
+ * FLEXCAN_CHECK_MB_MATCH if the message buffer matches the received
+ * frame and is free-to-receive,
+ * FLEXCAN_CHECK_MB_MATCH_LOCKED if the message buffer matches,
+ * but is locked,
+ * FLEXCAN_CHECK_MB_MATCH_NON_FREE if the message buffer matches,
+ * but is not free-to-receive
+ * for some other reason.
+ */
+static enum FlexcanCheck flexcan_mb_rx_check_mb(FlexcanState *s,
+ const qemu_can_frame *buf,
+ int mbid)
+{
+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbid];
+ const bool is_rtr = !!(buf->can_id & QEMU_CAN_RTR_FLAG);
+ const bool is_serviced = !(mb->can_ctrl & FLEXCAN_MB_CNT_NOT_SRV);
+ const bool is_locked = s->locked_mbidx == mbid;
+
+ bool is_free_to_receive = false;
+ bool is_matched = false;
+
+ switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
+ case FLEXCAN_MB_CODE_RX_RANSWER:
+ if (is_rtr && !(s->regs.ctrl2 & FLEXCAN_CTRL2_RRS)) {
+ /* todo: do the actual matching/filtering and RTR answer */
+ is_matched = true;
+ }
+ break;
+ case FLEXCAN_MB_CODE_RX_FULL:
+ QEMU_FALLTHROUGH;
+ case FLEXCAN_MB_CODE_RX_OVERRUN:
+ is_free_to_receive = is_serviced;
+ /* todo: do the actual matching/filtering */
+ is_matched = true;
+ break;
+ case FLEXCAN_MB_CODE_RX_EMPTY:
+ is_free_to_receive = true;
+ /* todo: do the actual matching/filtering */
+ is_matched = true;
+ break;
+ default:
+ break;
+ }
+
+ if (trace_event_get_state_backends(TRACE_FLEXCAN_MB_RX_CHECK_MB)) {
+ char code_str_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };
+ const char *code_str = flexcan_dbg_mb_code(mb->can_ctrl, code_str_buf);
+ trace_flexcan_mb_rx_check_mb(DEVICE(s)->canonical_path, mbid, code_str,
+ is_matched, is_free_to_receive,
+ is_serviced, is_locked);
+ }
+
+ if (!is_matched) {
+ return FLEXCAN_CHECK_MB_NIL;
+ }
+
+ if (is_locked) {
+ return FLEXCAN_CHECK_MB_MATCH_LOCKED;
+ }
+
+ if (is_free_to_receive) {
+ return FLEXCAN_CHECK_MB_MATCH;
+ }
+
+ return FLEXCAN_CHECK_MB_MATCH_NON_FREE;
+}
+
+static enum FlexcanRx flexcan_mb_rx(FlexcanState *s, const qemu_can_frame *buf)
+{
+ int last_not_free_to_receive_mbid = -1;
+ bool last_not_free_to_receive_locked = false;
+
+ FlexcanRegsMessageBuffer *first_mb = flexcan_get_first_message_buffer(s);
+ FlexcanRegsMessageBuffer *last_mb = flexcan_get_last_enabled_mailbox(s);
+
+ for (FlexcanRegsMessageBuffer *mb = first_mb;
+ mb <= last_mb; mb++) {
+ int mbid = mb - s->regs.mbs;
+ enum FlexcanCheck r = flexcan_mb_rx_check_mb(s, buf, mbid);
+ if (r == FLEXCAN_CHECK_MB_MATCH) {
+ flexcan_mb_move_in(s, buf, mb);
+ flexcan_irq_iflag_set(s, mbid);
+ return FLEXCAN_RX_SEARCH_ACCEPT;
+ }
+
+ if (r == FLEXCAN_CHECK_MB_MATCH_NON_FREE) {
+ last_not_free_to_receive_mbid = mbid;
+ last_not_free_to_receive_locked = false;
+ } else if (r == FLEXCAN_CHECK_MB_MATCH_LOCKED) {
+ /*
+ * message buffer is locked,
+ * we can move in the message after it's unlocked
+ */
+ last_not_free_to_receive_mbid = mbid;
+ last_not_free_to_receive_locked = true;
+ }
+ }
+
+ if (last_not_free_to_receive_mbid >= -1) {
+ if (last_not_free_to_receive_locked) {
+ /*
+ * copy to temporary mailbox (SMB)
+ * it will be moved in when the mailbox is unlocked
+ */
+ s->regs.rx_smb0.can_ctrl =
+ s->regs.mbs[last_not_free_to_receive_mbid].can_id;
+ flexcan_mb_move_in(s, buf, &s->regs.rx_smb0);
+ s->smb_target_mbidx = last_not_free_to_receive_mbid;
+ return FLEXCAN_RX_SEARCH_ACCEPT;
+ }
+
+ if (s->regs.mcr & FLEXCAN_MCR_IRMQ) {
+ flexcan_mb_move_in(s, buf,
+ &s->regs.mbs[last_not_free_to_receive_mbid]);
+ flexcan_irq_iflag_set(s, last_not_free_to_receive_mbid);
+ return FLEXCAN_RX_SEARCH_ACCEPT;
+ }
+ }
+
+ return FLEXCAN_RX_SEARCH_RETRY;
+}
+
+static ssize_t flexcan_receive(CanBusClientState *client,
+ const qemu_can_frame *frames, size_t frames_cnt)
+{
+ FlexcanState *s = container_of(client, FlexcanState, bus_client);
+ trace_flexcan_receive(DEVICE(s)->canonical_path, frames_cnt);
+
+ if (frames_cnt == 0) {
+ return 0;
+ }
+
+ /* clear the SMB, as it would be overriden in hardware */
+ memset(&s->regs.rx_smb0, 0, sizeof(FlexcanRegsMessageBuffer));
+ s->smb_target_mbidx = FLEXCAN_SMB_EMPTY;
+
+ for (size_t i = 0; i < frames_cnt; i++) {
+ int r;
+ const qemu_can_frame *frame = &frames[i];
+ if (frame->can_id & QEMU_CAN_ERR_FLAG) {
+ /* todo: error frame handling */
+ continue;
+ }
+ if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) {
+ /* CAN FD supported only in later FlexCAN version */
+ continue;
+ }
+
+ /* todo: this order logic is not complete and needs further work */
+ if (s->regs.mcr & FLEXCAN_MCR_FEN &&
+ s->regs.ctrl2 & FLEXCAN_CTRL2_MRP) {
+ r = flexcan_mb_rx(s, frame);
+ if (r == FLEXCAN_RX_SEARCH_RETRY) {
+ flexcan_fifo_rx(s, frame);
+ }
+ } else if (s->regs.mcr & FLEXCAN_MCR_FEN) {
+ r = flexcan_fifo_rx(s, frame);
+ if (r == FLEXCAN_RX_SEARCH_RETRY) {
+ flexcan_mb_rx(s, frame);
+ }
+ } else {
+ flexcan_mb_rx(s, frame);
+ }
+ }
+
+ flexcan_irq_update(s);
+ return 1;
+}
+
+/* ========== I/O handling ========== */
+static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ FlexcanState *s = opaque;
+ uint32_t write_mask = ((const uint32_t *)
+ &flexcan_regs_write_mask)[addr / 4];
+ uint32_t old_value = s->regs_raw[addr / 4];
+
+ /*
+ * 0 for bits that can "only be written in Freeze mode as it is blocked
+ * by hardware in other modes"
+ */
+ const uint32_t freeze_mask_mcr = 0xDF54CC80;
+ const uint32_t freeze_mask_ctrl1 = 0x0000E740;
+
+ flexcan_trace_mem_op(s, addr, val, size, true);
+ switch (addr) {
+ case offsetof(FlexcanRegs, mcr):
+ if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
+ write_mask &= freeze_mask_mcr;
+ }
+ s->regs.mcr = (val & write_mask) | (old_value & ~write_mask);
+ flexcan_set_mcr(s, old_value);
+ break;
+ case offsetof(FlexcanRegs, ctrl):
+ if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
+ write_mask &= freeze_mask_ctrl1;
+ }
+ s->regs.ctrl = (val & write_mask) | (old_value & ~write_mask);
+ break;
+ case offsetof(FlexcanRegs, iflag1):
+ s->regs.iflag1 &= ~val;
+ if ((s->regs.mcr & FLEXCAN_MCR_FEN) &&
+ (val & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE)) {
+ flexcan_fifo_pop(s);
+ }
+ break;
+ case offsetof(FlexcanRegs, iflag2):
+ s->regs.iflag2 &= ~val;
+ break;
+ case offsetof(FlexcanRegs, ctrl2):
+ QEMU_FALLTHROUGH;
+ case offsetof(FlexcanRegs, ecr):
+ QEMU_FALLTHROUGH;
+ case offsetof(FlexcanRegs, rxmgmask):
+ QEMU_FALLTHROUGH;
+ case offsetof(FlexcanRegs, rx14mask):
+ QEMU_FALLTHROUGH;
+ case offsetof(FlexcanRegs, rx15mask):
+ QEMU_FALLTHROUGH;
+ case offsetof(FlexcanRegs, rxfgmask):
+ QEMU_FALLTHROUGH;
+ case offsetof(FlexcanRegs, rximr[0]) ... offsetof(FlexcanRegs, rximr[63]):
+ /* these registers can only be written in freeze mode */
+ if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
+ break;
+ }
+ QEMU_FALLTHROUGH;
+ default:
+ s->regs_raw[addr / 4] = (val & write_mask) | (old_value & ~write_mask);
+
+ if (addr >= offsetof(FlexcanRegs, mb) &&
+ addr < offsetof(FlexcanRegs, _reserved4)) {
+ /* access to mailbox */
+ int mbid = (addr - offsetof(FlexcanRegs, mb)) /
+ sizeof(FlexcanRegsMessageBuffer);
+
+ if (s->locked_mbidx == mbid) {
+ flexcan_mb_unlock(s);
+ }
+
+ /* check for invalid writes into FIFO region */
+ if (s->regs.mcr & FLEXCAN_MCR_FEN && mbid < FLEXCAN_FIFO_DEPTH) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Invalid write to Rx-FIFO structure",
+ DEVICE(s)->canonical_path);
+ return;
+ }
+
+ /* run mailbox processing function on write to control word */
+ if ((addr & 0xF) == 0) {
+ flexcan_mb_write(s, mbid);
+ }
+ }
+ break;
+ }
+
+ flexcan_irq_update(s);
+}
+
+static uint64_t flexcan_mem_read(void *opqaue, hwaddr addr, unsigned size)
+{
+ FlexcanState *s = opqaue;
+ uint32_t rv = s->regs_raw[addr >> 2];
+
+ if (addr >= offsetof(FlexcanRegs, mb) &&
+ addr < offsetof(FlexcanRegs, _reserved4)) {
+ /* reading from mailbox */
+ hwaddr offset = addr - offsetof(FlexcanRegs, mb);
+ int mbid = offset / sizeof(FlexcanRegsMessageBuffer);
+
+ if (addr % 16 == 0 && s->locked_mbidx != mbid) {
+ /* reading control word locks the mailbox */
+ flexcan_mb_unlock(s);
+ flexcan_mb_lock(s, mbid);
+ flexcan_irq_update(s);
+ rv = s->regs.mbs[mbid].can_ctrl & ~FLEXCAN_MB_CNT_NOT_SRV;
+ }
+ } else if (addr == offsetof(FlexcanRegs, timer)) {
+ flexcan_mb_unlock(s);
+ flexcan_irq_update(s);
+ rv = flexcan_get_timestamp(s, false);
+ }
+
+ flexcan_trace_mem_op(s, addr, rv, size, false);
+ return rv;
+}
+
+static bool flexcan_mem_accepts(void *opaque, hwaddr addr,
+ unsigned size, bool is_write,
+ MemTxAttrs attrs)
+{
+ FlexcanState *s = opaque;
+
+ if ((s->regs.ctrl2 & FLEXCAN_CTRL2_WRMFRZ) &&
+ (s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
+ /* unrestricted access to FlexCAN memory in freeze mode */
+ return true;
+ } else if (attrs.user && (s->regs.mcr & FLEXCAN_MCR_SUPV)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Invalid user-mode access to restricted register",
+ DEVICE(s)->canonical_path);
+ return false;
+ } else if (attrs.user && is_write && addr < 4) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Invalid user-mode access to MCR",
+ DEVICE(s)->canonical_path);
+ return false;
+ }
+
+ return true;
+}
+
+static const struct MemoryRegionOps flexcan_ops = {
+ .read = flexcan_mem_read,
+ .write = flexcan_mem_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ .unaligned = true,
+ .accepts = flexcan_mem_accepts
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false
+ },
+};
+
+static CanBusClientInfo flexcan_bus_client_info = {
+ .can_receive = flexcan_can_receive,
+ .receive = flexcan_receive,
+};
+
+static int flexcan_connect_to_bus(FlexcanState *s, CanBusState *bus)
+{
+ s->bus_client.info = &flexcan_bus_client_info;
+
+ if (can_bus_insert_client(bus, &s->bus_client) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static void flexcan_init(Object *obj)
+{
+ FlexcanState *s = CAN_FLEXCAN(obj);
+
+ memory_region_init_io(
+ &s->iomem, obj, &flexcan_ops, s, TYPE_CAN_FLEXCAN,
+ offsetof(FlexcanRegs, _reserved6)
+ );
+}
+
+static void flexcan_realize(DeviceState *dev, Error **errp)
+{
+ FlexcanState *s = CAN_FLEXCAN(dev);
+
+ if (s->canbus) {
+ if (flexcan_connect_to_bus(s, s->canbus) < 0) {
+ error_setg(errp, "%s: flexcan_connect_to_bus failed",
+ dev->canonical_path);
+ return;
+ }
+ }
+
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(SYS_BUS_DEVICE(dev)), &s->irq);
+}
+
+static const VMStateDescription vmstate_can = {
+ .name = TYPE_CAN_FLEXCAN,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_INT64(timer_start, FlexcanState),
+ VMSTATE_UINT32_ARRAY(regs_raw, FlexcanState, sizeof(FlexcanRegs) / 4),
+ VMSTATE_INT32(locked_mbidx, FlexcanState),
+ VMSTATE_INT32(smb_target_mbidx, FlexcanState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static const Property flexcan_properties[] = {
+ DEFINE_PROP_LINK("canbus", FlexcanState, canbus, TYPE_CAN_BUS,
+ CanBusState *),
+};
+
+static void flexcan_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.enter = flexcan_reset_enter;
+ rc->phases.hold = flexcan_reset_hold;
+ dc->realize = flexcan_realize;
+ device_class_set_props(dc, flexcan_properties);
+ dc->vmsd = &vmstate_can;
+ dc->desc = "i.MX FLEXCAN Controller";
+}
+
+static const TypeInfo flexcan_info = {
+ .name = TYPE_CAN_FLEXCAN,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(FlexcanState),
+ .class_init = flexcan_class_init,
+ .instance_init = flexcan_init,
+};
+
+static void can_register_types(void)
+{
+ type_register_static(&flexcan_info);
+}
+type_init(can_register_types)
diff --git a/hw/net/can/flexcan_regs.h b/hw/net/can/flexcan_regs.h
new file mode 100644
index 0000000000..c18bc4736e
--- /dev/null
+++ b/hw/net/can/flexcan_regs.h
@@ -0,0 +1,197 @@
+/*
+ * Field bitmasks and register structs definitions for FlexCAN
+ *
+ * This implementation is based on the following datasheet:
+ * i.MX 6Dual/6Quad Applications Processor Reference Manual
+ * Document Number: IMX6DQRM, Rev. 6, 05/2020
+ *
+ * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/bitops.h"
+
+#ifndef HW_CAN_FLEXCAN_REGS_H
+#define HW_CAN_FLEXCAN_REGS_H
+
+#define FLEXCAN_GENMASK(h, l) (((~(uint32_t)0) >> (31 - (h) + (l))) << (l))
+
+/**
+ * The following macros were originally written for the Linux kernel by:
+ * Andrey Volkov <andrey@volkov.fr>
+ * Sascha Hauer <s.hauer@pengutronix.de>
+ * Marc Kleine-Budde <mkl@pengutronix.de>
+ * David Jander <david@protonic.nl>
+ * and they have agreed to license them under GPL-2.0-or-later.
+ */
+
+/* FLEXCAN module configuration register (CANMCR) bits */
+#define FLEXCAN_MCR_MDIS BIT(31)
+#define FLEXCAN_MCR_FRZ BIT(30)
+#define FLEXCAN_MCR_FEN BIT(29)
+#define FLEXCAN_MCR_HALT BIT(28)
+#define FLEXCAN_MCR_NOT_RDY BIT(27)
+#define FLEXCAN_MCR_WAK_MSK BIT(26)
+#define FLEXCAN_MCR_SOFTRST BIT(25)
+#define FLEXCAN_MCR_FRZ_ACK BIT(24)
+#define FLEXCAN_MCR_SUPV BIT(23)
+#define FLEXCAN_MCR_SLF_WAK BIT(22)
+#define FLEXCAN_MCR_WRN_EN BIT(21)
+#define FLEXCAN_MCR_LPM_ACK BIT(20)
+#define FLEXCAN_MCR_WAK_SRC BIT(19)
+#define FLEXCAN_MCR_DOZE BIT(18)
+#define FLEXCAN_MCR_SRX_DIS BIT(17)
+#define FLEXCAN_MCR_IRMQ BIT(16)
+#define FLEXCAN_MCR_LPRIO_EN BIT(13)
+#define FLEXCAN_MCR_AEN BIT(12)
+#define FLEXCAN_MCR_FDEN BIT(11)
+#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f)
+#define FLEXCAN_MCR_IDAM_A (0x0 << 8)
+#define FLEXCAN_MCR_IDAM_B (0x1 << 8)
+#define FLEXCAN_MCR_IDAM_C (0x2 << 8)
+#define FLEXCAN_MCR_IDAM_D (0x3 << 8)
+#define FLEXCAN_MCR_IDAM_MASK (0x3 << 8)
+
+/* FLEXCAN control register (CANCTRL) bits */
+#define FLEXCAN_CTRL_PRESDIV(x) (((x) & 0xFF) << 24)
+#define FLEXCAN_CTRL_PRESDIV_MASK FLEXCAN_CTRL_PRESDIV(UINT32_MAX)
+#define FLEXCAN_CTRL_RJW(x) (((x) & 0x03) << 22)
+#define FLEXCAN_CTRL_RJW_MASK FLEXCAN_CTRL_RJW(UINT32_MAX)
+#define FLEXCAN_CTRL_PSEG1(x) (((x) & 0x07) << 19)
+#define FLEXCAN_CTRL_PSEG1_MASK FLEXCAN_CTRL_PSEG1(UINT32_MAX)
+#define FLEXCAN_CTRL_PSEG2(x) (((x) & 0x07) << 16)
+#define FLEXCAN_CTRL_PSEG2_MASK FLEXCAN_CTRL_PSEG2(UINT32_MAX)
+#define FLEXCAN_CTRL_BOFF_MSK BIT(15)
+#define FLEXCAN_CTRL_ERR_MSK BIT(14)
+#define FLEXCAN_CTRL_CLK_SRC BIT(13)
+#define FLEXCAN_CTRL_LPB BIT(12)
+#define FLEXCAN_CTRL_TWRN_MSK BIT(11)
+#define FLEXCAN_CTRL_RWRN_MSK BIT(10)
+#define FLEXCAN_CTRL_SMP BIT(7)
+#define FLEXCAN_CTRL_BOFF_REC BIT(6)
+#define FLEXCAN_CTRL_TSYN BIT(5)
+#define FLEXCAN_CTRL_LBUF BIT(4)
+#define FLEXCAN_CTRL_LOM BIT(3)
+#define FLEXCAN_CTRL_PROPSEG(x) ((x) & 0x07)
+#define FLEXCAN_CTRL_PROPSEG_MASK FLEXCAN_CTRL_PROPSEG(UINT32_MAX)
+#define FLEXCAN_CTRL_ERR_BUS (FLEXCAN_CTRL_ERR_MSK)
+#define FLEXCAN_CTRL_ERR_STATE \
+ (FLEXCAN_CTRL_TWRN_MSK | FLEXCAN_CTRL_RWRN_MSK | \
+ FLEXCAN_CTRL_BOFF_MSK)
+#define FLEXCAN_CTRL_ERR_ALL \
+ (FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE)
+
+/* FLEXCAN control register 2 (CTRL2) bits */
+#define FLEXCAN_CTRL2_ECRWRE BIT(29)
+#define FLEXCAN_CTRL2_WRMFRZ BIT(28)
+#define FLEXCAN_CTRL2_RFFN(x) (((x) & 0x0f) << 24)
+#define FLEXCAN_CTRL2_TASD(x) (((x) & 0x1f) << 19)
+#define FLEXCAN_CTRL2_MRP BIT(18)
+#define FLEXCAN_CTRL2_RRS BIT(17)
+#define FLEXCAN_CTRL2_EACEN BIT(16)
+#define FLEXCAN_CTRL2_ISOCANFDEN BIT(12)
+
+/* FLEXCAN memory error control register (MECR) bits */
+#define FLEXCAN_MECR_ECRWRDIS BIT(31)
+#define FLEXCAN_MECR_HANCEI_MSK BIT(19)
+#define FLEXCAN_MECR_FANCEI_MSK BIT(18)
+#define FLEXCAN_MECR_CEI_MSK BIT(16)
+#define FLEXCAN_MECR_HAERRIE BIT(15)
+#define FLEXCAN_MECR_FAERRIE BIT(14)
+#define FLEXCAN_MECR_EXTERRIE BIT(13)
+#define FLEXCAN_MECR_RERRDIS BIT(9)
+#define FLEXCAN_MECR_ECCDIS BIT(8)
+#define FLEXCAN_MECR_NCEFAFRZ BIT(7)
+
+/* FLEXCAN error and status register (ESR) bits */
+#define FLEXCAN_ESR_SYNCH BIT(18)
+#define FLEXCAN_ESR_TWRN_INT BIT(17)
+#define FLEXCAN_ESR_RWRN_INT BIT(16)
+#define FLEXCAN_ESR_BIT1_ERR BIT(15)
+#define FLEXCAN_ESR_BIT0_ERR BIT(14)
+#define FLEXCAN_ESR_ACK_ERR BIT(13)
+#define FLEXCAN_ESR_CRC_ERR BIT(12)
+#define FLEXCAN_ESR_FRM_ERR BIT(11)
+#define FLEXCAN_ESR_STF_ERR BIT(10)
+#define FLEXCAN_ESR_TX_WRN BIT(9)
+#define FLEXCAN_ESR_RX_WRN BIT(8)
+#define FLEXCAN_ESR_IDLE BIT(7)
+#define FLEXCAN_ESR_BOFF_INT BIT(2)
+#define FLEXCAN_ESR_ERR_INT BIT(1)
+#define FLEXCAN_ESR_WAK_INT BIT(0)
+
+/* FLEXCAN Bit Timing register (CBT) bits */
+#define FLEXCAN_CBT_BTF BIT(31)
+#define FLEXCAN_CBT_EPRESDIV_MASK FLEXCAN_GENMASK(30, 21)
+#define FLEXCAN_CBT_ERJW_MASK FLEXCAN_GENMASK(20, 16)
+#define FLEXCAN_CBT_EPROPSEG_MASK FLEXCAN_GENMASK(15, 10)
+#define FLEXCAN_CBT_EPSEG1_MASK FLEXCAN_GENMASK(9, 5)
+#define FLEXCAN_CBT_EPSEG2_MASK FLEXCAN_GENMASK(4, 0)
+
+/* FLEXCAN FD control register (FDCTRL) bits */
+#define FLEXCAN_FDCTRL_FDRATE BIT(31)
+#define FLEXCAN_FDCTRL_MBDSR1 FLEXCAN_GENMASK(20, 19)
+#define FLEXCAN_FDCTRL_MBDSR0 FLEXCAN_GENMASK(17, 16)
+#define FLEXCAN_FDCTRL_MBDSR_8 0x0
+#define FLEXCAN_FDCTRL_MBDSR_12 0x1
+#define FLEXCAN_FDCTRL_MBDSR_32 0x2
+#define FLEXCAN_FDCTRL_MBDSR_64 0x3
+#define FLEXCAN_FDCTRL_TDCEN BIT(15)
+#define FLEXCAN_FDCTRL_TDCFAIL BIT(14)
+#define FLEXCAN_FDCTRL_TDCOFF FLEXCAN_GENMASK(12, 8)
+#define FLEXCAN_FDCTRL_TDCVAL FLEXCAN_GENMASK(5, 0)
+
+/* FLEXCAN FD Bit Timing register (FDCBT) bits */
+#define FLEXCAN_FDCBT_FPRESDIV_MASK FLEXCAN_GENMASK(29, 20)
+#define FLEXCAN_FDCBT_FRJW_MASK FLEXCAN_GENMASK(18, 16)
+#define FLEXCAN_FDCBT_FPROPSEG_MASK FLEXCAN_GENMASK(14, 10)
+#define FLEXCAN_FDCBT_FPSEG1_MASK FLEXCAN_GENMASK(7, 5)
+#define FLEXCAN_FDCBT_FPSEG2_MASK FLEXCAN_GENMASK(2, 0)
+
+/* FLEXCAN CRC Register (CRCR) bits */
+#define FLEXCAN_CRCR_MBCRC_MASK FLEXCAN_GENMASK(22, 16)
+#define FLEXCAN_CRCR_MBCRC(x) (((x) & FLEXCAN_CRCR_MBCRC_MASK) << 16)
+#define FLEXCAN_CRCR_TXCRC_MASK FLEXCAN_GENMASK(14, 0)
+#define FLEXCAN_CRCR_TXCRC(x) ((x) & FLEXCAN_CRCR_TXCRC_MASK)
+
+/* FLEXCAN interrupt flag register (IFLAG) bits */
+/* Errata ERR005829 step7: Reserve first valid MB */
+#define I_FIFO_OVERFLOW 7
+#define I_FIFO_WARN 6
+#define I_FIFO_AVAILABLE 5
+
+#define FLEXCAN_TX_MB_RESERVED_RX_FIFO 8
+#define FLEXCAN_TX_MB_RESERVED_RX_MAILBOX 0
+#define FLEXCAN_RX_MB_RX_MAILBOX_FIRST (FLEXCAN_TX_MB_RESERVED_RX_MAILBOX + 1)
+#define FLEXCAN_IFLAG_MB(x) BIT_ULL(x)
+#define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(I_FIFO_OVERFLOW)
+#define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(I_FIFO_WARN)
+#define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(I_FIFO_AVAILABLE)
+
+/* FLEXCAN message buffers */
+#define FLEXCAN_MB_CODE_RX_BUSY_BIT (0x1 << 24)
+#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24)
+#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24)
+#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24)
+#define FLEXCAN_MB_CODE_RX_OVERRUN (0x6 << 24)
+#define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24)
+
+#define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24)
+#define FLEXCAN_MB_CODE_TX_ABORT (0x9 << 24)
+#define FLEXCAN_MB_CODE_TX_DATA (0xc << 24)
+#define FLEXCAN_MB_CODE_TX_TANSWER (0xe << 24)
+
+#define FLEXCAN_MB_CODE(x) (((x) & 0xF) << 24)
+#define FLEXCAN_MB_CODE_MASK FLEXCAN_MB_CODE(UINT32_MAX)
+
+#define FLEXCAN_MB_CNT_EDL BIT(31)
+#define FLEXCAN_MB_CNT_BRS BIT(30)
+#define FLEXCAN_MB_CNT_ESI BIT(29)
+#define FLEXCAN_MB_CNT_SRR BIT(22)
+#define FLEXCAN_MB_CNT_IDE BIT(21)
+#define FLEXCAN_MB_CNT_RTR BIT(20)
+#define FLEXCAN_MB_CNT_LENGTH(x) (((x) & 0xF) << 16)
+#define FLEXCAN_MB_CNT_TIMESTAMP(x) ((x) & 0xFFFF)
+#define FLEXCAN_MB_CNT_TIMESTAMP_MASK FLEXCAN_MB_CNT_TIMESTAMP(UINT32_MAX)
+
+#endif
diff --git a/hw/net/can/meson.build b/hw/net/can/meson.build
index 7382344628..401afde2e4 100644
--- a/hw/net/can/meson.build
+++ b/hw/net/can/meson.build
@@ -6,3 +6,4 @@ system_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c'))
system_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c'))
system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c'))
system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-canfd.c'))
+system_ss.add(when: 'CONFIG_CAN_FLEXCAN', if_true: files('flexcan.c'))
diff --git a/hw/net/can/trace-events b/hw/net/can/trace-events
index de64ac1b31..7500f10d7a 100644
--- a/hw/net/can/trace-events
+++ b/hw/net/can/trace-events
@@ -1,3 +1,21 @@
+# flexcan.c
+flexcan_irq_update(const char *inst, uint32_t mb_irqs1, uint32_t mb_irqs2, int setting) "%s: irqs1 0x%08x irqs2 0x%08x request %i"
+flexcan_set_mcr(const char *inst, const char *enabled, const char *freeze, const char *fifo, const char *rdy, const char *sync) "%s: %s %s %s %s %s"
+flexcan_mb_write(const char *inst, int mbidx, const char *code, int is_mailbox, uint32_t ctrl, uint32_t id) "%s: mbidx %i code %s is_mailbox %i ctrl 0x%08x id 0x%08x"
+flexcan_mb_lock(const char *inst, int mbidx, int had_rx_code) "%s: mbidx %i had_rx_code %i"
+flexcan_mb_unlock(const char *inst, int mbidx, const char *pending_frame) "%s: mbidx %i%s"
+flexcan_fifo_pop(const char *inst, int non_empty_before, int non_empty_after) "%s: non_empty before %i non_empty_after %i"
+flexcan_fifo_push(const char *inst, int n_occupied) "%s: n_slots_occupied %i"
+flexcan_reset(const char *inst) "%s: resetting"
+flexcan_mem_op(const char *inst, const char *op, uint32_t v, int offset, const char *reg_name, int size) "%s: %s 0x%08x at offset %i register %s size %i"
+flexcan_get_timestamp(const char *inst, int64_t time_elapsed_ms, uint32_t bitrate, uint64_t cycles, uint32_t shift, uint32_t timestamp) "%s: time_elapsed %" PRIi64 "ms bitrate %ub/s cycles %" PRIu64 " shift %u timestamp 0x%04x"
+flexcan_get_bitrate(const char *inst, uint32_t pe_freq, uint32_t prediv, uint32_t s_freq, uint32_t tseg1, uint32_t tseg2, uint32_t quata_per_bit, uint32_t bitrate) "%s: pe_freq %uHz prescaler %u s_freq %uHz tseg1 %uq tseg2 %uq total %uq/b bitrate %ub/s"
+flexcan_timer_start(const char *inst, uint32_t bitrate, uint32_t value) "%s: bitrate %ub/s value 0x%04x"
+flexcan_timer_stop(const char *inst, uint32_t bitrate, uint32_t value) "%s: bitrate %ub/s value 0x%04x"
+flexcan_timer_overflow(const char *inst, int64_t current_time, int64_t timer_start, int64_t elapsed_ns) "%s: current_time %" PRIi64 "timer_start %" PRIi64 "elapsed_ns %" PRIi64
+flexcan_mb_rx_check_mb(const char *inst, int mbidx, const char *code, int is_matched, int is_ftr, int is_serviced, int is_locked) "%s: checking mb %i code %s is_matched %i is_free_to_receive %i is_serviced %i is_locked %i"
+flexcan_receive(const char *inst, size_t n_frames) "%s: received %zu frames"
+
# xlnx-zynqmp-can.c
xlnx_can_update_irq(uint32_t isr, uint32_t ier, uint32_t irq) "ISR: 0x%08x IER: 0x%08x IRQ: 0x%08x"
xlnx_can_reset(uint32_t val) "Resetting controller with value = 0x%08x"
diff --git a/include/hw/net/flexcan.h b/include/hw/net/flexcan.h
new file mode 100644
index 0000000000..153afc1e03
--- /dev/null
+++ b/include/hw/net/flexcan.h
@@ -0,0 +1,145 @@
+/*
+ * QEMU model of the NXP FLEXCAN device.
+ *
+ * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
+ *
+ * Based on CTU CAN FD emulation implemented by Jan Charvat.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_CAN_FLEXCAN_H
+#define HW_CAN_FLEXCAN_H
+
+#include "net/can_emu.h"
+#include "qom/object.h"
+#include "hw/misc/imx_ccm.h"
+
+#define FLEXCAN_FIFO_DEPTH 6
+#define FLEXCAN_MAILBOX_COUNT 64
+
+/**
+ * Definitions of structs FlexcanRegs and FlexcanRegsMessageBuffer were
+ * originally written for the Linux kernel by:
+ * Andrey Volkov <andrey@volkov.fr>
+ * Sascha Hauer <s.hauer@pengutronix.de>
+ * Marc Kleine-Budde <mkl@pengutronix.de>
+ * David Jander <david@protonic.nl>
+ * and they have agreed to license them under GPL-2.0-or-later.
+ */
+
+/* view of single message buffer registers */
+typedef struct FlexcanRegsMessageBuffer {
+ uint32_t can_ctrl;
+ uint32_t can_id;
+ uint32_t data[2];
+} FlexcanRegsMessageBuffer;
+
+/* RX FIFO view of message buffer registers */
+typedef struct FlexcanRegsRXFifo {
+ /* 6 message buffer deep queue, queue back first */
+ FlexcanRegsMessageBuffer mb_back;
+ FlexcanRegsMessageBuffer mbs_queue[FLEXCAN_FIFO_DEPTH - 1];
+
+ /* number of filter elements active depends on ctrl2 | FLEXCAN_CTRL2_RFFN */
+ uint32_t filter_table_els[128];
+} FlexcanRegsRXFifo;
+
+/* FlexCAN register in hw layout */
+typedef struct FlexcanRegs {
+ uint32_t mcr; /* 0x00 */
+ uint32_t ctrl; /* 0x04 - not affected by soft reset */
+ uint32_t timer; /* 0x08 */
+ uint32_t tcr; /* 0x0C */
+ uint32_t rxmgmask; /* 0x10 - not affected by soft reset */
+ uint32_t rx14mask; /* 0x14 - not affected by soft reset */
+ uint32_t rx15mask; /* 0x18 - not affected by soft reset */
+ uint32_t ecr; /* 0x1C */
+ uint32_t esr; /* 0x20 */
+ uint32_t imask2; /* 0x24 */
+ uint32_t imask1; /* 0x28 */
+ uint32_t iflag2; /* 0x2C */
+ uint32_t iflag1; /* 0x30 */
+ union { /* 0x34 */
+ uint32_t gfwr_mx28; /* MX28, MX53 */
+ uint32_t ctrl2; /* MX6, VF610 - not affected by soft reset */
+ };
+ uint32_t esr2; /* 0x38 */
+ uint32_t imeur; /* 0x3C, unused */
+ uint32_t lrfr; /* 0x40, unused */
+ uint32_t crcr; /* 0x44 */
+ uint32_t rxfgmask; /* 0x48 */
+ uint32_t rxfir; /* 0x4C - not affected by soft reset */
+ uint32_t cbt; /* 0x50, unused - not affected by soft reset */
+ uint32_t _reserved2; /* 0x54 */
+ uint32_t dbg1; /* 0x58, unused */
+ uint32_t dbg2; /* 0x5C, unused */
+ uint32_t _reserved3[8]; /* 0x60 */
+ union { /* 0x80 - not affected by soft reset */
+ uint32_t mb[sizeof(FlexcanRegsMessageBuffer) * FLEXCAN_MAILBOX_COUNT];
+ FlexcanRegsMessageBuffer mbs[FLEXCAN_MAILBOX_COUNT];
+ FlexcanRegsRXFifo fifo;
+ };
+ uint32_t _reserved4[256]; /* 0x480 */
+ uint32_t rximr[64]; /* 0x880 - not affected by soft reset */
+ uint32_t _reserved5[24]; /* 0x980 */
+ uint32_t gfwr_mx6; /* 0x9E0 - MX6 */
+
+ /* the rest is unused except for SMB */
+ uint32_t _reserved6[39]; /* 0x9E4 */
+ uint32_t _rxfir[6]; /* 0xA80 */
+ uint32_t _reserved8[2]; /* 0xA98 */
+ uint32_t _rxmgmask; /* 0xAA0 */
+ uint32_t _rxfgmask; /* 0xAA4 */
+ uint32_t _rx14mask; /* 0xAA8 */
+ uint32_t _rx15mask; /* 0xAAC */
+ uint32_t tx_smb[4]; /* 0xAB0 */
+ union { /* 0xAC0, used for SMB emulation */
+ uint32_t rx_smb0_raw[4];
+ FlexcanRegsMessageBuffer rx_smb0;
+ };
+ uint32_t rx_smb1[4]; /* 0xAD0 */
+ uint32_t mecr; /* 0xAE0 */
+ uint32_t erriar; /* 0xAE4 */
+ uint32_t erridpr; /* 0xAE8 */
+ uint32_t errippr; /* 0xAEC */
+ uint32_t rerrar; /* 0xAF0 */
+ uint32_t rerrdr; /* 0xAF4 */
+ uint32_t rerrsynr; /* 0xAF8 */
+ uint32_t errsr; /* 0xAFC */
+ uint32_t _reserved7[64]; /* 0xB00 */
+ uint32_t fdctrl; /* 0xC00 - not affected by soft reset */
+ uint32_t fdcbt; /* 0xC04 - not affected by soft reset */
+ uint32_t fdcrc; /* 0xC08 */
+ uint32_t _reserved9[199]; /* 0xC0C */
+ uint32_t tx_smb_fd[18]; /* 0xF28 */
+ uint32_t rx_smb0_fd[18]; /* 0xF70 */
+ uint32_t rx_smb1_fd[18]; /* 0xFB8 */
+} FlexcanRegs;
+
+typedef struct FlexcanState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ IMXCCMState *ccm;
+ qemu_irq irq;
+
+ CanBusState *canbus;
+ CanBusClientState bus_client;
+
+ union {
+ FlexcanRegs regs;
+ uint32_t regs_raw[sizeof(FlexcanRegs) / 4];
+ };
+ int64_t timer_start;
+ uint64_t last_rx_timer_cycles;
+ int32_t locked_mbidx;
+ int32_t smb_target_mbidx;
+ uint32_t timer_freq;
+} FlexcanState;
+
+#define TYPE_CAN_FLEXCAN "flexcan"
+
+OBJECT_DECLARE_SIMPLE_TYPE(FlexcanState, CAN_FLEXCAN);
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* Re: [PULL 34/54] hw/net/can/flexcan: NXP FlexCAN core emulation
2026-06-29 12:00 ` [PULL 34/54] hw/net/can/flexcan: NXP FlexCAN core emulation Peter Maydell
@ 2026-07-02 12:37 ` Philippe Mathieu-Daudé
2026-07-02 17:13 ` Bernhard Beschow
2026-07-03 11:34 ` Peter Maydell
1 sibling, 1 reply; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-02 12:37 UTC (permalink / raw)
To: qemu-devel, Matyáš Bobek
Cc: Peter Maydell, Pavel Pisa, Bernhard Beschow
Hi Matyáš,
On 29/6/26 14:00, Peter Maydell wrote:
> From: Matyáš Bobek <matyas.bobek@gmail.com>
>
> Added the FlexCAN2 emulator implementation core, with
> CAN_FLEXCAN Kconfig flag and MAINTAINERS entry.
>
> FlexCAN2 version can be found in i.MX6 SoCs and others.
>
> More information about the implementation can be found in [1].
>
> Some macro and struct defintions were borrowed from the Linux kernel.
> The original authors agreed with relicensing them to GPL-2.0-or-later on
> the qemu-devel mailing list.
>
> [1] http://dspace.cvut.cz/bitstream/handle/10467/122654/F3-BP-2025-Bobek-Matyas-BP_Bobek_FlexCAN_final_4.pdf
>
> Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com>
> Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
> Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
> Reviewed-by: Bernhard Beschow <shentey@gmail.com>
> Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz>
> Message-id: 03dc62ff8013bb946aab8f64e51638b810629529.1782140438.git.matyas.bobek@gmail.com
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> MAINTAINERS | 8 +
> hw/net/Kconfig | 5 +
> hw/net/can/flexcan.c | 1396 +++++++++++++++++++++++++++++++++++++
> hw/net/can/flexcan_regs.h | 197 ++++++
> hw/net/can/meson.build | 1 +
> hw/net/can/trace-events | 18 +
> include/hw/net/flexcan.h | 145 ++++
> 7 files changed, 1770 insertions(+)
This commit is huge, not easy to follow / review.
> create mode 100644 hw/net/can/flexcan.c
> create mode 100644 hw/net/can/flexcan_regs.h
> create mode 100644 include/hw/net/flexcan.h
> diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c
> new file mode 100644
> index 0000000000..1ea459d9f6
> --- /dev/null
> +++ b/hw/net/can/flexcan.c
> @@ -0,0 +1,1396 @@
> +/*
> + * QEMU model of the NXP FLEXCAN device.
> + *
> + * This implementation is based on the following reference manual:
> + * i.MX 6Dual/6Quad Applications Processor Reference Manual
> + * Document Number: IMX6DQRM, Rev. 6, 05/2020
> + *
> + * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
> + *
> + * Based on CTU CAN FD emulation implemented by Jan Charvat.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "hw/core/sysbus.h"
> +#include "qapi/error.h"
> +#include "hw/core/irq.h"
> +#include "migration/vmstate.h"
> +#include "net/can_emu.h"
> +#include "hw/core/qdev-properties.h"
> +#include "trace.h"
> +
> +#include "hw/net/flexcan.h"
> +#include "flexcan_regs.h"
> +#include "qemu/timer.h"
> +/**
> + * flexcan_get_bitrate() - Calculate CAN bitrate (in Hz)
> + * @s: FlexCAN device pointer
> + *
> + * The bitrate is determined by FlexCAN configuration in CTRL1 register,
> + * and CCM co
> + */
> +static uint32_t flexcan_get_bitrate(FlexcanState *s)
> +{
> + uint32_t conf_presdiv = (s->regs.ctrl & FLEXCAN_CTRL_PRESDIV_MASK) >> 24;
> + uint32_t conf_pseg1 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG1_MASK) >> 19;
> + uint32_t conf_pseg2 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG2_MASK) >> 16;
> + uint32_t conf_propseg = s->regs.ctrl & FLEXCAN_CTRL_PROPSEG_MASK;
> +
> + /* N of time quanta for segments */
> + uint32_t tseg1 = 2 + conf_pseg1 + conf_propseg;
> + uint32_t tseg2 = 1 + conf_pseg2;
> + uint32_t total_qpb = 1 + tseg1 + tseg2;
> +
> + uint32_t pe_freq, s_freq, bitrate;
> +
> + assert(s->ccm);
Instead of that deep buried late unexplained assertion crashing
when the guest is running, ...
> +
> + /* s_freq: CAN clock from CCM divided by the prescaler */
> + pe_freq = imx_ccm_get_clock_frequency(s->ccm, CLK_CAN);
> + s_freq = pe_freq / (1 + conf_presdiv);
> + bitrate = s_freq / total_qpb;
> +
> + trace_flexcan_get_bitrate(DEVICE(s)->canonical_path, pe_freq,
> + 1 + conf_presdiv, s_freq, tseg1, tseg2, total_qpb,
> + bitrate);
> + return bitrate;
> +}
> +static void flexcan_init(Object *obj)
> +{
> + FlexcanState *s = CAN_FLEXCAN(obj);
> +
> + memory_region_init_io(
> + &s->iomem, obj, &flexcan_ops, s, TYPE_CAN_FLEXCAN,
> + offsetof(FlexcanRegs, _reserved6)
> + );
> +}
> +
> +static void flexcan_realize(DeviceState *dev, Error **errp)
> +{
> + FlexcanState *s = CAN_FLEXCAN(dev);
> +
> + if (s->canbus) {
> + if (flexcan_connect_to_bus(s, s->canbus) < 0) {
> + error_setg(errp, "%s: flexcan_connect_to_bus failed",
> + dev->canonical_path);
> + return;
> + }
> + }
... expose it as a link in [*] and check it is set here, returning
an error to the upper layer before starting emulation:
if (!s->dma_mr) {
error_setg(errp, TYPE_CAN_FLEXCAN " 'clock-control-module' "
"link not set");
return;
}
> +
> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
> + sysbus_init_irq(SYS_BUS_DEVICE(SYS_BUS_DEVICE(dev)), &s->irq);
> +}
> +
> +static const VMStateDescription vmstate_can = {
> + .name = TYPE_CAN_FLEXCAN,
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (const VMStateField[]) {
> + VMSTATE_INT64(timer_start, FlexcanState),
> + VMSTATE_UINT32_ARRAY(regs_raw, FlexcanState, sizeof(FlexcanRegs) / 4),
> + VMSTATE_INT32(locked_mbidx, FlexcanState),
> + VMSTATE_INT32(smb_target_mbidx, FlexcanState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static const Property flexcan_properties[] = {
> + DEFINE_PROP_LINK("canbus", FlexcanState, canbus, TYPE_CAN_BUS,
> + CanBusState *),
[*] here:
DEFINE_PROP_LINK("clock-control-module", FlexcanState, ccm,
TYPE_IMX_CCM, IMXCCMState *),
> +};
> +
> +static void flexcan_class_init(ObjectClass *klass, const void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + ResettableClass *rc = RESETTABLE_CLASS(klass);
> +
> + rc->phases.enter = flexcan_reset_enter;
> + rc->phases.hold = flexcan_reset_hold;
> + dc->realize = flexcan_realize;
> + device_class_set_props(dc, flexcan_properties);
> + dc->vmsd = &vmstate_can;
> + dc->desc = "i.MX FLEXCAN Controller";
> +}
> +
> +static const TypeInfo flexcan_info = {
> + .name = TYPE_CAN_FLEXCAN,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(FlexcanState),
> + .class_init = flexcan_class_init,
> + .instance_init = flexcan_init,
> +};
^ permalink raw reply [flat|nested] 67+ messages in thread* Re: [PULL 34/54] hw/net/can/flexcan: NXP FlexCAN core emulation
2026-07-02 12:37 ` Philippe Mathieu-Daudé
@ 2026-07-02 17:13 ` Bernhard Beschow
0 siblings, 0 replies; 67+ messages in thread
From: Bernhard Beschow @ 2026-07-02 17:13 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, qemu-devel, Matyáš Bobek
Cc: Peter Maydell, Pavel Pisa
Am 2. Juli 2026 12:37:59 UTC schrieb "Philippe Mathieu-Daudé" <philmd@oss.qualcomm.com>:
>Hi Matyáš,
>
>On 29/6/26 14:00, Peter Maydell wrote:
>> From: Matyáš Bobek <matyas.bobek@gmail.com>
>>
>> Added the FlexCAN2 emulator implementation core, with
>> CAN_FLEXCAN Kconfig flag and MAINTAINERS entry.
>>
>> FlexCAN2 version can be found in i.MX6 SoCs and others.
>>
>> More information about the implementation can be found in [1].
>>
>> Some macro and struct defintions were borrowed from the Linux kernel.
>> The original authors agreed with relicensing them to GPL-2.0-or-later on
>> the qemu-devel mailing list.
>>
>> [1] http://dspace.cvut.cz/bitstream/handle/10467/122654/F3-BP-2025-Bobek-Matyas-BP_Bobek_FlexCAN_final_4.pdf
>>
>> Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com>
>> Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
>> Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
>> Reviewed-by: Bernhard Beschow <shentey@gmail.com>
>> Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz>
>> Message-id: 03dc62ff8013bb946aab8f64e51638b810629529.1782140438.git.matyas.bobek@gmail.com
>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>> ---
>> MAINTAINERS | 8 +
>> hw/net/Kconfig | 5 +
>> hw/net/can/flexcan.c | 1396 +++++++++++++++++++++++++++++++++++++
>> hw/net/can/flexcan_regs.h | 197 ++++++
>> hw/net/can/meson.build | 1 +
>> hw/net/can/trace-events | 18 +
>> include/hw/net/flexcan.h | 145 ++++
>> 7 files changed, 1770 insertions(+)
>
>This commit is huge, not easy to follow / review.
>
>> create mode 100644 hw/net/can/flexcan.c
>> create mode 100644 hw/net/can/flexcan_regs.h
>> create mode 100644 include/hw/net/flexcan.h
>
>
>> diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c
>> new file mode 100644
>> index 0000000000..1ea459d9f6
>> --- /dev/null
>> +++ b/hw/net/can/flexcan.c
>> @@ -0,0 +1,1396 @@
>> +/*
>> + * QEMU model of the NXP FLEXCAN device.
>> + *
>> + * This implementation is based on the following reference manual:
>> + * i.MX 6Dual/6Quad Applications Processor Reference Manual
>> + * Document Number: IMX6DQRM, Rev. 6, 05/2020
>> + *
>> + * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
>> + *
>> + * Based on CTU CAN FD emulation implemented by Jan Charvat.
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/log.h"
>> +#include "hw/core/sysbus.h"
>> +#include "qapi/error.h"
>> +#include "hw/core/irq.h"
>> +#include "migration/vmstate.h"
>> +#include "net/can_emu.h"
>> +#include "hw/core/qdev-properties.h"
>> +#include "trace.h"
>> +
>> +#include "hw/net/flexcan.h"
>> +#include "flexcan_regs.h"
>> +#include "qemu/timer.h"
>
>
>> +/**
>> + * flexcan_get_bitrate() - Calculate CAN bitrate (in Hz)
>> + * @s: FlexCAN device pointer
>> + *
>> + * The bitrate is determined by FlexCAN configuration in CTRL1 register,
>> + * and CCM co
>> + */
>> +static uint32_t flexcan_get_bitrate(FlexcanState *s)
>> +{
>> + uint32_t conf_presdiv = (s->regs.ctrl & FLEXCAN_CTRL_PRESDIV_MASK) >> 24;
>> + uint32_t conf_pseg1 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG1_MASK) >> 19;
>> + uint32_t conf_pseg2 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG2_MASK) >> 16;
>> + uint32_t conf_propseg = s->regs.ctrl & FLEXCAN_CTRL_PROPSEG_MASK;
>> +
>> + /* N of time quanta for segments */
>> + uint32_t tseg1 = 2 + conf_pseg1 + conf_propseg;
>> + uint32_t tseg2 = 1 + conf_pseg2;
>> + uint32_t total_qpb = 1 + tseg1 + tseg2;
>> +
>> + uint32_t pe_freq, s_freq, bitrate;
>> +
>> + assert(s->ccm);
>
>Instead of that deep buried late unexplained assertion crashing
>when the guest is running, ...
>
>> +
>> + /* s_freq: CAN clock from CCM divided by the prescaler */
>> + pe_freq = imx_ccm_get_clock_frequency(s->ccm, CLK_CAN);
>> + s_freq = pe_freq / (1 + conf_presdiv);
>> + bitrate = s_freq / total_qpb;
>> +
>> + trace_flexcan_get_bitrate(DEVICE(s)->canonical_path, pe_freq,
>> + 1 + conf_presdiv, s_freq, tseg1, tseg2, total_qpb,
>> + bitrate);
>> + return bitrate;
>> +}
>> +static void flexcan_init(Object *obj)
>> +{
>> + FlexcanState *s = CAN_FLEXCAN(obj);
>> +
>> + memory_region_init_io(
>> + &s->iomem, obj, &flexcan_ops, s, TYPE_CAN_FLEXCAN,
>> + offsetof(FlexcanRegs, _reserved6)
>> + );
>> +}
>> +
>> +static void flexcan_realize(DeviceState *dev, Error **errp)
>> +{
>> + FlexcanState *s = CAN_FLEXCAN(dev);
>> +
>> + if (s->canbus) {
>> + if (flexcan_connect_to_bus(s, s->canbus) < 0) {
>> + error_setg(errp, "%s: flexcan_connect_to_bus failed",
>> + dev->canonical_path);
>> + return;
>> + }
>> + }
>
>... expose it as a link in [*] and check it is set here, returning
>an error to the upper layer before starting emulation:
>
> if (!s->dma_mr) {
> error_setg(errp, TYPE_CAN_FLEXCAN " 'clock-control-module' "
> "link not set");
> return;
> }
>
>> +
>> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
>> + sysbus_init_irq(SYS_BUS_DEVICE(SYS_BUS_DEVICE(dev)), &s->irq);
>> +}
>> +
>> +static const VMStateDescription vmstate_can = {
>> + .name = TYPE_CAN_FLEXCAN,
>> + .version_id = 1,
>> + .minimum_version_id = 1,
>> + .fields = (const VMStateField[]) {
>> + VMSTATE_INT64(timer_start, FlexcanState),
>> + VMSTATE_UINT32_ARRAY(regs_raw, FlexcanState, sizeof(FlexcanRegs) / 4),
>> + VMSTATE_INT32(locked_mbidx, FlexcanState),
>> + VMSTATE_INT32(smb_target_mbidx, FlexcanState),
>> + VMSTATE_END_OF_LIST(),
>> + },
>> +};
>> +
>> +static const Property flexcan_properties[] = {
>> + DEFINE_PROP_LINK("canbus", FlexcanState, canbus, TYPE_CAN_BUS,
>> + CanBusState *),
>
>[*] here:
>
> DEFINE_PROP_LINK("clock-control-module", FlexcanState, ccm,
> TYPE_IMX_CCM, IMXCCMState *),
>
Yeah, this is even consistent with the "canbus" property. I'll take care of it in my imx8mp-flexcan series, so I can address your comment there, too.
Best regards,
Bernhard
>> +};
>> +
>> +static void flexcan_class_init(ObjectClass *klass, const void *data)
>> +{
>> + DeviceClass *dc = DEVICE_CLASS(klass);
>> + ResettableClass *rc = RESETTABLE_CLASS(klass);
>> +
>> + rc->phases.enter = flexcan_reset_enter;
>> + rc->phases.hold = flexcan_reset_hold;
>> + dc->realize = flexcan_realize;
>> + device_class_set_props(dc, flexcan_properties);
>> + dc->vmsd = &vmstate_can;
>> + dc->desc = "i.MX FLEXCAN Controller";
>> +}
>> +
>> +static const TypeInfo flexcan_info = {
>> + .name = TYPE_CAN_FLEXCAN,
>> + .parent = TYPE_SYS_BUS_DEVICE,
>> + .instance_size = sizeof(FlexcanState),
>> + .class_init = flexcan_class_init,
>> + .instance_init = flexcan_init,
>> +};
>
^ permalink raw reply [flat|nested] 67+ messages in thread
* Re: [PULL 34/54] hw/net/can/flexcan: NXP FlexCAN core emulation
2026-06-29 12:00 ` [PULL 34/54] hw/net/can/flexcan: NXP FlexCAN core emulation Peter Maydell
2026-07-02 12:37 ` Philippe Mathieu-Daudé
@ 2026-07-03 11:34 ` Peter Maydell
1 sibling, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-07-03 11:34 UTC (permalink / raw)
To: qemu-devel; +Cc: Matyáš Bobek, Pavel Pisa, Bernhard Beschow
On Mon, 29 Jun 2026 at 13:01, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> From: Matyáš Bobek <matyas.bobek@gmail.com>
>
> Added the FlexCAN2 emulator implementation core, with
> CAN_FLEXCAN Kconfig flag and MAINTAINERS entry.
>
> FlexCAN2 version can be found in i.MX6 SoCs and others.
>
> More information about the implementation can be found in [1].
>
> Some macro and struct defintions were borrowed from the Linux kernel.
> The original authors agreed with relicensing them to GPL-2.0-or-later on
> the qemu-devel mailing list.
>
> [1] http://dspace.cvut.cz/bitstream/handle/10467/122654/F3-BP-2025-Bobek-Matyas-BP_Bobek_FlexCAN_final_4.pdf
>
Hi; Coverity points out some issues with this change; details below.
> +/**
> + * flexcan_fifo_pop() - Pop message from FIFO and update IRQs
> + * @s: FlexCAN device pointer
> + *
> + * Does not require the queue to be non-empty.
> + */
> +static void flexcan_fifo_pop(FlexcanState *s)
> +{
> + if (s->regs.fifo.mb_back.can_ctrl != 0) {
> + /* move queue elements forward */
> + memmove(&s->regs.fifo.mb_back, &s->regs.fifo.mbs_queue[0],
> + sizeof(s->regs.fifo.mbs_queue));
CID 1662971: Coverity doesn't like this because mb_back is
only 1 element long and you're copying more than that.
We seem to use these fields only in this function -- did we
really need to specify them as two separate fields rather
than one array so that it's a bit clearer that we're just
copying within what is effectively a single 6 element array?
> +
> + /* clear the first-in slot */
> + memset(&s->regs.mbs[FLEXCAN_FIFO_DEPTH - 1], 0,
> + sizeof(FlexcanRegsMessageBuffer));
> +
> + trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 1,
> + s->regs.fifo.mb_back.can_ctrl != 0);
> + } else {
> + trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 0, 0);
> + }
> +
> + if (s->regs.fifo.mb_back.can_ctrl != 0) {
> + flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE);
> + } else {
> + flexcan_irq_iflag_clear(s, I_FIFO_AVAILABLE);
> + }
> +}
> +
> +static enum FlexcanRx flexcan_mb_rx(FlexcanState *s, const qemu_can_frame *buf)
> +{
> + int last_not_free_to_receive_mbid = -1;
CID 1662790:
Initially last_not_free_to_receive_mbid is -1.
> + bool last_not_free_to_receive_locked = false;
> +
> + FlexcanRegsMessageBuffer *first_mb = flexcan_get_first_message_buffer(s);
> + FlexcanRegsMessageBuffer *last_mb = flexcan_get_last_enabled_mailbox(s);
> +
> + for (FlexcanRegsMessageBuffer *mb = first_mb;
> + mb <= last_mb; mb++) {
> + int mbid = mb - s->regs.mbs;
In this loop mbid must always be positive, because the bounds on mb
mean it's always somewhere inside the s->regs.mbs array.
> + enum FlexcanCheck r = flexcan_mb_rx_check_mb(s, buf, mbid);
> + if (r == FLEXCAN_CHECK_MB_MATCH) {
> + flexcan_mb_move_in(s, buf, mb);
> + flexcan_irq_iflag_set(s, mbid);
> + return FLEXCAN_RX_SEARCH_ACCEPT;
> + }
> +
> + if (r == FLEXCAN_CHECK_MB_MATCH_NON_FREE) {
> + last_not_free_to_receive_mbid = mbid;
> + last_not_free_to_receive_locked = false;
> + } else if (r == FLEXCAN_CHECK_MB_MATCH_LOCKED) {
> + /*
> + * message buffer is locked,
> + * we can move in the message after it's unlocked
> + */
> + last_not_free_to_receive_mbid = mbid;
> + last_not_free_to_receive_locked = true;
> + }
In the loop body the only places where we assign to
last_not_free_to_receive_mbid are where we set it to mbid.
> + }
> +
> + if (last_not_free_to_receive_mbid >= -1) {
So when can this condition be false ?
> + if (last_not_free_to_receive_locked) {
> + /*
> + * copy to temporary mailbox (SMB)
> + * it will be moved in when the mailbox is unlocked
> + */
> + s->regs.rx_smb0.can_ctrl =
> + s->regs.mbs[last_not_free_to_receive_mbid].can_id;
> + flexcan_mb_move_in(s, buf, &s->regs.rx_smb0);
> + s->smb_target_mbidx = last_not_free_to_receive_mbid;
> + return FLEXCAN_RX_SEARCH_ACCEPT;
> + }
> +
> + if (s->regs.mcr & FLEXCAN_MCR_IRMQ) {
> + flexcan_mb_move_in(s, buf,
> + &s->regs.mbs[last_not_free_to_receive_mbid]);
Here we use last_not_free_to_receive_mbid as an index into mbs,
but it could be -1, which will underrun the array.
Should the condition above have been ">= 0" ?
> + flexcan_irq_iflag_set(s, last_not_free_to_receive_mbid);
> + return FLEXCAN_RX_SEARCH_ACCEPT;
> + }
> + }
> +
> + return FLEXCAN_RX_SEARCH_RETRY;
> +}
> +/* ========== I/O handling ========== */
> +static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val,
> + unsigned size)
> +{
> + FlexcanState *s = opaque;
> + uint32_t write_mask = ((const uint32_t *)
> + &flexcan_regs_write_mask)[addr / 4];
> + uint32_t old_value = s->regs_raw[addr / 4];
> +
> + /*
> + * 0 for bits that can "only be written in Freeze mode as it is blocked
> + * by hardware in other modes"
> + */
> + const uint32_t freeze_mask_mcr = 0xDF54CC80;
> + const uint32_t freeze_mask_ctrl1 = 0x0000E740;
> +
> + flexcan_trace_mem_op(s, addr, val, size, true);
> + switch (addr) {
> + case offsetof(FlexcanRegs, mcr):
> + if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
> + write_mask &= freeze_mask_mcr;
> + }
> + s->regs.mcr = (val & write_mask) | (old_value & ~write_mask);
> + flexcan_set_mcr(s, old_value);
> + break;
> + case offsetof(FlexcanRegs, ctrl):
> + if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
> + write_mask &= freeze_mask_ctrl1;
> + }
> + s->regs.ctrl = (val & write_mask) | (old_value & ~write_mask);
> + break;
> + case offsetof(FlexcanRegs, iflag1):
> + s->regs.iflag1 &= ~val;
> + if ((s->regs.mcr & FLEXCAN_MCR_FEN) &&
> + (val & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE)) {
> + flexcan_fifo_pop(s);
> + }
> + break;
> + case offsetof(FlexcanRegs, iflag2):
> + s->regs.iflag2 &= ~val;
> + break;
> + case offsetof(FlexcanRegs, ctrl2):
> + QEMU_FALLTHROUGH;
> + case offsetof(FlexcanRegs, ecr):
> + QEMU_FALLTHROUGH;
> + case offsetof(FlexcanRegs, rxmgmask):
> + QEMU_FALLTHROUGH;
> + case offsetof(FlexcanRegs, rx14mask):
> + QEMU_FALLTHROUGH;
> + case offsetof(FlexcanRegs, rx15mask):
> + QEMU_FALLTHROUGH;
> + case offsetof(FlexcanRegs, rxfgmask):
> + QEMU_FALLTHROUGH;
> + case offsetof(FlexcanRegs, rximr[0]) ... offsetof(FlexcanRegs, rximr[63]):
> + /* these registers can only be written in freeze mode */
> + if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
> + break;
> + }
> + QEMU_FALLTHROUGH;
> + default:
> + s->regs_raw[addr / 4] = (val & write_mask) | (old_value & ~write_mask);
> +
> + if (addr >= offsetof(FlexcanRegs, mb) &&
> + addr < offsetof(FlexcanRegs, _reserved4)) {
> + /* access to mailbox */
> + int mbid = (addr - offsetof(FlexcanRegs, mb)) /
> + sizeof(FlexcanRegsMessageBuffer);
CID 1662974: This bounds check isn't sufficient to ensure that
mbid is within the bounds of s->regs.mbs[], which flexcan_mb_write()
will access. This is the same as 1662975 below; see that for detail.
> +
> + if (s->locked_mbidx == mbid) {
> + flexcan_mb_unlock(s);
> + }
> +
> + /* check for invalid writes into FIFO region */
> + if (s->regs.mcr & FLEXCAN_MCR_FEN && mbid < FLEXCAN_FIFO_DEPTH) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: Invalid write to Rx-FIFO structure",
> + DEVICE(s)->canonical_path);
> + return;
> + }
> +
> + /* run mailbox processing function on write to control word */
> + if ((addr & 0xF) == 0) {
> + flexcan_mb_write(s, mbid);
> + }
> + }
> + break;
> + }
> +
> + flexcan_irq_update(s);
> +}
> +
> +static uint64_t flexcan_mem_read(void *opqaue, hwaddr addr, unsigned size)
> +{
> + FlexcanState *s = opqaue;
> + uint32_t rv = s->regs_raw[addr >> 2];
> +
> + if (addr >= offsetof(FlexcanRegs, mb) &&
> + addr < offsetof(FlexcanRegs, _reserved4)) {
> + /* reading from mailbox */
> + hwaddr offset = addr - offsetof(FlexcanRegs, mb);
> + int mbid = offset / sizeof(FlexcanRegsMessageBuffer);
> +
> + if (addr % 16 == 0 && s->locked_mbidx != mbid) {
> + /* reading control word locks the mailbox */
> + flexcan_mb_unlock(s);
> + flexcan_mb_lock(s, mbid);
> + flexcan_irq_update(s);
> + rv = s->regs.mbs[mbid].can_ctrl & ~FLEXCAN_MB_CNT_NOT_SRV;
CID 1662972, 1662975:
Here we guard the bounds on addr based on the struct layout,
where the relevant part is:
union { /* 0x80 - not affected by soft reset */
uint32_t mb[sizeof(FlexcanRegsMessageBuffer) * FLEXCAN_MAILBOX_COUNT];
FlexcanRegsMessageBuffer mbs[FLEXCAN_MAILBOX_COUNT];
FlexcanRegsRXFifo fifo;
};
uint32_t _reserved4[256]; /* 0x480 */
So we know that 'addr' is inside the union. However, 'mbs' is
not the smallest member of the union, because 'mbs' is 64 * 16 == 1024
bytes, but mb is 64 * 16 * 4 == 14436 bytes, and fifo is
(6 * 16 + 128 * 4) == 608 bytes. So because we only bounded
'mbid' based on the total size of the union, it can be larger
than the size of the mb[] array.
What is the intention here?
As an aside, using unions like this is rather error-prone, because
it is an easy way to introduce endianness bugs. This one happens to
work because all the fields in all the sub-structs end up being
uint32_t, but for instance if there was a uint16_t field in there
somewhere that would be an endianness bug.
> +
> + } else if (addr == offsetof(FlexcanRegs, timer)) {
> + flexcan_mb_unlock(s);
> + flexcan_irq_update(s);
> + rv = flexcan_get_timestamp(s, false);
> + }
> +
> + flexcan_trace_mem_op(s, addr, rv, size, false);
> + return rv;
> +}
> +#define FLEXCAN_GENMASK(h, l) (((~(uint32_t)0) >> (31 - (h) + (l))) << (l))
Please don't roll your own mask creation macros. We have
MAKE_64BIT_MASK() in bitops.h.
> +/* FLEXCAN CRC Register (CRCR) bits */
> +#define FLEXCAN_CRCR_MBCRC_MASK FLEXCAN_GENMASK(22, 16)
> +#define FLEXCAN_CRCR_MBCRC(x) (((x) & FLEXCAN_CRCR_MBCRC_MASK) << 16)
CID 1662973: FLEXCAN_CRCR_MBCRC_MASK is 0x7F0000. That means
that (((x) & FLEXCAN_CRCR_MBCRC_MASK) << 16) will always be 0,
because the field gets shifted off the top of the 32-bit type.
I suspect you meant here
(((x) << 16) & FLEXCAN_CRCR_MBCRC_MASK)
The standard QEMU FIELD macros let you use FIELD_DP32() which
avoids this kind of bug entirely.
thanks
-- PMM
^ permalink raw reply [flat|nested] 67+ messages in thread
* [PULL 35/54] hw/arm: Plug FlexCAN into FSL_IMX6 and Sabrelite
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (33 preceding siblings ...)
2026-06-29 12:00 ` [PULL 34/54] hw/net/can/flexcan: NXP FlexCAN core emulation Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 36/54] tests: Add qtests for FlexCAN Peter Maydell
` (19 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Matyáš Bobek <matyas.bobek@gmail.com>
FlexcanState is added to the FslIMX6State struct like other peripherals.
Add two new machine properties to Sabrelite machine for linking
the embedded FlexCAN instances to QEMU CAN buses by name.
No other machine uses FslIMX6State.
Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com>
Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
Reviewed-by: Bernhard Beschow <shentey@gmail.com>
Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz>
Message-id: 4159df998b0508bb411284ef406de23bbc5edfd8.1782140438.git.matyas.bobek@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/Kconfig | 1 +
hw/arm/fsl-imx6.c | 29 +++++++++++++++++++++++++++++
hw/arm/sabrelite.c | 24 ++++++++++++++++++++++++
include/hw/arm/fsl-imx6.h | 6 ++++++
4 files changed, 60 insertions(+)
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 500bfdfe2a..5f5c4899ad 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -520,6 +520,7 @@ config FSL_IMX6
select IMX_FEC
select IMX_I2C
select IMX_USBPHY
+ select CAN_FLEXCAN
select WDT_IMX2
select PL310 # cache controller
select PCI_EXPRESS_DESIGNWARE
diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
index f663ddbf0a..bf106a9063 100644
--- a/hw/arm/fsl-imx6.c
+++ b/hw/arm/fsl-imx6.c
@@ -90,6 +90,10 @@ static void fsl_imx6_init(Object *obj)
snprintf(name, NAME_SIZE, "spi%d", i + 1);
object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI);
}
+ for (i = 0; i < FSL_IMX6_NUM_CANS; i++) {
+ snprintf(name, NAME_SIZE, "flexcan%d", i + 1);
+ object_initialize_child(obj, name, &s->flexcan[i], TYPE_CAN_FLEXCAN);
+ }
for (i = 0; i < FSL_IMX6_NUM_WDTS; i++) {
snprintf(name, NAME_SIZE, "wdt%d", i);
object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT);
@@ -377,6 +381,27 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in(gic, spi_table[i].irq));
}
+ /* Initialize all FLEXCANs */
+ for (i = 0; i < FSL_IMX6_NUM_CANS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } flexcan_table[FSL_IMX6_NUM_CANS] = {
+ { FSL_IMX6_CAN1_ADDR, FSL_IMX6_FLEXCAN1_IRQ },
+ { FSL_IMX6_CAN2_ADDR, FSL_IMX6_FLEXCAN2_IRQ },
+ };
+
+ s->flexcan[i].ccm = IMX_CCM(&s->ccm);
+ object_property_set_link(OBJECT(&s->flexcan[i]), "canbus",
+ OBJECT(s->canbus[i]), &error_abort);
+
+ sysbus_realize(SYS_BUS_DEVICE(&s->flexcan[i]), &error_abort);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->flexcan[i]), 0, flexcan_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->flexcan[i]), 0,
+ qdev_get_gpio_in(gic, flexcan_table[i].irq));
+ }
+
object_property_set_uint(OBJECT(&s->eth), "phy-num", s->phy_num,
&error_abort);
qemu_configure_nic_device(DEVICE(&s->eth), true, NULL);
@@ -480,6 +505,10 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
static const Property fsl_imx6_properties[] = {
DEFINE_PROP_UINT32("fec-phy-num", FslIMX6State, phy_num, 0),
+ DEFINE_PROP_LINK("canbus0", FslIMX6State, canbus[0], TYPE_CAN_BUS,
+ CanBusState *),
+ DEFINE_PROP_LINK("canbus1", FslIMX6State, canbus[1], TYPE_CAN_BUS,
+ CanBusState *),
};
static void fsl_imx6_class_init(ObjectClass *oc, const void *data)
diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c
index 7e036e6388..84bbc2f979 100644
--- a/hw/arm/sabrelite.c
+++ b/hw/arm/sabrelite.c
@@ -24,6 +24,7 @@ struct SabreliteMachineState {
MachineState parent_obj;
FslIMX6State soc;
+ CanBusState *canbus[FSL_IMX6_NUM_CANS];
};
#define TYPE_SABRELITE_MACHINE MACHINE_TYPE_NAME("sabrelite")
@@ -64,6 +65,13 @@ static void sabrelite_init(MachineState *machine)
/* Ethernet PHY address is 6 */
object_property_set_int(OBJECT(&s->soc), "fec-phy-num", 6, &error_fatal);
+ for (int i = 0; i < FSL_IMX6_NUM_CANS; i++) {
+ g_autofree char *bus_name = g_strdup_printf("canbus%d", i);
+
+ object_property_set_link(OBJECT(&s->soc), bus_name,
+ OBJECT(s->canbus[i]), &error_fatal);
+ }
+
qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
memory_region_add_subregion(get_system_memory(), FSL_IMX6_MMDC_ADDR,
@@ -112,6 +120,21 @@ static void sabrelite_init(MachineState *machine)
}
}
+static void sabrelite_machine_instance_init(Object *obj)
+{
+ SabreliteMachineState *s = SABRELITE_MACHINE(obj);
+
+ object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
+ (Object **)&s->canbus[0],
+ object_property_allow_set_link,
+ 0);
+
+ object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
+ (Object **)&s->canbus[1],
+ object_property_allow_set_link,
+ 0);
+}
+
static void sabrelite_machine_class_init(ObjectClass *oc, const void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
@@ -128,6 +151,7 @@ static const TypeInfo sabrelite_machine_init_typeinfo = {
.name = TYPE_SABRELITE_MACHINE,
.parent = TYPE_MACHINE,
.class_init = sabrelite_machine_class_init,
+ .instance_init = sabrelite_machine_instance_init,
.instance_size = sizeof(SabreliteMachineState),
.abstract = false,
.interfaces = arm_machine_interfaces,
diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h
index cddd100dd6..376deea3a4 100644
--- a/include/hw/arm/fsl-imx6.h
+++ b/include/hw/arm/fsl-imx6.h
@@ -30,12 +30,14 @@
#include "hw/sd/sdhci.h"
#include "hw/ssi/imx_spi.h"
#include "hw/net/imx_fec.h"
+#include "hw/net/flexcan.h"
#include "hw/usb/chipidea.h"
#include "hw/usb/imx-usb-phy.h"
#include "hw/pci-host/designware.h"
#include "hw/core/or-irq.h"
#include "system/memory.h"
#include "target/arm/cpu.h"
+#include "net/can_emu.h"
#include "qom/object.h"
#define TYPE_FSL_IMX6 "fsl-imx6"
@@ -51,6 +53,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(FslIMX6State, FSL_IMX6)
#define FSL_IMX6_NUM_WDTS 2
#define FSL_IMX6_NUM_USB_PHYS 2
#define FSL_IMX6_NUM_USBS 4
+#define FSL_IMX6_NUM_CANS 2
struct FslIMX6State {
/*< private >*/
@@ -73,6 +76,7 @@ struct FslIMX6State {
IMXUSBPHYState usbphy[FSL_IMX6_NUM_USB_PHYS];
ChipideaState usb[FSL_IMX6_NUM_USBS];
IMXFECState eth;
+ FlexcanState flexcan[FSL_IMX6_NUM_CANS];
DesignwarePCIEHost pcie;
OrIRQState pcie4_msi_irq;
MemoryRegion rom;
@@ -80,6 +84,8 @@ struct FslIMX6State {
MemoryRegion ocram;
MemoryRegion ocram_alias;
uint32_t phy_num;
+
+ CanBusState *canbus[FSL_IMX6_NUM_CANS];
};
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 36/54] tests: Add qtests for FlexCAN
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (34 preceding siblings ...)
2026-06-29 12:00 ` [PULL 35/54] hw/arm: Plug FlexCAN into FSL_IMX6 and Sabrelite Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 37/54] docs/arm/sabrelite: Mention FlexCAN support Peter Maydell
` (18 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Matyáš Bobek <matyas.bobek@gmail.com>
The tests do not test all of the FlexCAN emulator functionality.
Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com>
Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
Reviewed-by: Bernhard Beschow <shentey@gmail.com>
Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz>
Message-id: 3d47746ef30f29c58fec10267bdce7dac2c756a7.1782140438.git.matyas.bobek@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
MAINTAINERS | 1 +
tests/qtest/flexcan-test.c | 421 +++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 1 +
3 files changed, 423 insertions(+)
create mode 100644 tests/qtest/flexcan-test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 91317818bf..0385418ce7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2114,6 +2114,7 @@ S: Maintained
F: hw/net/can/flexcan.c
F: hw/net/can/flexcan_regs.h
F: include/hw/net/flexcan.h
+F: tests/qtest/flexcan-test.c
EDU
M: Jiri Slaby <jslaby@suse.cz>
diff --git a/tests/qtest/flexcan-test.c b/tests/qtest/flexcan-test.c
new file mode 100644
index 0000000000..87d947a806
--- /dev/null
+++ b/tests/qtest/flexcan-test.c
@@ -0,0 +1,421 @@
+/*
+ * QTests for FlexCAN CAN controller device model
+ *
+ * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest-single.h"
+
+#include "hw/net/flexcan.h"
+#include "hw/net/can/flexcan_regs.h"
+
+#define FSL_IMX6_CAN2_ADDR 0x02094000
+#define FSL_IMX6_CAN2_SIZE 0x4000
+#define FSL_IMX6_CAN1_ADDR 0x02090000
+#define FSL_IMX6_CAN1_SIZE 0x4000
+
+#define FC_QEMU_ARGS "-nographic -M sabrelite " \
+ "-object can-bus,id=qcan0 " \
+ "-machine canbus0=qcan0 -machine canbus1=qcan0"
+
+/* used for masking out unused/reserved bits */
+#define FC_MB_CNT_USED_MASK (~0xF080FFFFu)
+
+#define FCREG(BASE_ADDR, REG) ((BASE_ADDR) + offsetof(FlexcanRegs, REG))
+#define FCMB(BASE_ADDR, MB_IDX, WORD_IDX) (FCREG(BASE_ADDR, mbs) + \
+ 0x10 * (MB_IDX) + 4 * (WORD_IDX))
+
+typedef struct FcTestFrame {
+ uint32_t id;
+ uint32_t data[2];
+ uint8_t len;
+ bool ide;
+ bool rtr;
+
+ /* rx only */
+ bool expect_overrun;
+} FcTestFrame;
+
+static const FcTestFrame fc_test_frame_1 = {
+ .id = 0x5AF,
+ .len = 8,
+ .data = {
+ 0x01020304,
+ 0x0A0B0C0D
+ },
+ .ide = false
+};
+
+static const FcTestFrame fc_test_frame_1_ide = {
+ .id = 0x105AF5AF,
+ .len = 8,
+ .data = {
+ 0x01020304,
+ 0x0A0B0C0D
+ },
+ .ide = true
+};
+
+static void fc_reset(hwaddr ba, uint32_t mcr_flags)
+{
+ /* disable */
+ writel(FCREG(ba, mcr), 0xD890000F);
+
+ /* enable in freeze mode */
+ writel(FCREG(ba, mcr), 0x5980000F);
+
+ /* soft reset */
+ writel(FCREG(ba, mcr), 0x5980000F | FLEXCAN_MCR_SOFTRST);
+
+ g_assert_cmpuint(readl(FCREG(ba, mcr)), ==, 0x5980000F);
+ g_assert_cmpuint(readl(FCREG(ba, ctrl)), ==, 0);
+ g_assert_cmpuint(readl(FCREG(ba, ctrl2)), ==, 0);
+
+ writel(FCREG(ba, mcr), (0x5980000F & ~FLEXCAN_MCR_HALT) | mcr_flags);
+ writel(FCREG(ba, ctrl2), FLEXCAN_CTRL2_RRS);
+
+ /* initialize all mailboxes as rx inactive */
+ for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
+ writel(FCMB(ba, i, 0), FLEXCAN_MB_CODE_RX_INACTIVE);
+ writel(FCMB(ba, i, 1), 0);
+ writel(FCMB(ba, i, 2), 0);
+ writel(FCMB(ba, i, 3), 0);
+ }
+}
+
+static uint64_t fc_get_irqs(hwaddr ba)
+{
+ return (uint64_t)readl(FCREG(ba, iflag1)) |
+ ((uint64_t)readl(FCREG(ba, iflag2)) << 32);
+}
+
+static void fc_clear_irq(hwaddr ba, int idx)
+{
+ if (idx >= 32) {
+ writel(FCREG(ba, iflag2), (uint32_t)1 << (idx - 32));
+ } else {
+ writel(FCREG(ba, iflag1), (uint32_t)1 << idx);
+ }
+
+ g_assert_cmpuint(fc_get_irqs(ba) & ((uint64_t)1 << idx), ==, 0);
+}
+
+static void fc_setup_rx_mb(hwaddr ba, int mbidx)
+{
+ writel(FCMB(ba, mbidx, 0), FLEXCAN_MB_CODE_RX_EMPTY);
+ writel(FCMB(ba, mbidx, 1), 0);
+ /* the data value should be ignored for RX mb */
+ writel(FCMB(ba, mbidx, 2), 0);
+ writel(FCMB(ba, mbidx, 3), 0);
+
+ g_assert_cmpuint(readl(FCMB(ba, mbidx, 0)), ==, FLEXCAN_MB_CODE_RX_EMPTY);
+}
+
+static void fc_tx(hwaddr ba, int mbidx, const FcTestFrame *frame)
+{
+ g_assert_cmpuint(frame->len, <=, 8);
+
+ writel(FCMB(ba, mbidx, 0), FLEXCAN_MB_CODE_TX_INACTIVE);
+ uint32_t id = frame->ide ? frame->id : frame->id << 18;
+ writel(FCMB(ba, mbidx, 1), id);
+ writel(FCMB(ba, mbidx, 2), frame->data[0]);
+ writel(FCMB(ba, mbidx, 3), frame->data[1]);
+
+ uint32_t ctrl = FLEXCAN_MB_CODE_TX_DATA | FLEXCAN_MB_CNT_LENGTH(frame->len);
+ if (frame->ide) {
+ ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR;
+ }
+ if (frame->rtr) {
+ ctrl |= FLEXCAN_MB_CNT_RTR;
+ }
+ writel(FCMB(ba, mbidx, 0), ctrl);
+
+ /* check frame was transmitted */
+ g_assert_cmpuint(fc_get_irqs(ba) & ((uint64_t)1 << mbidx),
+ !=, 0);
+
+ uint32_t xpectd_ctrl = (ctrl & ~FLEXCAN_MB_CODE_MASK) |
+ FLEXCAN_MB_CODE_TX_INACTIVE;
+ g_assert_cmpuint(readl(FCMB(ba, mbidx, 0)) & FC_MB_CNT_USED_MASK, ==,
+ xpectd_ctrl);
+ /* other fields should stay unchanged */
+ g_assert_cmpuint(readl(FCMB(ba, mbidx, 1)), ==, id);
+ g_assert_cmpuint(readl(FCMB(ba, mbidx, 2)), ==, frame->data[0]);
+ g_assert_cmpuint(readl(FCMB(ba, mbidx, 3)), ==, frame->data[1]);
+}
+
+static void fc_rx_check(hwaddr ba, int mbidx, const FcTestFrame *frame)
+{
+ uint32_t xpectd_ctrl = frame->expect_overrun ? FLEXCAN_MB_CODE_RX_OVERRUN
+ : FLEXCAN_MB_CODE_RX_FULL;
+ xpectd_ctrl |= FLEXCAN_MB_CNT_LENGTH(frame->len) | FLEXCAN_MB_CNT_SRR;
+ if (frame->ide) {
+ xpectd_ctrl |= FLEXCAN_MB_CNT_IDE;
+ }
+ if (frame->rtr) {
+ xpectd_ctrl |= FLEXCAN_MB_CNT_RTR;
+ }
+
+ uint32_t xpectd_id = frame->ide ? frame->id : frame->id << 18;
+
+ uint32_t ctrl = readl(FCMB(ba, mbidx, 0)) & FC_MB_CNT_USED_MASK;
+ if ((ctrl & FLEXCAN_MB_CODE_MASK) == FLEXCAN_MB_CODE_RX_EMPTY) {
+ fprintf(stderr, "expected frame (id=0x%X) not received\n", frame->id);
+ }
+
+ g_assert_cmpuint(ctrl, ==, xpectd_ctrl);
+ g_assert_cmpuint(readl(FCMB(ba, mbidx, 1)), ==, xpectd_id);
+ g_assert_cmpuint(readl(FCMB(ba, mbidx, 2)), ==, frame->data[0]);
+ g_assert_cmpuint(readl(FCMB(ba, mbidx, 3)), ==, frame->data[1]);
+}
+
+static void fc_check_empty_multi(hwaddr ba, int idx_count, int mbidxs[])
+{
+ for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
+ bool check_empty = true;
+ int ctrl = readl(FCMB(ba, i, 0));
+
+ for (int j = 0; j < idx_count; j++) {
+ if (i == mbidxs[j]) {
+ check_empty = false;
+ }
+ }
+
+ if (check_empty) {
+ if (!(ctrl == FLEXCAN_MB_CODE_RX_EMPTY ||
+ ctrl == FLEXCAN_MB_CODE_RX_INACTIVE)) {
+ g_assert_cmpuint(ctrl, ==, FLEXCAN_MB_CODE_RX_INACTIVE);
+ }
+ g_assert_cmpuint(readl(FCMB(ba, i, 1)), ==, 0);
+ g_assert_cmpuint(readl(FCMB(ba, i, 2)), ==, 0);
+ g_assert_cmpuint(readl(FCMB(ba, i, 3)), ==, 0);
+ } else {
+ g_assert_cmpuint(
+ ctrl & FLEXCAN_MB_CODE_MASK, !=,
+ FLEXCAN_MB_CODE_RX_INACTIVE
+ );
+ }
+ }
+}
+
+static void fc_check_empty(hwaddr ba, int mbidx)
+{
+ fc_check_empty_multi(ba, 1, &mbidx);
+}
+
+static void flexcan_test_linux_probe_impl(hwaddr fba)
+{
+ /* -- test a Linux driver-like probe sequence -- */
+ /* disable */
+ writel(FCREG(fba, mcr), 0xD890000F);
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xD890000F);
+ g_assert_cmpuint(readl(FCREG(fba, ctrl)), ==, 0);
+
+ /* set bit in reserved field we do not implement (CTRL_CLK_SRC) */
+ writel(FCREG(fba, ctrl), 0x00002000);
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xD890000F);
+
+ /* enable in freeze mode */
+ writel(FCREG(fba, mcr), 0x5980000F);
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x5980000F);
+
+ /* enable Rx-FIFO */
+ writel(FCREG(fba, mcr), 0x7980000F);
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x7980000F);
+ g_assert_cmpuint(readl(FCREG(fba, ecr)), ==, 0);
+
+ /* disable */
+ writel(FCREG(fba, mcr), 0xF890000F);
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
+}
+
+static void flexcan_test_freeze_disable_interaction_impl(hwaddr fba)
+{
+ /* -- test normal <=> freeze <=> disable transitions -- */
+
+ /* leave freeze in disabled, FRZ_ACK should stay cleared */
+ writel(FCREG(fba, mcr), 0xF890000F); /* disable */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
+ writel(FCREG(fba, mcr), 0xB890000F); /* by clearing FRZ */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xB890000F);
+
+ writel(FCREG(fba, mcr), 0xF890000F); /* disable */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
+ writel(FCREG(fba, mcr), 0xE890000F); /* by clearing HALT */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xE890000F);
+
+ writel(FCREG(fba, mcr), 0xF890000F); /* disable */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
+ writel(FCREG(fba, mcr), 0xA890000F); /* by clearing both */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xA890000F);
+
+ /* enter and leave freeze */
+ writel(FCREG(fba, mcr), 0x7980000F); /* enable in freeze mode */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x7980000F);
+ writel(FCREG(fba, mcr), 0x3980000F); /* leave by clearing FRZ */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x3080000F);
+
+ writel(FCREG(fba, mcr), 0x7980000F); /* enable in freeze mode */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x7980000F);
+ writel(FCREG(fba, mcr), 0x6980000F); /* leave by clearing HALT */
+ g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x6080000F);
+}
+
+static void flexcan_test_mailbox_io_impl(hwaddr ba_tx, hwaddr ba_rx)
+{
+ /* -- test correct handling of mailbox IO -- */
+ const int test_1_mbidx = 0;
+ fc_reset(ba_tx,
+ FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
+ fc_reset(ba_rx,
+ FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
+
+ fc_setup_rx_mb(ba_rx, test_1_mbidx);
+ fc_tx(ba_tx, test_1_mbidx, &fc_test_frame_1_ide);
+ g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 1 << test_1_mbidx);
+ fc_rx_check(ba_rx, test_1_mbidx, &fc_test_frame_1_ide);
+ readl(FCREG(ba_rx, timer)); /* reset lock */
+
+ writel(FCMB(ba_rx, test_1_mbidx, 0), 0);
+ g_assert_cmpuint(readl(FCMB(ba_rx, test_1_mbidx, 0)), ==, 0);
+ writel(FCMB(ba_rx, test_1_mbidx, 1), 0x99AABBCC);
+ g_assert_cmpuint(readl(FCMB(ba_rx, test_1_mbidx, 1)), ==, 0x99AABBCC);
+}
+
+static void flexcan_test_dual_transmit_receive_impl(hwaddr ba_tx, hwaddr ba_rx)
+{
+ /* -- test TX and RX between the two FlexCAN instances -- */
+ const int test_1_mbidx = 50;
+ const int test_rounds = 50;
+
+ /* self-receive enabled on tx FC */
+ fc_reset(ba_tx,
+ FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
+ fc_reset(ba_rx,
+ FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
+
+ /* tests self-receive on tx and reception on rx */
+ fc_setup_rx_mb(ba_rx, test_1_mbidx);
+ fc_check_empty(ba_rx, test_1_mbidx);
+ fc_setup_rx_mb(ba_tx, test_1_mbidx + 1);
+ fc_check_empty(ba_tx, test_1_mbidx + 1);
+ g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 0);
+ g_assert_cmpuint(fc_get_irqs(ba_tx), ==, 0);
+
+ fc_tx(ba_tx, test_1_mbidx, &fc_test_frame_1);
+ fc_clear_irq(ba_tx, test_1_mbidx);
+
+ fc_rx_check(ba_rx, test_1_mbidx, &fc_test_frame_1);
+ fc_check_empty(ba_rx, test_1_mbidx);
+ fc_rx_check(ba_tx, test_1_mbidx + 1, &fc_test_frame_1);
+ int tx_non_empty_mbidxs[] = {test_1_mbidx, test_1_mbidx + 1};
+
+ fc_check_empty_multi(ba_tx, 2, tx_non_empty_mbidxs);
+ fc_clear_irq(ba_rx, test_1_mbidx);
+ fc_clear_irq(ba_tx, test_1_mbidx + 1);
+ readl(FCREG(ba_rx, timer)); /* reset lock */
+
+ for (int ridx = 0; ridx < test_rounds; ridx++) {
+ /* test extended IDs sent to all mailboxes */
+ for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
+ fc_setup_rx_mb(ba_rx, i);
+ }
+ fc_check_empty_multi(ba_rx, 0, NULL);
+ g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 0);
+ g_assert_cmpuint(fc_get_irqs(ba_tx), ==, 0);
+
+ for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
+ fc_tx(ba_tx, i, &fc_test_frame_1_ide);
+ }
+ g_assert_cmpuint(fc_get_irqs(ba_rx), ==, UINT64_MAX);
+ g_assert_cmpuint(fc_get_irqs(ba_tx), ==, UINT64_MAX);
+ for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
+ fc_rx_check(ba_rx, i, &fc_test_frame_1_ide);
+ }
+
+ /* reset interrupts */
+ writel(FCREG(ba_rx, iflag1), UINT32_MAX);
+ writel(FCREG(ba_rx, iflag2), UINT32_MAX);
+ writel(FCREG(ba_tx, iflag1), UINT32_MAX);
+ writel(FCREG(ba_tx, iflag2), UINT32_MAX);
+ g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 0);
+ g_assert_cmpuint(fc_get_irqs(ba_tx), ==, 0);
+ }
+}
+
+static void flexcan_test_tx_abort_impl(hwaddr ba)
+{
+ /* -- test the TX abort feature -- */
+ fc_reset(ba,
+ FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
+
+
+ for (int mbidx = 0; mbidx < FLEXCAN_MAILBOX_COUNT; mbidx++) {
+ fc_tx(ba, mbidx, &fc_test_frame_1);
+
+ writel(FCMB(ba, mbidx, 0), FLEXCAN_MB_CODE_TX_ABORT);
+ g_assert_cmpuint(readl(FCMB(ba, mbidx, 0)), ==,
+ FLEXCAN_MB_CODE_TX_INACTIVE);
+ }
+}
+
+static void flexcan_test_freeze_disable_interaction(void)
+{
+ qtest_start(FC_QEMU_ARGS);
+ flexcan_test_freeze_disable_interaction_impl(FSL_IMX6_CAN1_ADDR);
+ flexcan_test_freeze_disable_interaction_impl(FSL_IMX6_CAN2_ADDR);
+ qtest_end();
+}
+
+static void flexcan_test_linux_probe(void)
+{
+ qtest_start(FC_QEMU_ARGS);
+ flexcan_test_linux_probe_impl(FSL_IMX6_CAN1_ADDR);
+ flexcan_test_linux_probe_impl(FSL_IMX6_CAN2_ADDR);
+ qtest_end();
+}
+
+static void flexcan_test_dual_transmit_receive(void)
+{
+ qtest_start(FC_QEMU_ARGS);
+ flexcan_test_dual_transmit_receive_impl(FSL_IMX6_CAN1_ADDR,
+ FSL_IMX6_CAN2_ADDR);
+ flexcan_test_dual_transmit_receive_impl(FSL_IMX6_CAN2_ADDR,
+ FSL_IMX6_CAN1_ADDR);
+ qtest_end();
+}
+
+static void flexcan_test_tx_abort(void)
+{
+ qtest_start(FC_QEMU_ARGS);
+ flexcan_test_tx_abort_impl(FSL_IMX6_CAN1_ADDR);
+ flexcan_test_tx_abort_impl(FSL_IMX6_CAN2_ADDR);
+ qtest_end();
+}
+
+static void flexcan_test_mailbox_io(void)
+{
+ qtest_start(FC_QEMU_ARGS);
+ flexcan_test_mailbox_io_impl(FSL_IMX6_CAN1_ADDR, FSL_IMX6_CAN2_ADDR);
+ flexcan_test_mailbox_io_impl(FSL_IMX6_CAN2_ADDR, FSL_IMX6_CAN1_ADDR);
+ qtest_end();
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/net/flexcan/linux_probe", flexcan_test_linux_probe);
+ qtest_add_func("/net/flexcan/freeze_disable_interaction",
+ flexcan_test_freeze_disable_interaction);
+ qtest_add_func("/net/flexcan/dual_transmit_receive",
+ flexcan_test_dual_transmit_receive);
+ qtest_add_func("/net/flexcan/tx_abort",
+ flexcan_test_tx_abort);
+ qtest_add_func("/net/flexcan/mailbox_io", flexcan_test_mailbox_io);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index e154abb05d..2a0ed815d2 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -251,6 +251,7 @@ qtests_arm = \
(config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \
(config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \
(config_all_devices.has_key('CONFIG_FSI_APB2OPB_ASPEED') ? ['aspeed_fsi-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_CAN_FLEXCAN') ? ['flexcan-test'] : []) + \
(config_all_devices.has_key('CONFIG_STM32L4X5_SOC') and
config_all_devices.has_key('CONFIG_DM163')? ['dm163-test'] : []) + \
['arm-cpu-features',
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 37/54] docs/arm/sabrelite: Mention FlexCAN support
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (35 preceding siblings ...)
2026-06-29 12:00 ` [PULL 36/54] tests: Add qtests for FlexCAN Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 38/54] target/arm: split evaluation of CNTHCTL timer IRQ masks Peter Maydell
` (17 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Matyáš Bobek <matyas.bobek@gmail.com>
Also added example command line usage of the Sabrelite board
with FlexCAN controllers.
Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com>
Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz>
Tested-by: Pavel Pisa <pisa@fel.cvut.cz>
Reviewed-by: Bernhard Beschow <shentey@gmail.com>
Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz>
Message-id: 19407fc30bdf6b264d1c093a82903609e9cfee48.1782140438.git.matyas.bobek@gmail.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
docs/system/arm/sabrelite.rst | 1 +
docs/system/devices/can.rst | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/docs/system/arm/sabrelite.rst b/docs/system/arm/sabrelite.rst
index 4ccb0560af..d3a3c01dd6 100644
--- a/docs/system/arm/sabrelite.rst
+++ b/docs/system/arm/sabrelite.rst
@@ -24,6 +24,7 @@ The SABRE Lite machine supports the following devices:
* 4 SDHC storage controllers
* 4 USB 2.0 host controllers
* 5 ECSPI controllers
+ * 2 FlexCAN CAN controllers
* 1 SST 25VF016B flash
Please note above list is a complete superset the QEMU SABRE Lite machine can
diff --git a/docs/system/devices/can.rst b/docs/system/devices/can.rst
index 09121836fd..622f898952 100644
--- a/docs/system/devices/can.rst
+++ b/docs/system/devices/can.rst
@@ -173,6 +173,30 @@ The test can also be run the other way around, generating messages in the
guest system and capturing them in the host system. Other combinations are
also possible.
+Examples on how to use CAN emulation for FlexCAN on SabreLite board
+-------------------------------------------------------------------
+FlexCANs are connected to QEMU CAN buses by passing the bus IDs as machine
+properties:
+
+* property ``canbus0`` for connecting ``flexcan1``
+* property ``canbus1`` for connecting ``flexcan2``
+
+Note that upstream Linux SabreLite DTs have only a single FlexCAN (``flexcan1``)
+enabled.
+
+An example command to run QEMU emulating a Sabrelite development board
+with both FlexCANs connected to a single QEMU CAN bus (called ``qcan0``),
+bridged to host system ``can0`` interface::
+
+ qemu-system-arm -M sabrelite -smp 4 -m 1G \
+ -object can-bus,id=qcan0 \
+ -machine canbus0=qcan0 -machine canbus1=qcan0 \
+ -object can-host-socketcan,if=can0,canbus=qcan0,id=qcan0-socketcan \
+ -kernel ... -dtb ... -initrd ...
+
+Note that in the Linux guest, bitrate for the FlexCAN device is ignored,
+but needs to be set via the ``ip`` command.
+
Links to other resources
------------------------
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 38/54] target/arm: split evaluation of CNTHCTL timer IRQ masks
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (36 preceding siblings ...)
2026-06-29 12:00 ` [PULL 37/54] docs/arm/sabrelite: Mention FlexCAN support Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 39/54] target/arm: trigger timer recalculation when toggling CNTHCTL:ECV Peter Maydell
` (16 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Alex Bennée <alex.bennee@linaro.org>
Whether the physical and virtual timer IRQs are masked are independent
of each other so the checking of CNTHCTL:CNTPMASK shouldn't depend of
not changing CNTVMASK.
While unlikely to be seen in real life we should still make sure we
behave correctly.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 20260624124527.1018912-2-alex.bennee@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/helper.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index ddca634538..202ac99f69 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -1802,7 +1802,8 @@ static void gt_cnthctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
if ((oldval ^ value) & R_CNTHCTL_CNTVMASK_MASK) {
gt_update_irq(cpu, GTIMER_VIRT);
- } else if ((oldval ^ value) & R_CNTHCTL_CNTPMASK_MASK) {
+ }
+ if ((oldval ^ value) & R_CNTHCTL_CNTPMASK_MASK) {
gt_update_irq(cpu, GTIMER_PHYS);
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 39/54] target/arm: trigger timer recalculation when toggling CNTHCTL:ECV
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (37 preceding siblings ...)
2026-06-29 12:00 ` [PULL 38/54] target/arm: split evaluation of CNTHCTL timer IRQ masks Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 40/54] target/arm: trigger timer recalc on SCR:ECVEN change Peter Maydell
` (15 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Alex Bennée <alex.bennee@linaro.org>
When toggling the state of ECV we affect the offset applied to timers.
As a result we should trigger a recalculation of the timer value to
take into account the new offset.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 20260624124527.1018912-3-alex.bennee@linaro.org
Signed-off-by: Peter Maydell <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 202ac99f69..71c0d3f4d3 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -1806,6 +1806,9 @@ static void gt_cnthctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
if ((oldval ^ value) & R_CNTHCTL_CNTPMASK_MASK) {
gt_update_irq(cpu, GTIMER_PHYS);
}
+ if ((oldval ^ value) & R_CNTHCTL_ECV_MASK) {
+ gt_recalc_timer(cpu, GTIMER_PHYS);
+ }
}
static void gt_cntvoff_write(CPUARMState *env, const ARMCPRegInfo *ri,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 40/54] target/arm: trigger timer recalc on SCR:ECVEN change
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (38 preceding siblings ...)
2026-06-29 12:00 ` [PULL 39/54] target/arm: trigger timer recalculation when toggling CNTHCTL:ECV Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 41/54] target/arm: gate check on scr_el3 behind ARM_FEATURE_EL3 check Peter Maydell
` (14 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Alex Bennée <alex.bennee@linaro.org>
Toggling the ECVEN state affects the offset calculated in
gt_phys_raw_cnt_offset so we should trigger a re-calculation on its
change.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 20260624124527.1018912-4-alex.bennee@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/helper.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 71c0d3f4d3..4f3281feaf 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -37,6 +37,9 @@
#include "qemu/plugin.h"
static void switch_mode(CPUARMState *env, int mode);
+#ifndef CONFIG_USER_ONLY
+static void gt_recalc_timer(ARMCPU *cpu, int timeridx);
+#endif
int compare_u64(const void *a, const void *b)
{
@@ -821,6 +824,12 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
changed = env->cp15.scr_el3 ^ value;
env->cp15.scr_el3 = value;
+#ifndef CONFIG_USER_ONLY
+ if (changed & SCR_ECVEN) {
+ gt_recalc_timer(cpu, GTIMER_PHYS);
+ }
+#endif
+
/*
* If SCR_EL3.{NS,NSE} changes, i.e. change of security state,
* we must invalidate all TLBs below EL3.
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 41/54] target/arm: gate check on scr_el3 behind ARM_FEATURE_EL3 check
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (39 preceding siblings ...)
2026-06-29 12:00 ` [PULL 40/54] target/arm: trigger timer recalc on SCR:ECVEN change Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 42/54] target/arm: trigger timer recalc on HCR:(E2H|TGE) changes Peter Maydell
` (13 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Alex Bennée <alex.bennee@linaro.org>
We shouldn't be reading SCR_EL3 unless ARM_FEATURE_EL3 is enabled, if
it is then we check SCR_ECVEN allows tweaking the offset.
Suggested-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260624124527.1018912-5-alex.bennee@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/helper.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 4f3281feaf..20165dbf48 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -1413,10 +1413,10 @@ void gt_rme_post_el_change(ARMCPU *cpu, void *ignored)
static uint64_t gt_phys_raw_cnt_offset(CPUARMState *env)
{
- if ((env->cp15.scr_el3 & SCR_ECVEN) &&
- FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, ECV) &&
- arm_is_el2_enabled(env) &&
- (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) {
+ if ((!arm_feature(env, ARM_FEATURE_EL3) || (env->cp15.scr_el3 & SCR_ECVEN))
+ && FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, ECV)
+ && arm_is_el2_enabled(env)
+ && (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) {
return env->cp15.cntpoff_el2;
}
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 42/54] target/arm: trigger timer recalc on HCR:(E2H|TGE) changes
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (40 preceding siblings ...)
2026-06-29 12:00 ` [PULL 41/54] target/arm: gate check on scr_el3 behind ARM_FEATURE_EL3 check Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 43/54] docs/system: add FEAT_ECV_POFF to the emulation list Peter Maydell
` (12 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Alex Bennée <alex.bennee@linaro.org>
Toggling the HCR state affects the offset calculated in
gt_phys_raw_cnt_offset so we should trigger a re-calculation on its
change.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 20260624124527.1018912-6-alex.bennee@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/helper.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 20165dbf48..93e3d8b575 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -3716,6 +3716,7 @@ static const ARMCPRegInfo v8_aa32_el1_reginfo[] = {
static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask)
{
ARMCPU *cpu = env_archcpu(env);
+ bool hcr_change_timer;
if (arm_feature(env, ARM_FEATURE_V8)) {
valid_mask |= MAKE_64BIT_MASK(0, 34); /* ARMv8.0 */
@@ -3807,6 +3808,10 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask)
(HCR_VM | HCR_PTW | HCR_DC | HCR_DCT | HCR_FWB | HCR_NV | HCR_NV1)) {
tlb_flush(CPU(cpu));
}
+ hcr_change_timer = (env->cp15.hcr_el2 ^ value) &
+ (HCR_E2H | HCR_TGE);
+
+ /* update */
env->cp15.hcr_el2 = value;
/*
@@ -3828,6 +3833,11 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask)
arm_cpu_update_vinmi(cpu);
arm_cpu_update_vfnmi(cpu);
}
+ if (hcr_change_timer) {
+#ifndef CONFIG_USER_ONLY
+ gt_recalc_timer(cpu, GTIMER_PHYS);
+#endif
+ }
}
static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 43/54] docs/system: add FEAT_ECV_POFF to the emulation list
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (41 preceding siblings ...)
2026-06-29 12:00 ` [PULL 42/54] target/arm: trigger timer recalc on HCR:(E2H|TGE) changes Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 44/54] target/arm: Enable FADD/FSUB (half-precision) with FEAT_SME_F8F16 Peter Maydell
` (11 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Alex Bennée <alex.bennee@linaro.org>
We already had this implemented since 2808d3b38a5 (target/arm:
Implement FEAT_ECV CNTPOFF_EL2 handling) but it has its own feature
name now. Add it to the list.
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 20260624124527.1018912-7-alex.bennee@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
docs/system/arm/emulation.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst
index a3a1607ff9..6b02bd829a 100644
--- a/docs/system/arm/emulation.rst
+++ b/docs/system/arm/emulation.rst
@@ -60,6 +60,7 @@ the following architecture extensions:
- FEAT_E2H0 (Programming of HCR_EL2.E2H)
- FEAT_EBF16 (AArch64 Extended BFloat16 instructions)
- FEAT_ECV (Enhanced Counter Virtualization)
+- FEAT_ECV_POFF (Enhanced Counter Virtualization Physical Offset)
- FEAT_EL0 (Support for execution at EL0)
- FEAT_EL1 (Support for execution at EL1)
- FEAT_EL2 (Support for execution at EL2)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 44/54] target/arm: Enable FADD/FSUB (half-precision) with FEAT_SME_F8F16
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (42 preceding siblings ...)
2026-06-29 12:00 ` [PULL 43/54] docs/system: add FEAT_ECV_POFF to the emulation list Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 45/54] target/arm: Rename SME FMLAL/FMLSL patterns Peter Maydell
` (10 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
These two instructions can be enabled with either
FEAT_SME_F8F16 or FEAT_SME_F16F16.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
[PMM: fix && vs || mixup in feature function]
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260625015159.719300-2-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/cpu-features.h | 11 +++++++++++
target/arm/tcg/translate-sme.c | 4 ++--
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h
index 2e4f22f193..98102d75bf 100644
--- a/target/arm/cpu-features.h
+++ b/target/arm/cpu-features.h
@@ -1610,6 +1610,11 @@ static inline bool isar_feature_aa64_sme_f8f32(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64SMFR0, F8F32);
}
+static inline bool isar_feature_aa64_sme_f8f16(const ARMISARegisters *id)
+{
+ return FIELD_EX64_IDREG(id, ID_AA64SMFR0, F8F16);
+}
+
static inline bool isar_feature_aa64_sme_f16f16(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64SMFR0, F16F16);
@@ -1781,6 +1786,12 @@ isar_feature_aa64_sme2_or_sve2_lut(const ARMISARegisters *id)
return isar_feature_aa64_sme2_or_sve2(id) && isar_feature_aa64_lut(id);
}
+static inline bool
+isar_feature_aa64_sme_f16f16_or_f8f16(const ARMISARegisters *id)
+{
+ return isar_feature_aa64_sme_f16f16(id) || isar_feature_aa64_sme_f8f16(id);
+}
+
/*
* Feature tests for "does this exist in either 32-bit or 64-bit?"
*/
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index a79b0a9b80..b8dde80c20 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1335,9 +1335,9 @@ static bool do_faddsub(DisasContext *s, arg_az_n *a, ARMFPStatusFlavour fpst,
return true;
}
-TRANS_FEAT(FADD_nn_h, aa64_sme_f16f16, do_faddsub, a,
+TRANS_FEAT(FADD_nn_h, aa64_sme_f16f16_or_f8f16, do_faddsub, a,
FPST_ZA_F16, gen_helper_gvec_fadd_h)
-TRANS_FEAT(FSUB_nn_h, aa64_sme_f16f16, do_faddsub, a,
+TRANS_FEAT(FSUB_nn_h, aa64_sme_f16f16_or_f8f16, do_faddsub, a,
FPST_ZA_F16, gen_helper_gvec_fsub_h)
TRANS_FEAT(FADD_nn_s, aa64_sme2, do_faddsub, a,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 45/54] target/arm: Rename SME FMLAL/FMLSL patterns
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (43 preceding siblings ...)
2026-06-29 12:00 ` [PULL 44/54] target/arm: Enable FADD/FSUB (half-precision) with FEAT_SME_F8F16 Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 46/54] target/arm: Implement FMLAL (multiple, multiple and single, FP8 to FP16) Peter Maydell
` (9 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Rename patterns to include _sh suffix, so that we can
distinguish insns of the same name from FEAT_SME_F8F16.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260625015159.719300-3-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 32 ++++++++++++++++----------------
target/arm/tcg/translate-sme.c | 12 ++++++------
2 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 1de5f341ef..90ee161461 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -324,13 +324,13 @@ SUB_azz_n1_d 11000001 0111 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=4
@azz_nx1_o2x2 ........ ... . zm:4 . .. ... zn:5 ... .. \
&azz_n off=%off2_x2 rv=%mova_rv
-FMLAL_n1 11000001 001 0 .... 0 .. 011 ..... 00 ... @azz_nx1_o3x2 n=1
-FMLAL_n1 11000001 001 0 .... 0 .. 010 ..... 000 .. @azz_nx1_o2x2 n=2
-FMLAL_n1 11000001 001 1 .... 0 .. 010 ..... 000 .. @azz_nx1_o2x2 n=4
+FMLAL_n1_sh 11000001 001 0 .... 0 .. 011 ..... 00 ... @azz_nx1_o3x2 n=1
+FMLAL_n1_sh 11000001 001 0 .... 0 .. 010 ..... 000 .. @azz_nx1_o2x2 n=2
+FMLAL_n1_sh 11000001 001 1 .... 0 .. 010 ..... 000 .. @azz_nx1_o2x2 n=4
-FMLSL_n1 11000001 001 0 .... 0 .. 011 ..... 01 ... @azz_nx1_o3x2 n=1
-FMLSL_n1 11000001 001 0 .... 0 .. 010 ..... 010 .. @azz_nx1_o2x2 n=2
-FMLSL_n1 11000001 001 1 .... 0 .. 010 ..... 010 .. @azz_nx1_o2x2 n=4
+FMLSL_n1_sh 11000001 001 0 .... 0 .. 011 ..... 01 ... @azz_nx1_o3x2 n=1
+FMLSL_n1_sh 11000001 001 0 .... 0 .. 010 ..... 010 .. @azz_nx1_o2x2 n=2
+FMLSL_n1_sh 11000001 001 1 .... 0 .. 010 ..... 010 .. @azz_nx1_o2x2 n=4
BFMLAL_n1 11000001 001 0 .... 0 .. 011 ..... 10 ... @azz_nx1_o3x2 n=1
BFMLAL_n1 11000001 001 0 .... 0 .. 010 ..... 100 .. @azz_nx1_o2x2 n=2
@@ -477,11 +477,11 @@ SUB_azz_nn_d 11000001 111 ...01 0 .. 110 ...00 11 ... @azz_4x4_o3
@azz_4x4_o2x2 ........ ... ..... . .. ... ..... ... .. \
&azz_n n=4 rv=%mova_rv zn=%zn_ax4 zm=%zm_ax4 off=%off2_x2
-FMLAL_nn 11000001 101 ....0 0 .. 010 ....0 000 .. @azz_2x2_o2x2
-FMLAL_nn 11000001 101 ...01 0 .. 010 ...00 000 .. @azz_4x4_o2x2
+FMLAL_nn_sh 11000001 101 ....0 0 .. 010 ....0 000 .. @azz_2x2_o2x2
+FMLAL_nn_sh 11000001 101 ...01 0 .. 010 ...00 000 .. @azz_4x4_o2x2
-FMLSL_nn 11000001 101 ....0 0 .. 010 ....0 010 .. @azz_2x2_o2x2
-FMLSL_nn 11000001 101 ...01 0 .. 010 ...00 010 .. @azz_4x4_o2x2
+FMLSL_nn_sh 11000001 101 ....0 0 .. 010 ....0 010 .. @azz_2x2_o2x2
+FMLSL_nn_sh 11000001 101 ...01 0 .. 010 ...00 010 .. @azz_4x4_o2x2
BFMLAL_nn 11000001 101 ....0 0 .. 010 ....0 100 .. @azz_2x2_o2x2
BFMLAL_nn 11000001 101 ...01 0 .. 010 ...00 100 .. @azz_4x4_o2x2
@@ -617,13 +617,13 @@ BFSUB_nn 11000001 111 00101 0 .. 111 ...00 01 ... @az_4x4_o3
@azx_4x1_o2x2 ........ .... zm:4 . .. . .. ..... .. ... \
&azx_n n=4 rv=%mova_rv off=%off2_x2 zn=%zn_ax4 idx=%idx2_10_2
-FMLAL_nx 11000001 1000 .... . .. 1 .. ..... 00 ... @azx_1x1_o3x2
-FMLAL_nx 11000001 1001 .... 0 .. 1 .. ....0 00 ... @azx_2x1_o2x2
-FMLAL_nx 11000001 1001 .... 1 .. 1 .. ...00 00 ... @azx_4x1_o2x2
+FMLAL_nx_sh 11000001 1000 .... . .. 1 .. ..... 00 ... @azx_1x1_o3x2
+FMLAL_nx_sh 11000001 1001 .... 0 .. 1 .. ....0 00 ... @azx_2x1_o2x2
+FMLAL_nx_sh 11000001 1001 .... 1 .. 1 .. ...00 00 ... @azx_4x1_o2x2
-FMLSL_nx 11000001 1000 .... . .. 1 .. ..... 01 ... @azx_1x1_o3x2
-FMLSL_nx 11000001 1001 .... 0 .. 1 .. ....0 01 ... @azx_2x1_o2x2
-FMLSL_nx 11000001 1001 .... 1 .. 1 .. ...00 01 ... @azx_4x1_o2x2
+FMLSL_nx_sh 11000001 1000 .... . .. 1 .. ..... 01 ... @azx_1x1_o3x2
+FMLSL_nx_sh 11000001 1001 .... 0 .. 1 .. ....0 01 ... @azx_2x1_o2x2
+FMLSL_nx_sh 11000001 1001 .... 1 .. 1 .. ...00 01 ... @azx_4x1_o2x2
BFMLAL_nx 11000001 1000 .... . .. 1 .. ..... 10 ... @azx_1x1_o3x2
BFMLAL_nx 11000001 1001 .... 0 .. 1 .. ....0 10 ... @azx_2x1_o2x2
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index b8dde80c20..7aa270d3ec 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1107,10 +1107,10 @@ static bool do_fmlal(DisasContext *s, arg_azz_n *a, bool sub, bool multi)
multi, FPST_ENV, gen_helper_sve2_fmlal_zzzw_s);
}
-TRANS_FEAT(FMLAL_n1, aa64_sme2, do_fmlal, a, false, false)
-TRANS_FEAT(FMLSL_n1, aa64_sme2, do_fmlal, a, true, false)
-TRANS_FEAT(FMLAL_nn, aa64_sme2, do_fmlal, a, false, true)
-TRANS_FEAT(FMLSL_nn, aa64_sme2, do_fmlal, a, true, true)
+TRANS_FEAT(FMLAL_n1_sh, aa64_sme2, do_fmlal, a, false, false)
+TRANS_FEAT(FMLSL_n1_sh, aa64_sme2, do_fmlal, a, true, false)
+TRANS_FEAT(FMLAL_nn_sh, aa64_sme2, do_fmlal, a, false, true)
+TRANS_FEAT(FMLSL_nn_sh, aa64_sme2, do_fmlal, a, true, true)
static bool do_fmlall_fp8(DisasContext *s, arg_azz_n *a, bool multi)
{
@@ -1128,8 +1128,8 @@ static bool do_fmlal_nx(DisasContext *s, arg_azx_n *a, bool sub)
false, FPST_ENV, gen_helper_sve2_fmlal_zzxw_s);
}
-TRANS_FEAT(FMLAL_nx, aa64_sme2, do_fmlal_nx, a, false)
-TRANS_FEAT(FMLSL_nx, aa64_sme2, do_fmlal_nx, a, true)
+TRANS_FEAT(FMLAL_nx_sh, aa64_sme2, do_fmlal_nx, a, false)
+TRANS_FEAT(FMLSL_nx_sh, aa64_sme2, do_fmlal_nx, a, true)
static bool do_bfmlal(DisasContext *s, arg_azz_n *a, bool sub, bool multi)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 46/54] target/arm: Implement FMLAL (multiple, multiple and single, FP8 to FP16)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (44 preceding siblings ...)
2026-06-29 12:00 ` [PULL 45/54] target/arm: Rename SME FMLAL/FMLSL patterns Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 47/54] target/arm: Implement FMLAL (multiple and indexed, " Peter Maydell
` (8 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260625015159.719300-4-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 7 +++++++
target/arm/tcg/translate-sme.c | 9 +++++++++
2 files changed, 16 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 90ee161461..b735f3de82 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -452,6 +452,10 @@ FMLALL_n1_b 11000001 001 1 .... 0 .. 000 ..... 0001 . @azz_nx1_o1x4 n=4
FDOT_n1_sb 11000001 001 0 .... 0 .. 100 ..... 11 ... @azz_nx1_o3 n=2
FDOT_n1_sb 11000001 001 1 .... 0 .. 100 ..... 11 ... @azz_nx1_o3 n=4
+FMLAL_n1_hb 11000001 001 1 .... 0 .. 011 ..... 00 ... @azz_nx1_o3x2 n=1
+FMLAL_n1_hb 11000001 001 0 .... 0 .. 010 ..... 001 .. @azz_nx1_o2x2 n=2
+FMLAL_n1_hb 11000001 001 1 .... 0 .. 010 ..... 001 .. @azz_nx1_o2x2 n=4
+
### SME2 Multi-vector Multiple Array Vectors
%zn_ax2 6:4 !function=times_2
@@ -578,6 +582,9 @@ FMLALL_nn_b 11000001 101 ...01 0 .. 000 ...01 0000 . @azz_4x4_o1x4
FDOT_nn_sb 11000001 101 ....0 0 .. 100 ....1 10 ... @azz_2x2_o3
FDOT_nn_sb 11000001 101 ...01 0 .. 100 ...01 10 ... @azz_4x4_o3
+FMLAL_nn_hb 11000001 101 ....0 0 .. 010 ....1 000 .. @azz_2x2_o2x2
+FMLAL_nn_hb 11000001 101 ...01 0 .. 010 ...01 000 .. @azz_4x4_o2x2
+
&az_n n off rv zm
@az_2x2_o3 ........ ... ..... . .. ... ..... .. off:3 \
&az_n n=2 rv=%mova_rv zm=%zn_ax2
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 7aa270d3ec..7cb7b71e74 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1121,6 +1121,15 @@ static bool do_fmlall_fp8(DisasContext *s, arg_azz_n *a, bool multi)
TRANS_FEAT(FMLALL_n1_b, aa64_sme_f8f32, do_fmlall_fp8, a, false)
TRANS_FEAT(FMLALL_nn_b, aa64_sme_f8f32, do_fmlall_fp8, a, true)
+static bool do_fmlal_fp8(DisasContext *s, arg_azz_n *a, bool multi)
+{
+ return do_azz_acc_fp8(s, a->n, 2, a->rv, a->off, a->zn, a->zm,
+ 0, 0, multi, gen_helper_gvec_fmla_hb);
+}
+
+TRANS_FEAT(FMLAL_n1_hb, aa64_sme_f8f16, do_fmlal_fp8, a, false)
+TRANS_FEAT(FMLAL_nn_hb, aa64_sme_f8f16, do_fmlal_fp8, a, true)
+
static bool do_fmlal_nx(DisasContext *s, arg_azx_n *a, bool sub)
{
return do_azz_acc_fp(s, a->n, 2, a->rv, a->off, a->zn, a->zm,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 47/54] target/arm: Implement FMLAL (multiple and indexed, FP8 to FP16)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (45 preceding siblings ...)
2026-06-29 12:00 ` [PULL 46/54] target/arm: Implement FMLAL (multiple, multiple and single, FP8 to FP16) Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 48/54] target/arm: Implement FDOT (multiple, multiple and single, " Peter Maydell
` (7 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260625015159.719300-5-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 13 +++++++++++++
target/arm/tcg/translate-sme.c | 4 ++++
2 files changed, 17 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index b735f3de82..c6e22c4999 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -791,6 +791,19 @@ FMLALL_nx_b 11000001 0001 .... 1 .. 0.. ...10 00.. . @azx_4x1_i4_o1
FDOT_nx_b 11000001 0101 .... 0 .. 0.. ....1 11 ... @azx_2x1_i2_o3
FDOT_nx_b 11000001 0101 .... 1 .. 0.. ...00 01 ... @azx_4x1_i2_o3
+%idx4_15_10_3 15:1 10:2 3:1
+%idx4_10_2 10:2 2:2
+@azx_1x1_i4_o3x2 ........ .... zm:4 . .. . .. zn:5 .. ... \
+ &azx_n n=1 rv=%mova_rv off=%off3_x2 idx=%idx4_15_10_3
+@azx_2x2_i4_o3x2 ........ .... zm:4 . .. . .. .... .. .. .. \
+ &azx_n n=2 rv=%mova_rv zn=%zn_ax2 off=%off2_x2 idx=%idx4_10_2
+@azx_4x4_i4_o3x2 ........ .... zm:4 . .. . .. ... ... .. .. \
+ &azx_n n=4 rv=%mova_rv zn=%zn_ax4 off=%off2_x2 idx=%idx4_10_2
+
+FMLAL_nx_hb 11000001 1100 .... . .. 0.. ..... 0. ... @azx_1x1_i4_o3x2
+FMLAL_nx_hb 11000001 1001 .... 0 .. 1.. ....1 1.. .. @azx_2x2_i4_o3x2
+FMLAL_nx_hb 11000001 1001 .... 1 .. 1.. ...01 0.. .. @azx_4x4_i4_o3x2
+
%idx2_10_3 10:1 3:1
@azx_4x2_i2_o3 ........ .... zm:4 . .. ... .... ... off:3 \
&azx_n n=4 rv=%mova_rv zn=%zn_ax2 idx=%idx2_10_3
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 7cb7b71e74..98f6eb0b70 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1140,6 +1140,10 @@ static bool do_fmlal_nx(DisasContext *s, arg_azx_n *a, bool sub)
TRANS_FEAT(FMLAL_nx_sh, aa64_sme2, do_fmlal_nx, a, false)
TRANS_FEAT(FMLSL_nx_sh, aa64_sme2, do_fmlal_nx, a, true)
+TRANS_FEAT(FMLAL_nx_hb, aa64_sme_f8f16, do_azz_acc_fp8,
+ a->n, 2, a->rv, a->off, a->zn, a->zm,
+ a->idx << 2, 0, false, gen_helper_gvec_fmla_idx_hb)
+
static bool do_bfmlal(DisasContext *s, arg_azz_n *a, bool sub, bool multi)
{
return do_azz_acc_fp(s, a->n, 2, a->rv, a->off, a->zn, a->zm,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 48/54] target/arm: Implement FDOT (multiple, multiple and single, FP8 to FP16)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (46 preceding siblings ...)
2026-06-29 12:00 ` [PULL 47/54] target/arm: Implement FMLAL (multiple and indexed, " Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 49/54] target/arm: Implement FDOT (multiple and indexed, " Peter Maydell
` (6 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20260625015159.719300-6-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 6 ++++++
target/arm/tcg/translate-sme.c | 9 +++++++++
2 files changed, 15 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index c6e22c4999..fbf5f3720d 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -456,6 +456,9 @@ FMLAL_n1_hb 11000001 001 1 .... 0 .. 011 ..... 00 ... @azz_nx1_o3x2 n=1
FMLAL_n1_hb 11000001 001 0 .... 0 .. 010 ..... 001 .. @azz_nx1_o2x2 n=2
FMLAL_n1_hb 11000001 001 1 .... 0 .. 010 ..... 001 .. @azz_nx1_o2x2 n=4
+FDOT_n1_hb 11000001 001 0 .... 0 .. 100 ..... 01 ... @azz_nx1_o3 n=2
+FDOT_n1_hb 11000001 001 1 .... 0 .. 100 ..... 01 ... @azz_nx1_o3 n=4
+
### SME2 Multi-vector Multiple Array Vectors
%zn_ax2 6:4 !function=times_2
@@ -585,6 +588,9 @@ FDOT_nn_sb 11000001 101 ...01 0 .. 100 ...01 10 ... @azz_4x4_o3
FMLAL_nn_hb 11000001 101 ....0 0 .. 010 ....1 000 .. @azz_2x2_o2x2
FMLAL_nn_hb 11000001 101 ...01 0 .. 010 ...01 000 .. @azz_4x4_o2x2
+FDOT_nn_hb 11000001 101 ....0 0 .. 100 ....1 00 ... @azz_2x2_o3
+FDOT_nn_hb 11000001 101 ...01 0 .. 100 ...01 00 ... @azz_4x4_o3
+
&az_n n off rv zm
@az_2x2_o3 ........ ... ..... . .. ... ..... .. off:3 \
&az_n n=2 rv=%mova_rv zm=%zn_ax2
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 98f6eb0b70..3174b10c30 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1196,6 +1196,15 @@ static bool do_fdot_fp8(DisasContext *s, arg_azz_n *a, bool multi)
TRANS_FEAT(FDOT_n1_sb, aa64_sme_f8f32, do_fdot_fp8, a, false)
TRANS_FEAT(FDOT_nn_sb, aa64_sme_f8f32, do_fdot_fp8, a, true)
+static bool do_fdot_hb(DisasContext *s, arg_azz_n *a, bool multi)
+{
+ return do_azz_acc_fp8(s, a->n, 1, a->rv, a->off, a->zn, a->zm,
+ 0, 0, multi, gen_helper_gvec_fdot_hb);
+}
+
+TRANS_FEAT(FDOT_n1_hb, aa64_sme_f8f16, do_fdot_hb, a, false)
+TRANS_FEAT(FDOT_nn_hb, aa64_sme_f8f16, do_fdot_hb, a, true)
+
static bool do_fdot_nx(DisasContext *s, arg_azx_n *a)
{
return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 49/54] target/arm: Implement FDOT (multiple and indexed, FP8 to FP16)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (47 preceding siblings ...)
2026-06-29 12:00 ` [PULL 48/54] target/arm: Implement FDOT (multiple, multiple and single, " Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 50/54] target/arm: Implement FMOPA (widening, 2-way, " Peter Maydell
` (5 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
[PMM: fixed subject line]
Message-id: 20260625015159.719300-7-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 3 +++
target/arm/tcg/translate-sme.c | 4 ++++
2 files changed, 7 insertions(+)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index fbf5f3720d..1dd3b7c8b2 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -810,6 +810,9 @@ FMLAL_nx_hb 11000001 1100 .... . .. 0.. ..... 0. ... @azx_1x1_i4_o3x2
FMLAL_nx_hb 11000001 1001 .... 0 .. 1.. ....1 1.. .. @azx_2x2_i4_o3x2
FMLAL_nx_hb 11000001 1001 .... 1 .. 1.. ...01 0.. .. @azx_4x4_i4_o3x2
+FDOT_nx_hb 11000001 1101 .... 0 .. 0.. ....1 0. ... @azx_2x1_i3_o3
+FDOT_nx_hb 11000001 0001 .... 1 .. 1.. ...10 0. ... @azx_4x1_i3_o3
+
%idx2_10_3 10:1 3:1
@azx_4x2_i2_o3 ........ .... zm:4 . .. ... .... ... off:3 \
&azx_n n=4 rv=%mova_rv zn=%zn_ax2 idx=%idx2_10_3
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 3174b10c30..197274d00e 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1178,6 +1178,10 @@ TRANS_FEAT(FDOT_nx_b, aa64_sme_f8f32, do_azz_acc_fp8,
a->n, 1, a->rv, a->off, a->zn, a->zm,
a->idx, 0, false, gen_helper_gvec_fdot_idx_sb)
+TRANS_FEAT(FDOT_nx_hb, aa64_sme_f8f16, do_azz_acc_fp8,
+ a->n, 1, a->rv, a->off, a->zn, a->zm,
+ a->idx, 0, false, gen_helper_gvec_fdot_idx_hb)
+
static bool do_fdot(DisasContext *s, arg_azz_n *a, bool multi)
{
return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm, 1, 0,
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 50/54] target/arm: Implement FMOPA (widening, 2-way, FP8 to FP16)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (48 preceding siblings ...)
2026-06-29 12:00 ` [PULL 49/54] target/arm: Implement FDOT (multiple and indexed, " Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 51/54] target/arm: Rename FVDOT pattern Peter Maydell
` (4 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260625015159.719300-8-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/fp8_helper.c | 35 ++++++++++++++++++++++++++++++++
target/arm/tcg/helper-fp8-defs.h | 1 +
target/arm/tcg/sme.decode | 1 +
target/arm/tcg/translate-sme.c | 24 +++++++++++++---------
4 files changed, 51 insertions(+), 10 deletions(-)
diff --git a/target/arm/tcg/fp8_helper.c b/target/arm/tcg/fp8_helper.c
index 3c2d959099..5606f0fd8e 100644
--- a/target/arm/tcg/fp8_helper.c
+++ b/target/arm/tcg/fp8_helper.c
@@ -893,6 +893,41 @@ void HELPER(sme_fmopa_sb)(void *vza, void *vzn, void *vzm, void *vpn,
}
}
+void HELPER(sme_fmopa_hb)(void *vza, void *vzn, void *vzm, void *vpn,
+ void *vpm, CPUARMState *env, uint32_t desc)
+{
+ FP8MulContext ctx = fp8_mul_start(env, 0xf);
+ intptr_t oprsz = simd_maxsz(desc);
+ uint16_t *pn = vpn, *pm = vpm;
+
+ for (intptr_t row = 0; row < oprsz; ) {
+ uint16_t prow = pn[H2(row >> 4)];
+ do {
+ void *vza_row = vza + tile_vslice_offset(row);
+ uint16_t n = *(uint16_t *)(vzn + H1_2(row));
+
+ n &= expand_pred_b(prow & 3);
+
+ for (intptr_t col = 0; col < oprsz; ) {
+ uint16_t pcol = pm[H2(col >> 4)];
+ do {
+ if (prow & pcol & 0x3) {
+ uint16_t *a = vza_row + H1_2(col);
+ uint16_t m = *(uint16_t *)(vzm + H1_2(col));
+
+ m &= expand_pred_b(pcol & 3);
+ *a = f8dotadd_h(n, m, 2, *a, &ctx);
+ }
+ col += 2;
+ pcol >>= 2;
+ } while (col & 15);
+ }
+ row += 2;
+ prow >>= 2;
+ } while (row & 15);
+ }
+}
+
void HELPER(sme_fvdot_idx_sb)(void *vd, void *vn, void *vm,
CPUARMState *env, uint32_t desc)
{
diff --git a/target/arm/tcg/helper-fp8-defs.h b/target/arm/tcg/helper-fp8-defs.h
index ef1375fea7..05bf8dbdc2 100644
--- a/target/arm/tcg/helper-fp8-defs.h
+++ b/target/arm/tcg/helper-fp8-defs.h
@@ -40,5 +40,6 @@ DEF_HELPER_FLAGS_5(gvec_fmmla_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32
DEF_HELPER_FLAGS_5(gvec_fmmla_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32)
DEF_HELPER_FLAGS_7(sme_fmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32)
+DEF_HELPER_FLAGS_7(sme_fmopa_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32)
DEF_HELPER_FLAGS_5(sme_fvdot_idx_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 1dd3b7c8b2..755d5f00d0 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -199,6 +199,7 @@ BFMOPA_w 10000001 100 ..... ... ... ..... . 00 .. @op_32
FMOPA_w_h 10000001 101 ..... ... ... ..... . 00 .. @op_32
FMOPA_sb 10000000 101 zm:5 pm:3 pn:3 zn:5 0 00 zad:2 &op sub=0
+FMOPA_hb 10000000 101 zm:5 pm:3 pn:3 zn:5 0100 zad:1 &op sub=0
SMOPA_s 1010000 0 10 0 ..... ... ... ..... . 00 .. @op_32
SUMOPA_s 1010000 0 10 1 ..... ... ... ..... . 00 .. @op_32
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 197274d00e..7eeac28480 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -616,25 +616,29 @@ TRANS_FEAT(BFMOPA_w, aa64_sme, do_outprod_env, a, MO_32,
: !s->fpcr_ah ? gen_helper_sme_bfmops_w
: gen_helper_sme_ah_bfmops_w)
-static bool trans_FMOPA_sb(DisasContext *s, arg_op *a)
+static bool do_outprod_fp8(DisasContext *s, arg_op *a, MemOp esz,
+ gen_helper_gvec_5_ptr *fn)
{
- if (!dc_isar_feature(aa64_sme_f8f32, s)) {
- return false;
- }
if (fpmr_access_check(s) && sme_smza_enabled_check(s)) {
int svl = streaming_vec_reg_size(s);
uint32_t desc = simd_desc(svl, svl, 0);
- gen_helper_sme_fmopa_sb(get_tile(s, MO_32, a->zad),
- vec_full_reg_ptr(s, a->zn),
- vec_full_reg_ptr(s, a->zm),
- pred_full_reg_ptr(s, a->pn),
- pred_full_reg_ptr(s, a->pm),
- tcg_env, tcg_constant_i32(desc));
+ TCGv_ptr za = get_tile(s, esz, a->zad);
+ TCGv_ptr zn = vec_full_reg_ptr(s, a->zn);
+ TCGv_ptr zm = vec_full_reg_ptr(s, a->zm);
+ TCGv_ptr pn = pred_full_reg_ptr(s, a->pn);
+ TCGv_ptr pm = pred_full_reg_ptr(s, a->pm);
+
+ fn(za, zn, zm, pn, pm, tcg_env, tcg_constant_i32(desc));
}
return true;
}
+TRANS_FEAT(FMOPA_sb, aa64_sme_f8f32, do_outprod_fp8,
+ a, MO_32, gen_helper_sme_fmopa_sb)
+TRANS_FEAT(FMOPA_hb, aa64_sme_f8f16, do_outprod_fp8,
+ a, MO_16, gen_helper_sme_fmopa_hb)
+
TRANS_FEAT(SMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_smopa_s)
TRANS_FEAT(UMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_umopa_s)
TRANS_FEAT(SUMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_sumopa_s)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 51/54] target/arm: Rename FVDOT pattern
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (49 preceding siblings ...)
2026-06-29 12:00 ` [PULL 50/54] target/arm: Implement FMOPA (widening, 2-way, " Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 52/54] target/arm: Implement FVDOT (FP8 to FP16) Peter Maydell
` (3 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Rename to FVDOT_sh so that we can introduce an insn
of the same name from FEAT_SME_F8F16.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260625015159.719300-9-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/sme.decode | 2 +-
target/arm/tcg/translate-sme.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 755d5f00d0..160cf130d4 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -662,7 +662,7 @@ FDOT_nx 11000001 0101 .... 1 .. 1 .. ...00 01 ... @azx_4x1_i2_o3
BFDOT_nx 11000001 0101 .... 0 .. 1 .. ....0 11 ... @azx_2x1_i2_o3
BFDOT_nx 11000001 0101 .... 1 .. 1 .. ...00 11 ... @azx_4x1_i2_o3
-FVDOT 11000001 0101 .... 0 .. 0 .. ....0 01 ... @azx_2x1_i2_o3
+FVDOT_sh 11000001 0101 .... 0 .. 0 .. ....0 01 ... @azx_2x1_i2_o3
BFVDOT 11000001 0101 .... 0 .. 0 .. ....0 11 ... @azx_2x1_i2_o3
SDOT_nx_2h 11000001 0101 .... 0 .. 1 .. ....0 00 ... @azx_2x1_i2_o3
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 7eeac28480..267a6b0d9b 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1260,7 +1260,7 @@ static bool do_vdot(DisasContext *s, arg_azx_n *a, gen_helper_gvec_4_ptr *fn)
return true;
}
-TRANS_FEAT(FVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_fvdot_idx_h)
+TRANS_FEAT(FVDOT_sh, aa64_sme, do_vdot, a, gen_helper_sme2_fvdot_idx_h)
TRANS_FEAT(BFVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_bfvdot_idx)
static bool do_fvdot_sb(DisasContext *s, arg_azx_n *a, bool top)
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 52/54] target/arm: Implement FVDOT (FP8 to FP16)
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (50 preceding siblings ...)
2026-06-29 12:00 ` [PULL 51/54] target/arm: Rename FVDOT pattern Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 53/54] target/arm: Enable FEAT_SME_F8F16 for -cpu max Peter Maydell
` (2 subsequent siblings)
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
[PMM: fix wrong sizeof(type) for fp16 output]
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260625015159.719300-10-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/tcg/fp8_helper.c | 23 +++++++++++++++++++++++
target/arm/tcg/helper-fp8-defs.h | 1 +
target/arm/tcg/sme.decode | 2 ++
target/arm/tcg/translate-sme.c | 4 ++++
4 files changed, 30 insertions(+)
diff --git a/target/arm/tcg/fp8_helper.c b/target/arm/tcg/fp8_helper.c
index 5606f0fd8e..902e4b0ae9 100644
--- a/target/arm/tcg/fp8_helper.c
+++ b/target/arm/tcg/fp8_helper.c
@@ -950,3 +950,26 @@ void HELPER(sme_fvdot_idx_sb)(void *vd, void *vn, void *vm,
} while (++i & 3);
} while (i < elements);
}
+
+void HELPER(sme_fvdot_idx_hb)(void *vd, void *vn, void *vm,
+ CPUARMState *env, uint32_t desc)
+{
+ FP8MulContext ctx = fp8_mul_start(env, 0xf);
+ intptr_t oprsz = simd_maxsz(desc);
+ intptr_t elements = oprsz / sizeof(float16);
+ int idx_n = extract32(desc, SIMD_DATA_SHIFT, 1);
+ int idx_m = extract32(desc, SIMD_DATA_SHIFT + 1, 3);
+ float16 *d = vd;
+ uint8_t *n0 = vn;
+ uint8_t *n1 = vn + sizeof(ARMVectorReg);
+ uint16_t *m = vm;
+ intptr_t i = 0;
+
+ do {
+ uint16_t mm = m[H2(2 * i + idx_m)];
+ do {
+ uint16_t nn = n0[H1(4 * i + idx_n)] | (n1[H1(4 * i + idx_n)] << 8);
+ d[H2(i)] = f8dotadd_h(nn, mm, 2, d[H2(i)], &ctx);
+ } while (++i & 7);
+ } while (i < elements);
+}
diff --git a/target/arm/tcg/helper-fp8-defs.h b/target/arm/tcg/helper-fp8-defs.h
index 05bf8dbdc2..126dcadf77 100644
--- a/target/arm/tcg/helper-fp8-defs.h
+++ b/target/arm/tcg/helper-fp8-defs.h
@@ -43,3 +43,4 @@ DEF_HELPER_FLAGS_7(sme_fmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr,
DEF_HELPER_FLAGS_7(sme_fmopa_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32)
DEF_HELPER_FLAGS_5(sme_fvdot_idx_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32)
+DEF_HELPER_FLAGS_5(sme_fvdot_idx_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32)
diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode
index 160cf130d4..3a65e1ad4b 100644
--- a/target/arm/tcg/sme.decode
+++ b/target/arm/tcg/sme.decode
@@ -821,6 +821,8 @@ FDOT_nx_hb 11000001 0001 .... 1 .. 1.. ...10 0. ... @azx_4x1_i3_o3
FVDOTB_sb 11000001 1101 .... 0 .. 01. ....0 0. ... @azx_4x2_i2_o3
FVDOTT_sb 11000001 1101 .... 0 .. 01. ....0 1. ... @azx_4x2_i2_o3
+FVDOT_hb 11000001 1101 .... 0 .. 1.. ....1 0. ... @azx_2x1_i3_o3
+
### SME2 Add / Sub array accumulators
ADD_aaz_s 11000001 101 000000 .. 111 ....0 10 ... @az_2x2_o3
diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c
index 267a6b0d9b..ff5554eefb 100644
--- a/target/arm/tcg/translate-sme.c
+++ b/target/arm/tcg/translate-sme.c
@@ -1273,6 +1273,10 @@ static bool do_fvdot_sb(DisasContext *s, arg_azx_n *a, bool top)
TRANS_FEAT(FVDOTB_sb, aa64_sme_f8f32, do_fvdot_sb, a, false)
TRANS_FEAT(FVDOTT_sb, aa64_sme_f8f32, do_fvdot_sb, a, true)
+TRANS_FEAT(FVDOT_hb, aa64_sme_f8f16, do_azz_acc_fp8,
+ a->n, 2, a->rv, a->off, a->zn, a->zm,
+ (a->idx << 1), 0, false, gen_helper_sme_fvdot_idx_hb)
+
static bool do_fmla(DisasContext *s, arg_azz_n *a, bool multi,
ARMFPStatusFlavour fpst, gen_helper_gvec_3_ptr *fn)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 53/54] target/arm: Enable FEAT_SME_F8F16 for -cpu max
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (51 preceding siblings ...)
2026-06-29 12:00 ` [PULL 52/54] target/arm: Implement FVDOT (FP8 to FP16) Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-06-29 12:00 ` [PULL 54/54] docs/system/arm/virt: Document accelerated SMMUv3 and Tegra241 CMDQV Peter Maydell
2026-07-02 10:31 ` [PULL 00/54] target-arm queue Stefan Hajnoczi
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20260625015159.719300-11-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
docs/system/arm/emulation.rst | 1 +
linux-user/aarch64/elfload.c | 1 +
target/arm/tcg/cpu64.c | 1 +
3 files changed, 3 insertions(+)
diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst
index 6b02bd829a..191d1a8c93 100644
--- a/docs/system/arm/emulation.rst
+++ b/docs/system/arm/emulation.rst
@@ -170,6 +170,7 @@ the following architecture extensions:
- FEAT_SME_FA64 (Full A64 instruction set in Streaming SVE mode)
- FEAT_SME_F16F16 (Non-widening half-precision FP16 arithmetic for SME2)
- FEAT_SME_F64F64 (Double-precision floating-point outer product instructions)
+- FEAT_SME_F8F16 (SME2 ZA-targeting FP8 multiply-accumulate, dot product, and outer product to half-precision instructions)
- FEAT_SME_F8F32 (SME2 ZA-targeting FP8 multiply-accumulate, dot product, and outer product to single-precision instructions)
- FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions)
- FEAT_SME_LUTv2 (Lookup table instructions with 4-bit indices and 8-bit elements)
diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c
index 20b9838520..42aeb29306 100644
--- a/linux-user/aarch64/elfload.c
+++ b/linux-user/aarch64/elfload.c
@@ -236,6 +236,7 @@ abi_ulong get_elf_hwcap2(CPUState *cs)
GET_FEATURE_ID(aa64_ssve_f8dp4, ARM_HWCAP2_A64_SME_SF8DP4);
GET_FEATURE_ID(aa64_ssve_f8dp2, ARM_HWCAP2_A64_SME_SF8DP2);
GET_FEATURE_ID(aa64_sme_f8f32, ARM_HWCAP2_A64_SME_F8F32);
+ GET_FEATURE_ID(aa64_sme_f8f16, ARM_HWCAP2_A64_SME_F8F16);
return hwcaps;
}
diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c
index a5734446dd..ae3952a1ed 100644
--- a/target/arm/tcg/cpu64.c
+++ b/target/arm/tcg/cpu64.c
@@ -1393,6 +1393,7 @@ void aarch64_max_tcg_initfn(Object *obj)
t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* FEAT_SME */
t = FIELD_DP64(t, ID_AA64SMFR0, I8I32, 0xf); /* FEAT_SME */
t = FIELD_DP64(t, ID_AA64SMFR0, F8F32, 1); /* FEAT_SME_F8F32 */
+ t = FIELD_DP64(t, ID_AA64SMFR0, F8F16, 1); /* FEAT_SME_F8F16 */
t = FIELD_DP64(t, ID_AA64SMFR0, F16F16, 1); /* FEAT_SME_F16F16 */
t = FIELD_DP64(t, ID_AA64SMFR0, B16B16, 1); /* FEAT_SME_B16B16 */
t = FIELD_DP64(t, ID_AA64SMFR0, I16I32, 5); /* FEAT_SME2 */
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* [PULL 54/54] docs/system/arm/virt: Document accelerated SMMUv3 and Tegra241 CMDQV
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (52 preceding siblings ...)
2026-06-29 12:00 ` [PULL 53/54] target/arm: Enable FEAT_SME_F8F16 for -cpu max Peter Maydell
@ 2026-06-29 12:00 ` Peter Maydell
2026-07-02 10:31 ` [PULL 00/54] target-arm queue Stefan Hajnoczi
54 siblings, 0 replies; 67+ messages in thread
From: Peter Maydell @ 2026-06-29 12:00 UTC (permalink / raw)
To: qemu-devel
From: Shameer Kolothum <skolothumtho@nvidia.com>
Add subsections for accel=on (nested Stage-1/Stage-2 translation) and
cmdqv (Tegra241 CMDQV hardware command queues) under "User-creatable
SMMUv3 devices".
Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
Message-id: 20260623104003.36590-1-skolothumtho@nvidia.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
docs/system/arm/virt.rst | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst
index f811e662d6..ae0be35d57 100644
--- a/docs/system/arm/virt.rst
+++ b/docs/system/arm/virt.rst
@@ -288,6 +288,32 @@ User-creatable SMMUv3 devices
-device pxb-pcie,id=pcie.1,numa_node=1
-device arm-smmuv3,primary-bus=pcie.1,id=smmuv3.1
+ *Accelerated SMMUv3 (nested translation)*
+
+ The ``accel=on`` option enables hardware-accelerated nested translation
+ for vfio-pci passthrough devices. In this mode the guest SMMU driver
+ programs its own Stage-1 page tables, with the host SMMUv3 handling both
+ Stage-1 (guest) and Stage-2 (host) translations in hardware. The host
+ SMMUv3 must support nested translation. This mode requires the iommufd
+ backend and is only supported when booting with ACPI (not device tree).
+
+ When ``accel=on``, QEMU automatically derives the values for the
+ ``ril``, ``ats``, ``oas``, ``ssidsize`` and ``cmdqv`` sub-options
+ from the host SMMUv3 capabilities unless they are set explicitly.
+
+ Example::
+
+ -device arm-smmuv3,primary-bus=pcie.0,id=smmuv3.0,accel=on
+
+ *Accelerated SMMUv3 command queues (Tegra241 CMDQV)*
+
+ The ``cmdqv`` sub-option enables NVIDIA Tegra241 Command Queue
+ Virtualization (CMDQV) on supported hosts. With CMDQV, each accelerated
+ ``arm-smmuv3`` device gets dedicated hardware command queues and the
+ guest issues SMMU invalidation commands directly to real hardware,
+ bypassing QEMU and improving throughput for workloads that issue many
+ invalidations. Without it, every invalidation command traps into QEMU.
+
Linux guest kernel configuration
""""""""""""""""""""""""""""""""
--
2.43.0
^ permalink raw reply related [flat|nested] 67+ messages in thread* Re: [PULL 00/54] target-arm queue
2026-06-29 11:59 [PULL 00/54] target-arm queue Peter Maydell
` (53 preceding siblings ...)
2026-06-29 12:00 ` [PULL 54/54] docs/system/arm/virt: Document accelerated SMMUv3 and Tegra241 CMDQV Peter Maydell
@ 2026-07-02 10:31 ` Stefan Hajnoczi
54 siblings, 0 replies; 67+ messages in thread
From: Stefan Hajnoczi @ 2026-07-02 10:31 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 116 bytes --]
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 67+ messages in thread