From 2b0d2ab895022814da13127e47c17890690488da Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:56 +0100 Subject: [PATCH 01/37] target/arm: Handle HCR_EL2 accesses for bits introduced with FEAT_NMI FEAT_NMI defines another three new bits in HCRX_EL2: TALLINT, HCRX_VINMI and HCRX_VFNMI. When the feature is enabled, allow these bits to be written in HCRX_EL2. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-2-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 +++++ target/arm/helper.c | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index e5758d9fbc..b300d0446d 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -681,6 +681,11 @@ static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; } +static inline bool isar_feature_aa64_nmi(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, NMI) != 0; +} + static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) { return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; diff --git a/target/arm/helper.c b/target/arm/helper.c index a620481d7c..7a25ea65c9 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6187,13 +6187,19 @@ bool el_is_in_host(CPUARMState *env, int el) static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + ARMCPU *cpu = env_archcpu(env); uint64_t valid_mask = 0; /* FEAT_MOPS adds MSCEn and MCE2 */ - if (cpu_isar_feature(aa64_mops, env_archcpu(env))) { + if (cpu_isar_feature(aa64_mops, cpu)) { valid_mask |= HCRX_MSCEN | HCRX_MCE2; } + /* FEAT_NMI adds TALLINT, VINMI and VFNMI */ + if (cpu_isar_feature(aa64_nmi, cpu)) { + valid_mask |= HCRX_TALLINT | HCRX_VINMI | HCRX_VFNMI; + } + /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; } From 6aa20415613096034ac943872aebd49fbe48e2d0 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:56 +0100 Subject: [PATCH 02/37] target/arm: Add PSTATE.ALLINT When PSTATE.ALLINT is set, an IRQ or FIQ interrupt that is targeted to ELx, with or without superpriority is masked. As Richard suggested, place ALLINT bit in PSTATE in env->pstate. In the pseudocode, AArch64.ExceptionReturn() calls SetPSTATEFromPSR(), which treats PSTATE.ALLINT as one of the bits which are reinstated from SPSR to PSTATE regardless of whether this is an illegal exception return or not. So handle PSTATE.ALLINT the same way as PSTATE.DAIF in the illegal_return exit path of the exception_return helper. With the change, exception entry and return are automatically handled. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-3-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/cpu.h | 1 + target/arm/tcg/helper-a64.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index bc0c84873f..de740d223f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1430,6 +1430,7 @@ void pmu_init(ARMCPU *cpu); #define PSTATE_D (1U << 9) #define PSTATE_BTYPE (3U << 10) #define PSTATE_SSBS (1U << 12) +#define PSTATE_ALLINT (1U << 13) #define PSTATE_IL (1U << 20) #define PSTATE_SS (1U << 21) #define PSTATE_PAN (1U << 22) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index ebaa7f00df..29f3ef274a 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -892,8 +892,8 @@ illegal_return: */ env->pstate |= PSTATE_IL; env->pc = new_pc; - spsr &= PSTATE_NZCV | PSTATE_DAIF; - spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF); + spsr &= PSTATE_NZCV | PSTATE_DAIF | PSTATE_ALLINT; + spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF | PSTATE_ALLINT); pstate_write(env, spsr); if (!arm_singlestep_active(env)) { env->pstate &= ~PSTATE_SS; From 4833c75611e334164b970c79be95f239ce676ab1 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:57 +0100 Subject: [PATCH 03/37] target/arm: Add support for FEAT_NMI, Non-maskable Interrupt Add support for FEAT_NMI. NMI (FEAT_NMI) is an mandatory feature in ARMv8.8-A and ARM v9.3-A. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-4-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/internals.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/internals.h b/target/arm/internals.h index dd3da211a3..516e0584bf 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1229,6 +1229,9 @@ static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id) if (isar_feature_aa64_mte(id)) { valid |= PSTATE_TCO; } + if (isar_feature_aa64_nmi(id)) { + valid |= PSTATE_ALLINT; + } return valid; } From cbf817a2ff7dc12b62e0bccc15ae93369ea5829e Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:57 +0100 Subject: [PATCH 04/37] target/arm: Implement ALLINT MSR (immediate) Add ALLINT MSR (immediate) to decodetree, in which the CRm is 0b000x. The EL0 check is necessary to ALLINT, and the EL1 check is necessary when imm == 1. So implement it inline for EL2/3, or EL1 with imm==0. Avoid the unconditional write to pc and use raise_exception_ra to unwind. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-5-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 1 + target/arm/tcg/helper-a64.c | 12 ++++++++++++ target/arm/tcg/helper-a64.h | 1 + target/arm/tcg/translate-a64.c | 19 +++++++++++++++++++ 4 files changed, 33 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 8a20dce3c8..0e7656fd15 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -207,6 +207,7 @@ MSR_i_DIT 1101 0101 0000 0 011 0100 .... 010 11111 @msr_i MSR_i_TCO 1101 0101 0000 0 011 0100 .... 100 11111 @msr_i MSR_i_DAIFSET 1101 0101 0000 0 011 0100 .... 110 11111 @msr_i MSR_i_DAIFCLEAR 1101 0101 0000 0 011 0100 .... 111 11111 @msr_i +MSR_i_ALLINT 1101 0101 0000 0 001 0100 000 imm:1 000 11111 MSR_i_SVCR 1101 0101 0000 0 011 0100 0 mask:2 imm:1 011 11111 # MRS, MSR (register), SYS, SYSL. These are all essentially the diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 29f3ef274a..0ea8668ab4 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -66,6 +66,18 @@ void HELPER(msr_i_spsel)(CPUARMState *env, uint32_t imm) update_spsel(env, imm); } +void HELPER(msr_set_allint_el1)(CPUARMState *env) +{ + /* ALLINT update to PSTATE. */ + if (arm_hcrx_el2_eff(env) & HCRX_TALLINT) { + raise_exception_ra(env, EXCP_UDEF, + syn_aa64_sysregtrap(0, 1, 0, 4, 1, 0x1f, 0), 2, + GETPC()); + } + + env->pstate |= PSTATE_ALLINT; +} + static void daif_check(CPUARMState *env, uint32_t op, uint32_t imm, uintptr_t ra) { diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 575a5dab7d..0518165399 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -22,6 +22,7 @@ DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_2(msr_i_spsel, void, env, i32) DEF_HELPER_2(msr_i_daifset, void, env, i32) DEF_HELPER_2(msr_i_daifclear, void, env, i32) +DEF_HELPER_1(msr_set_allint_el1, void, env) DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 2666d52711..976094a5c8 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2036,6 +2036,25 @@ static bool trans_MSR_i_DAIFCLEAR(DisasContext *s, arg_i *a) return true; } +static bool trans_MSR_i_ALLINT(DisasContext *s, arg_i *a) +{ + if (!dc_isar_feature(aa64_nmi, s) || s->current_el == 0) { + return false; + } + + if (a->imm == 0) { + clear_pstate_bits(PSTATE_ALLINT); + } else if (s->current_el > 1) { + set_pstate_bits(PSTATE_ALLINT); + } else { + gen_helper_msr_set_allint_el1(tcg_env); + } + + /* Exit the cpu loop to re-evaluate pending IRQs. */ + s->base.is_jmp = DISAS_UPDATE_EXIT; + return true; +} + static bool trans_MSR_i_SVCR(DisasContext *s, arg_MSR_i_SVCR *a) { if (!dc_isar_feature(aa64_sme, s) || a->mask == 0) { From 5c2169746136ffc93bf1e18c294a05e8446d8af7 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:57 +0100 Subject: [PATCH 05/37] target/arm: Support MSR access to ALLINT Support ALLINT msr access as follow: mrs , ALLINT // read allint msr ALLINT, // write allint with imm Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-6-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 7a25ea65c9..b9443b1813 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7500,6 +7500,37 @@ static const ARMCPRegInfo rme_mte_reginfo[] = { .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 14, .opc2 = 5, .access = PL3_W, .type = ARM_CP_NOP }, }; + +static void aa64_allint_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->pstate = (env->pstate & ~PSTATE_ALLINT) | (value & PSTATE_ALLINT); +} + +static uint64_t aa64_allint_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return env->pstate & PSTATE_ALLINT; +} + +static CPAccessResult aa64_allint_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + if (!isread && arm_current_el(env) == 1 && + (arm_hcrx_el2_eff(env) & HCRX_TALLINT)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo nmi_reginfo[] = { + { .name = "ALLINT", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 0, .crn = 4, .crm = 3, + .type = ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = aa64_allint_access, + .fieldoffset = offsetof(CPUARMState, pstate), + .writefn = aa64_allint_write, .readfn = aa64_allint_read, + .resetfn = arm_cp_reset_ignore }, +}; #endif /* TARGET_AARCH64 */ static void define_pmu_regs(ARMCPU *cpu) @@ -9894,6 +9925,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_nv2, cpu)) { define_arm_cp_regs(cpu, nv2_reginfo); } + + if (cpu_isar_feature(aa64_nmi, cpu)) { + define_arm_cp_regs(cpu, nmi_reginfo); + } #endif if (cpu_isar_feature(any_predinv, cpu)) { From b36a32ead1596929b1fa5436593d49cac20c20e6 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:58 +0100 Subject: [PATCH 06/37] target/arm: Add support for Non-maskable Interrupt This only implements the external delivery method via the GICv3. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-7-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/cpu-qom.h | 5 +- target/arm/cpu.c | 147 ++++++++++++++++++++++++++++++++++++++--- target/arm/cpu.h | 6 ++ target/arm/helper.c | 33 +++++++-- target/arm/internals.h | 18 +++++ 5 files changed, 193 insertions(+), 16 deletions(-) diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 8e032691db..b497667d61 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -36,11 +36,14 @@ DECLARE_CLASS_CHECKERS(AArch64CPUClass, AARCH64_CPU, #define ARM_CPU_TYPE_SUFFIX "-" TYPE_ARM_CPU #define ARM_CPU_TYPE_NAME(name) (name ARM_CPU_TYPE_SUFFIX) -/* Meanings of the ARMCPU object's four inbound GPIO lines */ +/* Meanings of the ARMCPU object's seven inbound GPIO lines */ #define ARM_CPU_IRQ 0 #define ARM_CPU_FIQ 1 #define ARM_CPU_VIRQ 2 #define ARM_CPU_VFIQ 3 +#define ARM_CPU_NMI 4 +#define ARM_CPU_VINMI 5 +#define ARM_CPU_VFNMI 6 /* For M profile, some registers are banked secure vs non-secure; * these are represented as a 2-element array where the first element diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ab8d007a86..d2dfd36fd4 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -122,6 +122,13 @@ void arm_restore_state_to_opc(CPUState *cs, } #endif /* CONFIG_TCG */ +/* + * With SCTLR_ELx.NMI == 0, IRQ with Superpriority is masked identically with + * IRQ without Superpriority. Moreover, if the GIC is configured so that + * FEAT_GICv3_NMI is only set if FEAT_NMI is set, then we won't ever see + * CPU_INTERRUPT_*NMI anyway. So we might as well accept NMI here + * unconditionally. + */ static bool arm_cpu_has_work(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -129,6 +136,7 @@ static bool arm_cpu_has_work(CPUState *cs) return (cpu->power_state != PSCI_OFF) && cs->interrupt_request & (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD + | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR | CPU_INTERRUPT_EXITTB); } @@ -668,6 +676,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, CPUARMState *env = cpu_env(cs); bool pstate_unmasked; bool unmasked = false; + bool allIntMask = false; /* * Don't take exceptions if they target a lower EL. @@ -678,13 +687,36 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, return false; } + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + env->cp15.sctlr_el[target_el] & SCTLR_NMI && cur_el == target_el) { + allIntMask = env->pstate & PSTATE_ALLINT || + ((env->cp15.sctlr_el[target_el] & SCTLR_SPINTMASK) && + (env->pstate & PSTATE_SP)); + } + switch (excp_idx) { + case EXCP_NMI: + pstate_unmasked = !allIntMask; + break; + + case EXCP_VINMI: + if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { + /* VINMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; + case EXCP_VFNMI: + if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { + /* VFNMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; case EXCP_FIQ: - pstate_unmasked = !(env->daif & PSTATE_F); + pstate_unmasked = (!(env->daif & PSTATE_F)) && (!allIntMask); break; case EXCP_IRQ: - pstate_unmasked = !(env->daif & PSTATE_I); + pstate_unmasked = (!(env->daif & PSTATE_I)) && (!allIntMask); break; case EXCP_VFIQ: @@ -692,13 +724,13 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, /* VFIQs are only taken when hypervized. */ return false; } - return !(env->daif & PSTATE_F); + return !(env->daif & PSTATE_F) && (!allIntMask); case EXCP_VIRQ: if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { /* VIRQs are only taken when hypervized. */ return false; } - return !(env->daif & PSTATE_I); + return !(env->daif & PSTATE_I) && (!allIntMask); case EXCP_VSERR: if (!(hcr_el2 & HCR_AMO) || (hcr_el2 & HCR_TGE)) { /* VIRQs are only taken when hypervized. */ @@ -804,6 +836,48 @@ static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) /* The prioritization of interrupts is IMPLEMENTATION DEFINED. */ + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + (arm_sctlr(env, cur_el) & SCTLR_NMI)) { + if (interrupt_request & CPU_INTERRUPT_NMI) { + excp_idx = EXCP_NMI; + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + excp_idx = EXCP_VINMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + excp_idx = EXCP_VFNMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + } else { + /* + * NMI disabled: interrupts with superpriority are handled + * as if they didn't have it + */ + if (interrupt_request & CPU_INTERRUPT_NMI) { + interrupt_request |= CPU_INTERRUPT_HARD; + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + interrupt_request |= CPU_INTERRUPT_VIRQ; + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + interrupt_request |= CPU_INTERRUPT_VFIQ; + } + } + if (interrupt_request & CPU_INTERRUPT_FIQ) { excp_idx = EXCP_FIQ; target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); @@ -867,7 +941,8 @@ void arm_cpu_update_virq(ARMCPU *cpu) CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); - bool new_state = (env->cp15.hcr_el2 & HCR_VI) || + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || (env->irq_line_state & CPU_INTERRUPT_VIRQ); if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VIRQ) != 0)) { @@ -888,7 +963,8 @@ void arm_cpu_update_vfiq(ARMCPU *cpu) CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); - bool new_state = (env->cp15.hcr_el2 & HCR_VF) || + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VF) && + !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || (env->irq_line_state & CPU_INTERRUPT_VFIQ); if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFIQ) != 0)) { @@ -900,6 +976,48 @@ void arm_cpu_update_vfiq(ARMCPU *cpu) } } +void arm_cpu_update_vinmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VINMI, which is the logical OR of + * the HCRX_EL2.VINMI bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || + (env->irq_line_state & CPU_INTERRUPT_VINMI); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VINMI) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VINMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VINMI); + } + } +} + +void arm_cpu_update_vfnmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VFNMI, which is the HCRX_EL2.VFNMI bit. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && + (arm_hcrx_el2_eff(env) & HCRX_VFNMI); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFNMI) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VFNMI); + } + } +} + void arm_cpu_update_vserr(ARMCPU *cpu) { /* @@ -929,7 +1047,9 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) [ARM_CPU_IRQ] = CPU_INTERRUPT_HARD, [ARM_CPU_FIQ] = CPU_INTERRUPT_FIQ, [ARM_CPU_VIRQ] = CPU_INTERRUPT_VIRQ, - [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ + [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ, + [ARM_CPU_NMI] = CPU_INTERRUPT_NMI, + [ARM_CPU_VINMI] = CPU_INTERRUPT_VINMI, }; if (!arm_feature(env, ARM_FEATURE_EL2) && @@ -955,8 +1075,12 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) case ARM_CPU_VFIQ: arm_cpu_update_vfiq(cpu); break; + case ARM_CPU_VINMI: + arm_cpu_update_vinmi(cpu); + break; case ARM_CPU_IRQ: case ARM_CPU_FIQ: + case ARM_CPU_NMI: if (level) { cpu_interrupt(cs, mask[irq]); } else { @@ -1350,12 +1474,13 @@ static void arm_cpu_initfn(Object *obj) #else /* Our inbound IRQ and FIQ lines */ if (kvm_enabled()) { - /* VIRQ and VFIQ are unused with KVM but we add them to maintain - * the same interface as non-KVM CPUs. + /* + * VIRQ, VFIQ, NMI, VINMI are unused with KVM but we add + * them to maintain the same interface as non-KVM CPUs. */ - qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 4); + qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 6); } else { - qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 4); + qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 6); } qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs, diff --git a/target/arm/cpu.h b/target/arm/cpu.h index de740d223f..08a6bc50de 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -61,6 +61,9 @@ #define EXCP_DIVBYZERO 23 /* v7M DIVBYZERO UsageFault */ #define EXCP_VSERR 24 #define EXCP_GPC 25 /* v9 Granule Protection Check Fault */ +#define EXCP_NMI 26 +#define EXCP_VINMI 27 +#define EXCP_VFNMI 28 /* NB: add new EXCP_ defines to the array in arm_log_exception() too */ #define ARMV7M_EXCP_RESET 1 @@ -80,6 +83,9 @@ #define CPU_INTERRUPT_VIRQ CPU_INTERRUPT_TGT_EXT_2 #define CPU_INTERRUPT_VFIQ CPU_INTERRUPT_TGT_EXT_3 #define CPU_INTERRUPT_VSERR CPU_INTERRUPT_TGT_INT_0 +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_4 +#define CPU_INTERRUPT_VINMI CPU_INTERRUPT_TGT_EXT_0 +#define CPU_INTERRUPT_VFNMI CPU_INTERRUPT_TGT_INT_1 /* The usual mapping for an AArch64 system register to its AArch32 * counterpart is for the 32 bit world to have access to the lower diff --git a/target/arm/helper.c b/target/arm/helper.c index b9443b1813..f61a65d811 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6046,15 +6046,19 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) * and the state of the input lines from the GIC. (This requires * that we have the BQL, which is done by marking the * reginfo structs as ARM_CP_IO.) - * Note that if a write to HCR pends a VIRQ or VFIQ it is never - * possible for it to be taken immediately, because VIRQ and - * VFIQ are masked unless running at EL0 or EL1, and HCR - * can only be written at EL2. + * Note that if a write to HCR pends a VIRQ or VFIQ or VINMI or + * VFNMI, it is never possible for it to be taken immediately + * because VIRQ, VFIQ, VINMI and VFNMI are masked unless running + * at EL0 or EL1, and HCR can only be written at EL2. */ g_assert(bql_locked()); arm_cpu_update_virq(cpu); arm_cpu_update_vfiq(cpu); arm_cpu_update_vserr(cpu); + if (cpu_isar_feature(aa64_nmi, cpu)) { + arm_cpu_update_vinmi(cpu); + arm_cpu_update_vfnmi(cpu); + } } static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -6202,6 +6206,23 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; + + /* + * Updates to VINMI and VFNMI require us to update the status of + * virtual NMI, which are the logical OR of these bits + * and the state of the input lines from the GIC. (This requires + * that we have the BQL, which is done by marking the + * reginfo structs as ARM_CP_IO.) + * Note that if a write to HCRX pends a VINMI or VFNMI it is never + * possible for it to be taken immediately, because VINMI and + * VFNMI are masked unless running at EL0 or EL1, and HCRX + * can only be written at EL2. + */ + if (cpu_isar_feature(aa64_nmi, cpu)) { + g_assert(bql_locked()); + arm_cpu_update_vinmi(cpu); + arm_cpu_update_vfnmi(cpu); + } } static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri, @@ -6217,6 +6238,7 @@ static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo hcrx_el2_reginfo = { .name = "HCRX_EL2", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_IO, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 2, .access = PL2_RW, .writefn = hcrx_write, .accessfn = access_hxen, .nv2_redirect_offset = 0xa0, @@ -10799,6 +10821,9 @@ void arm_log_exception(CPUState *cs) [EXCP_DIVBYZERO] = "v7M DIVBYZERO UsageFault", [EXCP_VSERR] = "Virtual SERR", [EXCP_GPC] = "Granule Protection Check", + [EXCP_NMI] = "NMI", + [EXCP_VINMI] = "Virtual IRQ NMI", + [EXCP_VFNMI] = "Virtual FIQ NMI", }; if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { diff --git a/target/arm/internals.h b/target/arm/internals.h index 516e0584bf..b53f5e8ff2 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1109,6 +1109,24 @@ void arm_cpu_update_virq(ARMCPU *cpu); */ void arm_cpu_update_vfiq(ARMCPU *cpu); +/** + * arm_cpu_update_vinmi: Update CPU_INTERRUPT_VINMI bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VINMI bit in cs->interrupt_request, following + * a change to either the input VNMI line from the GIC or the HCRX_EL2.VINMI. + * Must be called with the BQL held. + */ +void arm_cpu_update_vinmi(ARMCPU *cpu); + +/** + * arm_cpu_update_vfnmi: Update CPU_INTERRUPT_VFNMI bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VFNMI bit in cs->interrupt_request, following + * a change to the HCRX_EL2.VFNMI. + * Must be called with the BQL held. + */ +void arm_cpu_update_vfnmi(ARMCPU *cpu); + /** * arm_cpu_update_vserr: Update CPU_INTERRUPT_VSERR bit * From 963e4e3648e0601a8f0b288edaf524b3c98fffbd Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:58 +0100 Subject: [PATCH 07/37] target/arm: Add support for NMI in arm_phys_excp_target_el() According to Arm GIC section 4.6.3 Interrupt superpriority, the interrupt with superpriority is always IRQ, never FIQ, so handle NMI same as IRQ in arm_phys_excp_target_el(). Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-8-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index f61a65d811..4ee59b3705 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -10763,6 +10763,7 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, hcr_el2 = arm_hcr_el2_eff(env); switch (excp_idx) { case EXCP_IRQ: + case EXCP_NMI: scr = ((env->cp15.scr_el3 & SCR_IRQ) == SCR_IRQ); hcr = hcr_el2 & HCR_IMO; break; From 2e0be5f6b122e3dca53b926514287ffcead2689c Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:59 +0100 Subject: [PATCH 08/37] target/arm: Handle IS/FS in ISR_EL1 for NMI, VINMI and VFNMI Add IS and FS bit in ISR_EL1 and handle the read. With CPU_INTERRUPT_NMI or CPU_INTERRUPT_VINMI, both CPSR_I and ISR_IS must be set. With CPU_INTERRUPT_VFNMI, both CPSR_F and ISR_FS must be set. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-9-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/cpu.h | 2 ++ target/arm/helper.c | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 08a6bc50de..97997dbd08 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1398,6 +1398,8 @@ void pmu_init(ARMCPU *cpu); #define CPSR_N (1U << 31) #define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V) #define CPSR_AIF (CPSR_A | CPSR_I | CPSR_F) +#define ISR_FS (1U << 9) +#define ISR_IS (1U << 10) #define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7) #define CACHED_CPSR_BITS (CPSR_T | CPSR_AIF | CPSR_GE | CPSR_IT | CPSR_Q \ diff --git a/target/arm/helper.c b/target/arm/helper.c index 4ee59b3705..6b6d8a349a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2021,16 +2021,29 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { ret |= CPSR_I; } + if (cs->interrupt_request & CPU_INTERRUPT_VINMI) { + ret |= ISR_IS; + ret |= CPSR_I; + } } else { if (cs->interrupt_request & CPU_INTERRUPT_HARD) { ret |= CPSR_I; } + + if (cs->interrupt_request & CPU_INTERRUPT_NMI) { + ret |= ISR_IS; + ret |= CPSR_I; + } } if (hcr_el2 & HCR_FMO) { if (cs->interrupt_request & CPU_INTERRUPT_VFIQ) { ret |= CPSR_F; } + if (cs->interrupt_request & CPU_INTERRUPT_VFNMI) { + ret |= ISR_FS; + ret |= CPSR_F; + } } else { if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { ret |= CPSR_F; From 167f2631df98c5b1af622a9afe3afe00867ef080 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:32:59 +0100 Subject: [PATCH 09/37] target/arm: Handle PSTATE.ALLINT on taking an exception Set or clear PSTATE.ALLINT on taking an exception to ELx according to the SCTLR_ELx.SPINTMASK bit. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-10-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 6b6d8a349a..5ff9e44649 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11733,6 +11733,14 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } } + if (cpu_isar_feature(aa64_nmi, cpu)) { + if (!(env->cp15.sctlr_el[new_el] & SCTLR_SPINTMASK)) { + new_mode |= PSTATE_ALLINT; + } else { + new_mode &= ~PSTATE_ALLINT; + } + } + pstate_write(env, PSTATE_DAIF | new_mode); env->aarch64 = true; aarch64_restore_sp(env, new_el); From 83f320753827da6bd381b46b8f3e6736046c86cd Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:00 +0100 Subject: [PATCH 10/37] hw/intc/arm_gicv3: Add external IRQ lines for NMI Augment the GICv3's QOM device interface by adding one new set of sysbus IRQ line, to signal NMI to each CPU. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-11-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_common.c | 6 ++++++ include/hw/intc/arm_gic_common.h | 2 ++ include/hw/intc/arm_gicv3_common.h | 2 ++ 3 files changed, 10 insertions(+) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index cb55c72681..c52f060026 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -299,6 +299,12 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, for (i = 0; i < s->num_cpu; i++) { sysbus_init_irq(sbd, &s->cpu[i].parent_vfiq); } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->cpu[i].parent_nmi); + } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->cpu[i].parent_vnmi); + } memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, "gicv3_dist", 0x10000); diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index 7080375008..97fea4102d 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -71,6 +71,8 @@ struct GICState { qemu_irq parent_fiq[GIC_NCPU]; qemu_irq parent_virq[GIC_NCPU]; qemu_irq parent_vfiq[GIC_NCPU]; + qemu_irq parent_nmi[GIC_NCPU]; + qemu_irq parent_vnmi[GIC_NCPU]; qemu_irq maintenance_irq[GIC_NCPU]; /* GICD_CTLR; for a GIC with the security extensions the NS banked version diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 4e2fb518e7..7324c7d983 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -155,6 +155,8 @@ struct GICv3CPUState { qemu_irq parent_fiq; qemu_irq parent_virq; qemu_irq parent_vfiq; + qemu_irq parent_nmi; + qemu_irq parent_vnmi; /* Redistributor */ uint32_t level; /* Current IRQ level */ From 34d94b7af9f1dc4cae550ecc7b825c9567741a12 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:00 +0100 Subject: [PATCH 11/37] hw/arm/virt: Wire NMI and VINMI irq lines from GIC to CPU Wire the new NMI and VINMI interrupt line from the GIC to each CPU if it is not GICv2. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Message-id: 20240407081733.3231820-12-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c9119ef384..c4b03b09c2 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -821,7 +821,8 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) /* Wire the outputs from each CPU's generic timer and the GICv3 * maintenance interrupt signal to the appropriate GIC PPI inputs, - * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + * and the GIC's IRQ/FIQ/VIRQ/VFIQ/NMI/VINMI interrupt outputs to the + * CPU's inputs. */ for (i = 0; i < smp_cpus; i++) { DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); @@ -865,6 +866,13 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + if (vms->gic_version != VIRT_GIC_VERSION_2) { + sysbus_connect_irq(gicbusdev, i + 4 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_NMI)); + sysbus_connect_irq(gicbusdev, i + 5 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VINMI)); + } } fdt_add_gic_node(vms); From e4eb290571606c5e6dc7739547b5056389cd92fb Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:01 +0100 Subject: [PATCH 12/37] target/arm: Handle NMI in arm_cpu_do_interrupt_aarch64() According to Arm GIC section 4.6.3 Interrupt superpriority, the interrupt with superpriority is always IRQ, never FIQ, so the NMI exception trap entry behave like IRQ. And VINMI(vIRQ with Superpriority) can be raised from the GIC or come from the hcrx_el2.HCRX_VINMI bit, VFNMI(vFIQ with Superpriority) come from the hcrx_el2.HCRX_VFNMI bit. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-13-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5ff9e44649..6b224826fb 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11653,10 +11653,13 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) break; case EXCP_IRQ: case EXCP_VIRQ: + case EXCP_NMI: + case EXCP_VINMI: addr += 0x80; break; case EXCP_FIQ: case EXCP_VFIQ: + case EXCP_VFNMI: addr += 0x100; break; case EXCP_VSERR: From c9e86cbd3400032dc818bf24e823959e565b8b41 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:01 +0100 Subject: [PATCH 13/37] hw/intc/arm_gicv3: Add has-nmi property to GICv3 device Add a property has-nmi to the GICv3 device, and use this to set the NMI bit in the GICD_TYPER register. This isn't visible to guests yet because the property defaults to false and we won't set it in the board code until we've landed all of the changes needed to implement FEAT_GICV3_NMI. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-14-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_common.c | 1 + hw/intc/arm_gicv3_dist.c | 2 ++ hw/intc/gicv3_internal.h | 1 + include/hw/intc/arm_gicv3_common.h | 1 + 4 files changed, 5 insertions(+) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index c52f060026..2d2cea6858 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -569,6 +569,7 @@ static Property arm_gicv3_common_properties[] = { DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), DEFINE_PROP_BOOL("has-lpi", GICv3State, lpi_enable, 0), + DEFINE_PROP_BOOL("has-nmi", GICv3State, nmi_support, 0), DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0), /* * Compatibility property: force 8 bits of physical priority, even diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 35e850685c..22ddc0d666 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -389,6 +389,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, * by GICD_TYPER.IDbits) * MBIS == 0 (message-based SPIs not supported) * SecurityExtn == 1 if security extns supported + * NMI = 1 if Non-maskable interrupt property is supported * CPUNumber == 0 since for us ARE is always 1 * ITLinesNumber == (((max SPI IntID + 1) / 32) - 1) */ @@ -402,6 +403,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, bool dvis = s->revision >= 4; *data = (1 << 25) | (1 << 24) | (dvis << 18) | (sec_extn << 10) | + (s->nmi_support << GICD_TYPER_NMI_SHIFT) | (s->lpi_enable << GICD_TYPER_LPIS_SHIFT) | (0xf << 19) | itlinesnumber; return true; diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 29d5cdc1b6..8f4ebed2f4 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -68,6 +68,7 @@ #define GICD_CTLR_E1NWF (1U << 7) #define GICD_CTLR_RWP (1U << 31) +#define GICD_TYPER_NMI_SHIFT 9 #define GICD_TYPER_LPIS_SHIFT 17 /* 16 bits EventId */ diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 7324c7d983..4358c5319c 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -249,6 +249,7 @@ struct GICv3State { uint32_t num_irq; uint32_t revision; bool lpi_enable; + bool nmi_support; bool security_extn; bool force_8bit_prio; bool irq_reset_nonsecure; From 67d74e4c54236b53917edfa9f52efb4207064014 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:01 +0100 Subject: [PATCH 14/37] hw/intc/arm_gicv3_kvm: Not set has-nmi=true for the KVM GICv3 So far, there is no FEAT_GICv3_NMI support in the in-kernel GIC, so make it an error to try to set has-nmi=true for the KVM GICv3. Signed-off-by: Jinjie Ruan Message-id: 20240407081733.3231820-15-ruanjinjie@huawei.com Suggested-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 77eb37e131..00a383079b 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -805,6 +805,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } + if (s->nmi_support) { + error_setg(errp, "NMI is not supported with the in-kernel GIC"); + return; + } + gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); for (i = 0; i < s->num_cpu; i++) { From 0e9f4e8e7b9e3bde8b8c0a84c577f64c679b535c Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:02 +0100 Subject: [PATCH 15/37] hw/intc/arm_gicv3: Add irq non-maskable property A SPI, PPI or SGI interrupt can have non-maskable property. So maintain non-maskable property in PendingIrq and GICR/GICD. Since add new device state, it also needs to be migrated, so also save NMI info in vmstate_gicv3_cpu and vmstate_gicv3. Signed-off-by: Jinjie Ruan Acked-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-16-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_common.c | 38 ++++++++++++++++++++++++++++++ include/hw/intc/arm_gicv3_common.h | 4 ++++ 2 files changed, 42 insertions(+) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 2d2cea6858..9810558b07 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -164,6 +164,24 @@ const VMStateDescription vmstate_gicv3_gicv4 = { } }; +static bool gicv3_cpu_nmi_needed(void *opaque) +{ + GICv3CPUState *cs = opaque; + + return cs->gic->nmi_support; +} + +static const VMStateDescription vmstate_gicv3_cpu_nmi = { + .name = "arm_gicv3_cpu/nmi", + .version_id = 1, + .minimum_version_id = 1, + .needed = gicv3_cpu_nmi_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(gicr_inmir0, GICv3CPUState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3_cpu = { .name = "arm_gicv3_cpu", .version_id = 1, @@ -196,6 +214,7 @@ static const VMStateDescription vmstate_gicv3_cpu = { &vmstate_gicv3_cpu_virt, &vmstate_gicv3_cpu_sre_el1, &vmstate_gicv3_gicv4, + &vmstate_gicv3_cpu_nmi, NULL } }; @@ -238,6 +257,24 @@ const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = { } }; +static bool gicv3_nmi_needed(void *opaque) +{ + GICv3State *cs = opaque; + + return cs->nmi_support; +} + +const VMStateDescription vmstate_gicv3_gicd_nmi = { + .name = "arm_gicv3/gicd_nmi", + .version_id = 1, + .minimum_version_id = 1, + .needed = gicv3_nmi_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(nmi, GICv3State, GICV3_BMP_SIZE), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3 = { .name = "arm_gicv3", .version_id = 1, @@ -266,6 +303,7 @@ static const VMStateDescription vmstate_gicv3 = { }, .subsections = (const VMStateDescription * const []) { &vmstate_gicv3_gicd_no_migration_shift_bug, + &vmstate_gicv3_gicd_nmi, NULL } }; diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 4358c5319c..88533749eb 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -146,6 +146,7 @@ typedef struct { int irq; uint8_t prio; int grp; + bool nmi; } PendingIrq; struct GICv3CPUState { @@ -172,6 +173,7 @@ struct GICv3CPUState { uint32_t gicr_ienabler0; uint32_t gicr_ipendr0; uint32_t gicr_iactiver0; + uint32_t gicr_inmir0; uint32_t edge_trigger; /* ICFGR0 and ICFGR1 even bits */ uint32_t gicr_igrpmodr0; uint32_t gicr_nsacr; @@ -275,6 +277,7 @@ struct GICv3State { GIC_DECLARE_BITMAP(active); /* GICD_ISACTIVER */ GIC_DECLARE_BITMAP(level); /* Current level */ GIC_DECLARE_BITMAP(edge_trigger); /* GICD_ICFGR even bits */ + GIC_DECLARE_BITMAP(nmi); /* GICD_INMIR */ uint8_t gicd_ipriority[GICV3_MAXIRQ]; uint64_t gicd_irouter[GICV3_MAXIRQ]; /* Cached information: pointer to the cpu i/f for the CPUs specified @@ -314,6 +317,7 @@ GICV3_BITMAP_ACCESSORS(pending) GICV3_BITMAP_ACCESSORS(active) GICV3_BITMAP_ACCESSORS(level) GICV3_BITMAP_ACCESSORS(edge_trigger) +GICV3_BITMAP_ACCESSORS(nmi) #define TYPE_ARM_GICV3_COMMON "arm-gicv3-common" typedef struct ARMGICv3CommonClass ARMGICv3CommonClass; From 7c79d98d2e4de5b8c919002e6ead6bae7f46003d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:02 +0100 Subject: [PATCH 16/37] hw/intc/arm_gicv3_redist: Implement GICR_INMIR0 Add GICR_INMIR0 register and support access GICR_INMIR0. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-17-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_redist.c | 19 +++++++++++++++++++ hw/intc/gicv3_internal.h | 1 + 2 files changed, 20 insertions(+) diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 8153525849..ed1f9d1e44 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -35,6 +35,15 @@ static int gicr_ns_access(GICv3CPUState *cs, int irq) return extract32(cs->gicr_nsacr, irq * 2, 2); } +static void gicr_write_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, + uint32_t *reg, uint32_t val) +{ + /* Helper routine to implement writing to a "set" register */ + val &= mask_group(cs, attrs); + *reg = val; + gicv3_redist_update(cs); +} + static void gicr_write_set_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, uint32_t *reg, uint32_t val) { @@ -406,6 +415,10 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset, *data = value; return MEMTX_OK; } + case GICR_INMIR0: + *data = cs->gic->nmi_support ? + gicr_read_bitmap_reg(cs, attrs, cs->gicr_inmir0) : 0; + return MEMTX_OK; case GICR_ICFGR0: case GICR_ICFGR1: { @@ -555,6 +568,12 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset, gicv3_redist_update(cs); return MEMTX_OK; } + case GICR_INMIR0: + if (cs->gic->nmi_support) { + gicr_write_bitmap_reg(cs, attrs, &cs->gicr_inmir0, value); + } + return MEMTX_OK; + case GICR_ICFGR0: /* Register is all RAZ/WI or RAO/WI bits */ return MEMTX_OK; diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 8f4ebed2f4..21697ecf39 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -110,6 +110,7 @@ #define GICR_ICFGR1 (GICR_SGI_OFFSET + 0x0C04) #define GICR_IGRPMODR0 (GICR_SGI_OFFSET + 0x0D00) #define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00) +#define GICR_INMIR0 (GICR_SGI_OFFSET + 0x0F80) /* VLPI redistributor registers, offsets from VLPI_base */ #define GICR_VPROPBASER (GICR_VLPI_OFFSET + 0x70) From 44ed1e4b9a4df256bb56487ae5150b6807536703 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:03 +0100 Subject: [PATCH 17/37] hw/intc/arm_gicv3: Implement GICD_INMIR Add GICD_INMIR, GICD_INMIRnE register and support access GICD_INMIR0. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-18-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_dist.c | 34 ++++++++++++++++++++++++++++++++++ hw/intc/gicv3_internal.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 22ddc0d666..d8207acb22 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -89,6 +89,29 @@ static int gicd_ns_access(GICv3State *s, int irq) return extract32(s->gicd_nsacr[irq / 16], (irq % 16) * 2, 2); } +static void gicd_write_bitmap_reg(GICv3State *s, MemTxAttrs attrs, + uint32_t *bmp, maskfn *maskfn, + int offset, uint32_t val) +{ + /* + * Helper routine to implement writing to a "set" register + * (GICD_INMIR, etc). + * Semantics implemented here: + * RAZ/WI for SGIs, PPIs, unimplemented IRQs + * Bits corresponding to Group 0 or Secure Group 1 interrupts RAZ/WI. + * offset should be the offset in bytes of the register from the start + * of its group. + */ + int irq = offset * 8; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return; + } + val &= mask_group_and_nsacr(s, attrs, maskfn, irq); + *gic_bmp_ptr32(bmp, irq) = val; + gicv3_update(s, irq, 32); +} + static void gicd_write_set_bitmap_reg(GICv3State *s, MemTxAttrs attrs, uint32_t *bmp, maskfn *maskfn, @@ -545,6 +568,11 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, /* RAZ/WI since affinity routing is always enabled */ *data = 0; return true; + case GICD_INMIR ... GICD_INMIR + 0x7f: + *data = (!s->nmi_support) ? 0 : + gicd_read_bitmap_reg(s, attrs, s->nmi, NULL, + offset - GICD_INMIR); + return true; case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: { uint64_t r; @@ -754,6 +782,12 @@ static bool gicd_writel(GICv3State *s, hwaddr offset, case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf: /* RAZ/WI since affinity routing is always enabled */ return true; + case GICD_INMIR ... GICD_INMIR + 0x7f: + if (s->nmi_support) { + gicd_write_bitmap_reg(s, attrs, s->nmi, NULL, + offset - GICD_INMIR, value); + } + return true; case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: { uint64_t r; diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 21697ecf39..8d793243f4 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -52,6 +52,8 @@ #define GICD_SGIR 0x0F00 #define GICD_CPENDSGIR 0x0F10 #define GICD_SPENDSGIR 0x0F20 +#define GICD_INMIR 0x0F80 +#define GICD_INMIRnE 0x3B00 #define GICD_IROUTER 0x6000 #define GICD_IDREGS 0xFFD0 From 28cca59c469b16f1352e784b566fd36ace2be4b4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 19 Apr 2024 14:36:00 +0100 Subject: [PATCH 18/37] hw/intc/arm_gicv3: Add NMI handling CPU interface registers Add the NMIAR CPU interface registers which deal with acknowledging NMI. When introduce NMI interrupt, there are some updates to the semantics for the register ICC_IAR1_EL1 and ICC_HPPIR1_EL1. For ICC_IAR1_EL1 register, it should return 1022 if the intid has non-maskable property. And for ICC_NMIAR1_EL1 register, it should return 1023 if the intid do not have non-maskable property. Howerever, these are not necessary for ICC_HPPIR1_EL1 register. And the APR and RPR has NMI bits which should be handled correctly. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson [PMM: Separate out whether cpuif supports NMI from whether the GIC proper (IRI) supports NMI] Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-19-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 147 ++++++++++++++++++++++++++++- hw/intc/gicv3_internal.h | 5 + hw/intc/trace-events | 1 + include/hw/intc/arm_gicv3_common.h | 7 ++ 4 files changed, 155 insertions(+), 5 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 67d8fd07b7..715909d0f7 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -21,6 +21,7 @@ #include "hw/irq.h" #include "cpu.h" #include "target/arm/cpregs.h" +#include "target/arm/cpu-features.h" #include "sysemu/tcg.h" #include "sysemu/qtest.h" @@ -795,6 +796,13 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) return intid; } +static uint64_t icv_nmiar1_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* todo */ + uint64_t intid = INTID_SPURIOUS; + return intid; +} + static uint32_t icc_fullprio_mask(GICv3CPUState *cs) { /* @@ -832,6 +840,23 @@ static int icc_highest_active_prio(GICv3CPUState *cs) */ int i; + if (cs->nmi_support) { + /* + * If an NMI is active this takes precedence over anything else + * for priority purposes; the NMI bit is only in the AP1R0 bit. + * We return here the effective priority of the NMI, which is + * either 0x0 or 0x80. Callers will need to check NMI again for + * purposes of either setting the RPR register bits or for + * prioritization of NMI vs non-NMI. + */ + if (cs->icc_apr[GICV3_G1][0] & ICC_AP1R_EL1_NMI) { + return 0; + } + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + return (cs->gic->gicd_ctlr & GICD_CTLR_DS) ? 0 : 0x80; + } + } + for (i = 0; i < icc_num_aprs(cs); i++) { uint32_t apr = cs->icc_apr[GICV3_G0][i] | cs->icc_apr[GICV3_G1][i] | cs->icc_apr[GICV3_G1NS][i]; @@ -898,12 +923,24 @@ static bool icc_hppi_can_preempt(GICv3CPUState *cs) */ int rprio; uint32_t mask; + ARMCPU *cpu = ARM_CPU(cs->cpu); + CPUARMState *env = &cpu->env; if (icc_no_enabled_hppi(cs)) { return false; } - if (cs->hppi.prio >= cs->icc_pmr_el1) { + if (cs->hppi.nmi) { + if (!(cs->gic->gicd_ctlr & GICD_CTLR_DS) && + cs->hppi.grp == GICV3_G1NS) { + if (cs->icc_pmr_el1 < 0x80) { + return false; + } + if (arm_is_secure(env) && cs->icc_pmr_el1 == 0x80) { + return false; + } + } + } else if (cs->hppi.prio >= cs->icc_pmr_el1) { /* Priority mask masks this interrupt */ return false; } @@ -923,6 +960,12 @@ static bool icc_hppi_can_preempt(GICv3CPUState *cs) return true; } + if (cs->hppi.nmi && (cs->hppi.prio & mask) == (rprio & mask)) { + if (!(cs->icc_apr[cs->hppi.grp][0] & ICC_AP1R_EL1_NMI)) { + return true; + } + } + return false; } @@ -1044,8 +1087,13 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq) int aprbit = prio >> (8 - cs->prebits); int regno = aprbit / 32; int regbit = aprbit % 32; + bool nmi = cs->hppi.nmi; - cs->icc_apr[cs->hppi.grp][regno] |= (1 << regbit); + if (nmi) { + cs->icc_apr[cs->hppi.grp][regno] |= ICC_AP1R_EL1_NMI; + } else { + cs->icc_apr[cs->hppi.grp][regno] |= (1 << regbit); + } if (irq < GIC_INTERNAL) { cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 1); @@ -1159,6 +1207,7 @@ static uint64_t icc_iar0_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t icc_iar1_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); + int el = arm_current_el(env); uint64_t intid; if (icv_access(env, HCR_IMO)) { @@ -1172,13 +1221,44 @@ static uint64_t icc_iar1_read(CPUARMState *env, const ARMCPRegInfo *ri) } if (!gicv3_intid_is_special(intid)) { - icc_activate_irq(cs, intid); + if (cs->hppi.nmi && env->cp15.sctlr_el[el] & SCTLR_NMI) { + intid = INTID_NMI; + } else { + icc_activate_irq(cs, intid); + } } trace_gicv3_icc_iar1_read(gicv3_redist_affid(cs), intid); return intid; } +static uint64_t icc_nmiar1_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t intid; + + if (icv_access(env, HCR_IMO)) { + return icv_nmiar1_read(env, ri); + } + + if (!icc_hppi_can_preempt(cs)) { + intid = INTID_SPURIOUS; + } else { + intid = icc_hppir1_value(cs, env); + } + + if (!gicv3_intid_is_special(intid)) { + if (!cs->hppi.nmi) { + intid = INTID_SPURIOUS; + } else { + icc_activate_irq(cs, intid); + } + } + + trace_gicv3_icc_nmiar1_read(gicv3_redist_affid(cs), intid); + return intid; +} + static void icc_drop_prio(GICv3CPUState *cs, int grp) { /* Drop the priority of the currently active interrupt in @@ -1205,6 +1285,12 @@ static void icc_drop_prio(GICv3CPUState *cs, int grp) if (!*papr) { continue; } + + if (i == 0 && cs->nmi_support && (*papr & ICC_AP1R_EL1_NMI)) { + *papr &= (~ICC_AP1R_EL1_NMI); + break; + } + /* Clear the lowest set bit */ *papr &= *papr - 1; break; @@ -1239,6 +1325,15 @@ static int icc_highest_active_group(GICv3CPUState *cs) */ int i; + if (cs->nmi_support) { + if (cs->icc_apr[GICV3_G1][0] & ICC_AP1R_EL1_NMI) { + return GICV3_G1; + } + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + return GICV3_G1NS; + } + } + for (i = 0; i < ARRAY_SIZE(cs->icc_apr[0]); i++) { int g0ctz = ctz32(cs->icc_apr[GICV3_G0][i]); int g1ctz = ctz32(cs->icc_apr[GICV3_G1][i]); @@ -1693,7 +1788,11 @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, return; } - cs->icc_apr[grp][regno] = value & 0xFFFFFFFFU; + if (cs->nmi_support) { + cs->icc_apr[grp][regno] = value & (0xFFFFFFFFU | ICC_AP1R_EL1_NMI); + } else { + cs->icc_apr[grp][regno] = value & 0xFFFFFFFFU; + } gicv3_cpuif_update(cs); } @@ -1783,7 +1882,7 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t icc_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); - int prio; + uint64_t prio; if (icv_access(env, HCR_FMO | HCR_IMO)) { return icv_rpr_read(env, ri); @@ -1803,6 +1902,22 @@ static uint64_t icc_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) } } + if (cs->nmi_support) { + /* NMI info is reported in the high bits of RPR */ + if (arm_feature(env, ARM_FEATURE_EL3) && !arm_is_secure(env)) { + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + prio |= ICC_RPR_EL1_NMI; + } + } else { + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + prio |= ICC_RPR_EL1_NSNMI; + } + if (cs->icc_apr[GICV3_G1][0] & ICC_AP1R_EL1_NMI) { + prio |= ICC_RPR_EL1_NMI; + } + } + } + trace_gicv3_icc_rpr_read(gicv3_redist_affid(cs), prio); return prio; } @@ -2482,6 +2597,15 @@ static const ARMCPRegInfo gicv3_cpuif_icc_apxr23_reginfo[] = { }, }; +static const ARMCPRegInfo gicv3_cpuif_gicv3_nmi_reginfo[] = { + { .name = "ICC_NMIAR1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 9, .opc2 = 5, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_R, .accessfn = gicv3_irq_access, + .readfn = icc_nmiar1_read, + }, +}; + static uint64_t ich_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); @@ -2838,6 +2962,19 @@ void gicv3_init_cpuif(GICv3State *s) */ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); + /* + * If the CPU implements FEAT_NMI and FEAT_GICv3 it must also + * implement FEAT_GICv3_NMI, which is the CPU interface part + * of NMI support. This is distinct from whether the GIC proper + * (redistributors and distributor) have NMI support. In QEMU + * that is a property of the GIC device in s->nmi_support; + * cs->nmi_support indicates the CPU interface's support. + */ + if (cpu_isar_feature(aa64_nmi, cpu)) { + cs->nmi_support = true; + define_arm_cp_regs(cpu, gicv3_cpuif_gicv3_nmi_reginfo); + } + /* * The CPU implementation specifies the number of supported * bits of physical priority. For backwards compatibility diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 8d793243f4..81200eb90e 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -194,6 +194,10 @@ FIELD(GICR_VPENDBASER, VALID, 63, 1) #define ICC_CTLR_EL3_A3V (1U << 15) #define ICC_CTLR_EL3_NDS (1U << 17) +#define ICC_AP1R_EL1_NMI (1ULL << 63) +#define ICC_RPR_EL1_NSNMI (1ULL << 62) +#define ICC_RPR_EL1_NMI (1ULL << 63) + #define ICH_VMCR_EL2_VENG0_SHIFT 0 #define ICH_VMCR_EL2_VENG0 (1U << ICH_VMCR_EL2_VENG0_SHIFT) #define ICH_VMCR_EL2_VENG1_SHIFT 1 @@ -511,6 +515,7 @@ FIELD(VTE, RDBASE, 42, RDBASE_PROCNUM_LENGTH) /* Special interrupt IDs */ #define INTID_SECURE 1020 #define INTID_NONSECURE 1021 +#define INTID_NMI 1022 #define INTID_SPURIOUS 1023 /* Functions internal to the emulated GICv3 */ diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 1ef29d0256..94030550d5 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -116,6 +116,7 @@ gicv3_cpuif_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f gicv3_icc_generate_sgi(uint32_t cpuid, int irq, int irm, uint32_t aff, uint32_t targetlist) "GICv3 CPU i/f 0x%x generating SGI %d IRM %d target affinity 0x%xxx targetlist 0x%x" gicv3_icc_iar0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR0 read cpu 0x%x value 0x%" PRIx64 gicv3_icc_iar1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR1 read cpu 0x%x value 0x%" PRIx64 +gicv3_icc_nmiar1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_NMIAR1 read cpu 0x%x value 0x%" PRIx64 gicv3_icc_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICC_EOIR%d write cpu 0x%x value 0x%" PRIx64 gicv3_icc_hppir0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR0 read cpu 0x%x value 0x%" PRIx64 gicv3_icc_hppir1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR1 read cpu 0x%x value 0x%" PRIx64 diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 88533749eb..cd09bee3bc 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -225,6 +225,13 @@ struct GICv3CPUState { /* This is temporary working state, to avoid a malloc in gicv3_update() */ bool seenbetter; + + /* + * Whether the CPU interface has NMI support (FEAT_GICv3_NMI). The + * CPU interface may support NMIs even when the GIC proper (what the + * spec calls the IRI; the redistributors and distributor) does not. + */ + bool nmi_support; }; /* From d2c0c6aab6c6748726149c37159a75751ec6ac92 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 19 Apr 2024 14:36:33 +0100 Subject: [PATCH 19/37] hw/intc/arm_gicv3: Handle icv_nmiar1_read() for icc_nmiar1_read() Implement icv_nmiar1_read() for icc_nmiar1_read(), so add definition for ICH_LR_EL2.NMI and ICH_AP1R_EL2.NMI bit. If FEAT_GICv3_NMI is supported, ich_ap_write() should consider ICV_AP1R_EL1.NMI bit. In icv_activate_irq() and icv_eoir_write(), the ICV_AP1R_EL1.NMI bit should be set or clear according to the Non-maskable property. And the RPR priority should also update the NMI bit according to the APR priority NMI bit. By the way, add gicv3_icv_nmiar1_read trace event. If the hpp irq is a NMI, the icv iar read should return 1022 and trap for NMI again Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson [PMM: use cs->nmi_support instead of cs->gic->nmi_support] Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-20-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 105 +++++++++++++++++++++++++++++++++----- hw/intc/gicv3_internal.h | 4 ++ hw/intc/trace-events | 1 + 3 files changed, 98 insertions(+), 12 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 715909d0f7..b1f6c16ffe 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -158,6 +158,10 @@ static int ich_highest_active_virt_prio(GICv3CPUState *cs) int i; int aprmax = ich_num_aprs(cs); + if (cs->ich_apr[GICV3_G1NS][0] & ICV_AP1R_EL1_NMI) { + return 0x0; + } + for (i = 0; i < aprmax; i++) { uint32_t apr = cs->ich_apr[GICV3_G0][i] | cs->ich_apr[GICV3_G1NS][i]; @@ -192,6 +196,7 @@ static int hppvi_index(GICv3CPUState *cs) * correct behaviour. */ int prio = 0xff; + bool nmi = false; if (!(cs->ich_vmcr_el2 & (ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1))) { /* Both groups disabled, definitely nothing to do */ @@ -200,6 +205,7 @@ static int hppvi_index(GICv3CPUState *cs) for (i = 0; i < cs->num_list_regs; i++) { uint64_t lr = cs->ich_lr_el2[i]; + bool thisnmi; int thisprio; if (ich_lr_state(lr) != ICH_LR_EL2_STATE_PENDING) { @@ -218,10 +224,12 @@ static int hppvi_index(GICv3CPUState *cs) } } + thisnmi = lr & ICH_LR_EL2_NMI; thisprio = ich_lr_prio(lr); - if (thisprio < prio) { + if ((thisprio < prio) || ((thisprio == prio) && (thisnmi & (!nmi)))) { prio = thisprio; + nmi = thisnmi; idx = i; } } @@ -290,6 +298,7 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) * equivalent of these checks. */ int grp; + bool is_nmi; uint32_t mask, prio, rprio, vpmr; if (!(cs->ich_hcr_el2 & ICH_HCR_EL2_EN)) { @@ -302,10 +311,11 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) */ prio = ich_lr_prio(lr); + is_nmi = lr & ICH_LR_EL2_NMI; vpmr = extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT, ICH_VMCR_EL2_VPMR_LENGTH); - if (prio >= vpmr) { + if (!is_nmi && prio >= vpmr) { /* Priority mask masks this interrupt */ return false; } @@ -327,6 +337,11 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) return true; } + if ((prio & mask) == (rprio & mask) && is_nmi && + !(cs->ich_apr[GICV3_G1NS][0] & ICV_AP1R_EL1_NMI)) { + return true; + } + return false; } @@ -551,7 +566,11 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_icv_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); - cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + if (cs->nmi_support) { + cs->ich_apr[grp][regno] = value & (0xFFFFFFFFU | ICV_AP1R_EL1_NMI); + } else { + cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + } gicv3_cpuif_virt_irq_fiq_update(cs); return; @@ -698,7 +717,11 @@ static void icv_ctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t icv_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); - int prio = ich_highest_active_virt_prio(cs); + uint64_t prio = ich_highest_active_virt_prio(cs); + + if (cs->ich_apr[GICV3_G1NS][0] & ICV_AP1R_EL1_NMI) { + prio |= ICV_RPR_EL1_NMI; + } trace_gicv3_icv_rpr_read(gicv3_redist_affid(cs), prio); return prio; @@ -737,13 +760,19 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp) */ uint32_t mask = icv_gprio_mask(cs, grp); int prio = ich_lr_prio(cs->ich_lr_el2[idx]) & mask; + bool nmi = cs->ich_lr_el2[idx] & ICH_LR_EL2_NMI; int aprbit = prio >> (8 - cs->vprebits); int regno = aprbit / 32; int regbit = aprbit % 32; cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; cs->ich_lr_el2[idx] |= ICH_LR_EL2_STATE_ACTIVE_BIT; - cs->ich_apr[grp][regno] |= (1 << regbit); + + if (nmi) { + cs->ich_apr[grp][regno] |= ICV_AP1R_EL1_NMI; + } else { + cs->ich_apr[grp][regno] |= (1 << regbit); + } } static void icv_activate_vlpi(GICv3CPUState *cs) @@ -764,6 +793,7 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; int idx = hppvi_index(cs); uint64_t intid = INTID_SPURIOUS; + int el = arm_current_el(env); if (idx == HPPVI_INDEX_VLPI) { if (cs->hppvlpi.grp == grp && icv_hppvlpi_can_preempt(cs)) { @@ -773,11 +803,16 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) } else if (idx >= 0) { uint64_t lr = cs->ich_lr_el2[idx]; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + bool nmi = env->cp15.sctlr_el[el] & SCTLR_NMI && lr & ICH_LR_EL2_NMI; if (thisgrp == grp && icv_hppi_can_preempt(cs, lr)) { intid = ich_lr_vintid(lr); if (!gicv3_intid_is_special(intid)) { - icv_activate_irq(cs, idx, grp); + if (!nmi) { + icv_activate_irq(cs, idx, grp); + } else { + intid = INTID_NMI; + } } else { /* Interrupt goes from Pending to Invalid */ cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; @@ -798,8 +833,37 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t icv_nmiar1_read(CPUARMState *env, const ARMCPRegInfo *ri) { - /* todo */ + GICv3CPUState *cs = icc_cs_from_env(env); + int idx = hppvi_index(cs); uint64_t intid = INTID_SPURIOUS; + + if (idx >= 0 && idx != HPPVI_INDEX_VLPI) { + uint64_t lr = cs->ich_lr_el2[idx]; + int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + + if ((thisgrp == GICV3_G1NS) && icv_hppi_can_preempt(cs, lr)) { + intid = ich_lr_vintid(lr); + if (!gicv3_intid_is_special(intid)) { + if (lr & ICH_LR_EL2_NMI) { + icv_activate_irq(cs, idx, GICV3_G1NS); + } else { + intid = INTID_SPURIOUS; + } + } else { + /* Interrupt goes from Pending to Invalid */ + cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; + /* + * We will now return the (bogus) ID from the list register, + * as per the pseudocode. + */ + } + } + } + + trace_gicv3_icv_nmiar1_read(gicv3_redist_affid(cs), intid); + + gicv3_cpuif_virt_update(cs); + return intid; } @@ -1424,7 +1488,7 @@ static void icv_increment_eoicount(GICv3CPUState *cs) ICH_HCR_EL2_EOICOUNT_LENGTH, eoicount + 1); } -static int icv_drop_prio(GICv3CPUState *cs) +static int icv_drop_prio(GICv3CPUState *cs, bool *nmi) { /* Drop the priority of the currently active virtual interrupt * (favouring group 0 if there is a set active bit at @@ -1446,6 +1510,12 @@ static int icv_drop_prio(GICv3CPUState *cs) continue; } + if (i == 0 && cs->nmi_support && (*papr1 & ICV_AP1R_EL1_NMI)) { + *papr1 &= (~ICV_AP1R_EL1_NMI); + *nmi = true; + return 0xff; + } + /* We can't just use the bit-twiddling hack icc_drop_prio() does * because we need to return the bit number we cleared so * it can be compared against the list register's priority field. @@ -1505,6 +1575,7 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, int irq = value & 0xffffff; int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; int idx, dropprio; + bool nmi = false; trace_gicv3_icv_eoir_write(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), value); @@ -1517,8 +1588,8 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, * error checks" (because that lets us avoid scanning the AP * registers twice). */ - dropprio = icv_drop_prio(cs); - if (dropprio == 0xff) { + dropprio = icv_drop_prio(cs, &nmi); + if (dropprio == 0xff && !nmi) { /* No active interrupt. It is CONSTRAINED UNPREDICTABLE * whether the list registers are checked in this * situation; we choose not to. @@ -1540,8 +1611,9 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t lr = cs->ich_lr_el2[idx]; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp); + bool thisnmi = lr & ICH_LR_EL2_NMI; - if (thisgrp == grp && lr_gprio == dropprio) { + if (thisgrp == grp && (lr_gprio == dropprio || (thisnmi & nmi))) { if (!icv_eoi_split(env, cs) || irq >= GICV3_LPI_INTID_START) { /* * Priority drop and deactivate not split: deactivate irq now. @@ -2627,7 +2699,11 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); - cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + if (cs->nmi_support) { + cs->ich_apr[grp][regno] = value & (0xFFFFFFFFU | ICV_AP1R_EL1_NMI); + } else { + cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + } gicv3_cpuif_virt_irq_fiq_update(cs); } @@ -2744,6 +2820,11 @@ static void ich_lr_write(CPUARMState *env, const ARMCPRegInfo *ri, 8 - cs->vpribits, 0); } + /* Enforce RES0 bit in NMI field when FEAT_GICv3_NMI is not implemented */ + if (!cs->nmi_support) { + value &= ~ICH_LR_EL2_NMI; + } + cs->ich_lr_el2[regno] = value; gicv3_cpuif_virt_update(cs); } diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 81200eb90e..bc9f518fe8 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -246,6 +246,7 @@ FIELD(GICR_VPENDBASER, VALID, 63, 1) #define ICH_LR_EL2_PRIORITY_SHIFT 48 #define ICH_LR_EL2_PRIORITY_LENGTH 8 #define ICH_LR_EL2_PRIORITY_MASK (0xffULL << ICH_LR_EL2_PRIORITY_SHIFT) +#define ICH_LR_EL2_NMI (1ULL << 59) #define ICH_LR_EL2_GROUP (1ULL << 60) #define ICH_LR_EL2_HW (1ULL << 61) #define ICH_LR_EL2_STATE_SHIFT 62 @@ -277,6 +278,9 @@ FIELD(GICR_VPENDBASER, VALID, 63, 1) #define ICH_VTR_EL2_PREBITS_SHIFT 26 #define ICH_VTR_EL2_PRIBITS_SHIFT 29 +#define ICV_AP1R_EL1_NMI (1ULL << 63) +#define ICV_RPR_EL1_NMI (1ULL << 63) + /* ITS Registers */ FIELD(GITS_BASER, SIZE, 0, 8) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 94030550d5..47340b5bc1 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -152,6 +152,7 @@ gicv3_icv_rpr_read(uint32_t cpu, uint64_t val) "GICv3 ICV_RPR read cpu 0x%x valu gicv3_icv_hppir_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_HPPIR%d read cpu 0x%x value 0x%" PRIx64 gicv3_icv_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICV_DIR write cpu 0x%x value 0x%" PRIx64 gicv3_icv_iar_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IAR%d read cpu 0x%x value 0x%" PRIx64 +gicv3_icv_nmiar1_read(uint32_t cpu, uint64_t val) "GICv3 ICV_NMIAR1 read cpu 0x%x value 0x%" PRIx64 gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d write cpu 0x%x value 0x%" PRIx64 gicv3_cpuif_virt_update(uint32_t cpuid, int idx, int hppvlpi, int grp, int prio) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d HPPVLPI %d grp %d prio %d" gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d" From d89daa893f51280652032640d77a8bc1dea95bdd Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:05 +0100 Subject: [PATCH 20/37] hw/intc/arm_gicv3: Implement NMI interrupt priority If GICD_CTLR_DS bit is zero and the NMI is non-secure, the NMI priority is higher than 0x80, otherwise it is higher than 0x0. And save the interrupt non-maskable property in hppi.nmi to deliver NMI exception. Since both GICR and GICD can deliver NMI, it is both necessary to check whether the pending irq is NMI in gicv3_redist_update_noirqset and gicv3_update_noirqset. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-21-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3.c | 67 +++++++++++++++++++++++++++++++++----- hw/intc/arm_gicv3_common.c | 3 ++ hw/intc/arm_gicv3_redist.c | 3 ++ 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 0b8f79a122..58e18fff54 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -21,7 +21,7 @@ #include "hw/intc/arm_gicv3.h" #include "gicv3_internal.h" -static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio) +static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio, bool nmi) { /* Return true if this IRQ at this priority should take * precedence over the current recorded highest priority @@ -30,14 +30,23 @@ static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio) * is the same as this one (a property which the calling code * relies on). */ - if (prio < cs->hppi.prio) { - return true; + if (prio != cs->hppi.prio) { + return prio < cs->hppi.prio; } + + /* + * The same priority IRQ with non-maskable property should signal to + * the CPU as it have the priority higher than the labelled 0x80 or 0x00. + */ + if (nmi != cs->hppi.nmi) { + return nmi; + } + /* If multiple pending interrupts have the same priority then it is an * IMPDEF choice which of them to signal to the CPU. We choose to * signal the one with the lowest interrupt number. */ - if (prio == cs->hppi.prio && irq <= cs->hppi.irq) { + if (irq <= cs->hppi.irq) { return true; } return false; @@ -129,6 +138,40 @@ static uint32_t gicr_int_pending(GICv3CPUState *cs) return pend; } +static bool gicv3_get_priority(GICv3CPUState *cs, bool is_redist, int irq, + uint8_t *prio) +{ + uint32_t nmi = 0x0; + + if (is_redist) { + nmi = extract32(cs->gicr_inmir0, irq, 1); + } else { + nmi = *gic_bmp_ptr32(cs->gic->nmi, irq); + nmi = nmi & (1 << (irq & 0x1f)); + } + + if (nmi) { + /* DS = 0 & Non-secure NMI */ + if (!(cs->gic->gicd_ctlr & GICD_CTLR_DS) && + ((is_redist && extract32(cs->gicr_igroupr0, irq, 1)) || + (!is_redist && gicv3_gicd_group_test(cs->gic, irq)))) { + *prio = 0x80; + } else { + *prio = 0x0; + } + + return true; + } + + if (is_redist) { + *prio = cs->gicr_ipriorityr[irq]; + } else { + *prio = cs->gic->gicd_ipriority[irq]; + } + + return false; +} + /* Update the interrupt status after state in a redistributor * or CPU interface has changed, but don't tell the CPU i/f. */ @@ -141,6 +184,7 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) uint8_t prio; int i; uint32_t pend; + bool nmi = false; /* Find out which redistributor interrupts are eligible to be * signaled to the CPU interface. @@ -152,10 +196,11 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) if (!(pend & (1 << i))) { continue; } - prio = cs->gicr_ipriorityr[i]; - if (irqbetter(cs, i, prio)) { + nmi = gicv3_get_priority(cs, true, i, &prio); + if (irqbetter(cs, i, prio, nmi)) { cs->hppi.irq = i; cs->hppi.prio = prio; + cs->hppi.nmi = nmi; seenbetter = true; } } @@ -168,9 +213,10 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) if ((cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) && cs->gic->lpi_enable && (cs->gic->gicd_ctlr & GICD_CTLR_EN_GRP1NS) && (cs->hpplpi.prio != 0xff)) { - if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) { + if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio, cs->hpplpi.nmi)) { cs->hppi.irq = cs->hpplpi.irq; cs->hppi.prio = cs->hpplpi.prio; + cs->hppi.nmi = cs->hpplpi.nmi; cs->hppi.grp = cs->hpplpi.grp; seenbetter = true; } @@ -213,6 +259,7 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) int i; uint8_t prio; uint32_t pend = 0; + bool nmi = false; assert(start >= GIC_INTERNAL); assert(len > 0); @@ -240,10 +287,11 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) */ continue; } - prio = s->gicd_ipriority[i]; - if (irqbetter(cs, i, prio)) { + nmi = gicv3_get_priority(cs, false, i, &prio); + if (irqbetter(cs, i, prio, nmi)) { cs->hppi.irq = i; cs->hppi.prio = prio; + cs->hppi.nmi = nmi; cs->seenbetter = true; } } @@ -293,6 +341,7 @@ void gicv3_full_update_noirqset(GICv3State *s) for (i = 0; i < s->num_cpu; i++) { s->cpu[i].hppi.prio = 0xff; + s->cpu[i].hppi.nmi = false; } /* Note that we can guarantee that these functions will not diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 9810558b07..207f8417e1 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -536,8 +536,11 @@ static void arm_gicv3_common_reset_hold(Object *obj) memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr)); cs->hppi.prio = 0xff; + cs->hppi.nmi = false; cs->hpplpi.prio = 0xff; + cs->hpplpi.nmi = false; cs->hppvlpi.prio = 0xff; + cs->hppvlpi.nmi = false; /* State in the CPU interface must *not* be reset here, because it * is part of the CPU's reset domain, not the GIC device's. diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index ed1f9d1e44..90b238fac0 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -120,6 +120,7 @@ static void update_for_one_lpi(GICv3CPUState *cs, int irq, ((prio == hpp->prio) && (irq <= hpp->irq))) { hpp->irq = irq; hpp->prio = prio; + hpp->nmi = false; /* LPIs and vLPIs are always non-secure Grp1 interrupts */ hpp->grp = GICV3_G1NS; } @@ -156,6 +157,7 @@ static void update_for_all_lpis(GICv3CPUState *cs, uint64_t ptbase, int i, bit; hpp->prio = 0xff; + hpp->nmi = false; for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) { address_space_read(as, ptbase + i, MEMTXATTRS_UNSPECIFIED, &pend, 1); @@ -241,6 +243,7 @@ static void gicv3_redist_update_vlpi_only(GICv3CPUState *cs) if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) { cs->hppvlpi.prio = 0xff; + cs->hppvlpi.nmi = false; return; } From f3c26a44fe3dc27988f07b7e1c4155b9a55818fc Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:05 +0100 Subject: [PATCH 21/37] hw/intc/arm_gicv3: Report the NMI interrupt in gicv3_cpuif_update() In CPU Interface, if the IRQ has the non-maskable property, report NMI to the corresponding PE. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-22-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index b1f6c16ffe..2cf232d099 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -1038,6 +1038,7 @@ void gicv3_cpuif_update(GICv3CPUState *cs) /* Tell the CPU about its highest priority pending interrupt */ int irqlevel = 0; int fiqlevel = 0; + int nmilevel = 0; ARMCPU *cpu = ARM_CPU(cs->cpu); CPUARMState *env = &cpu->env; @@ -1076,6 +1077,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs) if (isfiq) { fiqlevel = 1; + } else if (cs->hppi.nmi) { + nmilevel = 1; } else { irqlevel = 1; } @@ -1085,6 +1088,7 @@ void gicv3_cpuif_update(GICv3CPUState *cs) qemu_set_irq(cs->parent_fiq, fiqlevel); qemu_set_irq(cs->parent_irq, irqlevel); + qemu_set_irq(cs->parent_nmi, nmilevel); } static uint64_t icc_pmr_read(CPUARMState *env, const ARMCPRegInfo *ri) From c57e81889faa5823d0d47c14a4dfc45914205d71 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:05 +0100 Subject: [PATCH 22/37] hw/intc/arm_gicv3: Report the VINMI interrupt In vCPU Interface, if the vIRQ has the non-maskable property, report vINMI to the corresponding vPE. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-23-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_cpuif.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 2cf232d099..bdb13b00e9 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -481,6 +481,7 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) int idx; int irqlevel = 0; int fiqlevel = 0; + int nmilevel = 0; idx = hppvi_index(cs); trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx, @@ -498,9 +499,17 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) uint64_t lr = cs->ich_lr_el2[idx]; if (icv_hppi_can_preempt(cs, lr)) { - /* Virtual interrupts are simple: G0 are always FIQ, and G1 IRQ */ + /* + * Virtual interrupts are simple: G0 are always FIQ, and G1 are + * IRQ or NMI which depends on the ICH_LR_EL2.NMI to have + * non-maskable property. + */ if (lr & ICH_LR_EL2_GROUP) { - irqlevel = 1; + if (lr & ICH_LR_EL2_NMI) { + nmilevel = 1; + } else { + irqlevel = 1; + } } else { fiqlevel = 1; } @@ -510,6 +519,7 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel, irqlevel); qemu_set_irq(cs->parent_vfiq, fiqlevel); qemu_set_irq(cs->parent_virq, irqlevel); + qemu_set_irq(cs->parent_vnmi, nmilevel); } static void gicv3_cpuif_virt_update(GICv3CPUState *cs) From 14a164030ada5c5d6f346e152521cb878b142b2a Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:06 +0100 Subject: [PATCH 23/37] target/arm: Add FEAT_NMI to max Enable FEAT_NMI on the 'max' CPU. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20240407081733.3231820-24-ruanjinjie@huawei.com Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/tcg/cpu64.c | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 2a7bbb82dc..a9ae7ede9f 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -64,6 +64,7 @@ the following architecture extensions: - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) +- FEAT_NMI (Non-maskable Interrupt) - FEAT_NV (Nested Virtualization) - FEAT_NV2 (Enhanced nested virtualization support) - FEAT_PACIMP (Pointer authentication - IMPLEMENTATION DEFINED algorithm) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 9f7a9f3d2c..62c4663512 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1175,6 +1175,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* FEAT_RASv1p1 + FEAT_DoubleFault */ t = FIELD_DP64(t, ID_AA64PFR1, SME, 1); /* FEAT_SME */ t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_2 */ + t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* FEAT_NMI */ cpu->isar.id_aa64pfr1 = t; t = cpu->isar.id_aa64mmfr0; From 5ae47f7aece19c5a6efbf99d000f389278e93c1d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Fri, 19 Apr 2024 14:33:06 +0100 Subject: [PATCH 24/37] hw/arm/virt: Enable NMI support in the GIC if the CPU has FEAT_NMI If the CPU implements FEAT_NMI, then turn on the NMI support in the GICv3 too. It's permitted to have a configuration with FEAT_NMI in the CPU (and thus NMI support in the CPU interfaces too) but no NMI support in the distributor and redistributor, but this isn't a very useful setup as it's close to having no NMI support at all. We don't need to gate the enabling of NMI in the GIC behind a machine version property, because none of our current CPUs implement FEAT_NMI, and '-cpu max' is not something we maintain migration compatibility across versions for. So we can always enable the GIC NMI support when the CPU has it. Neither hvf nor KVM support NMI in the GIC yet, so we don't enable it unless we're using TCG. Signed-off-by: Jinjie Ruan Reviewed-by: Richard Henderson Message-id: 20240407081733.3231820-25-ruanjinjie@huawei.com [PMM: Update comment and commit message] Suggested-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c4b03b09c2..3c93c0c0a6 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -729,6 +729,20 @@ static void create_v2m(VirtMachineState *vms) vms->msi_controller = VIRT_MSI_CTRL_GICV2M; } +/* + * If the CPU has FEAT_NMI, then turn on the NMI support in the GICv3 too. + * It's permitted to have a configuration with NMI in the CPU (and thus the + * GICv3 CPU interface) but not in the distributor/redistributors, but it's + * not very useful. + */ +static bool gicv3_nmi_present(VirtMachineState *vms) +{ + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(0)); + + return tcg_enabled() && cpu_isar_feature(aa64_nmi, cpu) && + (vms->gic_version != VIRT_GIC_VERSION_2); +} + static void create_gic(VirtMachineState *vms, MemoryRegion *mem) { MachineState *ms = MACHINE(vms); @@ -802,6 +816,11 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) vms->virt); } } + + if (gicv3_nmi_present(vms)) { + qdev_prop_set_bit(vms->gic, "has-nmi", true); + } + gicbusdev = SYS_BUS_DEVICE(vms->gic); sysbus_realize_and_unref(gicbusdev, &error_fatal); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); From c3a68dfd191682b140951e423b72866102097df9 Mon Sep 17 00:00:00 2001 From: Anastasia Belova Date: Tue, 9 Apr 2024 14:53:01 +0300 Subject: [PATCH 25/37] hw/dma: avoid apparent overflow in soc_dma_set_request In soc_dma_set_request() we try to set a bit in a uint64_t, but we do it with "1 << ch->num", which can't set any bits past 31; any use for a channel number of 32 or more would fail due to integer overflow. This doesn't happen in practice for our current use of this code, because the worst case is when we call soc_dma_init() with an argument of 32 for the number of channels, and QEMU builds with -fwrapv so the shift into the sign bit is well-defined. However, it's obviously not the intended behaviour of the code. Add casts to force the shift to be done as 64-bit arithmetic, allowing up to 64 channels. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: afbb5194d4 ("Handle on-chip DMA controllers in one place, convert OMAP DMA to use it.") Signed-off-by: Anastasia Belova Message-id: 20240409115301.21829-1-abelova@astralinux.ru [PMM: Edit commit message to clarify that this doesn't actually bite us in our current usage of this code.] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/dma/soc_dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index 3a430057f5..d5c52b804f 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -209,9 +209,9 @@ void soc_dma_set_request(struct soc_dma_ch_s *ch, int level) dma->enabled_count += level - ch->enable; if (level) - dma->ch_enable_mask |= 1 << ch->num; + dma->ch_enable_mask |= (uint64_t)1 << ch->num; else - dma->ch_enable_mask &= ~(1 << ch->num); + dma->ch_enable_mask &= ~((uint64_t)1 << ch->num); if (level != ch->enable) { soc_dma_ch_freq_update(dma); From a6819c1bd0b76d7fa0c8f4ae19e4e80dd61a1502 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 11 Apr 2024 12:53:13 +0100 Subject: [PATCH 26/37] linux-user/flatload.c: Remove unused bFLT shared-library and ZFLAT code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ever since the bFLT format support was added in 2006, there has been a chunk of code in the file guarded by CONFIG_BINFMT_SHARED_FLAT which is supposedly for shared library support. This is not enabled and it's not possible to enable it, because if you do you'll run into the "#error needs checking" in the calc_reloc() function. Similarly, CONFIG_BINFMT_ZFLAT exists but can't be enabled because of an "#error code needs checking" in load_flat_file(). This code is obviously unfinished and has never been used; nobody in the intervening 18 years has complained about this or fixed it, so just delete the dead code. If anybody ever wants the feature they can always pull it out of git, or (perhaps better) write it from scratch based on the current Linux bFLT loader rather than the one of 18 years ago. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240411115313.680433-1-peter.maydell@linaro.org --- linux-user/flat.h | 5 +- linux-user/flatload.c | 293 ++---------------------------------------- 2 files changed, 11 insertions(+), 287 deletions(-) diff --git a/linux-user/flat.h b/linux-user/flat.h index ed518e2013..e374b73e26 100644 --- a/linux-user/flat.h +++ b/linux-user/flat.h @@ -12,11 +12,8 @@ #define FLAT_VERSION 0x00000004L -#ifdef CONFIG_BINFMT_SHARED_FLAT -#define MAX_SHARED_LIBS (4) -#else +/* QEMU doesn't support bflt shared libraries */ #define MAX_SHARED_LIBS (1) -#endif /* * To make everything easier to port and manage cross platform diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 5b62aa0a2b..04d8138d12 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -29,8 +29,6 @@ * JAN/99 -- coded full program relocation (gerg@snapgear.com) */ -/* ??? ZFLAT and shared library support is currently disabled. */ - /****************************************************************************/ #include "qemu/osdep.h" @@ -64,10 +62,6 @@ struct lib_info { short loaded; /* Has this library been loaded? */ }; -#ifdef CONFIG_BINFMT_SHARED_FLAT -static int load_flat_shared_library(int id, struct lib_info *p); -#endif - struct linux_binprm; /****************************************************************************/ @@ -108,153 +102,6 @@ static int target_pread(int fd, abi_ulong ptr, abi_ulong len, unlock_user(buf, ptr, len); return ret; } -/****************************************************************************/ - -#ifdef CONFIG_BINFMT_ZFLAT - -#include - -#define LBUFSIZE 4000 - -/* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ -#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ -#define RESERVED 0xC0 /* bit 6,7: reserved */ - -static int decompress_exec( - struct linux_binprm *bprm, - unsigned long offset, - char *dst, - long len, - int fd) -{ - unsigned char *buf; - z_stream strm; - loff_t fpos; - int ret, retval; - - DBG_FLT("decompress_exec(offset=%x,buf=%x,len=%x)\n",(int)offset, (int)dst, (int)len); - - memset(&strm, 0, sizeof(strm)); - strm.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL); - if (strm.workspace == NULL) { - DBG_FLT("binfmt_flat: no memory for decompress workspace\n"); - return -ENOMEM; - } - buf = kmalloc(LBUFSIZE, GFP_KERNEL); - if (buf == NULL) { - DBG_FLT("binfmt_flat: no memory for read buffer\n"); - retval = -ENOMEM; - goto out_free; - } - - /* Read in first chunk of data and parse gzip header. */ - fpos = offset; - ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); - - strm.next_in = buf; - strm.avail_in = ret; - strm.total_in = 0; - - retval = -ENOEXEC; - - /* Check minimum size -- gzip header */ - if (ret < 10) { - DBG_FLT("binfmt_flat: file too small?\n"); - goto out_free_buf; - } - - /* Check gzip magic number */ - if ((buf[0] != 037) || ((buf[1] != 0213) && (buf[1] != 0236))) { - DBG_FLT("binfmt_flat: unknown compression magic?\n"); - goto out_free_buf; - } - - /* Check gzip method */ - if (buf[2] != 8) { - DBG_FLT("binfmt_flat: unknown compression method?\n"); - goto out_free_buf; - } - /* Check gzip flags */ - if ((buf[3] & ENCRYPTED) || (buf[3] & CONTINUATION) || - (buf[3] & RESERVED)) { - DBG_FLT("binfmt_flat: unknown flags?\n"); - goto out_free_buf; - } - - ret = 10; - if (buf[3] & EXTRA_FIELD) { - ret += 2 + buf[10] + (buf[11] << 8); - if (unlikely(LBUFSIZE == ret)) { - DBG_FLT("binfmt_flat: buffer overflow (EXTRA)?\n"); - goto out_free_buf; - } - } - if (buf[3] & ORIG_NAME) { - for (; ret < LBUFSIZE && (buf[ret] != 0); ret++) - ; - if (unlikely(LBUFSIZE == ret)) { - DBG_FLT("binfmt_flat: buffer overflow (ORIG_NAME)?\n"); - goto out_free_buf; - } - } - if (buf[3] & COMMENT) { - for (; ret < LBUFSIZE && (buf[ret] != 0); ret++) - ; - if (unlikely(LBUFSIZE == ret)) { - DBG_FLT("binfmt_flat: buffer overflow (COMMENT)?\n"); - goto out_free_buf; - } - } - - strm.next_in += ret; - strm.avail_in -= ret; - - strm.next_out = dst; - strm.avail_out = len; - strm.total_out = 0; - - if (zlib_inflateInit2(&strm, -MAX_WBITS) != Z_OK) { - DBG_FLT("binfmt_flat: zlib init failed?\n"); - goto out_free_buf; - } - - while ((ret = zlib_inflate(&strm, Z_NO_FLUSH)) == Z_OK) { - ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); - if (ret <= 0) - break; - if (is_error(ret)) { - break; - } - len -= ret; - - strm.next_in = buf; - strm.avail_in = ret; - strm.total_in = 0; - } - - if (ret < 0) { - DBG_FLT("binfmt_flat: decompression failed (%d), %s\n", - ret, strm.msg); - goto out_zlib; - } - - retval = 0; -out_zlib: - zlib_inflateEnd(&strm); -out_free_buf: - kfree(buf); -out_free: - kfree(strm.workspace); -out: - return retval; -} - -#endif /* CONFIG_BINFMT_ZFLAT */ /****************************************************************************/ @@ -268,40 +115,7 @@ calc_reloc(abi_ulong r, struct lib_info *p, int curid, int internalp) abi_ulong text_len; abi_ulong start_code; -#ifdef CONFIG_BINFMT_SHARED_FLAT -#error needs checking - if (r == 0) - id = curid; /* Relocs of 0 are always self referring */ - else { - id = (r >> 24) & 0xff; /* Find ID for this reloc */ - r &= 0x00ffffff; /* Trim ID off here */ - } - if (id >= MAX_SHARED_LIBS) { - fprintf(stderr, "BINFMT_FLAT: reference 0x%x to shared library %d\n", - (unsigned) r, id); - goto failed; - } - if (curid != id) { - if (internalp) { - fprintf(stderr, "BINFMT_FLAT: reloc address 0x%x not " - "in same module (%d != %d)\n", - (unsigned) r, curid, id); - goto failed; - } else if (!p[id].loaded && is_error(load_flat_shared_library(id, p))) { - fprintf(stderr, "BINFMT_FLAT: failed to load library %d\n", id); - goto failed; - } - /* Check versioning information (i.e. time stamps) */ - if (p[id].build_date && p[curid].build_date - && p[curid].build_date < p[id].build_date) { - fprintf(stderr, "BINFMT_FLAT: library %d is younger than %d\n", - id, curid); - goto failed; - } - } -#else id = 0; -#endif start_brk = p[id].start_brk; start_data = p[id].start_data; @@ -425,12 +239,10 @@ static int load_flat_file(struct linux_binprm * bprm, if (rev == OLD_FLAT_VERSION && flat_old_ram_flag(flags)) flags = FLAT_FLAG_RAM; -#ifndef CONFIG_BINFMT_ZFLAT if (flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) { - fprintf(stderr, "Support for ZFLAT executables is not enabled\n"); + fprintf(stderr, "ZFLAT executables are not supported\n"); return -ENOEXEC; } -#endif /* * calculate the extra space we need to map in @@ -483,17 +295,9 @@ static int load_flat_file(struct linux_binprm * bprm, (int)(data_len + bss_len + stack_len), (int)datapos); fpos = ntohl(hdr->data_start); -#ifdef CONFIG_BINFMT_ZFLAT - if (flags & FLAT_FLAG_GZDATA) { - result = decompress_exec(bprm, fpos, (char *) datapos, - data_len + (relocs * sizeof(abi_ulong))) - } else -#endif - { - result = target_pread(bprm->src.fd, datapos, - data_len + (relocs * sizeof(abi_ulong)), - fpos); - } + result = target_pread(bprm->src.fd, datapos, + data_len + (relocs * sizeof(abi_ulong)), + fpos); if (result < 0) { fprintf(stderr, "Unable to read data+bss\n"); return result; @@ -515,38 +319,12 @@ static int load_flat_file(struct linux_binprm * bprm, datapos = realdatastart + indx_len; reloc = (textpos + ntohl(hdr->reloc_start) + indx_len); -#ifdef CONFIG_BINFMT_ZFLAT -#error code needs checking - /* - * load it all in and treat it like a RAM load from now on - */ - if (flags & FLAT_FLAG_GZIP) { - result = decompress_exec(bprm, sizeof (struct flat_hdr), - (((char *) textpos) + sizeof (struct flat_hdr)), - (text_len + data_len + (relocs * sizeof(unsigned long)) - - sizeof (struct flat_hdr)), - 0); - memmove((void *) datapos, (void *) realdatastart, - data_len + (relocs * sizeof(unsigned long))); - } else if (flags & FLAT_FLAG_GZDATA) { - fpos = 0; - result = bprm->file->f_op->read(bprm->file, - (char *) textpos, text_len, &fpos); - if (!is_error(result)) { - result = decompress_exec(bprm, text_len, (char *) datapos, - data_len + (relocs * sizeof(unsigned long)), 0); - } - } - else -#endif - { - result = target_pread(bprm->src.fd, textpos, - text_len, 0); - if (result >= 0) { - result = target_pread(bprm->src.fd, datapos, - data_len + (relocs * sizeof(abi_ulong)), - ntohl(hdr->data_start)); - } + result = target_pread(bprm->src.fd, textpos, + text_len, 0); + if (result >= 0) { + result = target_pread(bprm->src.fd, datapos, + data_len + (relocs * sizeof(abi_ulong)), + ntohl(hdr->data_start)); } if (result < 0) { fprintf(stderr, "Unable to read code+data+bss\n"); @@ -678,44 +456,6 @@ static int load_flat_file(struct linux_binprm * bprm, /****************************************************************************/ -#ifdef CONFIG_BINFMT_SHARED_FLAT - -/* - * Load a shared library into memory. The library gets its own data - * segment (including bss) but not argv/argc/environ. - */ - -static int load_flat_shared_library(int id, struct lib_info *libs) -{ - struct linux_binprm bprm; - int res; - char buf[16]; - - /* Create the file name */ - sprintf(buf, "/lib/lib%d.so", id); - - /* Open the file up */ - bprm.filename = buf; - bprm.file = open_exec(bprm.filename); - res = PTR_ERR(bprm.file); - if (IS_ERR(bprm.file)) - return res; - - res = prepare_binprm(&bprm); - - if (!is_error(res)) { - res = load_flat_file(&bprm, libs, id, NULL); - } - if (bprm.file) { - allow_write_access(bprm.file); - fput(bprm.file); - bprm.file = NULL; - } - return(res); -} - -#endif /* CONFIG_BINFMT_SHARED_FLAT */ - int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) { struct lib_info libinfo[MAX_SHARED_LIBS]; @@ -793,19 +533,6 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) */ start_addr = libinfo[0].entry; -#ifdef CONFIG_BINFMT_SHARED_FLAT -#error here - for (i = MAX_SHARED_LIBS-1; i>0; i--) { - if (libinfo[i].loaded) { - /* Push previous first to call address */ - --sp; - if (put_user_ual(start_addr, sp)) - return -EFAULT; - start_addr = libinfo[i].entry; - } - } -#endif - /* Stash our initial stack pointer into the mm structure */ info->start_code = libinfo[0].start_code; info->end_code = libinfo[0].start_code + libinfo[0].text_len; From 1e0f2b38ac104ec3606750cce847cc9d8e4f66ac Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:04 +0100 Subject: [PATCH 27/37] hw/misc: Don't special case RESET_TYPE_COLD in npcm7xx_clk, gcr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The npcm7xx_clk and npcm7xx_gcr device reset methods look at the ResetType argument and only handle RESET_TYPE_COLD, producing a warning if another reset type is passed. This is different from how every other three-phase-reset method we have works, and makes it difficult to add new reset types. A better pattern is "assume that any reset type you don't know about should be handled like RESET_TYPE_COLD"; switch these devices to do that. Then adding a new reset type will only need to touch those devices where its behaviour really needs to be different from the standard cold reset. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-2-peter.maydell@linaro.org --- hw/misc/npcm7xx_clk.c | 13 +++---------- hw/misc/npcm7xx_gcr.c | 12 ++++-------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c index ac1622c38a..2098c85ee0 100644 --- a/hw/misc/npcm7xx_clk.c +++ b/hw/misc/npcm7xx_clk.c @@ -873,20 +873,13 @@ static void npcm7xx_clk_enter_reset(Object *obj, ResetType type) QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); - switch (type) { - case RESET_TYPE_COLD: - memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values)); - s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - npcm7xx_clk_update_all_clocks(s); - return; - } - + memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values)); + s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + npcm7xx_clk_update_all_clocks(s); /* * A small number of registers need to be reset on a core domain reset, * but no such reset type exists yet. */ - qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.", - __func__, type); } static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s) diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c index 9252f9d148..c4c4e246d7 100644 --- a/hw/misc/npcm7xx_gcr.c +++ b/hw/misc/npcm7xx_gcr.c @@ -159,14 +159,10 @@ static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); - switch (type) { - case RESET_TYPE_COLD: - memcpy(s->regs, cold_reset_values, sizeof(s->regs)); - s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron; - s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr; - s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; - break; - } + memcpy(s->regs, cold_reset_values, sizeof(s->regs)); + s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron; + s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr; + s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; } static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp) From ef6ab2922fc94956c0b28c55ec2abe61f71446a9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:05 +0100 Subject: [PATCH 28/37] allwinner-i2c, adm1272: Use device_cold_reset() for software-triggered reset Rather than directly calling the device's implementation of its 'hold' reset phase, call device_cold_reset(). This means we don't have to adjust this callsite when we add another argument to the function signature for the hold and exit reset methods. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-3-peter.maydell@linaro.org --- hw/i2c/allwinner-i2c.c | 3 +-- hw/sensor/adm1272.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c index 8abcc39a5c..96c20c8637 100644 --- a/hw/i2c/allwinner-i2c.c +++ b/hw/i2c/allwinner-i2c.c @@ -385,8 +385,7 @@ static void allwinner_i2c_write(void *opaque, hwaddr offset, break; case TWI_SRST_REG: if (((value & TWI_SRST_MASK) == 0) && (s->srst & TWI_SRST_MASK)) { - /* Perform reset */ - allwinner_i2c_reset_hold(OBJECT(s)); + device_cold_reset(DEVICE(s)); } s->srst = value & TWI_SRST_MASK; break; diff --git a/hw/sensor/adm1272.c b/hw/sensor/adm1272.c index 1f7c8abb83..a19557ec9e 100644 --- a/hw/sensor/adm1272.c +++ b/hw/sensor/adm1272.c @@ -386,7 +386,7 @@ static int adm1272_write_data(PMBusDevice *pmdev, const uint8_t *buf, break; case ADM1272_MFR_POWER_CYCLE: - adm1272_exit_reset((Object *)s); + device_cold_reset(DEVICE(s)); break; case ADM1272_HYSTERESIS_LOW: From aadea887f4429fcc96429b126c254de94317b474 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:06 +0100 Subject: [PATCH 29/37] scripts/coccinelle: New script to add ResetType to hold and exit phases We pass a ResetType argument to the Resettable class enter phase method, but we don't pass it to hold and exit, even though the callsites have it readily available. This means that if a device cared about the ResetType it would need to record it in the enter phase method to use later on. We should pass the type to all three of the phase methods to avoid having to do that. This coccinelle script adds the ResetType argument to the hold and exit phases of the Resettable interface. The first part of the script (rules holdfn_assigned, holdfn_defined, exitfn_assigned, exitfn_defined) update implementations of the interface within device models, both to change the signature of their method implementations and to pass on the reset type when they invoke reset on some other device. The second part of the script is various special cases: * method callsites in resettable_phase_hold(), resettable_phase_exit() and device_phases_reset() * updating the typedefs for the methods * isl_pmbus_vr.c has some code where one device's reset method directly calls the implementation of a different device's method Signed-off-by: Peter Maydell Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-4-peter.maydell@linaro.org --- scripts/coccinelle/reset-type.cocci | 133 ++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 scripts/coccinelle/reset-type.cocci diff --git a/scripts/coccinelle/reset-type.cocci b/scripts/coccinelle/reset-type.cocci new file mode 100644 index 0000000000..14abdd7bd0 --- /dev/null +++ b/scripts/coccinelle/reset-type.cocci @@ -0,0 +1,133 @@ +// Convert device code using three-phase reset to add a ResetType +// argument to implementations of ResettableHoldPhase and +// ResettableEnterPhase methods. +// +// Copyright Linaro Ltd 2024 +// SPDX-License-Identifier: GPL-2.0-or-later +// +// for dir in include hw target; do \ +// spatch --macro-file scripts/cocci-macro-file.h \ +// --sp-file scripts/coccinelle/reset-type.cocci \ +// --keep-comments --smpl-spacing --in-place --include-headers \ +// --dir $dir; done +// +// This coccinelle script aims to produce a complete change that needs +// no human interaction, so as well as the generic "update device +// implementations of the hold and exit phase methods" it includes +// the special-case transformations needed for the core code and for +// one device model that does something a bit nonstandard. Those +// special cases are at the end of the file. + +// Look for where we use a function as a ResettableHoldPhase method, +// either by directly assigning it to phases.hold or by calling +// resettable_class_set_parent_phases, and remember the function name. +@ holdfn_assigned @ +identifier enterfn, holdfn, exitfn; +identifier rc; +expression e; +@@ +ResettableClass *rc; +... +( + rc->phases.hold = holdfn; +| + resettable_class_set_parent_phases(rc, enterfn, holdfn, exitfn, e); +) + +// Look for the definition of the function we found in holdfn_assigned, +// and add the new argument. If the function calls a hold function +// itself (probably chaining to the parent class reset) then add the +// new argument there too. +@ holdfn_defined @ +identifier holdfn_assigned.holdfn; +typedef Object; +identifier obj; +expression parent; +@@ +-holdfn(Object *obj) ++holdfn(Object *obj, ResetType type) +{ + <... +- parent.hold(obj) ++ parent.hold(obj, type) + ...> +} + +// Similarly for ResettableExitPhase. +@ exitfn_assigned @ +identifier enterfn, holdfn, exitfn; +identifier rc; +expression e; +@@ +ResettableClass *rc; +... +( + rc->phases.exit = exitfn; +| + resettable_class_set_parent_phases(rc, enterfn, holdfn, exitfn, e); +) +@ exitfn_defined @ +identifier exitfn_assigned.exitfn; +typedef Object; +identifier obj; +expression parent; +@@ +-exitfn(Object *obj) ++exitfn(Object *obj, ResetType type) +{ + <... +- parent.exit(obj) ++ parent.exit(obj, type) + ...> +} + +// SPECIAL CASES ONLY BELOW HERE +// We use a python scripted constraint on the position of the match +// to ensure that they only match in a particular function. See +// https://public-inbox.org/git/alpine.DEB.2.21.1808240652370.2344@hadrien/ +// which recommends this as the way to do "match only in this function". + +// Special case: isl_pmbus_vr.c has some reset methods calling others directly +@ isl_pmbus_vr @ +identifier obj; +@@ +- isl_pmbus_vr_exit_reset(obj); ++ isl_pmbus_vr_exit_reset(obj, type); + +// Special case: device_phases_reset() needs to pass RESET_TYPE_COLD +@ device_phases_reset_hold @ +expression obj; +identifier rc; +identifier phase; +position p : script:python() { p[0].current_element == "device_phases_reset" }; +@@ +- rc->phases.phase(obj)@p ++ rc->phases.phase(obj, RESET_TYPE_COLD) + +// Special case: in resettable_phase_hold() and resettable_phase_exit() +// we need to pass through the ResetType argument to the method being called +@ resettable_phase_hold @ +expression obj; +identifier rc; +position p : script:python() { p[0].current_element == "resettable_phase_hold" }; +@@ +- rc->phases.hold(obj)@p ++ rc->phases.hold(obj, type) +@ resettable_phase_exit @ +expression obj; +identifier rc; +position p : script:python() { p[0].current_element == "resettable_phase_exit" }; +@@ +- rc->phases.exit(obj)@p ++ rc->phases.exit(obj, type) +// Special case: the typedefs for the methods need to declare the new argument +@ phase_typedef_hold @ +identifier obj; +@@ +- typedef void (*ResettableHoldPhase)(Object *obj); ++ typedef void (*ResettableHoldPhase)(Object *obj, ResetType type); +@ phase_typedef_exit @ +identifier obj; +@@ +- typedef void (*ResettableExitPhase)(Object *obj); ++ typedef void (*ResettableExitPhase)(Object *obj, ResetType type); From ad80e36744785fe9326d4104d98e976822e90cc2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:07 +0100 Subject: [PATCH 30/37] hw, target: Add ResetType argument to hold and exit phase methods We pass a ResetType argument to the Resettable class enter phase method, but we don't pass it to hold and exit, even though the callsites have it readily available. This means that if a device cared about the ResetType it would need to record it in the enter phase method to use later on. Pass the type to all three of the phase methods to avoid having to do that. Commit created with for dir in hw target include; do \ spatch --macro-file scripts/cocci-macro-file.h \ --sp-file scripts/coccinelle/reset-type.cocci \ --keep-comments --smpl-spacing --in-place \ --include-headers --dir $dir; done and no manual edits. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Richard Henderson Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-5-peter.maydell@linaro.org --- hw/adc/npcm7xx_adc.c | 2 +- hw/arm/pxa2xx_pic.c | 2 +- hw/arm/smmu-common.c | 2 +- hw/arm/smmuv3.c | 4 ++-- hw/arm/stellaris.c | 10 +++++----- hw/audio/asc.c | 2 +- hw/char/cadence_uart.c | 2 +- hw/char/sifive_uart.c | 2 +- hw/core/cpu-common.c | 2 +- hw/core/qdev.c | 4 ++-- hw/core/reset.c | 2 +- hw/core/resettable.c | 4 ++-- hw/display/virtio-vga.c | 4 ++-- hw/gpio/npcm7xx_gpio.c | 2 +- hw/gpio/pl061.c | 2 +- hw/gpio/stm32l4x5_gpio.c | 2 +- hw/hyperv/vmbus.c | 2 +- hw/i2c/allwinner-i2c.c | 2 +- hw/i2c/npcm7xx_smbus.c | 2 +- hw/input/adb.c | 2 +- hw/input/ps2.c | 12 ++++++------ hw/intc/arm_gic_common.c | 2 +- hw/intc/arm_gic_kvm.c | 4 ++-- hw/intc/arm_gicv3_common.c | 2 +- hw/intc/arm_gicv3_its.c | 4 ++-- hw/intc/arm_gicv3_its_common.c | 2 +- hw/intc/arm_gicv3_its_kvm.c | 4 ++-- hw/intc/arm_gicv3_kvm.c | 4 ++-- hw/intc/xics.c | 2 +- hw/m68k/q800-glue.c | 2 +- hw/misc/djmemc.c | 2 +- hw/misc/iosb.c | 2 +- hw/misc/mac_via.c | 8 ++++---- hw/misc/macio/cuda.c | 4 ++-- hw/misc/macio/pmu.c | 4 ++-- hw/misc/mos6522.c | 2 +- hw/misc/npcm7xx_mft.c | 2 +- hw/misc/npcm7xx_pwm.c | 2 +- hw/misc/stm32l4x5_exti.c | 2 +- hw/misc/stm32l4x5_rcc.c | 10 +++++----- hw/misc/stm32l4x5_syscfg.c | 2 +- hw/misc/xlnx-versal-cframe-reg.c | 2 +- hw/misc/xlnx-versal-crl.c | 2 +- hw/misc/xlnx-versal-pmc-iou-slcr.c | 2 +- hw/misc/xlnx-versal-trng.c | 2 +- hw/misc/xlnx-versal-xramc.c | 2 +- hw/misc/xlnx-zynqmp-apu-ctrl.c | 2 +- hw/misc/xlnx-zynqmp-crf.c | 2 +- hw/misc/zynq_slcr.c | 4 ++-- hw/net/can/xlnx-zynqmp-can.c | 2 +- hw/net/e1000.c | 2 +- hw/net/e1000e.c | 2 +- hw/net/igb.c | 2 +- hw/net/igbvf.c | 2 +- hw/nvram/xlnx-bbram.c | 2 +- hw/nvram/xlnx-versal-efuse-ctrl.c | 2 +- hw/nvram/xlnx-zynqmp-efuse.c | 2 +- hw/pci-bridge/cxl_root_port.c | 4 ++-- hw/pci-bridge/pcie_root_port.c | 2 +- hw/pci-host/bonito.c | 2 +- hw/pci-host/pnv_phb.c | 4 ++-- hw/pci-host/pnv_phb3_msi.c | 4 ++-- hw/pci/pci.c | 4 ++-- hw/rtc/mc146818rtc.c | 2 +- hw/s390x/css-bridge.c | 2 +- hw/sensor/adm1266.c | 2 +- hw/sensor/adm1272.c | 2 +- hw/sensor/isl_pmbus_vr.c | 10 +++++----- hw/sensor/max31785.c | 2 +- hw/sensor/max34451.c | 2 +- hw/ssi/npcm7xx_fiu.c | 2 +- hw/timer/etraxfs_timer.c | 2 +- hw/timer/npcm7xx_timer.c | 2 +- hw/usb/hcd-dwc2.c | 8 ++++---- hw/usb/xlnx-versal-usb2-ctrl-regs.c | 2 +- hw/virtio/virtio-pci.c | 2 +- include/hw/resettable.h | 4 ++-- target/arm/cpu.c | 4 ++-- target/avr/cpu.c | 4 ++-- target/cris/cpu.c | 4 ++-- target/hexagon/cpu.c | 4 ++-- target/i386/cpu.c | 4 ++-- target/loongarch/cpu.c | 4 ++-- target/m68k/cpu.c | 4 ++-- target/microblaze/cpu.c | 4 ++-- target/mips/cpu.c | 4 ++-- target/openrisc/cpu.c | 4 ++-- target/ppc/cpu_init.c | 4 ++-- target/riscv/cpu.c | 4 ++-- target/rx/cpu.c | 4 ++-- target/sh4/cpu.c | 4 ++-- target/sparc/cpu.c | 4 ++-- target/tricore/cpu.c | 4 ++-- target/xtensa/cpu.c | 4 ++-- 94 files changed, 150 insertions(+), 150 deletions(-) diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c index c6647eec6d..de8469dae4 100644 --- a/hw/adc/npcm7xx_adc.c +++ b/hw/adc/npcm7xx_adc.c @@ -218,7 +218,7 @@ static void npcm7xx_adc_enter_reset(Object *obj, ResetType type) npcm7xx_adc_reset(s); } -static void npcm7xx_adc_hold_reset(Object *obj) +static void npcm7xx_adc_hold_reset(Object *obj, ResetType type) { NPCM7xxADCState *s = NPCM7XX_ADC(obj); diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index f54546cd4d..34c5555dba 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -272,7 +272,7 @@ static int pxa2xx_pic_post_load(void *opaque, int version_id) return 0; } -static void pxa2xx_pic_reset_hold(Object *obj) +static void pxa2xx_pic_reset_hold(Object *obj, ResetType type) { PXA2xxPICState *s = PXA2XX_PIC(obj); diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index c4b540656c..1ce706bf94 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -682,7 +682,7 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) } } -static void smmu_base_reset_hold(Object *obj) +static void smmu_base_reset_hold(Object *obj, ResetType type) { SMMUState *s = ARM_SMMU(obj); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 9eb56a70f3..2d1e0d55ec 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1727,13 +1727,13 @@ static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev) } } -static void smmu_reset_hold(Object *obj) +static void smmu_reset_hold(Object *obj, ResetType type) { SMMUv3State *s = ARM_SMMUV3(obj); SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s); if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } smmuv3_init_regs(s); diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index a2f998bf9e..376746251e 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -394,7 +394,7 @@ static void stellaris_sys_reset_enter(Object *obj, ResetType type) s->dcgc[0] = 1; } -static void stellaris_sys_reset_hold(Object *obj) +static void stellaris_sys_reset_hold(Object *obj, ResetType type) { ssys_state *s = STELLARIS_SYS(obj); @@ -402,7 +402,7 @@ static void stellaris_sys_reset_hold(Object *obj) ssys_calculate_system_clock(s, true); } -static void stellaris_sys_reset_exit(Object *obj) +static void stellaris_sys_reset_exit(Object *obj, ResetType type) { } @@ -618,7 +618,7 @@ static void stellaris_i2c_reset_enter(Object *obj, ResetType type) i2c_end_transfer(s->bus); } -static void stellaris_i2c_reset_hold(Object *obj) +static void stellaris_i2c_reset_hold(Object *obj, ResetType type) { stellaris_i2c_state *s = STELLARIS_I2C(obj); @@ -631,7 +631,7 @@ static void stellaris_i2c_reset_hold(Object *obj) s->mcr = 0; } -static void stellaris_i2c_reset_exit(Object *obj) +static void stellaris_i2c_reset_exit(Object *obj, ResetType type) { stellaris_i2c_state *s = STELLARIS_I2C(obj); @@ -787,7 +787,7 @@ static void stellaris_adc_trigger(void *opaque, int irq, int level) } } -static void stellaris_adc_reset_hold(Object *obj) +static void stellaris_adc_reset_hold(Object *obj, ResetType type) { StellarisADCState *s = STELLARIS_ADC(obj); int n; diff --git a/hw/audio/asc.c b/hw/audio/asc.c index 87b5624326..805416372c 100644 --- a/hw/audio/asc.c +++ b/hw/audio/asc.c @@ -610,7 +610,7 @@ static void asc_fifo_init(ASCFIFOState *fs, int index) g_free(name); } -static void asc_reset_hold(Object *obj) +static void asc_reset_hold(Object *obj, ResetType type) { ASCState *s = ASC(obj); diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index db31d7cc85..77d9a2a221 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -525,7 +525,7 @@ static void cadence_uart_reset_init(Object *obj, ResetType type) s->r[R_TTRIG] = 0x00000020; } -static void cadence_uart_reset_hold(Object *obj) +static void cadence_uart_reset_hold(Object *obj, ResetType type) { CadenceUARTState *s = CADENCE_UART(obj); diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index e8716c4252..7fc6787f69 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -214,7 +214,7 @@ static void sifive_uart_reset_enter(Object *obj, ResetType type) s->rx_fifo_len = 0; } -static void sifive_uart_reset_hold(Object *obj) +static void sifive_uart_reset_hold(Object *obj, ResetType type) { SiFiveUARTState *s = SIFIVE_UART(obj); qemu_irq_lower(s->irq); diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 4bd9c70a83..a72d48d9e1 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -113,7 +113,7 @@ void cpu_reset(CPUState *cpu) trace_cpu_reset(cpu->cpu_index); } -static void cpu_common_reset_hold(Object *obj) +static void cpu_common_reset_hold(Object *obj, ResetType type) { CPUState *cpu = CPU(obj); CPUClass *cc = CPU_GET_CLASS(cpu); diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 00efaf1bd1..f3a996f57d 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -760,10 +760,10 @@ static void device_phases_reset(DeviceState *dev) rc->phases.enter(OBJECT(dev), RESET_TYPE_COLD); } if (rc->phases.hold) { - rc->phases.hold(OBJECT(dev)); + rc->phases.hold(OBJECT(dev), RESET_TYPE_COLD); } if (rc->phases.exit) { - rc->phases.exit(OBJECT(dev)); + rc->phases.exit(OBJECT(dev), RESET_TYPE_COLD); } } diff --git a/hw/core/reset.c b/hw/core/reset.c index d50da7e304..f9fef45e05 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -73,7 +73,7 @@ static ResettableState *legacy_reset_get_state(Object *obj) return &lr->reset_state; } -static void legacy_reset_hold(Object *obj) +static void legacy_reset_hold(Object *obj, ResetType type) { LegacyReset *lr = LEGACY_RESET(obj); diff --git a/hw/core/resettable.c b/hw/core/resettable.c index c3df75c6ba..bebf7f10b2 100644 --- a/hw/core/resettable.c +++ b/hw/core/resettable.c @@ -181,7 +181,7 @@ static void resettable_phase_hold(Object *obj, void *opaque, ResetType type) trace_resettable_transitional_function(obj, obj_typename); tr_func(obj); } else if (rc->phases.hold) { - rc->phases.hold(obj); + rc->phases.hold(obj, type); } } trace_resettable_phase_hold_end(obj, obj_typename, s->count); @@ -204,7 +204,7 @@ static void resettable_phase_exit(Object *obj, void *opaque, ResetType type) if (--s->count == 0) { trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit); if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) { - rc->phases.exit(obj); + rc->phases.exit(obj, type); } } s->exit_phase_in_progress = false; diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 94d3353f54..276f315108 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -180,14 +180,14 @@ static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_vga_base_reset_hold(Object *obj) +static void virtio_vga_base_reset_hold(Object *obj, ResetType type) { VirtIOVGABaseClass *klass = VIRTIO_VGA_BASE_GET_CLASS(obj); VirtIOVGABase *vvga = VIRTIO_VGA_BASE(obj); /* reset virtio-gpu */ if (klass->parent_phases.hold) { - klass->parent_phases.hold(obj); + klass->parent_phases.hold(obj, type); } /* reset vga */ diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c index 6e70ac1f24..ba19b9ebad 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -352,7 +352,7 @@ static void npcm7xx_gpio_enter_reset(Object *obj, ResetType type) s->regs[NPCM7XX_GPIO_ODSC] = s->reset_odsc; } -static void npcm7xx_gpio_hold_reset(Object *obj) +static void npcm7xx_gpio_hold_reset(Object *obj, ResetType type) { NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 86f2383655..d5838b8e98 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -484,7 +484,7 @@ static void pl061_enter_reset(Object *obj, ResetType type) s->amsel = 0; } -static void pl061_hold_reset(Object *obj) +static void pl061_hold_reset(Object *obj, ResetType type) { PL061State *s = PL061(obj); int i, level; diff --git a/hw/gpio/stm32l4x5_gpio.c b/hw/gpio/stm32l4x5_gpio.c index 63b8763e9d..71bf5fddb2 100644 --- a/hw/gpio/stm32l4x5_gpio.c +++ b/hw/gpio/stm32l4x5_gpio.c @@ -70,7 +70,7 @@ static bool is_push_pull(Stm32l4x5GpioState *s, unsigned pin) return extract32(s->otyper, pin, 1) == 0; } -static void stm32l4x5_gpio_reset_hold(Object *obj) +static void stm32l4x5_gpio_reset_hold(Object *obj, ResetType type) { Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index f33afeeea2..490d805d29 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2453,7 +2453,7 @@ static void vmbus_unrealize(BusState *bus) qemu_mutex_destroy(&vmbus->rx_queue_lock); } -static void vmbus_reset_hold(Object *obj) +static void vmbus_reset_hold(Object *obj, ResetType type) { vmbus_deinit(VMBUS(obj)); } diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c index 96c20c8637..16f1d6d40e 100644 --- a/hw/i2c/allwinner-i2c.c +++ b/hw/i2c/allwinner-i2c.c @@ -170,7 +170,7 @@ static inline bool allwinner_i2c_interrupt_is_enabled(AWI2CState *s) return s->cntr & TWI_CNTR_INT_EN; } -static void allwinner_i2c_reset_hold(Object *obj) +static void allwinner_i2c_reset_hold(Object *obj, ResetType type) { AWI2CState *s = AW_I2C(obj); diff --git a/hw/i2c/npcm7xx_smbus.c b/hw/i2c/npcm7xx_smbus.c index 0ea3083bb6..22d68fc67d 100644 --- a/hw/i2c/npcm7xx_smbus.c +++ b/hw/i2c/npcm7xx_smbus.c @@ -1022,7 +1022,7 @@ static void npcm7xx_smbus_enter_reset(Object *obj, ResetType type) s->rx_cur = 0; } -static void npcm7xx_smbus_hold_reset(Object *obj) +static void npcm7xx_smbus_hold_reset(Object *obj, ResetType type) { NPCM7xxSMBusState *s = NPCM7XX_SMBUS(obj); diff --git a/hw/input/adb.c b/hw/input/adb.c index 98f39b4281..aff7130fd0 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -231,7 +231,7 @@ static const VMStateDescription vmstate_adb_bus = { } }; -static void adb_bus_reset_hold(Object *obj) +static void adb_bus_reset_hold(Object *obj, ResetType type) { ADBBusState *adb_bus = ADB_BUS(obj); diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 00b695a0b9..d6f834443d 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -1007,7 +1007,7 @@ void ps2_write_mouse(PS2MouseState *s, int val) } } -static void ps2_reset_hold(Object *obj) +static void ps2_reset_hold(Object *obj, ResetType type) { PS2State *s = PS2_DEVICE(obj); @@ -1015,7 +1015,7 @@ static void ps2_reset_hold(Object *obj) ps2_reset_queue(s); } -static void ps2_reset_exit(Object *obj) +static void ps2_reset_exit(Object *obj, ResetType type) { PS2State *s = PS2_DEVICE(obj); @@ -1048,7 +1048,7 @@ static void ps2_common_post_load(PS2State *s) q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1; } -static void ps2_kbd_reset_hold(Object *obj) +static void ps2_kbd_reset_hold(Object *obj, ResetType type) { PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); PS2KbdState *s = PS2_KBD_DEVICE(obj); @@ -1056,7 +1056,7 @@ static void ps2_kbd_reset_hold(Object *obj) trace_ps2_kbd_reset(s); if (ps2dc->parent_phases.hold) { - ps2dc->parent_phases.hold(obj); + ps2dc->parent_phases.hold(obj, type); } s->scan_enabled = 1; @@ -1065,7 +1065,7 @@ static void ps2_kbd_reset_hold(Object *obj) s->modifiers = 0; } -static void ps2_mouse_reset_hold(Object *obj) +static void ps2_mouse_reset_hold(Object *obj, ResetType type) { PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); PS2MouseState *s = PS2_MOUSE_DEVICE(obj); @@ -1073,7 +1073,7 @@ static void ps2_mouse_reset_hold(Object *obj) trace_ps2_mouse_reset(s); if (ps2dc->parent_phases.hold) { - ps2dc->parent_phases.hold(obj); + ps2dc->parent_phases.hold(obj, type); } s->mouse_status = 0; diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 94c173cb07..53fb2c4e2d 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -263,7 +263,7 @@ static inline void arm_gic_common_reset_irq_state(GICState *s, int cidx, } } -static void arm_gic_common_reset_hold(Object *obj) +static void arm_gic_common_reset_hold(Object *obj, ResetType type) { GICState *s = ARM_GIC_COMMON(obj); int i, j; diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index e0d9e512a3..53defee7d5 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -473,13 +473,13 @@ static void kvm_arm_gic_get(GICState *s) } } -static void kvm_arm_gic_reset_hold(Object *obj) +static void kvm_arm_gic_reset_hold(Object *obj, ResetType type) { GICState *s = ARM_GIC_COMMON(obj); KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); if (kgc->parent_phases.hold) { - kgc->parent_phases.hold(obj); + kgc->parent_phases.hold(obj, type); } if (kvm_arm_gic_can_save_restore(s)) { diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 207f8417e1..bd50a1b079 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -495,7 +495,7 @@ static void arm_gicv3_finalize(Object *obj) g_free(s->redist_region_count); } -static void arm_gicv3_common_reset_hold(Object *obj) +static void arm_gicv3_common_reset_hold(Object *obj, ResetType type) { GICv3State *s = ARM_GICV3_COMMON(obj); int i; diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 52e9aca9c6..bf31158470 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -1950,13 +1950,13 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp) } } -static void gicv3_its_reset_hold(Object *obj) +static void gicv3_its_reset_hold(Object *obj, ResetType type) { GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s); if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } /* Quiescent bit reset to 1 */ diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c index 331d6b93cc..0b97362cd2 100644 --- a/hw/intc/arm_gicv3_its_common.c +++ b/hw/intc/arm_gicv3_its_common.c @@ -123,7 +123,7 @@ void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops, msi_nonbroken = true; } -static void gicv3_its_common_reset_hold(Object *obj) +static void gicv3_its_common_reset_hold(Object *obj, ResetType type) { GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 3befc960db..35539c099f 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -197,14 +197,14 @@ static void kvm_arm_its_post_load(GICv3ITSState *s) GITS_CTLR, &s->ctlr, true, &error_abort); } -static void kvm_arm_its_reset_hold(Object *obj) +static void kvm_arm_its_reset_hold(Object *obj, ResetType type) { GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); KVMARMITSClass *c = KVM_ARM_ITS_GET_CLASS(s); int i; if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 00a383079b..9ea6b8e218 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -703,7 +703,7 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS]; } -static void kvm_arm_gicv3_reset_hold(Object *obj) +static void kvm_arm_gicv3_reset_hold(Object *obj, ResetType type) { GICv3State *s = ARM_GICV3_COMMON(obj); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s); @@ -711,7 +711,7 @@ static void kvm_arm_gicv3_reset_hold(Object *obj) DPRINTF("Reset\n"); if (kgc->parent_phases.hold) { - kgc->parent_phases.hold(obj); + kgc->parent_phases.hold(obj, type); } if (s->migration_blocker) { diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 700abfa7a6..9b3b7abaea 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -579,7 +579,7 @@ static void ics_reset_irq(ICSIRQState *irq) irq->saved_priority = 0xff; } -static void ics_reset_hold(Object *obj) +static void ics_reset_hold(Object *obj, ResetType type) { ICSState *ics = ICS(obj); g_autofree uint8_t *flags = g_malloc(ics->nr_irqs); diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index b5a7713863..e2ae7c3201 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -175,7 +175,7 @@ static void glue_nmi_release(void *opaque) GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0); } -static void glue_reset_hold(Object *obj) +static void glue_reset_hold(Object *obj, ResetType type) { GLUEState *s = GLUE(obj); diff --git a/hw/misc/djmemc.c b/hw/misc/djmemc.c index 9b69656c3a..96d5efb5e3 100644 --- a/hw/misc/djmemc.c +++ b/hw/misc/djmemc.c @@ -96,7 +96,7 @@ static void djmemc_init(Object *obj) sysbus_init_mmio(sbd, &s->mem_regs); } -static void djmemc_reset_hold(Object *obj) +static void djmemc_reset_hold(Object *obj, ResetType type) { DJMEMCState *s = DJMEMC(obj); diff --git a/hw/misc/iosb.c b/hw/misc/iosb.c index e20305e801..31927eaedb 100644 --- a/hw/misc/iosb.c +++ b/hw/misc/iosb.c @@ -81,7 +81,7 @@ static const MemoryRegionOps iosb_mmio_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void iosb_reset_hold(Object *obj) +static void iosb_reset_hold(Object *obj, ResetType type) { IOSBState *s = IOSB(obj); diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index db6142b5f4..652395b84f 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -1203,7 +1203,7 @@ static int via1_post_load(void *opaque, int version_id) } /* VIA 1 */ -static void mos6522_q800_via1_reset_hold(Object *obj) +static void mos6522_q800_via1_reset_hold(Object *obj, ResetType type) { MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(obj); MOS6522State *ms = MOS6522(v1s); @@ -1211,7 +1211,7 @@ static void mos6522_q800_via1_reset_hold(Object *obj) ADBBusState *adb_bus = &v1s->adb_bus; if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = VIA_TIMER_FREQ; @@ -1359,13 +1359,13 @@ static void mos6522_q800_via2_portB_write(MOS6522State *s) } } -static void mos6522_q800_via2_reset_hold(Object *obj) +static void mos6522_q800_via2_reset_hold(Object *obj, ResetType type) { MOS6522State *ms = MOS6522(obj); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = VIA_TIMER_FREQ; diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 41934e2cf8..beab0ffb13 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -586,13 +586,13 @@ static void mos6522_cuda_portB_write(MOS6522State *s) cuda_update(cs); } -static void mos6522_cuda_reset_hold(Object *obj) +static void mos6522_cuda_reset_hold(Object *obj, ResetType type) { MOS6522State *ms = MOS6522(obj); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = CUDA_TIMER_FREQ; diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index e40c51bf52..238da58ead 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -792,7 +792,7 @@ static void mos6522_pmu_portB_write(MOS6522State *s) pmu_update(ps); } -static void mos6522_pmu_reset_hold(Object *obj) +static void mos6522_pmu_reset_hold(Object *obj, ResetType type) { MOS6522State *ms = MOS6522(obj); MOS6522PMUState *mps = container_of(ms, MOS6522PMUState, parent_obj); @@ -800,7 +800,7 @@ static void mos6522_pmu_reset_hold(Object *obj) MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = VIA_TIMER_FREQ; diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index e3fe87c20c..515f62e687 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -642,7 +642,7 @@ const VMStateDescription vmstate_mos6522 = { } }; -static void mos6522_reset_hold(Object *obj) +static void mos6522_reset_hold(Object *obj, ResetType type) { MOS6522State *s = MOS6522(obj); diff --git a/hw/misc/npcm7xx_mft.c b/hw/misc/npcm7xx_mft.c index 9a848584e1..9fcc69fe5c 100644 --- a/hw/misc/npcm7xx_mft.c +++ b/hw/misc/npcm7xx_mft.c @@ -467,7 +467,7 @@ static void npcm7xx_mft_enter_reset(Object *obj, ResetType type) npcm7xx_mft_reset(s); } -static void npcm7xx_mft_hold_reset(Object *obj) +static void npcm7xx_mft_hold_reset(Object *obj, ResetType type) { NPCM7xxMFTState *s = NPCM7XX_MFT(obj); diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c index fca2dd2e5a..f7f77e30a2 100644 --- a/hw/misc/npcm7xx_pwm.c +++ b/hw/misc/npcm7xx_pwm.c @@ -468,7 +468,7 @@ static void npcm7xx_pwm_enter_reset(Object *obj, ResetType type) s->piir = 0x00000000; } -static void npcm7xx_pwm_hold_reset(Object *obj) +static void npcm7xx_pwm_hold_reset(Object *obj, ResetType type) { NPCM7xxPWMState *s = NPCM7XX_PWM(obj); int i; diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c index 9fd859160d..a090dbd366 100644 --- a/hw/misc/stm32l4x5_exti.c +++ b/hw/misc/stm32l4x5_exti.c @@ -77,7 +77,7 @@ static unsigned configurable_mask(unsigned bank) return valid_mask(bank) & ~exti_romask[bank]; } -static void stm32l4x5_exti_reset_hold(Object *obj) +static void stm32l4x5_exti_reset_hold(Object *obj, ResetType type) { Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj); diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c index ed2dbd9dc3..417bd5e85f 100644 --- a/hw/misc/stm32l4x5_rcc.c +++ b/hw/misc/stm32l4x5_rcc.c @@ -113,13 +113,13 @@ static void clock_mux_reset_enter(Object *obj, ResetType type) set_clock_mux_init_info(s, s->id); } -static void clock_mux_reset_hold(Object *obj) +static void clock_mux_reset_hold(Object *obj, ResetType type) { RccClockMuxState *s = RCC_CLOCK_MUX(obj); clock_mux_update(s, true); } -static void clock_mux_reset_exit(Object *obj) +static void clock_mux_reset_exit(Object *obj, ResetType type) { RccClockMuxState *s = RCC_CLOCK_MUX(obj); clock_mux_update(s, false); @@ -263,13 +263,13 @@ static void pll_reset_enter(Object *obj, ResetType type) set_pll_init_info(s, s->id); } -static void pll_reset_hold(Object *obj) +static void pll_reset_hold(Object *obj, ResetType type) { RccPllState *s = RCC_PLL(obj); pll_update(s, true); } -static void pll_reset_exit(Object *obj) +static void pll_reset_exit(Object *obj, ResetType type) { RccPllState *s = RCC_PLL(obj); pll_update(s, false); @@ -907,7 +907,7 @@ static void rcc_update_csr(Stm32l4x5RccState *s) rcc_update_irq(s); } -static void stm32l4x5_rcc_reset_hold(Object *obj) +static void stm32l4x5_rcc_reset_hold(Object *obj, ResetType type) { Stm32l4x5RccState *s = STM32L4X5_RCC(obj); s->cr = 0x00000063; diff --git a/hw/misc/stm32l4x5_syscfg.c b/hw/misc/stm32l4x5_syscfg.c index 3dafc00b49..a5a1ce2680 100644 --- a/hw/misc/stm32l4x5_syscfg.c +++ b/hw/misc/stm32l4x5_syscfg.c @@ -65,7 +65,7 @@ #define NUM_LINES_PER_EXTICR_REG 4 -static void stm32l4x5_syscfg_hold_reset(Object *obj) +static void stm32l4x5_syscfg_hold_reset(Object *obj, ResetType type) { Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(obj); diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c index a6ab287b01..3fc838bd54 100644 --- a/hw/misc/xlnx-versal-cframe-reg.c +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -542,7 +542,7 @@ static void cframe_reg_reset_enter(Object *obj, ResetType type) } } -static void cframe_reg_reset_hold(Object *obj) +static void cframe_reg_reset_hold(Object *obj, ResetType type) { XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index 1f1762ef16..f143900d5b 100644 --- a/hw/misc/xlnx-versal-crl.c +++ b/hw/misc/xlnx-versal-crl.c @@ -311,7 +311,7 @@ static void crl_reset_enter(Object *obj, ResetType type) } } -static void crl_reset_hold(Object *obj) +static void crl_reset_hold(Object *obj, ResetType type) { XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); diff --git a/hw/misc/xlnx-versal-pmc-iou-slcr.c b/hw/misc/xlnx-versal-pmc-iou-slcr.c index 60e13a78ab..e469c04d76 100644 --- a/hw/misc/xlnx-versal-pmc-iou-slcr.c +++ b/hw/misc/xlnx-versal-pmc-iou-slcr.c @@ -1350,7 +1350,7 @@ static void xlnx_versal_pmc_iou_slcr_reset_init(Object *obj, ResetType type) } } -static void xlnx_versal_pmc_iou_slcr_reset_hold(Object *obj) +static void xlnx_versal_pmc_iou_slcr_reset_hold(Object *obj, ResetType type) { XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(obj); diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index 6495188dc7..51eb760041 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -632,7 +632,7 @@ static void trng_unrealize(DeviceState *dev) s->prng = NULL; } -static void trng_reset_hold(Object *obj) +static void trng_reset_hold(Object *obj, ResetType type) { trng_reset(XLNX_VERSAL_TRNG(obj)); } diff --git a/hw/misc/xlnx-versal-xramc.c b/hw/misc/xlnx-versal-xramc.c index a5f78c190e..ad839ce7e9 100644 --- a/hw/misc/xlnx-versal-xramc.c +++ b/hw/misc/xlnx-versal-xramc.c @@ -137,7 +137,7 @@ static void xram_ctrl_reset_enter(Object *obj, ResetType type) ARRAY_FIELD_DP32(s->regs, XRAM_IMP, SIZE, s->cfg.encoded_size); } -static void xram_ctrl_reset_hold(Object *obj) +static void xram_ctrl_reset_hold(Object *obj, ResetType type) { XlnxXramCtrl *s = XLNX_XRAM_CTRL(obj); diff --git a/hw/misc/xlnx-zynqmp-apu-ctrl.c b/hw/misc/xlnx-zynqmp-apu-ctrl.c index 1d441b41df..87e4a14067 100644 --- a/hw/misc/xlnx-zynqmp-apu-ctrl.c +++ b/hw/misc/xlnx-zynqmp-apu-ctrl.c @@ -150,7 +150,7 @@ static void zynqmp_apu_reset_enter(Object *obj, ResetType type) s->cpu_in_wfi = 0; } -static void zynqmp_apu_reset_hold(Object *obj) +static void zynqmp_apu_reset_hold(Object *obj, ResetType type) { XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj); diff --git a/hw/misc/xlnx-zynqmp-crf.c b/hw/misc/xlnx-zynqmp-crf.c index a83efb44e3..e5aba56f69 100644 --- a/hw/misc/xlnx-zynqmp-crf.c +++ b/hw/misc/xlnx-zynqmp-crf.c @@ -191,7 +191,7 @@ static void crf_reset_enter(Object *obj, ResetType type) } } -static void crf_reset_hold(Object *obj) +static void crf_reset_hold(Object *obj, ResetType type) { XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); ir_update_irq(s); diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index d2ac2e77f2..3412ff099e 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -416,7 +416,7 @@ static void zynq_slcr_reset_init(Object *obj, ResetType type) s->regs[R_DDRIOB + 12] = 0x00000021; } -static void zynq_slcr_reset_hold(Object *obj) +static void zynq_slcr_reset_hold(Object *obj, ResetType type) { ZynqSLCRState *s = ZYNQ_SLCR(obj); @@ -425,7 +425,7 @@ static void zynq_slcr_reset_hold(Object *obj) zynq_slcr_propagate_clocks(s); } -static void zynq_slcr_reset_exit(Object *obj) +static void zynq_slcr_reset_exit(Object *obj, ResetType type) { ZynqSLCRState *s = ZYNQ_SLCR(obj); diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c index ca0ce4e8bb..58f1432bb3 100644 --- a/hw/net/can/xlnx-zynqmp-can.c +++ b/hw/net/can/xlnx-zynqmp-can.c @@ -1006,7 +1006,7 @@ static void xlnx_zynqmp_can_reset_init(Object *obj, ResetType type) ptimer_transaction_commit(s->can_timer); } -static void xlnx_zynqmp_can_reset_hold(Object *obj) +static void xlnx_zynqmp_can_reset_hold(Object *obj, ResetType type) { XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj); unsigned int i; diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 43f3a4a701..5012b96464 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -373,7 +373,7 @@ static bool e1000_vet_init_need(void *opaque) return chkflag(VET); } -static void e1000_reset_hold(Object *obj) +static void e1000_reset_hold(Object *obj, ResetType type) { E1000State *d = E1000(obj); E1000BaseClass *edc = E1000_GET_CLASS(d); diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 7c6f602951..edc101eaf6 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -513,7 +513,7 @@ static void e1000e_pci_uninit(PCIDevice *pci_dev) msi_uninit(pci_dev); } -static void e1000e_qdev_reset_hold(Object *obj) +static void e1000e_qdev_reset_hold(Object *obj, ResetType type) { E1000EState *s = E1000E(obj); diff --git a/hw/net/igb.c b/hw/net/igb.c index 9b37523d6d..1ef6170465 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -486,7 +486,7 @@ static void igb_pci_uninit(PCIDevice *pci_dev) msi_uninit(pci_dev); } -static void igb_qdev_reset_hold(Object *obj) +static void igb_qdev_reset_hold(Object *obj, ResetType type) { IGBState *s = IGB(obj); diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c index 94a4e885f2..21a97d4d61 100644 --- a/hw/net/igbvf.c +++ b/hw/net/igbvf.c @@ -282,7 +282,7 @@ static void igbvf_pci_realize(PCIDevice *dev, Error **errp) pcie_ari_init(dev, 0x150); } -static void igbvf_qdev_reset_hold(Object *obj) +static void igbvf_qdev_reset_hold(Object *obj, ResetType type) { PCIDevice *vf = PCI_DEVICE(obj); diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 0a71a005c6..09575a77d7 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -417,7 +417,7 @@ static RegisterAccessInfo bbram_ctrl_regs_info[] = { } }; -static void bbram_ctrl_reset_hold(Object *obj) +static void bbram_ctrl_reset_hold(Object *obj, ResetType type) { XlnxBBRam *s = XLNX_BBRAM(obj); unsigned int i; diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index e4b9e11a3d..def6fe3302 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -658,7 +658,7 @@ static void efuse_ctrl_register_reset(RegisterInfo *reg) register_reset(reg); } -static void efuse_ctrl_reset_hold(Object *obj) +static void efuse_ctrl_reset_hold(Object *obj, ResetType type) { XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); unsigned int i; diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c index ec98456e5d..2d465f0fc6 100644 --- a/hw/nvram/xlnx-zynqmp-efuse.c +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -770,7 +770,7 @@ static void zynqmp_efuse_register_reset(RegisterInfo *reg) register_reset(reg); } -static void zynqmp_efuse_reset_hold(Object *obj) +static void zynqmp_efuse_reset_hold(Object *obj, ResetType type) { XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(obj); unsigned int i; diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index 8a30da602c..2dd10239bd 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -186,13 +186,13 @@ static void cxl_rp_realize(DeviceState *dev, Error **errp) component_bar); } -static void cxl_rp_reset_hold(Object *obj) +static void cxl_rp_reset_hold(Object *obj, ResetType type) { PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(obj); CXLRootPort *crp = CXL_ROOT_PORT(obj); if (rpc->parent_phases.hold) { - rpc->parent_phases.hold(obj); + rpc->parent_phases.hold(obj, type); } latch_registers(crp); diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index efd96bf174..09a34786bc 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -43,7 +43,7 @@ static void rp_write_config(PCIDevice *d, uint32_t address, pcie_aer_root_write_config(d, address, val, len, root_cmd); } -static void rp_reset_hold(Object *obj) +static void rp_reset_hold(Object *obj, ResetType type) { PCIDevice *d = PCI_DEVICE(obj); DeviceState *qdev = DEVICE(obj); diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 1f0c435348..1516d0074d 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -590,7 +590,7 @@ static int pci_bonito_map_irq(PCIDevice *pci_dev, int irq_num) } } -static void bonito_reset_hold(Object *obj) +static void bonito_reset_hold(Object *obj, ResetType type) { PCIBonitoState *s = PCI_BONITO(obj); uint32_t val = 0; diff --git a/hw/pci-host/pnv_phb.c b/hw/pci-host/pnv_phb.c index 157c00782c..d4c118d443 100644 --- a/hw/pci-host/pnv_phb.c +++ b/hw/pci-host/pnv_phb.c @@ -208,7 +208,7 @@ static void pnv_phb_class_init(ObjectClass *klass, void *data) dc->user_creatable = true; } -static void pnv_phb_root_port_reset_hold(Object *obj) +static void pnv_phb_root_port_reset_hold(Object *obj, ResetType type) { PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(obj); PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(obj); @@ -216,7 +216,7 @@ static void pnv_phb_root_port_reset_hold(Object *obj) uint8_t *conf = d->config; if (rpc->parent_phases.hold) { - rpc->parent_phases.hold(obj); + rpc->parent_phases.hold(obj, type); } if (phb_rp->version == 3) { diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c index dc8d8637f2..a6d827f903 100644 --- a/hw/pci-host/pnv_phb3_msi.c +++ b/hw/pci-host/pnv_phb3_msi.c @@ -228,13 +228,13 @@ static void phb3_msi_resend(ICSState *ics) } } -static void phb3_msi_reset_hold(Object *obj) +static void phb3_msi_reset_hold(Object *obj, ResetType type) { Phb3MsiState *msi = PHB3_MSI(obj); ICSStateClass *icsc = ICS_GET_CLASS(obj); if (icsc->parent_phases.hold) { - icsc->parent_phases.hold(obj); + icsc->parent_phases.hold(obj, type); } memset(msi->rba, 0, sizeof(msi->rba)); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e7a39cb203..324c1302d2 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -64,7 +64,7 @@ bool pci_available = true; static char *pcibus_get_dev_path(DeviceState *dev); static char *pcibus_get_fw_dev_path(DeviceState *dev); -static void pcibus_reset_hold(Object *obj); +static void pcibus_reset_hold(Object *obj, ResetType type); static bool pcie_has_upstream_port(PCIDevice *dev); static Property pci_props[] = { @@ -427,7 +427,7 @@ void pci_device_reset(PCIDevice *dev) * Called via bus_cold_reset on RST# assert, after the devices * have been reset device_cold_reset-ed already. */ -static void pcibus_reset_hold(Object *obj) +static void pcibus_reset_hold(Object *obj, ResetType type) { PCIBus *bus = PCI_BUS(obj); int i; diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index f4c1869232..3379f92748 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -998,7 +998,7 @@ static void rtc_reset_enter(Object *obj, ResetType type) } } -static void rtc_reset_hold(Object *obj) +static void rtc_reset_hold(Object *obj, ResetType type) { MC146818RtcState *s = MC146818_RTC(obj); diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 34639f2143..8657ff7bf4 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -56,7 +56,7 @@ static void ccw_device_unplug(HotplugHandler *hotplug_dev, qdev_unrealize(dev); } -static void virtual_css_bus_reset_hold(Object *obj) +static void virtual_css_bus_reset_hold(Object *obj, ResetType type) { /* This should actually be modelled via the generic css */ css_reset(); diff --git a/hw/sensor/adm1266.c b/hw/sensor/adm1266.c index 5454b73a63..25b87a7296 100644 --- a/hw/sensor/adm1266.c +++ b/hw/sensor/adm1266.c @@ -76,7 +76,7 @@ static const uint8_t adm1266_ic_device_id[] = {0x03, 0x41, 0x12, 0x66}; static const uint8_t adm1266_ic_device_rev[] = {0x08, 0x01, 0x08, 0x07, 0x0, 0x0, 0x07, 0x41, 0x30}; -static void adm1266_exit_reset(Object *obj) +static void adm1266_exit_reset(Object *obj, ResetType type) { ADM1266State *s = ADM1266(obj); PMBusDevice *pmdev = PMBUS_DEVICE(obj); diff --git a/hw/sensor/adm1272.c b/hw/sensor/adm1272.c index a19557ec9e..3fc1e5d0ad 100644 --- a/hw/sensor/adm1272.c +++ b/hw/sensor/adm1272.c @@ -185,7 +185,7 @@ static uint32_t adm1272_direct_to_watts(uint16_t value) return pmbus_direct_mode2data(c, value); } -static void adm1272_exit_reset(Object *obj) +static void adm1272_exit_reset(Object *obj, ResetType type) { ADM1272State *s = ADM1272(obj); PMBusDevice *pmdev = PMBUS_DEVICE(obj); diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c index e51269f6b8..304a66ea8b 100644 --- a/hw/sensor/isl_pmbus_vr.c +++ b/hw/sensor/isl_pmbus_vr.c @@ -63,7 +63,7 @@ static void isl_pmbus_vr_set(Object *obj, Visitor *v, const char *name, pmbus_check_limits(pmdev); } -static void isl_pmbus_vr_exit_reset(Object *obj) +static void isl_pmbus_vr_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); @@ -102,11 +102,11 @@ static void isl_pmbus_vr_exit_reset(Object *obj) } /* The raa228000 uses different direct mode coefficients from most isl devices */ -static void raa228000_exit_reset(Object *obj) +static void raa228000_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); - isl_pmbus_vr_exit_reset(obj); + isl_pmbus_vr_exit_reset(obj, type); pmdev->pages[0].read_iout = 0; pmdev->pages[0].read_pout = 0; @@ -119,13 +119,13 @@ static void raa228000_exit_reset(Object *obj) pmdev->pages[0].read_temperature_3 = 0; } -static void isl69259_exit_reset(Object *obj) +static void isl69259_exit_reset(Object *obj, ResetType type) { ISLState *s = ISL69260(obj); static const uint8_t ic_device_id[] = {0x04, 0x00, 0x81, 0xD2, 0x49, 0x3c}; g_assert(sizeof(ic_device_id) <= sizeof(s->ic_device_id)); - isl_pmbus_vr_exit_reset(obj); + isl_pmbus_vr_exit_reset(obj, type); s->ic_device_id_len = sizeof(ic_device_id); memcpy(s->ic_device_id, ic_device_id, sizeof(ic_device_id)); diff --git a/hw/sensor/max31785.c b/hw/sensor/max31785.c index 916ed4d457..3577a7c218 100644 --- a/hw/sensor/max31785.c +++ b/hw/sensor/max31785.c @@ -444,7 +444,7 @@ static int max31785_write_data(PMBusDevice *pmdev, const uint8_t *buf, return 0; } -static void max31785_exit_reset(Object *obj) +static void max31785_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); MAX31785State *s = MAX31785(obj); diff --git a/hw/sensor/max34451.c b/hw/sensor/max34451.c index 031ae53f59..93b53f3db2 100644 --- a/hw/sensor/max34451.c +++ b/hw/sensor/max34451.c @@ -608,7 +608,7 @@ static inline void *memset_word(void *s, uint16_t c, size_t n) return s; } -static void max34451_exit_reset(Object *obj) +static void max34451_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); MAX34451State *s = MAX34451(obj); diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index 81dd972ee8..119c38c415 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -483,7 +483,7 @@ static void npcm7xx_fiu_enter_reset(Object *obj, ResetType type) s->regs[NPCM7XX_FIU_CFG] = 0x0000000b; } -static void npcm7xx_fiu_hold_reset(Object *obj) +static void npcm7xx_fiu_hold_reset(Object *obj, ResetType type) { NPCM7xxFIUState *s = NPCM7XX_FIU(obj); int i; diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c index da7c946af5..dd6d96b0a1 100644 --- a/hw/timer/etraxfs_timer.c +++ b/hw/timer/etraxfs_timer.c @@ -357,7 +357,7 @@ static void etraxfs_timer_reset_enter(Object *obj, ResetType type) t->rw_intr_mask = 0; } -static void etraxfs_timer_reset_hold(Object *obj) +static void etraxfs_timer_reset_hold(Object *obj, ResetType type) { ETRAXTimerState *t = ETRAX_TIMER(obj); diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c index 779c6049fa..c55ba02235 100644 --- a/hw/timer/npcm7xx_timer.c +++ b/hw/timer/npcm7xx_timer.c @@ -592,7 +592,7 @@ static void npcm7xx_watchdog_timer_expired(void *opaque) } } -static void npcm7xx_timer_hold_reset(Object *obj) +static void npcm7xx_timer_hold_reset(Object *obj, ResetType type) { NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj); int i; diff --git a/hw/usb/hcd-dwc2.c b/hw/usb/hcd-dwc2.c index 222eef82a5..8cac9c0a06 100644 --- a/hw/usb/hcd-dwc2.c +++ b/hw/usb/hcd-dwc2.c @@ -1305,7 +1305,7 @@ static void dwc2_reset_enter(Object *obj, ResetType type) } } -static void dwc2_reset_hold(Object *obj) +static void dwc2_reset_hold(Object *obj, ResetType type) { DWC2Class *c = DWC2_USB_GET_CLASS(obj); DWC2State *s = DWC2_USB(obj); @@ -1313,13 +1313,13 @@ static void dwc2_reset_hold(Object *obj) trace_usb_dwc2_reset_hold(); if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } dwc2_update_irq(s); } -static void dwc2_reset_exit(Object *obj) +static void dwc2_reset_exit(Object *obj, ResetType type) { DWC2Class *c = DWC2_USB_GET_CLASS(obj); DWC2State *s = DWC2_USB(obj); @@ -1327,7 +1327,7 @@ static void dwc2_reset_exit(Object *obj) trace_usb_dwc2_reset_exit(); if (c->parent_phases.exit) { - c->parent_phases.exit(obj); + c->parent_phases.exit(obj, type); } s->hprt0 = HPRT0_PWR; diff --git a/hw/usb/xlnx-versal-usb2-ctrl-regs.c b/hw/usb/xlnx-versal-usb2-ctrl-regs.c index 6fc453817e..66c793a602 100644 --- a/hw/usb/xlnx-versal-usb2-ctrl-regs.c +++ b/hw/usb/xlnx-versal-usb2-ctrl-regs.c @@ -153,7 +153,7 @@ static void usb2_ctrl_regs_reset_init(Object *obj, ResetType type) } } -static void usb2_ctrl_regs_reset_hold(Object *obj) +static void usb2_ctrl_regs_reset_hold(Object *obj, ResetType type) { VersalUsb2CtrlRegs *s = XILINX_VERSAL_USB2_CTRL_REGS(obj); diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index cb159fd078..b1d02f4b3d 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2292,7 +2292,7 @@ static void virtio_pci_reset(DeviceState *qdev) } } -static void virtio_pci_bus_reset_hold(Object *obj) +static void virtio_pci_bus_reset_hold(Object *obj, ResetType type) { PCIDevice *dev = PCI_DEVICE(obj); DeviceState *qdev = DEVICE(obj); diff --git a/include/hw/resettable.h b/include/hw/resettable.h index bdcd1276b6..3161e471c9 100644 --- a/include/hw/resettable.h +++ b/include/hw/resettable.h @@ -103,8 +103,8 @@ typedef enum ResetType { * the callback. */ typedef void (*ResettableEnterPhase)(Object *obj, ResetType type); -typedef void (*ResettableHoldPhase)(Object *obj); -typedef void (*ResettableExitPhase)(Object *obj); +typedef void (*ResettableHoldPhase)(Object *obj, ResetType type); +typedef void (*ResettableExitPhase)(Object *obj, ResetType type); typedef ResettableState * (*ResettableGetState)(Object *obj); typedef void (*ResettableTrFunction)(Object *obj); typedef ResettableTrFunction (*ResettableGetTrFunction)(Object *obj); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d2dfd36fd4..a152def241 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -220,7 +220,7 @@ static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque) assert(oldvalue == newvalue); } -static void arm_cpu_reset_hold(Object *obj) +static void arm_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); ARMCPU *cpu = ARM_CPU(cs); @@ -228,7 +228,7 @@ static void arm_cpu_reset_hold(Object *obj) CPUARMState *env = &cpu->env; if (acc->parent_phases.hold) { - acc->parent_phases.hold(obj); + acc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUARMState, end_reset_fields)); diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 45ee1b5f89..71ce62a4c2 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -66,7 +66,7 @@ static void avr_restore_state_to_opc(CPUState *cs, cpu_env(cs)->pc_w = data[0]; } -static void avr_cpu_reset_hold(Object *obj) +static void avr_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); AVRCPU *cpu = AVR_CPU(cs); @@ -74,7 +74,7 @@ static void avr_cpu_reset_hold(Object *obj) CPUAVRState *env = &cpu->env; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } env->pc_w = 0; diff --git a/target/cris/cpu.c b/target/cris/cpu.c index eb4bddcb7e..535ec39c73 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -61,7 +61,7 @@ static int cris_cpu_mmu_index(CPUState *cs, bool ifetch) return !!(cpu_env(cs)->pregs[PR_CCS] & U_FLAG); } -static void cris_cpu_reset_hold(Object *obj) +static void cris_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(obj); @@ -69,7 +69,7 @@ static void cris_cpu_reset_hold(Object *obj) uint32_t vr; if (ccc->parent_phases.hold) { - ccc->parent_phases.hold(obj); + ccc->parent_phases.hold(obj, type); } vr = env->pregs[PR_VR]; diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 3a716b9be3..a56bb4b075 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -273,14 +273,14 @@ static void hexagon_restore_state_to_opc(CPUState *cs, cpu_env(cs)->gpr[HEX_REG_PC] = data[0]; } -static void hexagon_cpu_reset_hold(Object *obj) +static void hexagon_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(obj); CPUHexagonState *env = cpu_env(cs); if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } set_default_nan_mode(1, &env->fp_status); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index fd6af0d763..fa1ea3735d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6830,7 +6830,7 @@ static void x86_cpu_set_sgxlepubkeyhash(CPUX86State *env) #endif } -static void x86_cpu_reset_hold(Object *obj) +static void x86_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); X86CPU *cpu = X86_CPU(cs); @@ -6841,7 +6841,7 @@ static void x86_cpu_reset_hold(Object *obj) int i; if (xcc->parent_phases.hold) { - xcc->parent_phases.hold(obj); + xcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUX86State, end_reset_fields)); diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 203a349055..bac84dca7a 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -495,14 +495,14 @@ static void loongarch_max_initfn(Object *obj) loongarch_la464_initfn(obj); } -static void loongarch_cpu_reset_hold(Object *obj) +static void loongarch_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(obj); CPULoongArchState *env = cpu_env(cs); if (lacc->parent_phases.hold) { - lacc->parent_phases.hold(obj); + lacc->parent_phases.hold(obj, type); } env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index df49ff1880..efd6bbded8 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -71,7 +71,7 @@ static void m68k_unset_feature(CPUM68KState *env, int feature) env->features &= ~BIT_ULL(feature); } -static void m68k_cpu_reset_hold(Object *obj) +static void m68k_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(obj); @@ -80,7 +80,7 @@ static void m68k_cpu_reset_hold(Object *obj) int i; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUM68KState, end_reset_fields)); diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 96c2b71f7f..f8dc3173fc 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -181,7 +181,7 @@ static void microblaze_cpu_set_irq(void *opaque, int irq, int level) } #endif -static void mb_cpu_reset_hold(Object *obj) +static void mb_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); @@ -189,7 +189,7 @@ static void mb_cpu_reset_hold(Object *obj) CPUMBState *env = &cpu->env; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUMBState, end_reset_fields)); diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 8d8f690a53..bbe01d07dd 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -185,7 +185,7 @@ static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) #include "cpu-defs.c.inc" -static void mips_cpu_reset_hold(Object *obj) +static void mips_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); MIPSCPU *cpu = MIPS_CPU(cs); @@ -193,7 +193,7 @@ static void mips_cpu_reset_hold(Object *obj) CPUMIPSState *env = &cpu->env; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUMIPSState, end_reset_fields)); diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 33c45dbf04..d711035cf5 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -85,14 +85,14 @@ static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) info->print_insn = print_insn_or1k; } -static void openrisc_cpu_reset_hold(Object *obj) +static void openrisc_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); OpenRISCCPU *cpu = OPENRISC_CPU(cs); OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(obj); if (occ->parent_phases.hold) { - occ->parent_phases.hold(obj); + occ->parent_phases.hold(obj, type); } memset(&cpu->env, 0, offsetof(CPUOpenRISCState, end_reset_fields)); diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 6241de62ce..6d82f24c87 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7136,7 +7136,7 @@ static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) return ppc_env_mmu_index(cpu_env(cs), ifetch); } -static void ppc_cpu_reset_hold(Object *obj) +static void ppc_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -7146,7 +7146,7 @@ static void ppc_cpu_reset_hold(Object *obj) int i; if (pcc->parent_phases.hold) { - pcc->parent_phases.hold(obj); + pcc->parent_phases.hold(obj, type); } msr = (target_ulong)0; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 36e3e5fdaf..eb1a2e7d6d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -918,7 +918,7 @@ static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) return riscv_env_mmu_index(cpu_env(cs), ifetch); } -static void riscv_cpu_reset_hold(Object *obj) +static void riscv_cpu_reset_hold(Object *obj, ResetType type) { #ifndef CONFIG_USER_ONLY uint8_t iprio; @@ -930,7 +930,7 @@ static void riscv_cpu_reset_hold(Object *obj) CPURISCVState *env = &cpu->env; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } #ifndef CONFIG_USER_ONLY env->misa_mxl = mcc->misa_mxl_max; diff --git a/target/rx/cpu.c b/target/rx/cpu.c index da673a595d..e3dfb09722 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -69,7 +69,7 @@ static int riscv_cpu_mmu_index(CPUState *cs, bool ifunc) return 0; } -static void rx_cpu_reset_hold(Object *obj) +static void rx_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); RXCPUClass *rcc = RX_CPU_GET_CLASS(obj); @@ -77,7 +77,7 @@ static void rx_cpu_reset_hold(Object *obj) uint32_t *resetvec; if (rcc->parent_phases.hold) { - rcc->parent_phases.hold(obj); + rcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPURXState, end_reset_fields)); diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 4f5a4a3d98..43e35ec2ca 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -103,14 +103,14 @@ static int sh4_cpu_mmu_index(CPUState *cs, bool ifetch) } } -static void superh_cpu_reset_hold(Object *obj) +static void superh_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(obj); CPUSH4State *env = cpu_env(cs); if (scc->parent_phases.hold) { - scc->parent_phases.hold(obj); + scc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUSH4State, end_reset_fields)); diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index e820f50acf..485d416925 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -29,14 +29,14 @@ //#define DEBUG_FEATURES -static void sparc_cpu_reset_hold(Object *obj) +static void sparc_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(obj); CPUSPARCState *env = cpu_env(cs); if (scc->parent_phases.hold) { - scc->parent_phases.hold(obj); + scc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUSPARCState, end_reset_fields)); diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index a9af73aeb5..8f9b72c3a0 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -58,13 +58,13 @@ static void tricore_restore_state_to_opc(CPUState *cs, cpu_env(cs)->PC = data[0]; } -static void tricore_cpu_reset_hold(Object *obj) +static void tricore_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(obj); if (tcc->parent_phases.hold) { - tcc->parent_phases.hold(obj); + tcc->parent_phases.hold(obj, type); } cpu_state_reset(cpu_env(cs)); diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 875cf843c9..de907cfeb1 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -93,7 +93,7 @@ bool xtensa_abi_call0(void) } #endif -static void xtensa_cpu_reset_hold(Object *obj) +static void xtensa_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(obj); @@ -102,7 +102,7 @@ static void xtensa_cpu_reset_hold(Object *obj) XTENSA_OPTION_DFP_COPROCESSOR); if (xcc->parent_phases.hold) { - xcc->parent_phases.hold(obj); + xcc->parent_phases.hold(obj, type); } env->pc = env->config->exception_vector[EXC_RESET0 + env->static_vectors]; From 41d49ec190db9171d2ebb158fd4d5daad06ed8e1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:08 +0100 Subject: [PATCH 31/37] docs/devel/reset: Update to new API for hold and exit phase methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the reset documentation's example code to match the new API for the hold and exit phase method APIs where they take a ResetType argument. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-6-peter.maydell@linaro.org --- docs/devel/reset.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst index 2ea85e7779..49baa1ea27 100644 --- a/docs/devel/reset.rst +++ b/docs/devel/reset.rst @@ -150,25 +150,25 @@ in reset. mydev->var = 0; } - static void mydev_reset_hold(Object *obj) + static void mydev_reset_hold(Object *obj, ResetType type) { MyDevClass *myclass = MYDEV_GET_CLASS(obj); MyDevState *mydev = MYDEV(obj); /* call parent class hold phase */ if (myclass->parent_phases.hold) { - myclass->parent_phases.hold(obj); + myclass->parent_phases.hold(obj, type); } /* set an IO */ qemu_set_irq(mydev->irq, 1); } - static void mydev_reset_exit(Object *obj) + static void mydev_reset_exit(Object *obj, ResetType type) { MyDevClass *myclass = MYDEV_GET_CLASS(obj); MyDevState *mydev = MYDEV(obj); /* call parent class exit phase */ if (myclass->parent_phases.exit) { - myclass->parent_phases.exit(obj); + myclass->parent_phases.exit(obj, type); } /* clear an IO */ qemu_set_irq(mydev->irq, 0); From 631f46d4ea7cb7ac0e529aacc1e7d832473f96c3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 12 Apr 2024 17:08:09 +0100 Subject: [PATCH 32/37] reset: Add RESET_TYPE_SNAPSHOT_LOAD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices and machines need to handle the reset before a vmsave snapshot is loaded differently -- the main user is the handling of RNG seed information, which does not want to put a new RNG seed into a ROM blob when we are doing a snapshot load. Currently this kind of reset handling is supported only for: * TYPE_MACHINE reset methods, which take a ShutdownCause argument * reset functions registered with qemu_register_reset_nosnapshotload To allow a three-phase-reset device to also distinguish "snapshot load" reset from the normal kind, add a new ResetType RESET_TYPE_SNAPSHOT_LOAD. All our existing reset methods ignore the reset type, so we don't need to update any device code. Add the enum type, and make qemu_devices_reset() use the right reset type for the ShutdownCause it is passed. This allows us to get rid of the device_reset_reason global we were using to implement qemu_register_reset_nosnapshotload(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Message-id: 20240412160809.1260625-7-peter.maydell@linaro.org --- docs/devel/reset.rst | 17 ++++++++++++++--- hw/core/reset.c | 15 ++++----------- hw/core/resettable.c | 4 ---- include/hw/resettable.h | 1 + 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst index 49baa1ea27..9746a4e8a0 100644 --- a/docs/devel/reset.rst +++ b/docs/devel/reset.rst @@ -27,9 +27,7 @@ instantly reset an object, without keeping it in reset state, just call ``resettable_reset()``. These functions take two parameters: a pointer to the object to reset and a reset type. -Several types of reset will be supported. For now only cold reset is defined; -others may be added later. The Resettable interface handles reset types with an -enum: +The Resettable interface handles reset types with an enum ``ResetType``: ``RESET_TYPE_COLD`` Cold reset is supported by every resettable object. In QEMU, it means we reset @@ -37,6 +35,19 @@ enum: from what is a real hardware cold reset. It differs from other resets (like warm or bus resets) which may keep certain parts untouched. +``RESET_TYPE_SNAPSHOT_LOAD`` + This is called for a reset which is being done to put the system into a + clean state prior to loading a snapshot. (This corresponds to a reset + with ``SHUTDOWN_CAUSE_SNAPSHOT_LOAD``.) Almost all devices should treat + this the same as ``RESET_TYPE_COLD``. The main exception is devices which + have some non-deterministic state they want to reinitialize to a different + value on each cold reset, such as RNG seed information, and which they + must not reinitialize on a snapshot-load reset. + +Devices which implement reset methods must treat any unknown ``ResetType`` +as equivalent to ``RESET_TYPE_COLD``; this will reduce the amount of +existing code we need to change if we add more types in future. + Calling ``resettable_reset()`` is equivalent to calling ``resettable_assert_reset()`` then ``resettable_release_reset()``. It is possible to interleave multiple calls to these three functions. There may diff --git a/hw/core/reset.c b/hw/core/reset.c index f9fef45e05..58dfc8db3d 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -43,13 +43,6 @@ static ResettableContainer *get_root_reset_container(void) return root_reset_container; } -/* - * Reason why the currently in-progress qemu_devices_reset() was called. - * If we made at least SHUTDOWN_CAUSE_SNAPSHOT_LOAD have a corresponding - * ResetType we could perhaps avoid the need for this global. - */ -static ShutdownCause device_reset_reason; - /* * This is an Object which implements Resettable simply to call the * callback function in the hold phase. @@ -77,8 +70,7 @@ static void legacy_reset_hold(Object *obj, ResetType type) { LegacyReset *lr = LEGACY_RESET(obj); - if (device_reset_reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD && - lr->skip_on_snapshot_load) { + if (type == RESET_TYPE_SNAPSHOT_LOAD && lr->skip_on_snapshot_load) { return; } lr->func(lr->opaque); @@ -180,8 +172,9 @@ void qemu_unregister_resettable(Object *obj) void qemu_devices_reset(ShutdownCause reason) { - device_reset_reason = reason; + ResetType type = (reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD) ? + RESET_TYPE_SNAPSHOT_LOAD : RESET_TYPE_COLD; /* Reset the simulation */ - resettable_reset(OBJECT(get_root_reset_container()), RESET_TYPE_COLD); + resettable_reset(OBJECT(get_root_reset_container()), type); } diff --git a/hw/core/resettable.c b/hw/core/resettable.c index bebf7f10b2..6dd3e3dc48 100644 --- a/hw/core/resettable.c +++ b/hw/core/resettable.c @@ -48,8 +48,6 @@ void resettable_reset(Object *obj, ResetType type) void resettable_assert_reset(Object *obj, ResetType type) { - /* TODO: change this assert when adding support for other reset types */ - assert(type == RESET_TYPE_COLD); trace_resettable_reset_assert_begin(obj, type); assert(!enter_phase_in_progress); @@ -64,8 +62,6 @@ void resettable_assert_reset(Object *obj, ResetType type) void resettable_release_reset(Object *obj, ResetType type) { - /* TODO: change this assert when adding support for other reset types */ - assert(type == RESET_TYPE_COLD); trace_resettable_reset_release_begin(obj, type); assert(!enter_phase_in_progress); diff --git a/include/hw/resettable.h b/include/hw/resettable.h index 3161e471c9..7e249deb8b 100644 --- a/include/hw/resettable.h +++ b/include/hw/resettable.h @@ -35,6 +35,7 @@ typedef struct ResettableState ResettableState; */ typedef enum ResetType { RESET_TYPE_COLD, + RESET_TYPE_SNAPSHOT_LOAD, } ResetType; /* From 4fb37aea7e01679db44758eb9407d5a49e3dbd7a Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:43:58 +0100 Subject: [PATCH 33/37] hw/char: Implement STM32L4x5 USART skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the basic infrastructure (register read/write, type...) to implement the STM32L4x5 USART. Also create different types for the USART, UART and LPUART of the STM32L4x5 to deduplicate code and enable the implementation of different behaviors depending on the type. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-2-arnaud.minier@telecom-paris.fr [PMM: update to new reset hold method signature; fixed a few checkpatch nits] Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + hw/char/Kconfig | 3 + hw/char/meson.build | 1 + hw/char/stm32l4x5_usart.c | 396 ++++++++++++++++++++++++++++++ hw/char/trace-events | 4 + include/hw/char/stm32l4x5_usart.h | 66 +++++ 6 files changed, 471 insertions(+) create mode 100644 hw/char/stm32l4x5_usart.c create mode 100644 include/hw/char/stm32l4x5_usart.h diff --git a/MAINTAINERS b/MAINTAINERS index 8bb32f4a7e..d5a5181242 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1115,6 +1115,7 @@ M: Inès Varhol L: qemu-arm@nongnu.org S: Maintained F: hw/arm/stm32l4x5_soc.c +F: hw/char/stm32l4x5_usart.c F: hw/misc/stm32l4x5_exti.c F: hw/misc/stm32l4x5_syscfg.c F: hw/misc/stm32l4x5_rcc.c diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 6b6cf2fc1d..4fd74ea878 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -41,6 +41,9 @@ config VIRTIO_SERIAL config STM32F2XX_USART bool +config STM32L4X5_USART + bool + config CMSDK_APB_UART bool diff --git a/hw/char/meson.build b/hw/char/meson.build index 006d20f1e2..e5b13b6958 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -31,6 +31,7 @@ system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) system_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) system_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) system_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) +system_ss.add(when: 'CONFIG_STM32L4X5_USART', if_true: files('stm32l4x5_usart.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) system_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) system_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c')) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c new file mode 100644 index 0000000000..62957ec3e5 --- /dev/null +++ b/hw/char/stm32l4x5_usart.c @@ -0,0 +1,396 @@ +/* + * STM32L4X5 USART (Universal Synchronous Asynchronous Receiver Transmitter) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * The STM32L4X5 USART is heavily inspired by the stm32f2xx_usart + * by Alistair Francis. + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" +#include "migration/vmstate.h" +#include "hw/char/stm32l4x5_usart.h" +#include "hw/clock.h" +#include "hw/irq.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/registerfields.h" +#include "trace.h" + + +REG32(CR1, 0x00) + FIELD(CR1, M1, 28, 1) /* Word length (part 2, see M0) */ + FIELD(CR1, EOBIE, 27, 1) /* End of Block interrupt enable */ + FIELD(CR1, RTOIE, 26, 1) /* Receiver timeout interrupt enable */ + FIELD(CR1, DEAT, 21, 5) /* Driver Enable assertion time */ + FIELD(CR1, DEDT, 16, 5) /* Driver Enable de-assertion time */ + FIELD(CR1, OVER8, 15, 1) /* Oversampling mode */ + FIELD(CR1, CMIE, 14, 1) /* Character match interrupt enable */ + FIELD(CR1, MME, 13, 1) /* Mute mode enable */ + FIELD(CR1, M0, 12, 1) /* Word length (part 1, see M1) */ + FIELD(CR1, WAKE, 11, 1) /* Receiver wakeup method */ + FIELD(CR1, PCE, 10, 1) /* Parity control enable */ + FIELD(CR1, PS, 9, 1) /* Parity selection */ + FIELD(CR1, PEIE, 8, 1) /* PE interrupt enable */ + FIELD(CR1, TXEIE, 7, 1) /* TXE interrupt enable */ + FIELD(CR1, TCIE, 6, 1) /* Transmission complete interrupt enable */ + FIELD(CR1, RXNEIE, 5, 1) /* RXNE interrupt enable */ + FIELD(CR1, IDLEIE, 4, 1) /* IDLE interrupt enable */ + FIELD(CR1, TE, 3, 1) /* Transmitter enable */ + FIELD(CR1, RE, 2, 1) /* Receiver enable */ + FIELD(CR1, UESM, 1, 1) /* USART enable in Stop mode */ + FIELD(CR1, UE, 0, 1) /* USART enable */ +REG32(CR2, 0x04) + FIELD(CR2, ADD_1, 28, 4) /* ADD[7:4] */ + FIELD(CR2, ADD_0, 24, 1) /* ADD[3:0] */ + FIELD(CR2, RTOEN, 23, 1) /* Receiver timeout enable */ + FIELD(CR2, ABRMOD, 21, 2) /* Auto baud rate mode */ + FIELD(CR2, ABREN, 20, 1) /* Auto baud rate enable */ + FIELD(CR2, MSBFIRST, 19, 1) /* Most significant bit first */ + FIELD(CR2, DATAINV, 18, 1) /* Binary data inversion */ + FIELD(CR2, TXINV, 17, 1) /* TX pin active level inversion */ + FIELD(CR2, RXINV, 16, 1) /* RX pin active level inversion */ + FIELD(CR2, SWAP, 15, 1) /* Swap RX/TX pins */ + FIELD(CR2, LINEN, 14, 1) /* LIN mode enable */ + FIELD(CR2, STOP, 12, 2) /* STOP bits */ + FIELD(CR2, CLKEN, 11, 1) /* Clock enable */ + FIELD(CR2, CPOL, 10, 1) /* Clock polarity */ + FIELD(CR2, CPHA, 9, 1) /* Clock phase */ + FIELD(CR2, LBCL, 8, 1) /* Last bit clock pulse */ + FIELD(CR2, LBDIE, 6, 1) /* LIN break detection interrupt enable */ + FIELD(CR2, LBDL, 5, 1) /* LIN break detection length */ + FIELD(CR2, ADDM7, 4, 1) /* 7-bit / 4-bit Address Detection */ + +REG32(CR3, 0x08) + /* TCBGTIE only on STM32L496xx/4A6xx devices */ + FIELD(CR3, UCESM, 23, 1) /* USART Clock Enable in Stop Mode */ + FIELD(CR3, WUFIE, 22, 1) /* Wakeup from Stop mode interrupt enable */ + FIELD(CR3, WUS, 20, 2) /* Wakeup from Stop mode interrupt flag selection */ + FIELD(CR3, SCARCNT, 17, 3) /* Smartcard auto-retry count */ + FIELD(CR3, DEP, 15, 1) /* Driver enable polarity selection */ + FIELD(CR3, DEM, 14, 1) /* Driver enable mode */ + FIELD(CR3, DDRE, 13, 1) /* DMA Disable on Reception Error */ + FIELD(CR3, OVRDIS, 12, 1) /* Overrun Disable */ + FIELD(CR3, ONEBIT, 11, 1) /* One sample bit method enable */ + FIELD(CR3, CTSIE, 10, 1) /* CTS interrupt enable */ + FIELD(CR3, CTSE, 9, 1) /* CTS enable */ + FIELD(CR3, RTSE, 8, 1) /* RTS enable */ + FIELD(CR3, DMAT, 7, 1) /* DMA enable transmitter */ + FIELD(CR3, DMAR, 6, 1) /* DMA enable receiver */ + FIELD(CR3, SCEN, 5, 1) /* Smartcard mode enable */ + FIELD(CR3, NACK, 4, 1) /* Smartcard NACK enable */ + FIELD(CR3, HDSEL, 3, 1) /* Half-duplex selection */ + FIELD(CR3, IRLP, 2, 1) /* IrDA low-power */ + FIELD(CR3, IREN, 1, 1) /* IrDA mode enable */ + FIELD(CR3, EIE, 0, 1) /* Error interrupt enable */ +REG32(BRR, 0x0C) + FIELD(BRR, BRR, 0, 16) +REG32(GTPR, 0x10) + FIELD(GTPR, GT, 8, 8) /* Guard time value */ + FIELD(GTPR, PSC, 0, 8) /* Prescaler value */ +REG32(RTOR, 0x14) + FIELD(RTOR, BLEN, 24, 8) /* Block Length */ + FIELD(RTOR, RTO, 0, 24) /* Receiver timeout value */ +REG32(RQR, 0x18) + FIELD(RQR, TXFRQ, 4, 1) /* Transmit data flush request */ + FIELD(RQR, RXFRQ, 3, 1) /* Receive data flush request */ + FIELD(RQR, MMRQ, 2, 1) /* Mute mode request */ + FIELD(RQR, SBKRQ, 1, 1) /* Send break request */ + FIELD(RQR, ABBRRQ, 0, 1) /* Auto baud rate request */ +REG32(ISR, 0x1C) + /* TCBGT only for STM32L475xx/476xx/486xx devices */ + FIELD(ISR, REACK, 22, 1) /* Receive enable acknowledge flag */ + FIELD(ISR, TEACK, 21, 1) /* Transmit enable acknowledge flag */ + FIELD(ISR, WUF, 20, 1) /* Wakeup from Stop mode flag */ + FIELD(ISR, RWU, 19, 1) /* Receiver wakeup from Mute mode */ + FIELD(ISR, SBKF, 18, 1) /* Send break flag */ + FIELD(ISR, CMF, 17, 1) /* Character match flag */ + FIELD(ISR, BUSY, 16, 1) /* Busy flag */ + FIELD(ISR, ABRF, 15, 1) /* Auto Baud rate flag */ + FIELD(ISR, ABRE, 14, 1) /* Auto Baud rate error */ + FIELD(ISR, EOBF, 12, 1) /* End of block flag */ + FIELD(ISR, RTOF, 11, 1) /* Receiver timeout */ + FIELD(ISR, CTS, 10, 1) /* CTS flag */ + FIELD(ISR, CTSIF, 9, 1) /* CTS interrupt flag */ + FIELD(ISR, LBDF, 8, 1) /* LIN break detection flag */ + FIELD(ISR, TXE, 7, 1) /* Transmit data register empty */ + FIELD(ISR, TC, 6, 1) /* Transmission complete */ + FIELD(ISR, RXNE, 5, 1) /* Read data register not empty */ + FIELD(ISR, IDLE, 4, 1) /* Idle line detected */ + FIELD(ISR, ORE, 3, 1) /* Overrun error */ + FIELD(ISR, NF, 2, 1) /* START bit Noise detection flag */ + FIELD(ISR, FE, 1, 1) /* Framing Error */ + FIELD(ISR, PE, 0, 1) /* Parity Error */ +REG32(ICR, 0x20) + FIELD(ICR, WUCF, 20, 1) /* Wakeup from Stop mode clear flag */ + FIELD(ICR, CMCF, 17, 1) /* Character match clear flag */ + FIELD(ICR, EOBCF, 12, 1) /* End of block clear flag */ + FIELD(ICR, RTOCF, 11, 1) /* Receiver timeout clear flag */ + FIELD(ICR, CTSCF, 9, 1) /* CTS clear flag */ + FIELD(ICR, LBDCF, 8, 1) /* LIN break detection clear flag */ + /* TCBGTCF only on STM32L496xx/4A6xx devices */ + FIELD(ICR, TCCF, 6, 1) /* Transmission complete clear flag */ + FIELD(ICR, IDLECF, 4, 1) /* Idle line detected clear flag */ + FIELD(ICR, ORECF, 3, 1) /* Overrun error clear flag */ + FIELD(ICR, NCF, 2, 1) /* Noise detected clear flag */ + FIELD(ICR, FECF, 1, 1) /* Framing error clear flag */ + FIELD(ICR, PECF, 0, 1) /* Parity error clear flag */ +REG32(RDR, 0x24) + FIELD(RDR, RDR, 0, 9) +REG32(TDR, 0x28) + FIELD(TDR, TDR, 0, 9) + +static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) +{ + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); + + s->cr1 = 0x00000000; + s->cr2 = 0x00000000; + s->cr3 = 0x00000000; + s->brr = 0x00000000; + s->gtpr = 0x00000000; + s->rtor = 0x00000000; + s->isr = 0x020000C0; + s->rdr = 0x00000000; + s->tdr = 0x00000000; +} + +static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Stm32l4x5UsartBaseState *s = opaque; + uint64_t retvalue = 0; + + switch (addr) { + case A_CR1: + retvalue = s->cr1; + break; + case A_CR2: + retvalue = s->cr2; + break; + case A_CR3: + retvalue = s->cr3; + break; + case A_BRR: + retvalue = FIELD_EX32(s->brr, BRR, BRR); + break; + case A_GTPR: + retvalue = s->gtpr; + break; + case A_RTOR: + retvalue = s->rtor; + break; + case A_RQR: + /* RQR is a write only register */ + retvalue = 0x00000000; + break; + case A_ISR: + retvalue = s->isr; + break; + case A_ICR: + /* ICR is a clear register */ + retvalue = 0x00000000; + break; + case A_RDR: + retvalue = FIELD_EX32(s->rdr, RDR, RDR); + /* Reset RXNE flag */ + s->isr &= ~R_ISR_RXNE_MASK; + break; + case A_TDR: + retvalue = FIELD_EX32(s->tdr, TDR, TDR); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + break; + } + + trace_stm32l4x5_usart_read(addr, retvalue); + + return retvalue; +} + +static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Stm32l4x5UsartBaseState *s = opaque; + const uint32_t value = val64; + + trace_stm32l4x5_usart_write(addr, value); + + switch (addr) { + case A_CR1: + s->cr1 = value; + return; + case A_CR2: + s->cr2 = value; + return; + case A_CR3: + s->cr3 = value; + return; + case A_BRR: + s->brr = value; + return; + case A_GTPR: + s->gtpr = value; + return; + case A_RTOR: + s->rtor = value; + return; + case A_RQR: + return; + case A_ISR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: ISR is read only !\n", __func__); + return; + case A_ICR: + /* Clear the status flags */ + s->isr &= ~value; + return; + case A_RDR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: RDR is read only !\n", __func__); + return; + case A_TDR: + s->tdr = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32l4x5_usart_base_ops = { + .read = stm32l4x5_usart_base_read, + .write = stm32l4x5_usart_base_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .max_access_size = 4, + .min_access_size = 4, + .unaligned = false + }, + .impl = { + .max_access_size = 4, + .min_access_size = 4, + .unaligned = false + }, +}; + +static Property stm32l4x5_usart_base_properties[] = { + DEFINE_PROP_CHR("chardev", Stm32l4x5UsartBaseState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32l4x5_usart_base_init(Object *obj) +{ + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_usart_base_ops, s, + TYPE_STM32L4X5_USART_BASE, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); +} + +static const VMStateDescription vmstate_stm32l4x5_usart_base = { + .name = TYPE_STM32L4X5_USART_BASE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr1, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(cr2, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(cr3, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(brr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(gtpr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(rtor, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(isr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(rdr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(tdr, Stm32l4x5UsartBaseState), + VMSTATE_CLOCK(clk, Stm32l4x5UsartBaseState), + VMSTATE_END_OF_LIST() + } +}; + + +static void stm32l4x5_usart_base_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(dev); + if (!clock_has_source(s->clk)) { + error_setg(errp, "USART clock must be wired up by SoC code"); + return; + } +} + +static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = stm32l4x5_usart_base_reset_hold; + device_class_set_props(dc, stm32l4x5_usart_base_properties); + dc->realize = stm32l4x5_usart_base_realize; + dc->vmsd = &vmstate_stm32l4x5_usart_base; +} + +static void stm32l4x5_usart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_USART; +} + +static void stm32l4x5_uart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_UART; +} + +static void stm32l4x5_lpuart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_LPUART; +} + +static const TypeInfo stm32l4x5_usart_types[] = { + { + .name = TYPE_STM32L4X5_USART_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5UsartBaseState), + .instance_init = stm32l4x5_usart_base_init, + .class_init = stm32l4x5_usart_base_class_init, + .abstract = true, + }, { + .name = TYPE_STM32L4X5_USART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_usart_class_init, + }, { + .name = TYPE_STM32L4X5_UART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_uart_class_init, + }, { + .name = TYPE_STM32L4X5_LPUART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_lpuart_class_init, + } +}; + +DEFINE_TYPES(stm32l4x5_usart_types) diff --git a/hw/char/trace-events b/hw/char/trace-events index 7a398c82a5..689bed9e02 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -106,6 +106,10 @@ cadence_uart_baudrate(unsigned baudrate) "baudrate %u" sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64 sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64 +# stm32l4x5_usart.c +stm32l4x5_usart_read(uint64_t addr, uint32_t data) "USART: Read <0x%" PRIx64 "> -> 0x%" PRIx32 "" +stm32l4x5_usart_write(uint64_t addr, uint32_t data) "USART: Write <0x%" PRIx64 "> <- 0x%" PRIx32 "" + # xen_console.c xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int port, unsigned int limit) "idx %u ring_ref %u port %u limit %u" xen_console_disconnect(unsigned int idx) "idx %u" diff --git a/include/hw/char/stm32l4x5_usart.h b/include/hw/char/stm32l4x5_usart.h new file mode 100644 index 0000000000..8d38a85a6e --- /dev/null +++ b/include/hw/char/stm32l4x5_usart.h @@ -0,0 +1,66 @@ +/* + * STM32L4X5 USART (Universal Synchronous Asynchronous Receiver Transmitter) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * The STM32L4X5 USART is heavily inspired by the stm32f2xx_usart + * by Alistair Francis. + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + */ + +#ifndef HW_STM32L4X5_USART_H +#define HW_STM32L4X5_USART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_USART_BASE "stm32l4x5-usart-base" +#define TYPE_STM32L4X5_USART "stm32l4x5-usart" +#define TYPE_STM32L4X5_UART "stm32l4x5-uart" +#define TYPE_STM32L4X5_LPUART "stm32l4x5-lpuart" +OBJECT_DECLARE_TYPE(Stm32l4x5UsartBaseState, Stm32l4x5UsartBaseClass, + STM32L4X5_USART_BASE) + +typedef enum { + STM32L4x5_USART, + STM32L4x5_UART, + STM32L4x5_LPUART, +} Stm32l4x5UsartType; + +struct Stm32l4x5UsartBaseState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t cr1; + uint32_t cr2; + uint32_t cr3; + uint32_t brr; + uint32_t gtpr; + uint32_t rtor; + /* rqr is write-only */ + uint32_t isr; + /* icr is a clear register */ + uint32_t rdr; + uint32_t tdr; + + Clock *clk; + CharBackend chr; + qemu_irq irq; +}; + +struct Stm32l4x5UsartBaseClass { + SysBusDeviceClass parent_class; + + Stm32l4x5UsartType type; +}; + +#endif /* HW_STM32L4X5_USART_H */ From 87b77e6e01cad9a6cbdc3532a5bb3c674e34d089 Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:43:59 +0100 Subject: [PATCH 34/37] hw/char/stm32l4x5_usart: Enable serial read and write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the ability to read and write characters to the usart using the serial port. The character transmission is based on the cmsdk-apb-uart implementation. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-3-arnaud.minier@telecom-paris.fr [PMM: fixed a few checkpatch nits] Signed-off-by: Peter Maydell --- hw/char/stm32l4x5_usart.c | 143 ++++++++++++++++++++++++++++++ hw/char/trace-events | 7 ++ include/hw/char/stm32l4x5_usart.h | 1 + 3 files changed, 151 insertions(+) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 62957ec3e5..755fc0bb5a 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -154,6 +154,123 @@ REG32(RDR, 0x24) REG32(TDR, 0x28) FIELD(TDR, TDR, 0, 9) +static void stm32l4x5_update_irq(Stm32l4x5UsartBaseState *s) +{ + if (((s->isr & R_ISR_WUF_MASK) && (s->cr3 & R_CR3_WUFIE_MASK)) || + ((s->isr & R_ISR_CMF_MASK) && (s->cr1 & R_CR1_CMIE_MASK)) || + ((s->isr & R_ISR_ABRF_MASK) && (s->cr1 & R_CR1_RXNEIE_MASK)) || + ((s->isr & R_ISR_EOBF_MASK) && (s->cr1 & R_CR1_EOBIE_MASK)) || + ((s->isr & R_ISR_RTOF_MASK) && (s->cr1 & R_CR1_RTOIE_MASK)) || + ((s->isr & R_ISR_CTSIF_MASK) && (s->cr3 & R_CR3_CTSIE_MASK)) || + ((s->isr & R_ISR_LBDF_MASK) && (s->cr2 & R_CR2_LBDIE_MASK)) || + ((s->isr & R_ISR_TXE_MASK) && (s->cr1 & R_CR1_TXEIE_MASK)) || + ((s->isr & R_ISR_TC_MASK) && (s->cr1 & R_CR1_TCIE_MASK)) || + ((s->isr & R_ISR_RXNE_MASK) && (s->cr1 & R_CR1_RXNEIE_MASK)) || + ((s->isr & R_ISR_IDLE_MASK) && (s->cr1 & R_CR1_IDLEIE_MASK)) || + ((s->isr & R_ISR_ORE_MASK) && + ((s->cr1 & R_CR1_RXNEIE_MASK) || (s->cr3 & R_CR3_EIE_MASK))) || + /* TODO: Handle NF ? */ + ((s->isr & R_ISR_FE_MASK) && (s->cr3 & R_CR3_EIE_MASK)) || + ((s->isr & R_ISR_PE_MASK) && (s->cr1 & R_CR1_PEIE_MASK))) { + qemu_irq_raise(s->irq); + trace_stm32l4x5_usart_irq_raised(s->isr); + } else { + qemu_irq_lower(s->irq); + trace_stm32l4x5_usart_irq_lowered(); + } +} + +static int stm32l4x5_usart_base_can_receive(void *opaque) +{ + Stm32l4x5UsartBaseState *s = opaque; + + if (!(s->isr & R_ISR_RXNE_MASK)) { + return 1; + } + + return 0; +} + +static void stm32l4x5_usart_base_receive(void *opaque, const uint8_t *buf, + int size) +{ + Stm32l4x5UsartBaseState *s = opaque; + + if (!((s->cr1 & R_CR1_UE_MASK) && (s->cr1 & R_CR1_RE_MASK))) { + trace_stm32l4x5_usart_receiver_not_enabled( + FIELD_EX32(s->cr1, CR1, UE), FIELD_EX32(s->cr1, CR1, RE)); + return; + } + + /* Check if overrun detection is enabled and if there is an overrun */ + if (!(s->cr3 & R_CR3_OVRDIS_MASK) && (s->isr & R_ISR_RXNE_MASK)) { + /* + * A character has been received while + * the previous has not been read = Overrun. + */ + s->isr |= R_ISR_ORE_MASK; + trace_stm32l4x5_usart_overrun_detected(s->rdr, *buf); + } else { + /* No overrun */ + s->rdr = *buf; + s->isr |= R_ISR_RXNE_MASK; + trace_stm32l4x5_usart_rx(s->rdr); + } + + stm32l4x5_update_irq(s); +} + +/* + * Try to send tx data, and arrange to be called back later if + * we can't (ie the char backend is busy/blocking). + */ +static gboolean usart_transmit(void *do_not_use, GIOCondition cond, + void *opaque) +{ + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(opaque); + int ret; + /* TODO: Handle 9 bits transmission */ + uint8_t ch = s->tdr; + + s->watch_tag = 0; + + if (!(s->cr1 & R_CR1_TE_MASK) || (s->isr & R_ISR_TXE_MASK)) { + return G_SOURCE_REMOVE; + } + + ret = qemu_chr_fe_write(&s->chr, &ch, 1); + if (ret <= 0) { + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + usart_transmit, s); + if (!s->watch_tag) { + /* + * Most common reason to be here is "no chardev backend": + * just insta-drain the buffer, so the serial output + * goes into a void, rather than blocking the guest. + */ + goto buffer_drained; + } + /* Transmit pending */ + trace_stm32l4x5_usart_tx_pending(); + return G_SOURCE_REMOVE; + } + +buffer_drained: + /* Character successfully sent */ + trace_stm32l4x5_usart_tx(ch); + s->isr |= R_ISR_TC_MASK | R_ISR_TXE_MASK; + stm32l4x5_update_irq(s); + return G_SOURCE_REMOVE; +} + +static void usart_cancel_transmit(Stm32l4x5UsartBaseState *s) +{ + if (s->watch_tag) { + g_source_remove(s->watch_tag); + s->watch_tag = 0; + } +} + static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) { Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); @@ -167,6 +284,22 @@ static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) s->isr = 0x020000C0; s->rdr = 0x00000000; s->tdr = 0x00000000; + + usart_cancel_transmit(s); + stm32l4x5_update_irq(s); +} + +static void usart_update_rqr(Stm32l4x5UsartBaseState *s, uint32_t value) +{ + /* TXFRQ */ + /* Reset RXNE flag */ + if (value & R_RQR_RXFRQ_MASK) { + s->isr &= ~R_ISR_RXNE_MASK; + } + /* MMRQ */ + /* SBKRQ */ + /* ABRRQ */ + stm32l4x5_update_irq(s); } static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr, @@ -209,6 +342,7 @@ static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr, retvalue = FIELD_EX32(s->rdr, RDR, RDR); /* Reset RXNE flag */ s->isr &= ~R_ISR_RXNE_MASK; + stm32l4x5_update_irq(s); break; case A_TDR: retvalue = FIELD_EX32(s->tdr, TDR, TDR); @@ -235,6 +369,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, switch (addr) { case A_CR1: s->cr1 = value; + stm32l4x5_update_irq(s); return; case A_CR2: s->cr2 = value; @@ -252,6 +387,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, s->rtor = value; return; case A_RQR: + usart_update_rqr(s, value); return; case A_ISR: qemu_log_mask(LOG_GUEST_ERROR, @@ -260,6 +396,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, case A_ICR: /* Clear the status flags */ s->isr &= ~value; + stm32l4x5_update_irq(s); return; case A_RDR: qemu_log_mask(LOG_GUEST_ERROR, @@ -267,6 +404,8 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, return; case A_TDR: s->tdr = value; + s->isr &= ~R_ISR_TXE_MASK; + usart_transmit(NULL, G_IO_OUT, s); return; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -336,6 +475,10 @@ static void stm32l4x5_usart_base_realize(DeviceState *dev, Error **errp) error_setg(errp, "USART clock must be wired up by SoC code"); return; } + + qemu_chr_fe_set_handlers(&s->chr, stm32l4x5_usart_base_can_receive, + stm32l4x5_usart_base_receive, NULL, NULL, + s, NULL, true); } static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/trace-events b/hw/char/trace-events index 689bed9e02..f22f0ee2bc 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -109,6 +109,13 @@ sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size % # stm32l4x5_usart.c stm32l4x5_usart_read(uint64_t addr, uint32_t data) "USART: Read <0x%" PRIx64 "> -> 0x%" PRIx32 "" stm32l4x5_usart_write(uint64_t addr, uint32_t data) "USART: Write <0x%" PRIx64 "> <- 0x%" PRIx32 "" +stm32l4x5_usart_rx(uint8_t c) "USART: got character 0x%x from backend" +stm32l4x5_usart_tx(uint8_t c) "USART: character 0x%x sent to backend" +stm32l4x5_usart_tx_pending(void) "USART: character send to backend pending" +stm32l4x5_usart_irq_raised(uint32_t reg) "USART: IRQ raised: 0x%08"PRIx32 +stm32l4x5_usart_irq_lowered(void) "USART: IRQ lowered" +stm32l4x5_usart_overrun_detected(uint8_t current, uint8_t received) "USART: Overrun detected, RDR='0x%x', received 0x%x" +stm32l4x5_usart_receiver_not_enabled(uint8_t ue_bit, uint8_t re_bit) "USART: Receiver not enabled, UE=0x%x, RE=0x%x" # xen_console.c xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int port, unsigned int limit) "idx %u ring_ref %u port %u limit %u" diff --git a/include/hw/char/stm32l4x5_usart.h b/include/hw/char/stm32l4x5_usart.h index 8d38a85a6e..dd3866682a 100644 --- a/include/hw/char/stm32l4x5_usart.h +++ b/include/hw/char/stm32l4x5_usart.h @@ -55,6 +55,7 @@ struct Stm32l4x5UsartBaseState { Clock *clk; CharBackend chr; qemu_irq irq; + guint watch_tag; }; struct Stm32l4x5UsartBaseClass { From c4c12ee48709aeb896ebb60163e8eb26cdaa3d65 Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:44:00 +0100 Subject: [PATCH 35/37] hw/char/stm32l4x5_usart: Add options for serial parameters setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function to change the settings of the serial connection. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-4-arnaud.minier@telecom-paris.fr Signed-off-by: Peter Maydell --- hw/char/stm32l4x5_usart.c | 98 +++++++++++++++++++++++++++++++++++++++ hw/char/trace-events | 1 + 2 files changed, 99 insertions(+) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 755fc0bb5a..2627aab832 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -271,6 +271,92 @@ static void usart_cancel_transmit(Stm32l4x5UsartBaseState *s) } } +static void stm32l4x5_update_params(Stm32l4x5UsartBaseState *s) +{ + int speed, parity, data_bits, stop_bits; + uint32_t value, usart_div; + QEMUSerialSetParams ssp; + + /* Select the parity type */ + if (s->cr1 & R_CR1_PCE_MASK) { + if (s->cr1 & R_CR1_PS_MASK) { + parity = 'O'; + } else { + parity = 'E'; + } + } else { + parity = 'N'; + } + + /* Select the number of stop bits */ + switch (FIELD_EX32(s->cr2, CR2, STOP)) { + case 0: + stop_bits = 1; + break; + case 2: + stop_bits = 2; + break; + default: + qemu_log_mask(LOG_UNIMP, + "UNIMPLEMENTED: fractionnal stop bits; CR2[13:12] = %u", + FIELD_EX32(s->cr2, CR2, STOP)); + return; + } + + /* Select the length of the word */ + switch ((FIELD_EX32(s->cr1, CR1, M1) << 1) | FIELD_EX32(s->cr1, CR1, M0)) { + case 0: + data_bits = 8; + break; + case 1: + data_bits = 9; + break; + case 2: + data_bits = 7; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "UNDEFINED: invalid word length, CR1.M = 0b11"); + return; + } + + /* Select the baud rate */ + value = FIELD_EX32(s->brr, BRR, BRR); + if (value < 16) { + qemu_log_mask(LOG_GUEST_ERROR, + "UNDEFINED: BRR less than 16: %u", value); + return; + } + + if (FIELD_EX32(s->cr1, CR1, OVER8) == 0) { + /* + * Oversampling by 16 + * BRR = USARTDIV + */ + usart_div = value; + } else { + /* + * Oversampling by 8 + * - BRR[2:0] = USARTDIV[3:0] shifted 1 bit to the right. + * - BRR[3] must be kept cleared. + * - BRR[15:4] = USARTDIV[15:4] + * - The frequency is multiplied by 2 + */ + usart_div = ((value & 0xFFF0) | ((value & 0x0007) << 1)) / 2; + } + + speed = clock_get_hz(s->clk) / usart_div; + + ssp.speed = speed; + ssp.parity = parity; + ssp.data_bits = data_bits; + ssp.stop_bits = stop_bits; + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + + trace_stm32l4x5_usart_update_params(speed, parity, data_bits, stop_bits); +} + static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) { Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); @@ -369,16 +455,19 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, switch (addr) { case A_CR1: s->cr1 = value; + stm32l4x5_update_params(s); stm32l4x5_update_irq(s); return; case A_CR2: s->cr2 = value; + stm32l4x5_update_params(s); return; case A_CR3: s->cr3 = value; return; case A_BRR: s->brr = value; + stm32l4x5_update_params(s); return; case A_GTPR: s->gtpr = value; @@ -447,10 +536,19 @@ static void stm32l4x5_usart_base_init(Object *obj) s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); } +static int stm32l4x5_usart_base_post_load(void *opaque, int version_id) +{ + Stm32l4x5UsartBaseState *s = (Stm32l4x5UsartBaseState *)opaque; + + stm32l4x5_update_params(s); + return 0; +} + static const VMStateDescription vmstate_stm32l4x5_usart_base = { .name = TYPE_STM32L4X5_USART_BASE, .version_id = 1, .minimum_version_id = 1, + .post_load = stm32l4x5_usart_base_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(cr1, Stm32l4x5UsartBaseState), VMSTATE_UINT32(cr2, Stm32l4x5UsartBaseState), diff --git a/hw/char/trace-events b/hw/char/trace-events index f22f0ee2bc..8875758076 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -116,6 +116,7 @@ stm32l4x5_usart_irq_raised(uint32_t reg) "USART: IRQ raised: 0x%08"PRIx32 stm32l4x5_usart_irq_lowered(void) "USART: IRQ lowered" stm32l4x5_usart_overrun_detected(uint8_t current, uint8_t received) "USART: Overrun detected, RDR='0x%x', received 0x%x" stm32l4x5_usart_receiver_not_enabled(uint8_t ue_bit, uint8_t re_bit) "USART: Receiver not enabled, UE=0x%x, RE=0x%x" +stm32l4x5_usart_update_params(int speed, uint8_t parity, int data, int stop) "USART: speed: %d, parity: %c, data bits: %d, stop bits: %d" # xen_console.c xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int port, unsigned int limit) "idx %u ring_ref %u port %u limit %u" From 92741432ed6f98ab01bdace5baaf2894c7855563 Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:44:01 +0100 Subject: [PATCH 36/37] hw/arm: Add the USART to the stm32l4x5 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the USART to the SoC and connect it to the other implemented devices. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-5-arnaud.minier@telecom-paris.fr [PMM: fixed a few checkpatch nits] Signed-off-by: Peter Maydell --- docs/system/arm/b-l475e-iot01a.rst | 2 +- hw/arm/Kconfig | 1 + hw/arm/stm32l4x5_soc.c | 83 +++++++++++++++++++++++++++--- include/hw/arm/stm32l4x5_soc.h | 7 +++ 4 files changed, 86 insertions(+), 7 deletions(-) diff --git a/docs/system/arm/b-l475e-iot01a.rst b/docs/system/arm/b-l475e-iot01a.rst index 0afef8e4f4..a76c9976c5 100644 --- a/docs/system/arm/b-l475e-iot01a.rst +++ b/docs/system/arm/b-l475e-iot01a.rst @@ -19,13 +19,13 @@ Currently B-L475E-IOT01A machine's only supports the following devices: - STM32L4x5 SYSCFG (System configuration controller) - STM32L4x5 RCC (Reset and clock control) - STM32L4x5 GPIOs (General-purpose I/Os) +- STM32L4x5 USARTs, UARTs and LPUART (Serial ports) Missing devices """"""""""""""" The B-L475E-IOT01A does *not* support the following devices: -- Serial ports (UART) - Analog to Digital Converter (ADC) - SPI controller - Timer controller (TIMER) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 893a7bff66..098d043375 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -477,6 +477,7 @@ config STM32L4X5_SOC select STM32L4X5_SYSCFG select STM32L4X5_RCC select STM32L4X5_GPIO + select STM32L4X5_USART config XLNX_ZYNQMP_ARM bool diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index 40e294f838..39924822f3 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -28,6 +28,7 @@ #include "sysemu/sysemu.h" #include "hw/or-irq.h" #include "hw/arm/stm32l4x5_soc.h" +#include "hw/char/stm32l4x5_usart.h" #include "hw/gpio/stm32l4x5_gpio.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" @@ -116,6 +117,22 @@ static const struct { { 0x48001C00, 0x0000000F, 0x00000000, 0x00000000 }, }; +static const hwaddr usart_addr[] = { + 0x40013800, /* "USART1", 0x400 */ + 0x40004400, /* "USART2", 0x400 */ + 0x40004800, /* "USART3", 0x400 */ +}; +static const hwaddr uart_addr[] = { + 0x40004C00, /* "UART4" , 0x400 */ + 0x40005000 /* "UART5" , 0x400 */ +}; + +#define LPUART_BASE_ADDRESS 0x40008000 + +static const int usart_irq[] = { 37, 38, 39 }; +static const int uart_irq[] = { 52, 53 }; +#define LPUART_IRQ 70 + static void stm32l4x5_soc_initfn(Object *obj) { Stm32l4x5SocState *s = STM32L4X5_SOC(obj); @@ -132,6 +149,18 @@ static void stm32l4x5_soc_initfn(Object *obj) g_autofree char *name = g_strdup_printf("gpio%c", 'a' + i); object_initialize_child(obj, name, &s->gpio[i], TYPE_STM32L4X5_GPIO); } + + for (int i = 0; i < STM_NUM_USARTS; i++) { + object_initialize_child(obj, "usart[*]", &s->usart[i], + TYPE_STM32L4X5_USART); + } + + for (int i = 0; i < STM_NUM_UARTS; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], + TYPE_STM32L4X5_UART); + } + object_initialize_child(obj, "lpuart1", &s->lpuart, + TYPE_STM32L4X5_LPUART); } static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) @@ -279,6 +308,54 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) sysbus_mmio_map(busdev, 0, RCC_BASE_ADDRESS); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, RCC_IRQ)); + /* USART devices */ + for (int i = 0; i < STM_NUM_USARTS; i++) { + g_autofree char *name = g_strdup_printf("usart%d-out", i + 1); + dev = DEVICE(&(s->usart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, usart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i])); + } + + /* + * TODO: Connect the USARTs, UARTs and LPUART to the EXTI once the EXTI + * can handle other gpio-in than the gpios. (e.g. Direct Lines for the + * usarts) + */ + + /* UART devices */ + for (int i = 0; i < STM_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d-out", STM_NUM_USARTS + i + 1); + dev = DEVICE(&(s->uart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + i)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, uart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, uart_irq[i])); + } + + /* LPUART device*/ + dev = DEVICE(&(s->lpuart)); + qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + STM_NUM_UARTS)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), "lpuart1-out")); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, LPUART_BASE_ADDRESS); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, LPUART_IRQ)); + /* APB1 BUS */ create_unimplemented_device("TIM2", 0x40000000, 0x400); create_unimplemented_device("TIM3", 0x40000400, 0x400); @@ -294,10 +371,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("SPI2", 0x40003800, 0x400); create_unimplemented_device("SPI3", 0x40003C00, 0x400); /* RESERVED: 0x40004000, 0x400 */ - create_unimplemented_device("USART2", 0x40004400, 0x400); - create_unimplemented_device("USART3", 0x40004800, 0x400); - create_unimplemented_device("UART4", 0x40004C00, 0x400); - create_unimplemented_device("UART5", 0x40005000, 0x400); create_unimplemented_device("I2C1", 0x40005400, 0x400); create_unimplemented_device("I2C2", 0x40005800, 0x400); create_unimplemented_device("I2C3", 0x40005C00, 0x400); @@ -308,7 +381,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("DAC1", 0x40007400, 0x400); create_unimplemented_device("OPAMP", 0x40007800, 0x400); create_unimplemented_device("LPTIM1", 0x40007C00, 0x400); - create_unimplemented_device("LPUART1", 0x40008000, 0x400); /* RESERVED: 0x40008400, 0x400 */ create_unimplemented_device("SWPMI1", 0x40008800, 0x400); /* RESERVED: 0x40008C00, 0x800 */ @@ -325,7 +397,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("TIM1", 0x40012C00, 0x400); create_unimplemented_device("SPI1", 0x40013000, 0x400); create_unimplemented_device("TIM8", 0x40013400, 0x400); - create_unimplemented_device("USART1", 0x40013800, 0x400); /* RESERVED: 0x40013C00, 0x400 */ create_unimplemented_device("TIM15", 0x40014000, 0x400); create_unimplemented_device("TIM16", 0x40014400, 0x400); diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h index ee5f362405..c243fb0e7f 100644 --- a/include/hw/arm/stm32l4x5_soc.h +++ b/include/hw/arm/stm32l4x5_soc.h @@ -31,6 +31,7 @@ #include "hw/misc/stm32l4x5_exti.h" #include "hw/misc/stm32l4x5_rcc.h" #include "hw/gpio/stm32l4x5_gpio.h" +#include "hw/char/stm32l4x5_usart.h" #include "qom/object.h" #define TYPE_STM32L4X5_SOC "stm32l4x5-soc" @@ -41,6 +42,9 @@ OBJECT_DECLARE_TYPE(Stm32l4x5SocState, Stm32l4x5SocClass, STM32L4X5_SOC) #define NUM_EXTI_OR_GATES 4 +#define STM_NUM_USARTS 3 +#define STM_NUM_UARTS 2 + struct Stm32l4x5SocState { SysBusDevice parent_obj; @@ -51,6 +55,9 @@ struct Stm32l4x5SocState { Stm32l4x5SyscfgState syscfg; Stm32l4x5RccState rcc; Stm32l4x5GpioState gpio[NUM_GPIOS]; + Stm32l4x5UsartBaseState usart[STM_NUM_USARTS]; + Stm32l4x5UsartBaseState uart[STM_NUM_UARTS]; + Stm32l4x5UsartBaseState lpuart; MemoryRegion sram1; MemoryRegion sram2; From 214652da123e3821657a64691ee556281e9f6238 Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:44:02 +0100 Subject: [PATCH 37/37] tests/qtest: Add tests for the STM32L4x5 USART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: - read/write from/to the usart registers - send/receive a character/string over the serial port Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-6-arnaud.minier@telecom-paris.fr [PMM: fix checkpatch nits, remove commented out code] Signed-off-by: Peter Maydell --- tests/qtest/meson.build | 4 +- tests/qtest/stm32l4x5_usart-test.c | 315 +++++++++++++++++++++++++++++ 2 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/stm32l4x5_usart-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 36c5c13a7b..b128fa5a4b 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -7,6 +7,7 @@ slow_qtests = { 'npcm7xx_pwm-test': 300, 'npcm7xx_watchdog_timer-test': 120, 'qom-test' : 900, + 'stm32l4x5_usart-test' : 600, 'test-hmp' : 240, 'pxe-test': 610, 'prom-env-test': 360, @@ -205,7 +206,8 @@ qtests_stm32l4x5 = \ ['stm32l4x5_exti-test', 'stm32l4x5_syscfg-test', 'stm32l4x5_rcc-test', - 'stm32l4x5_gpio-test'] + 'stm32l4x5_gpio-test', + 'stm32l4x5_usart-test'] qtests_arm = \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ diff --git a/tests/qtest/stm32l4x5_usart-test.c b/tests/qtest/stm32l4x5_usart-test.c new file mode 100644 index 0000000000..8902518233 --- /dev/null +++ b/tests/qtest/stm32l4x5_usart-test.c @@ -0,0 +1,315 @@ +/* + * QTest testcase for STML4X5_USART + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "hw/misc/stm32l4x5_rcc_internals.h" +#include "hw/registerfields.h" + +#define RCC_BASE_ADDR 0x40021000 +/* Use USART 1 ADDR, assume the others work the same */ +#define USART1_BASE_ADDR 0x40013800 + +/* See stm32l4x5_usart for definitions */ +REG32(CR1, 0x00) + FIELD(CR1, M1, 28, 1) + FIELD(CR1, OVER8, 15, 1) + FIELD(CR1, M0, 12, 1) + FIELD(CR1, PCE, 10, 1) + FIELD(CR1, TXEIE, 7, 1) + FIELD(CR1, RXNEIE, 5, 1) + FIELD(CR1, TE, 3, 1) + FIELD(CR1, RE, 2, 1) + FIELD(CR1, UE, 0, 1) +REG32(CR2, 0x04) +REG32(CR3, 0x08) + FIELD(CR3, OVRDIS, 12, 1) +REG32(BRR, 0x0C) +REG32(GTPR, 0x10) +REG32(RTOR, 0x14) +REG32(RQR, 0x18) +REG32(ISR, 0x1C) + FIELD(ISR, TXE, 7, 1) + FIELD(ISR, RXNE, 5, 1) + FIELD(ISR, ORE, 3, 1) +REG32(ICR, 0x20) +REG32(RDR, 0x24) +REG32(TDR, 0x28) + +#define NVIC_ISPR1 0XE000E204 +#define NVIC_ICPR1 0xE000E284 +#define USART1_IRQ 37 + +static bool check_nvic_pending(QTestState *qts, unsigned int n) +{ + /* No USART interrupts are less than 32 */ + assert(n > 32); + n -= 32; + return qtest_readl(qts, NVIC_ISPR1) & (1 << n); +} + +static bool clear_nvic_pending(QTestState *qts, unsigned int n) +{ + /* No USART interrupts are less than 32 */ + assert(n > 32); + n -= 32; + qtest_writel(qts, NVIC_ICPR1, (1 << n)); + return true; +} + +/* + * Wait indefinitely for the flag to be updated. + * If this is run on a slow CI runner, + * the meson harness will timeout after 10 minutes for us. + */ +static bool usart_wait_for_flag(QTestState *qts, uint32_t event_addr, + uint32_t flag) +{ + while (true) { + if ((qtest_readl(qts, event_addr) & flag)) { + return true; + } + g_usleep(1000); + } + + return false; +} + +static void usart_receive_string(QTestState *qts, int sock_fd, const char *in, + char *out) +{ + int i, in_len = strlen(in); + + g_assert_true(send(sock_fd, in, in_len, 0) == in_len); + for (i = 0; i < in_len; i++) { + g_assert_true(usart_wait_for_flag(qts, + USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK)); + out[i] = qtest_readl(qts, USART1_BASE_ADDR + A_RDR); + } + out[i] = '\0'; +} + +static void usart_send_string(QTestState *qts, const char *in) +{ + int i, in_len = strlen(in); + + for (i = 0; i < in_len; i++) { + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, in[i]); + g_assert_true(usart_wait_for_flag(qts, + USART1_BASE_ADDR + A_ISR, R_ISR_TXE_MASK)); + } +} + +/* Init the RCC clocks to run at 80 MHz */ +static void init_clocks(QTestState *qts) +{ + uint32_t value; + + /* MSIRANGE can be set only when MSI is OFF or READY */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CR), R_CR_MSION_MASK); + + /* Clocking from MSI, in case MSI was not the default source */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); + + /* + * Update PLL and set MSI as the source clock. + * PLLM = 1 --> 000 + * PLLN = 40 --> 40 + * PPLLR = 2 --> 00 + * PLLDIV = unused, PLLP = unused (SAI3), PLLQ = unused (48M1) + * SRC = MSI --> 01 + */ + qtest_writel(qts, (RCC_BASE_ADDR + A_PLLCFGR), R_PLLCFGR_PLLREN_MASK | + (40 << R_PLLCFGR_PLLN_SHIFT) | + (0b01 << R_PLLCFGR_PLLSRC_SHIFT)); + + /* PLL activation */ + + value = qtest_readl(qts, (RCC_BASE_ADDR + A_CR)); + qtest_writel(qts, (RCC_BASE_ADDR + A_CR), value | R_CR_PLLON_MASK); + + /* RCC_CFGR is OK by defaut */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); + + /* CCIPR : no periph clock by default */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); + + /* Switches on the PLL clock source */ + value = qtest_readl(qts, (RCC_BASE_ADDR + A_CFGR)); + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), (value & ~R_CFGR_SW_MASK) | + (0b11 << R_CFGR_SW_SHIFT)); + + /* Enable SYSCFG clock enabled */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), R_APB2ENR_SYSCFGEN_MASK); + + /* Enable the IO port B clock (See p.252) */ + qtest_writel(qts, (RCC_BASE_ADDR + A_AHB2ENR), R_AHB2ENR_GPIOBEN_MASK); + + /* Enable the clock for USART1 (cf p.259) */ + /* We rewrite SYSCFGEN to not disable it */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), + R_APB2ENR_SYSCFGEN_MASK | R_APB2ENR_USART1EN_MASK); + + /* TODO: Enable usart via gpio */ + + /* Set PCLK as the clock for USART1(cf p.272) i.e. reset both bits */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); + + /* Reset USART1 (see p.249) */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 1 << 14); + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 0); +} + +static void init_uart(QTestState *qts) +{ + uint32_t cr1; + + init_clocks(qts); + + /* + * For 115200 bauds, see p.1349. + * The clock has a frequency of 80Mhz, + * for 115200, we have to put a divider of 695 = 0x2B7. + */ + qtest_writel(qts, (USART1_BASE_ADDR + A_BRR), 0x2B7); + + /* + * Set the oversampling by 16, + * disable the parity control and + * set the word length to 8. (cf p.1377) + */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 &= ~(R_CR1_M1_MASK | R_CR1_M0_MASK | R_CR1_OVER8_MASK | R_CR1_PCE_MASK); + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), cr1); + + /* Enable the transmitter, the receiver and the USART. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK); +} + +static void test_write_read(void) +{ + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + + /* Test that we can write and retrieve a value from the device */ + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 0xFFFFFFFF); + const uint32_t tdr = qtest_readl(qts, USART1_BASE_ADDR + A_TDR); + g_assert_cmpuint(tdr, ==, 0x000001FF); +} + +static void test_receive_char(void) +{ + int sock_fd; + uint32_t cr1; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + /* Try without initializing IRQ */ + g_assert_true(send(sock_fd, "a", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'a'); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + + /* Now with the IRQ */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 |= R_CR1_RXNEIE_MASK; + qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); + g_assert_true(send(sock_fd, "b", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'b'); + g_assert_true(check_nvic_pending(qts, USART1_IRQ)); + clear_nvic_pending(qts, USART1_IRQ); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_send_char(void) +{ + int sock_fd; + char s[1]; + uint32_t cr1; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + /* Try without initializing IRQ */ + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'c'); + g_assert_true(recv(sock_fd, s, 1, 0) == 1); + g_assert_cmphex(s[0], ==, 'c'); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + + /* Now with the IRQ */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 |= R_CR1_TXEIE_MASK; + qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'd'); + g_assert_true(recv(sock_fd, s, 1, 0) == 1); + g_assert_cmphex(s[0], ==, 'd'); + g_assert_true(check_nvic_pending(qts, USART1_IRQ)); + clear_nvic_pending(qts, USART1_IRQ); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_receive_str(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + usart_receive_string(qts, sock_fd, "hello", s); + g_assert_true(memcmp(s, "hello", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_send_str(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + usart_send_string(qts, "world"); + g_assert_true(recv(sock_fd, s, 10, 0) == 5); + g_assert_true(memcmp(s, "world", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + qtest_add_func("stm32l4x5/usart/write_read", test_write_read); + qtest_add_func("stm32l4x5/usart/receive_char", test_receive_char); + qtest_add_func("stm32l4x5/usart/send_char", test_send_char); + qtest_add_func("stm32l4x5/usart/receive_str", test_receive_str); + qtest_add_func("stm32l4x5/usart/send_str", test_send_str); + ret = g_test_run(); + + return ret; +} +