* [PATCH v1 11/11] KVM: arm64: Implement lazy vCPU state sync for non-protected guests
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260612065925.755562-1-tabba@google.com>
pKVM copies a non-protected guest's register context between the host
and the hypervisor on every world switch, even when the host never
inspects it. Defer the copy: on entry, flush the host context into the
hyp vCPU only when the host marked it dirty (PKVM_HOST_STATE_DIRTY); on
exit, leave it in the hyp vCPU and copy it back only when the host needs
it, via a __pkvm_vcpu_sync_state hypercall on trap handling or at vcpu
put. A protected guest's context is copied as before, since lazy sync
only helps where the host is trusted to see the guest's registers.
The PC is the exception: it is copied back on every exit so the
kvm_exit tracepoint reports the guest's real exit PC rather than the
value left by the previous sync.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/include/asm/kvm_asm.h | 1 +
arch/arm64/include/asm/kvm_host.h | 2 +
arch/arm64/kvm/arm.c | 7 +++
arch/arm64/kvm/handle_exit.c | 22 ++++++++
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 88 ++++++++++++++++++++++++++++--
5 files changed, 115 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 043495f7fc78..6e1135b3ded4 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -113,6 +113,7 @@ enum __kvm_host_smccc_func {
__KVM_HOST_SMCCC_FUNC___pkvm_finalize_teardown_vm,
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load,
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put,
+ __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_sync_state,
__KVM_HOST_SMCCC_FUNC___pkvm_tlb_flush_vmid,
MARKER(__KVM_HOST_SMCCC_FUNC_MAX)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index a49042bfa801..1ef660774adc 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -1113,6 +1113,8 @@ struct kvm_vcpu_arch {
/* SError pending for nested guest */
#define NESTED_SERROR_PENDING __vcpu_single_flag(sflags, BIT(8))
+/* pKVM host vcpu state is dirty, needs resync (nVHE-only) */
+#define PKVM_HOST_STATE_DIRTY __vcpu_single_flag(iflags, BIT(4))
/* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
#define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.sve_state) + \
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index c9f36932c980..a5c54e37778b 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -734,6 +734,10 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
if (is_protected_kvm_enabled()) {
kvm_call_hyp(__vgic_v3_save_aprs, &vcpu->arch.vgic_cpu.vgic_v3);
kvm_call_hyp_nvhe(__pkvm_vcpu_put);
+
+ /* __pkvm_vcpu_put implies a sync of the state */
+ if (!kvm_vm_is_protected(vcpu->kvm))
+ vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
}
kvm_vcpu_put_debug(vcpu);
@@ -961,6 +965,9 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
return ret;
if (is_protected_kvm_enabled()) {
+ /* Start with the vcpu in a dirty state */
+ if (!kvm_vm_is_protected(vcpu->kvm))
+ vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
ret = pkvm_create_hyp_vm(kvm);
if (ret)
return ret;
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index 54aedf93c78b..dccc3786548b 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -422,6 +422,21 @@ static int handle_trap_exceptions(struct kvm_vcpu *vcpu)
{
int handled;
+ /*
+ * If we run a non-protected VM when protection is enabled
+ * system-wide, resync the state from the hypervisor and mark
+ * it as dirty on the host side if it wasn't dirty already
+ * (which could happen if preemption has taken place).
+ */
+ if (is_protected_kvm_enabled() && !kvm_vm_is_protected(vcpu->kvm)) {
+ preempt_disable();
+ if (!(vcpu_get_flag(vcpu, PKVM_HOST_STATE_DIRTY))) {
+ kvm_call_hyp_nvhe(__pkvm_vcpu_sync_state);
+ vcpu_set_flag(vcpu, PKVM_HOST_STATE_DIRTY);
+ }
+ preempt_enable();
+ }
+
/*
* See ARM ARM B1.14.1: "Hyp traps on instructions
* that fail their condition code check"
@@ -489,6 +504,13 @@ int handle_exit(struct kvm_vcpu *vcpu, int exception_index)
/* For exit types that need handling before we can be preempted */
void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index)
{
+ /*
+ * We just exited, so the state is clean from a hypervisor
+ * perspective.
+ */
+ if (is_protected_kvm_enabled())
+ vcpu_clear_flag(vcpu, PKVM_HOST_STATE_DIRTY);
+
if (ARM_SERROR_PENDING(exception_index)) {
if (this_cpu_has_cap(ARM64_HAS_RAS_EXTN)) {
u64 disr = kvm_vcpu_get_disr(vcpu);
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 23e644c24a03..02383b372258 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -139,6 +139,49 @@ static void sync_hyp_vgic_state(struct pkvm_hyp_vcpu *hyp_vcpu)
host_cpu_if->vgic_lr[i] = hyp_cpu_if->vgic_lr[i];
}
+
+static void __copy_vcpu_state(const struct kvm_vcpu *from_vcpu,
+ struct kvm_vcpu *to_vcpu)
+{
+ int i;
+
+ to_vcpu->arch.ctxt.regs = from_vcpu->arch.ctxt.regs;
+ to_vcpu->arch.ctxt.spsr_abt = from_vcpu->arch.ctxt.spsr_abt;
+ to_vcpu->arch.ctxt.spsr_und = from_vcpu->arch.ctxt.spsr_und;
+ to_vcpu->arch.ctxt.spsr_irq = from_vcpu->arch.ctxt.spsr_irq;
+ to_vcpu->arch.ctxt.spsr_fiq = from_vcpu->arch.ctxt.spsr_fiq;
+ to_vcpu->arch.ctxt.fp_regs = from_vcpu->arch.ctxt.fp_regs;
+
+ /*
+ * Copy the sysregs, but don't mess with the timer state which
+ * is directly handled by EL1 and is expected to be preserved.
+ * enum vcpu_sysreg is sparse: VNCR-mapped registers take values
+ * derived from their VNCR page offset, so the timer registers do
+ * not form a contiguous numeric range and must be skipped by name.
+ */
+ for (i = 1; i < NR_SYS_REGS; i++) {
+ switch (i) {
+ case CNTVOFF_EL2:
+ case CNTV_CVAL_EL0:
+ case CNTV_CTL_EL0:
+ case CNTP_CVAL_EL0:
+ case CNTP_CTL_EL0:
+ continue;
+ }
+ to_vcpu->arch.ctxt.sys_regs[i] = from_vcpu->arch.ctxt.sys_regs[i];
+ }
+}
+
+static void __sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
+{
+ __copy_vcpu_state(&hyp_vcpu->vcpu, hyp_vcpu->host_vcpu);
+}
+
+static void __flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
+{
+ __copy_vcpu_state(hyp_vcpu->host_vcpu, &hyp_vcpu->vcpu);
+}
+
static void flush_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
@@ -168,7 +211,17 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
fpsimd_sve_flush();
flush_debug_state(hyp_vcpu);
- hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
+ /*
+ * If we deal with a non-protected guest and the state is potentially
+ * dirty (from a host perspective), copy the state back into the hyp
+ * vcpu.
+ */
+ if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
+ if (vcpu_get_flag(host_vcpu, PKVM_HOST_STATE_DIRTY))
+ __flush_hyp_vcpu(hyp_vcpu);
+ } else {
+ hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
+ }
hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
@@ -191,9 +244,11 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
fpsimd_sve_sync(&hyp_vcpu->vcpu);
sync_debug_state(hyp_vcpu);
- host_vcpu->arch.ctxt = hyp_vcpu->vcpu.arch.ctxt;
-
- host_vcpu->arch.hcr_el2 = hyp_vcpu->vcpu.arch.hcr_el2;
+ if (pkvm_hyp_vcpu_is_protected(hyp_vcpu))
+ host_vcpu->arch.ctxt = hyp_vcpu->vcpu.arch.ctxt;
+ else
+ /* Keep the PC current for the kvm_exit tracepoint (lazy ctxt sync). */
+ host_vcpu->arch.ctxt.regs.pc = hyp_vcpu->vcpu.arch.ctxt.regs.pc;
host_vcpu->arch.fault = hyp_vcpu->vcpu.arch.fault;
@@ -227,8 +282,30 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
{
struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
- if (hyp_vcpu)
+ if (hyp_vcpu) {
+ struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
+
+ if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu) &&
+ !vcpu_get_flag(host_vcpu, PKVM_HOST_STATE_DIRTY)) {
+ __sync_hyp_vcpu(hyp_vcpu);
+ }
+
pkvm_put_hyp_vcpu(hyp_vcpu);
+ }
+}
+
+static void handle___pkvm_vcpu_sync_state(struct kvm_cpu_context *host_ctxt)
+{
+ struct pkvm_hyp_vcpu *hyp_vcpu;
+
+ if (!is_protected_kvm_enabled())
+ return;
+
+ hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
+ if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
+ return;
+
+ __sync_hyp_vcpu(hyp_vcpu);
}
static struct kvm_vcpu *__get_host_hyp_vcpus(struct kvm_vcpu *arg,
@@ -859,6 +936,7 @@ static const hcall_t host_hcall[] = {
HANDLE_FUNC(__pkvm_finalize_teardown_vm),
HANDLE_FUNC(__pkvm_vcpu_load),
HANDLE_FUNC(__pkvm_vcpu_put),
+ HANDLE_FUNC(__pkvm_vcpu_sync_state),
HANDLE_FUNC(__pkvm_tlb_flush_vmid),
};
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [PATCH v1 07/11] KVM: arm64: Move PSCI helper functions to a shared header
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260612065925.755562-1-tabba@google.com>
Move kvm_psci_valid_affinity() and kvm_psci_narrow_to_32bit() from
psci.c to include/kvm/arm_psci.h, and move psci_affinity_mask() there
too, renaming it kvm_psci_affinity_mask() now that it is no longer
file-local. A follow-up series handles some protected-guest PSCI calls
at EL2 using these helpers.
No functional change intended.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/psci.c | 30 +-----------------------------
include/kvm/arm_psci.h | 28 ++++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 29 deletions(-)
diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
index e1389c525e9d..228e5040c379 100644
--- a/arch/arm64/kvm/psci.c
+++ b/arch/arm64/kvm/psci.c
@@ -21,16 +21,6 @@
* as described in ARM document number ARM DEN 0022A.
*/
-#define AFFINITY_MASK(level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1)
-
-static unsigned long psci_affinity_mask(unsigned long affinity_level)
-{
- if (affinity_level <= 3)
- return MPIDR_HWID_BITMASK & AFFINITY_MASK(affinity_level);
-
- return 0;
-}
-
static unsigned long kvm_psci_vcpu_suspend(struct kvm_vcpu *vcpu)
{
/*
@@ -51,12 +41,6 @@ static unsigned long kvm_psci_vcpu_suspend(struct kvm_vcpu *vcpu)
return PSCI_RET_SUCCESS;
}
-static inline bool kvm_psci_valid_affinity(struct kvm_vcpu *vcpu,
- unsigned long affinity)
-{
- return !(affinity & ~MPIDR_HWID_BITMASK);
-}
-
static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
{
struct vcpu_reset_state *reset_state;
@@ -131,7 +115,7 @@ static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu)
return PSCI_RET_INVALID_PARAMS;
/* Determine target affinity mask */
- target_affinity_mask = psci_affinity_mask(lowest_affinity_level);
+ target_affinity_mask = kvm_psci_affinity_mask(lowest_affinity_level);
if (!target_affinity_mask)
return PSCI_RET_INVALID_PARAMS;
@@ -215,18 +199,6 @@ static void kvm_psci_system_suspend(struct kvm_vcpu *vcpu)
run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
}
-static void kvm_psci_narrow_to_32bit(struct kvm_vcpu *vcpu)
-{
- int i;
-
- /*
- * Zero the input registers' upper 32 bits. They will be fully
- * zeroed on exit, so we're fine changing them in place.
- */
- for (i = 1; i < 4; i++)
- vcpu_set_reg(vcpu, i, lower_32_bits(vcpu_get_reg(vcpu, i)));
-}
-
static unsigned long kvm_psci_check_allowed_function(struct kvm_vcpu *vcpu, u32 fn)
{
/*
diff --git a/include/kvm/arm_psci.h b/include/kvm/arm_psci.h
index cbaec804eb83..f12b74a4b176 100644
--- a/include/kvm/arm_psci.h
+++ b/include/kvm/arm_psci.h
@@ -38,6 +38,34 @@ static inline int kvm_psci_version(struct kvm_vcpu *vcpu)
return KVM_ARM_PSCI_0_1;
}
+/* Narrow the PSCI register arguments (r1 to r3) to 32 bits. */
+static inline void kvm_psci_narrow_to_32bit(struct kvm_vcpu *vcpu)
+{
+ int i;
+
+ /*
+ * Zero the input registers' upper 32 bits. They will be fully
+ * zeroed on exit, so we're fine changing them in place.
+ */
+ for (i = 1; i < 4; i++)
+ vcpu_set_reg(vcpu, i, lower_32_bits(vcpu_get_reg(vcpu, i)));
+}
+
+static inline bool kvm_psci_valid_affinity(struct kvm_vcpu *vcpu,
+ unsigned long affinity)
+{
+ return !(affinity & ~MPIDR_HWID_BITMASK);
+}
+
+
+static inline unsigned long kvm_psci_affinity_mask(unsigned long affinity_level)
+{
+ if (affinity_level <= 3)
+ return MPIDR_HWID_BITMASK &
+ ~((0x1UL << (affinity_level * MPIDR_LEVEL_BITS)) - 1);
+
+ return 0;
+}
int kvm_psci_call(struct kvm_vcpu *vcpu);
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [PATCH v1 10/11] KVM: arm64: Add primitives to flush/sync the VGIC state at EL2
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260612065925.755562-1-tabba@google.com>
From: Marc Zyngier <maz@kernel.org>
pKVM performs its own world switch for protected VMs but has no
primitives to move the per-vCPU VGIC state between the host and
hypervisor vCPU contexts.
Add flush_hyp_vgic_state() and sync_hyp_vgic_state(). Flush copies
vgic_hcr, the in-use list registers and used_lrs from the host into the
hyp vCPU and pins vgic_sre to a fixed value; sync copies vgic_hcr,
vgic_vmcr and the in-use list registers back. The active priority
registers are handled separately by the save/restore-aprs path.
Signed-off-by: Marc Zyngier <maz@kernel.org>
Co-developed-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 50 +++++++++++++++++++++++++-----
1 file changed, 42 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 2f165b6c7b07..23e644c24a03 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -99,6 +99,46 @@ static void fpsimd_sve_sync(struct kvm_vcpu *vcpu)
*host_data_ptr(fp_owner) = FP_STATE_HOST_OWNED;
}
+static void flush_hyp_vgic_state(struct pkvm_hyp_vcpu *hyp_vcpu)
+{
+ struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
+ struct vgic_v3_cpu_if *host_cpu_if, *hyp_cpu_if;
+ unsigned int used_lrs, max_lrs, i;
+
+ host_cpu_if = &host_vcpu->arch.vgic_cpu.vgic_v3;
+ hyp_cpu_if = &hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3;
+
+ max_lrs = (read_gicreg(ICH_VTR_EL2) & ICH_VTR_EL2_ListRegs) + 1;
+ used_lrs = host_cpu_if->used_lrs;
+ used_lrs = min(used_lrs, max_lrs);
+
+ hyp_cpu_if->vgic_hcr = host_cpu_if->vgic_hcr;
+ /* Should be a one-off */
+ hyp_cpu_if->vgic_sre = (ICC_SRE_EL1_DIB |
+ ICC_SRE_EL1_DFB |
+ ICC_SRE_EL1_SRE);
+ hyp_cpu_if->used_lrs = used_lrs;
+
+ for (i = 0; i < used_lrs; i++)
+ hyp_cpu_if->vgic_lr[i] = host_cpu_if->vgic_lr[i];
+}
+
+static void sync_hyp_vgic_state(struct pkvm_hyp_vcpu *hyp_vcpu)
+{
+ struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
+ struct vgic_v3_cpu_if *host_cpu_if, *hyp_cpu_if;
+ unsigned int i;
+
+ host_cpu_if = &host_vcpu->arch.vgic_cpu.vgic_v3;
+ hyp_cpu_if = &hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3;
+
+ host_cpu_if->vgic_hcr = hyp_cpu_if->vgic_hcr;
+ host_cpu_if->vgic_vmcr = hyp_cpu_if->vgic_vmcr;
+
+ for (i = 0; i < hyp_cpu_if->used_lrs; i++)
+ host_cpu_if->vgic_lr[i] = hyp_cpu_if->vgic_lr[i];
+}
+
static void flush_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
@@ -139,7 +179,7 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.vsesr_el2 = host_vcpu->arch.vsesr_el2;
- hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
+ flush_hyp_vgic_state(hyp_vcpu);
hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid;
}
@@ -147,9 +187,6 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
- struct vgic_v3_cpu_if *hyp_cpu_if = &hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3;
- struct vgic_v3_cpu_if *host_cpu_if = &host_vcpu->arch.vgic_cpu.vgic_v3;
- unsigned int i;
fpsimd_sve_sync(&hyp_vcpu->vcpu);
sync_debug_state(hyp_vcpu);
@@ -162,10 +199,7 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
host_vcpu->arch.iflags = hyp_vcpu->vcpu.arch.iflags;
- host_cpu_if->vgic_hcr = hyp_cpu_if->vgic_hcr;
- host_cpu_if->vgic_vmcr = hyp_cpu_if->vgic_vmcr;
- for (i = 0; i < hyp_cpu_if->used_lrs; ++i)
- host_cpu_if->vgic_lr[i] = hyp_cpu_if->vgic_lr[i];
+ sync_hyp_vgic_state(hyp_vcpu);
}
static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [PATCH v1 05/11] KVM: arm64: Make vcpu_{read,write}_sys_reg available to HYP code
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260612065925.755562-1-tabba@google.com>
The vcpu_{read,write}_sys_reg() accessors are host-only, so helpers
built on them such as kvm_vcpu_set_be()/kvm_vcpu_is_be() cannot be
shared with hyp code. Add _vcpu_read_sys_reg()/_vcpu_write_sys_reg()
inlines in kvm_emulate.h that dispatch on is_nvhe_hyp_code() to the
host- or hyp-side accessor. A follow-up series uses this to share that
emulation code at EL2.
No functional change intended.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/include/asm/kvm_emulate.h | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index 5bf3d7e1d92c..aed9fc0b717b 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -506,6 +506,22 @@ static inline unsigned long kvm_vcpu_get_mpidr_aff(struct kvm_vcpu *vcpu)
return __vcpu_sys_reg(vcpu, MPIDR_EL1) & MPIDR_HWID_BITMASK;
}
+static inline u64 _vcpu_read_sys_reg(struct kvm_vcpu *vcpu, enum vcpu_sysreg reg)
+{
+ if (!is_nvhe_hyp_code())
+ return vcpu_read_sys_reg(vcpu, reg);
+
+ return __vcpu_sys_reg(vcpu, reg);
+}
+
+static inline void _vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, enum vcpu_sysreg reg)
+{
+ if (!is_nvhe_hyp_code())
+ vcpu_write_sys_reg(vcpu, val, reg);
+ else
+ __vcpu_assign_sys_reg(vcpu, reg, val);
+}
+
static inline void kvm_vcpu_set_be(struct kvm_vcpu *vcpu)
{
if (vcpu_mode_is_32bit(vcpu)) {
@@ -516,9 +532,9 @@ static inline void kvm_vcpu_set_be(struct kvm_vcpu *vcpu)
r = vcpu_has_nv(vcpu) ? SCTLR_EL2 : SCTLR_EL1;
- sctlr = vcpu_read_sys_reg(vcpu, r);
+ sctlr = _vcpu_read_sys_reg(vcpu, r);
sctlr |= SCTLR_ELx_EE;
- vcpu_write_sys_reg(vcpu, sctlr, r);
+ _vcpu_write_sys_reg(vcpu, sctlr, r);
}
}
@@ -533,7 +549,7 @@ static inline bool kvm_vcpu_is_be(struct kvm_vcpu *vcpu)
r = is_hyp_ctxt(vcpu) ? SCTLR_EL2 : SCTLR_EL1;
bit = vcpu_mode_priv(vcpu) ? SCTLR_ELx_EE : SCTLR_EL1_E0E;
- return vcpu_read_sys_reg(vcpu, r) & bit;
+ return _vcpu_read_sys_reg(vcpu, r) & bit;
}
static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu,
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [PATCH v1 08/11] KVM: arm64: Add host and hypervisor vCPU lookup primitives
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260612065925.755562-1-tabba@google.com>
From: Marc Zyngier <maz@kernel.org>
The nVHE hypervisor repeatedly resolves a host vCPU into the EL2
address space and validates that the loaded hyp vCPU matches it, with
that logic open-coded in each handler.
Add __get_host_hyp_vcpus() and the get_host_hyp_vcpus() macro, which
translate the host vCPU into the hypervisor's address space and, when
pKVM is enabled, also return the loaded hyp vCPU if it matches. If pKVM
is enabled but the loaded hyp vCPU does not correspond to the requested
host vCPU, both the host and hyp vCPU are returned as NULL. Convert
handle___kvm_vcpu_run() to use it.
No functional change intended.
Signed-off-by: Marc Zyngier <maz@kernel.org>
Co-developed-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 52 ++++++++++++++++++++++--------
1 file changed, 38 insertions(+), 14 deletions(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 06db299c37a8..420fb19a6476 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -195,14 +195,45 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
pkvm_put_hyp_vcpu(hyp_vcpu);
}
-static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
+static struct kvm_vcpu *__get_host_hyp_vcpus(struct kvm_vcpu *arg,
+ struct pkvm_hyp_vcpu **hyp_vcpup)
{
- DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 1);
- int ret;
+ struct kvm_vcpu *host_vcpu = kern_hyp_va(arg);
+ struct pkvm_hyp_vcpu *hyp_vcpu = NULL;
if (unlikely(is_protected_kvm_enabled())) {
- struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
+ hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
+ if (!hyp_vcpu || hyp_vcpu->host_vcpu != host_vcpu) {
+ hyp_vcpu = NULL;
+ host_vcpu = NULL;
+ }
+ }
+
+ *hyp_vcpup = hyp_vcpu;
+ return host_vcpu;
+}
+
+#define get_host_hyp_vcpus(ctxt, regnr, hyp_vcpup) \
+ ({ \
+ DECLARE_REG(struct kvm_vcpu *, __vcpu, ctxt, regnr); \
+ __get_host_hyp_vcpus(__vcpu, hyp_vcpup); \
+ })
+
+static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
+{
+ struct pkvm_hyp_vcpu *hyp_vcpu;
+ struct kvm_vcpu *host_vcpu;
+ int ret;
+
+ host_vcpu = get_host_hyp_vcpus(host_ctxt, 1, &hyp_vcpu);
+
+ if (!host_vcpu) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (unlikely(hyp_vcpu)) {
/*
* KVM (and pKVM) doesn't support SME guests for now, and
* ensures that SME features aren't enabled in pstate when
@@ -214,23 +245,16 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
goto out;
}
- if (!hyp_vcpu) {
- ret = -EINVAL;
- goto out;
- }
-
flush_hyp_vcpu(hyp_vcpu);
ret = __kvm_vcpu_run(&hyp_vcpu->vcpu);
sync_hyp_vcpu(hyp_vcpu);
} else {
- struct kvm_vcpu *vcpu = kern_hyp_va(host_vcpu);
-
/* The host is fully trusted, run its vCPU directly. */
- fpsimd_lazy_switch_to_guest(vcpu);
- ret = __kvm_vcpu_run(vcpu);
- fpsimd_lazy_switch_to_host(vcpu);
+ fpsimd_lazy_switch_to_guest(host_vcpu);
+ ret = __kvm_vcpu_run(host_vcpu);
+ fpsimd_lazy_switch_to_host(host_vcpu);
}
out:
cpu_reg(host_ctxt, 1) = ret;
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [PATCH v1 04/11] KVM: arm64: Extract MPIDR computation into a shared header
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260612065925.755562-1-tabba@google.com>
Extract the vCPU MPIDR computation embedded in reset_mpidr() into a
kvm_calculate_mpidr() inline in sys_regs.h, so it can be computed
without duplicating the logic. A follow-up series reuses it to reset
protected vCPUs at EL2.
No functional change intended.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/sys_regs.c | 14 +-------------
arch/arm64/kvm/sys_regs.h | 19 +++++++++++++++++++
2 files changed, 20 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index fa5c93c7a135..869a4bac96d6 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -979,21 +979,9 @@ static u64 reset_actlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
static u64 reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
{
- u64 mpidr;
+ u64 mpidr = kvm_calculate_mpidr(vcpu);
- /*
- * Map the vcpu_id into the first three affinity level fields of
- * the MPIDR. We limit the number of VCPUs in level 0 due to a
- * limitation to 16 CPUs in that level in the ICC_SGIxR registers
- * of the GICv3 to be able to address each CPU directly when
- * sending IPIs.
- */
- mpidr = (vcpu->vcpu_id & 0x0f) << MPIDR_LEVEL_SHIFT(0);
- mpidr |= ((vcpu->vcpu_id >> 4) & 0xff) << MPIDR_LEVEL_SHIFT(1);
- mpidr |= ((vcpu->vcpu_id >> 12) & 0xff) << MPIDR_LEVEL_SHIFT(2);
- mpidr |= (1ULL << 31);
vcpu_write_sys_reg(vcpu, mpidr, MPIDR_EL1);
-
return mpidr;
}
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index 2a983664220c..bd56a45abbf9 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -222,6 +222,25 @@ find_reg(const struct sys_reg_params *params, const struct sys_reg_desc table[],
return __inline_bsearch((void *)pval, table, num, sizeof(table[0]), match_sys_reg);
}
+static inline u64 kvm_calculate_mpidr(const struct kvm_vcpu *vcpu)
+{
+ u64 mpidr;
+
+ /*
+ * Map the vcpu_id into the first three affinity level fields of
+ * the MPIDR. We limit the number of VCPUs in level 0 due to a
+ * limitation to 16 CPUs in that level in the ICC_SGIxR registers
+ * of the GICv3 to be able to address each CPU directly when
+ * sending IPIs.
+ */
+ mpidr = (vcpu->vcpu_id & 0x0f) << MPIDR_LEVEL_SHIFT(0);
+ mpidr |= ((vcpu->vcpu_id >> 4) & 0xff) << MPIDR_LEVEL_SHIFT(1);
+ mpidr |= ((vcpu->vcpu_id >> 12) & 0xff) << MPIDR_LEVEL_SHIFT(2);
+ mpidr |= (1ULL << 31);
+
+ return mpidr;
+}
+
const struct sys_reg_desc *get_reg_by_id(u64 id,
const struct sys_reg_desc table[],
unsigned int num);
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [PATCH v1 02/11] KVM: arm64: Use guard(hyp_spinlock) in pKVM hypervisor code
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260612065925.755562-1-tabba@google.com>
Convert the manual hyp_spin_lock()/hyp_spin_unlock() pairs in
arch/arm64/kvm/hyp/nvhe/{pkvm,mm,page_alloc,ffa}.c to
guard(hyp_spinlock) and scoped_guard(hyp_spinlock), dropping several
unlock-only goto labels in favour of direct returns.
hyp_fixblock_lock in mm.c is left as an explicit lock/unlock pair: it is
acquired in hyp_fixblock_map() and released in hyp_fixblock_unmap(), so
its critical section spans two functions and cannot be expressed as a
single lexical scope.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/hyp/nvhe/ffa.c | 154 +++++++++++----------------
arch/arm64/kvm/hyp/nvhe/mm.c | 37 ++-----
arch/arm64/kvm/hyp/nvhe/page_alloc.c | 13 +--
arch/arm64/kvm/hyp/nvhe/pkvm.c | 86 +++++----------
4 files changed, 105 insertions(+), 185 deletions(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c
index 1af722771178..46cd4fa924be 100644
--- a/arch/arm64/kvm/hyp/nvhe/ffa.c
+++ b/arch/arm64/kvm/hyp/nvhe/ffa.c
@@ -313,17 +313,16 @@ static void do_ffa_rxtx_unmap(struct arm_smccc_1_2_regs *res,
struct kvm_cpu_context *ctxt)
{
DECLARE_REG(u32, id, ctxt, 1);
- int ret = 0;
if (id != HOST_FFA_ID) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
- hyp_spin_lock(&host_buffers.lock);
+ guard(hyp_spinlock)(&host_buffers.lock);
if (!host_buffers.tx) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
hyp_unpin_shared_mem(host_buffers.tx, host_buffers.tx + 1);
@@ -336,10 +335,7 @@ static void do_ffa_rxtx_unmap(struct arm_smccc_1_2_regs *res,
ffa_unmap_hyp_buffers();
-out_unlock:
- hyp_spin_unlock(&host_buffers.lock);
-out:
- ffa_to_smccc_res(res, ret);
+ ffa_to_smccc_res(res, 0);
}
static u32 __ffa_host_share_ranges(struct ffa_mem_region_addr_range *ranges,
@@ -418,18 +414,20 @@ static void do_ffa_mem_frag_tx(struct arm_smccc_1_2_regs *res,
DECLARE_REG(u32, fraglen, ctxt, 3);
DECLARE_REG(u32, endpoint_id, ctxt, 4);
struct ffa_mem_region_addr_range *buf;
- int ret = FFA_RET_INVALID_PARAMETERS;
+ int ret;
u32 nr_ranges;
- if (fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE)
- goto out;
+ if (fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE ||
+ fraglen % sizeof(*buf)) {
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
+ }
- if (fraglen % sizeof(*buf))
- goto out;
-
- hyp_spin_lock(&host_buffers.lock);
- if (!host_buffers.tx)
- goto out_unlock;
+ guard(hyp_spinlock)(&host_buffers.lock);
+ if (!host_buffers.tx) {
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
+ }
buf = hyp_buffers.tx;
memcpy(buf, host_buffers.tx, fraglen);
@@ -444,19 +442,14 @@ static void do_ffa_mem_frag_tx(struct arm_smccc_1_2_regs *res,
*/
ffa_mem_reclaim(res, handle_lo, handle_hi, 0);
WARN_ON(res->a0 != FFA_SUCCESS);
- goto out_unlock;
+ ffa_to_smccc_res(res, ret);
+ return;
}
ffa_mem_frag_tx(res, handle_lo, handle_hi, fraglen, endpoint_id);
if (res->a0 != FFA_SUCCESS && res->a0 != FFA_MEM_FRAG_RX)
WARN_ON(ffa_host_unshare_ranges(buf, nr_ranges));
-out_unlock:
- hyp_spin_unlock(&host_buffers.lock);
-out:
- if (ret)
- ffa_to_smccc_res(res, ret);
-
/*
* If for any reason this did not succeed, we're in trouble as we have
* now lost the content of the previous fragments and we can't rollback
@@ -465,7 +458,6 @@ static void do_ffa_mem_frag_tx(struct arm_smccc_1_2_regs *res,
* sharing/donating them again and may possibly lead to subsequent
* failures, but this will not compromise confidentiality.
*/
- return;
}
static void __do_ffa_mem_xfer(const u64 func_id,
@@ -480,29 +472,29 @@ static void __do_ffa_mem_xfer(const u64 func_id,
struct ffa_composite_mem_region *reg;
struct ffa_mem_region *buf;
u32 offset, nr_ranges, checked_offset;
- int ret = 0;
+ int ret;
if (addr_mbz || npages_mbz || fraglen > len ||
fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
if (fraglen < sizeof(struct ffa_mem_region) +
sizeof(struct ffa_mem_region_attributes)) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
- hyp_spin_lock(&host_buffers.lock);
+ guard(hyp_spinlock)(&host_buffers.lock);
if (!host_buffers.tx) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
if (len > ffa_desc_buf.len) {
- ret = FFA_RET_NO_MEMORY;
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_NO_MEMORY);
+ return;
}
buf = hyp_buffers.tx;
@@ -512,53 +504,41 @@ static void __do_ffa_mem_xfer(const u64 func_id,
ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
offset = ep_mem_access->composite_off;
if (!offset || buf->ep_count != 1 || buf->sender_id != HOST_FFA_ID) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
if (check_add_overflow(offset, sizeof(struct ffa_composite_mem_region), &checked_offset)) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
if (fraglen < checked_offset) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
reg = (void *)buf + offset;
nr_ranges = ((void *)buf + fraglen) - (void *)reg->constituents;
if (nr_ranges % sizeof(reg->constituents[0])) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
nr_ranges /= sizeof(reg->constituents[0]);
ret = ffa_host_share_ranges(reg->constituents, nr_ranges);
- if (ret)
- goto out_unlock;
+ if (ret) {
+ ffa_to_smccc_res(res, ret);
+ return;
+ }
ffa_mem_xfer(res, func_id, len, fraglen);
if (fraglen != len) {
- if (res->a0 != FFA_MEM_FRAG_RX)
- goto err_unshare;
-
- if (res->a3 != fraglen)
- goto err_unshare;
+ if (res->a0 != FFA_MEM_FRAG_RX || res->a3 != fraglen)
+ WARN_ON(ffa_host_unshare_ranges(reg->constituents, nr_ranges));
} else if (res->a0 != FFA_SUCCESS) {
- goto err_unshare;
+ WARN_ON(ffa_host_unshare_ranges(reg->constituents, nr_ranges));
}
-
-out_unlock:
- hyp_spin_unlock(&host_buffers.lock);
-out:
- if (ret)
- ffa_to_smccc_res(res, ret);
- return;
-
-err_unshare:
- WARN_ON(ffa_host_unshare_ranges(reg->constituents, nr_ranges));
- goto out_unlock;
}
#define do_ffa_mem_xfer(fid, res, ctxt) \
@@ -578,12 +558,11 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
struct ffa_composite_mem_region *reg;
u32 offset, len, fraglen, fragoff;
struct ffa_mem_region *buf;
- int ret = 0;
u64 handle;
handle = PACK_HANDLE(handle_lo, handle_hi);
- hyp_spin_lock(&host_buffers.lock);
+ guard(hyp_spinlock)(&host_buffers.lock);
buf = hyp_buffers.tx;
*buf = (struct ffa_mem_region) {
@@ -594,7 +573,7 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
ffa_retrieve_req(res, sizeof(*buf));
buf = hyp_buffers.rx;
if (res->a0 != FFA_MEM_RETRIEVE_RESP)
- goto out_unlock;
+ return;
len = res->a1;
fraglen = res->a2;
@@ -609,15 +588,15 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
*/
if (WARN_ON(offset > len ||
fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE)) {
- ret = FFA_RET_ABORTED;
ffa_rx_release(res);
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_ABORTED);
+ return;
}
if (len > ffa_desc_buf.len) {
- ret = FFA_RET_NO_MEMORY;
ffa_rx_release(res);
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_NO_MEMORY);
+ return;
}
buf = ffa_desc_buf.buf;
@@ -627,8 +606,8 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
for (fragoff = fraglen; fragoff < len; fragoff += fraglen) {
ffa_mem_frag_rx(res, handle_lo, handle_hi, fragoff);
if (res->a0 != FFA_MEM_FRAG_TX) {
- ret = FFA_RET_INVALID_PARAMETERS;
- goto out_unlock;
+ ffa_to_smccc_res(res, FFA_RET_INVALID_PARAMETERS);
+ return;
}
fraglen = res->a3;
@@ -638,17 +617,12 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
ffa_mem_reclaim(res, handle_lo, handle_hi, flags);
if (res->a0 != FFA_SUCCESS)
- goto out_unlock;
+ return;
reg = (void *)buf + offset;
/* If the SPMD was happy, then we should be too. */
WARN_ON(ffa_host_unshare_ranges(reg->constituents,
reg->addr_range_cnt));
-out_unlock:
- hyp_spin_unlock(&host_buffers.lock);
-
- if (ret)
- ffa_to_smccc_res(res, ret);
}
/*
@@ -774,13 +748,13 @@ static void do_ffa_version(struct arm_smccc_1_2_regs *res,
return;
}
- hyp_spin_lock(&version_lock);
+ guard(hyp_spinlock)(&version_lock);
if (has_version_negotiated) {
if (FFA_MINOR_VERSION(ffa_req_version) < FFA_MINOR_VERSION(hyp_ffa_version))
res->a0 = FFA_RET_NOT_SUPPORTED;
else
res->a0 = hyp_ffa_version;
- goto unlock;
+ return;
}
/*
@@ -793,7 +767,7 @@ static void do_ffa_version(struct arm_smccc_1_2_regs *res,
.a1 = ffa_req_version,
}, res);
if ((s32)res->a0 == FFA_RET_NOT_SUPPORTED)
- goto unlock;
+ return;
hyp_ffa_version = ffa_req_version;
}
@@ -804,8 +778,6 @@ static void do_ffa_version(struct arm_smccc_1_2_regs *res,
smp_store_release(&has_version_negotiated, true);
res->a0 = hyp_ffa_version;
}
-unlock:
- hyp_spin_unlock(&version_lock);
}
static void do_ffa_part_get(struct arm_smccc_1_2_regs *res,
@@ -818,10 +790,10 @@ static void do_ffa_part_get(struct arm_smccc_1_2_regs *res,
DECLARE_REG(u32, flags, ctxt, 5);
u32 count, partition_sz, copy_sz;
- hyp_spin_lock(&host_buffers.lock);
+ guard(hyp_spinlock)(&host_buffers.lock);
if (!host_buffers.rx) {
ffa_to_smccc_res(res, FFA_RET_BUSY);
- goto out_unlock;
+ return;
}
hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) {
@@ -834,16 +806,16 @@ static void do_ffa_part_get(struct arm_smccc_1_2_regs *res,
}, res);
if (res->a0 != FFA_SUCCESS)
- goto out_unlock;
+ return;
count = res->a2;
if (!count)
- goto out_unlock;
+ return;
if (hyp_ffa_version > FFA_VERSION_1_0) {
/* Get the number of partitions deployed in the system */
if (flags & 0x1)
- goto out_unlock;
+ return;
partition_sz = res->a3;
} else {
@@ -854,12 +826,10 @@ static void do_ffa_part_get(struct arm_smccc_1_2_regs *res,
copy_sz = partition_sz * count;
if (copy_sz > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) {
ffa_to_smccc_res(res, FFA_RET_ABORTED);
- goto out_unlock;
+ return;
}
memcpy(host_buffers.rx, hyp_buffers.rx, copy_sz);
-out_unlock:
- hyp_spin_unlock(&host_buffers.lock);
}
bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id)
diff --git a/arch/arm64/kvm/hyp/nvhe/mm.c b/arch/arm64/kvm/hyp/nvhe/mm.c
index 3b0bee496bff..56c3eb4a2251 100644
--- a/arch/arm64/kvm/hyp/nvhe/mm.c
+++ b/arch/arm64/kvm/hyp/nvhe/mm.c
@@ -35,13 +35,8 @@ static DEFINE_PER_CPU(struct hyp_fixmap_slot, fixmap_slots);
static int __pkvm_create_mappings(unsigned long start, unsigned long size,
unsigned long phys, enum kvm_pgtable_prot prot)
{
- int err;
-
- hyp_spin_lock(&pkvm_pgd_lock);
- err = kvm_pgtable_hyp_map(&pkvm_pgtable, start, size, phys, prot);
- hyp_spin_unlock(&pkvm_pgd_lock);
-
- return err;
+ guard(hyp_spinlock)(&pkvm_pgd_lock);
+ return kvm_pgtable_hyp_map(&pkvm_pgtable, start, size, phys, prot);
}
static int __pkvm_alloc_private_va_range(unsigned long start, size_t size)
@@ -80,10 +75,9 @@ int pkvm_alloc_private_va_range(size_t size, unsigned long *haddr)
unsigned long addr;
int ret;
- hyp_spin_lock(&pkvm_pgd_lock);
+ guard(hyp_spinlock)(&pkvm_pgd_lock);
addr = __io_map_base;
ret = __pkvm_alloc_private_va_range(addr, size);
- hyp_spin_unlock(&pkvm_pgd_lock);
*haddr = addr;
@@ -137,13 +131,8 @@ int pkvm_create_mappings_locked(void *from, void *to, enum kvm_pgtable_prot prot
int pkvm_create_mappings(void *from, void *to, enum kvm_pgtable_prot prot)
{
- int ret;
-
- hyp_spin_lock(&pkvm_pgd_lock);
- ret = pkvm_create_mappings_locked(from, to, prot);
- hyp_spin_unlock(&pkvm_pgd_lock);
-
- return ret;
+ guard(hyp_spinlock)(&pkvm_pgd_lock);
+ return pkvm_create_mappings_locked(from, to, prot);
}
int hyp_back_vmemmap(phys_addr_t back)
@@ -340,22 +329,17 @@ static int create_fixblock(void)
if (i >= hyp_memblock_nr)
return -EINVAL;
- hyp_spin_lock(&pkvm_pgd_lock);
+ guard(hyp_spinlock)(&pkvm_pgd_lock);
addr = ALIGN(__io_map_base, PMD_SIZE);
ret = __pkvm_alloc_private_va_range(addr, PMD_SIZE);
if (ret)
- goto unlock;
+ return ret;
ret = kvm_pgtable_hyp_map(&pkvm_pgtable, addr, PMD_SIZE, phys, PAGE_HYP);
if (ret)
- goto unlock;
+ return ret;
- ret = kvm_pgtable_walk(&pkvm_pgtable, addr, PMD_SIZE, &walker);
-
-unlock:
- hyp_spin_unlock(&pkvm_pgd_lock);
-
- return ret;
+ return kvm_pgtable_walk(&pkvm_pgtable, addr, PMD_SIZE, &walker);
#else
return 0;
#endif
@@ -437,7 +421,7 @@ int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr)
size_t size;
int ret;
- hyp_spin_lock(&pkvm_pgd_lock);
+ guard(hyp_spinlock)(&pkvm_pgd_lock);
prev_base = __io_map_base;
/*
@@ -463,7 +447,6 @@ int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr)
if (ret)
__io_map_base = prev_base;
}
- hyp_spin_unlock(&pkvm_pgd_lock);
*haddr = addr + size;
diff --git a/arch/arm64/kvm/hyp/nvhe/page_alloc.c b/arch/arm64/kvm/hyp/nvhe/page_alloc.c
index a1eb27a1a747..f43d8ad507e9 100644
--- a/arch/arm64/kvm/hyp/nvhe/page_alloc.c
+++ b/arch/arm64/kvm/hyp/nvhe/page_alloc.c
@@ -167,18 +167,16 @@ void hyp_put_page(struct hyp_pool *pool, void *addr)
{
struct hyp_page *p = hyp_virt_to_page(addr);
- hyp_spin_lock(&pool->lock);
+ guard(hyp_spinlock)(&pool->lock);
__hyp_put_page(pool, p);
- hyp_spin_unlock(&pool->lock);
}
void hyp_get_page(struct hyp_pool *pool, void *addr)
{
struct hyp_page *p = hyp_virt_to_page(addr);
- hyp_spin_lock(&pool->lock);
+ guard(hyp_spinlock)(&pool->lock);
hyp_page_ref_inc(p);
- hyp_spin_unlock(&pool->lock);
}
void hyp_split_page(struct hyp_page *p)
@@ -200,22 +198,19 @@ void *hyp_alloc_pages(struct hyp_pool *pool, u8 order)
struct hyp_page *p;
u8 i = order;
- hyp_spin_lock(&pool->lock);
+ guard(hyp_spinlock)(&pool->lock);
/* Look for a high-enough-order page */
while (i <= pool->max_order && list_empty(&pool->free_area[i]))
i++;
- if (i > pool->max_order) {
- hyp_spin_unlock(&pool->lock);
+ if (i > pool->max_order)
return NULL;
- }
/* Extract it from the tree at the right order */
p = node_to_page(pool->free_area[i].next);
p = __hyp_extract_page(pool, p, order);
hyp_set_page_refcounted(p);
- hyp_spin_unlock(&pool->lock);
return hyp_page_to_virt(p);
}
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index eb1c10120f9f..7d843afd8c0a 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -258,32 +258,27 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
if (__this_cpu_read(loaded_hyp_vcpu))
return NULL;
- hyp_spin_lock(&vm_table_lock);
+ guard(hyp_spinlock)(&vm_table_lock);
hyp_vm = get_vm_by_handle(handle);
if (!hyp_vm || hyp_vm->kvm.arch.pkvm.is_dying)
- goto unlock;
+ return NULL;
if (hyp_vm->kvm.created_vcpus <= vcpu_idx)
- goto unlock;
+ return NULL;
/* Pairs with smp_store_release() in register_hyp_vcpu(). */
hyp_vcpu = smp_load_acquire(&hyp_vm->vcpus[vcpu_idx]);
if (!hyp_vcpu)
- goto unlock;
+ return NULL;
/* Ensure vcpu isn't loaded on more than one cpu simultaneously. */
- if (unlikely(hyp_vcpu->loaded_hyp_vcpu)) {
- hyp_vcpu = NULL;
- goto unlock;
- }
+ if (unlikely(hyp_vcpu->loaded_hyp_vcpu))
+ return NULL;
hyp_vcpu->loaded_hyp_vcpu = this_cpu_ptr(&loaded_hyp_vcpu);
hyp_page_ref_inc(hyp_virt_to_page(hyp_vm));
-unlock:
- hyp_spin_unlock(&vm_table_lock);
- if (hyp_vcpu)
- __this_cpu_write(loaded_hyp_vcpu, hyp_vcpu);
+ __this_cpu_write(loaded_hyp_vcpu, hyp_vcpu);
return hyp_vcpu;
}
@@ -291,11 +286,10 @@ void pkvm_put_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
- hyp_spin_lock(&vm_table_lock);
+ guard(hyp_spinlock)(&vm_table_lock);
hyp_vcpu->loaded_hyp_vcpu = NULL;
__this_cpu_write(loaded_hyp_vcpu, NULL);
hyp_page_ref_dec(hyp_virt_to_page(hyp_vm));
- hyp_spin_unlock(&vm_table_lock);
}
struct pkvm_hyp_vcpu *pkvm_get_loaded_hyp_vcpu(void)
@@ -308,20 +302,18 @@ struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle)
{
struct pkvm_hyp_vm *hyp_vm;
- hyp_spin_lock(&vm_table_lock);
+ guard(hyp_spinlock)(&vm_table_lock);
hyp_vm = get_vm_by_handle(handle);
if (hyp_vm)
hyp_page_ref_inc(hyp_virt_to_page(hyp_vm));
- hyp_spin_unlock(&vm_table_lock);
return hyp_vm;
}
void put_pkvm_hyp_vm(struct pkvm_hyp_vm *hyp_vm)
{
- hyp_spin_lock(&vm_table_lock);
+ guard(hyp_spinlock)(&vm_table_lock);
hyp_page_ref_dec(hyp_virt_to_page(hyp_vm));
- hyp_spin_unlock(&vm_table_lock);
}
struct pkvm_hyp_vm *get_np_pkvm_hyp_vm(pkvm_handle_t handle)
@@ -620,13 +612,8 @@ static int __insert_vm_table_entry(pkvm_handle_t handle,
static int insert_vm_table_entry(pkvm_handle_t handle,
struct pkvm_hyp_vm *hyp_vm)
{
- int ret;
-
- hyp_spin_lock(&vm_table_lock);
- ret = __insert_vm_table_entry(handle, hyp_vm);
- hyp_spin_unlock(&vm_table_lock);
-
- return ret;
+ guard(hyp_spinlock)(&vm_table_lock);
+ return __insert_vm_table_entry(handle, hyp_vm);
}
/*
@@ -701,9 +688,8 @@ int __pkvm_reserve_vm(void)
{
int ret;
- hyp_spin_lock(&vm_table_lock);
+ guard(hyp_spinlock)(&vm_table_lock);
ret = allocate_vm_table_entry();
- hyp_spin_unlock(&vm_table_lock);
if (ret < 0)
return ret;
@@ -722,10 +708,9 @@ void __pkvm_unreserve_vm(pkvm_handle_t handle)
if (unlikely(!vm_table))
return;
- hyp_spin_lock(&vm_table_lock);
+ guard(hyp_spinlock)(&vm_table_lock);
if (likely(idx < KVM_MAX_PVMS && vm_table[idx] == RESERVED_ENTRY))
remove_vm_table_entry(handle);
- hyp_spin_unlock(&vm_table_lock);
}
#ifdef CONFIG_NVHE_EL2_DEBUG
@@ -785,9 +770,8 @@ struct pkvm_hyp_vcpu *init_selftest_vm(void *virt)
void teardown_selftest_vm(void)
{
- hyp_spin_lock(&vm_table_lock);
+ guard(hyp_spinlock)(&vm_table_lock);
remove_vm_table_entry(selftest_vm.kvm.arch.pkvm.handle);
- hyp_spin_unlock(&vm_table_lock);
}
#endif /* CONFIG_NVHE_EL2_DEBUG */
@@ -973,20 +957,15 @@ static struct pkvm_hyp_vm *get_pkvm_unref_hyp_vm_locked(pkvm_handle_t handle)
int __pkvm_start_teardown_vm(pkvm_handle_t handle)
{
struct pkvm_hyp_vm *hyp_vm;
- int ret = 0;
- hyp_spin_lock(&vm_table_lock);
+ guard(hyp_spinlock)(&vm_table_lock);
hyp_vm = get_pkvm_unref_hyp_vm_locked(handle);
- if (!hyp_vm || hyp_vm->kvm.arch.pkvm.is_dying) {
- ret = -EINVAL;
- goto unlock;
- }
+ if (!hyp_vm || hyp_vm->kvm.arch.pkvm.is_dying)
+ return -EINVAL;
hyp_vm->kvm.arch.pkvm.is_dying = true;
-unlock:
- hyp_spin_unlock(&vm_table_lock);
- return ret;
+ return 0;
}
int __pkvm_finalize_teardown_vm(pkvm_handle_t handle)
@@ -996,22 +975,19 @@ int __pkvm_finalize_teardown_vm(pkvm_handle_t handle)
struct kvm *host_kvm;
unsigned int idx;
size_t vm_size;
- int err;
- hyp_spin_lock(&vm_table_lock);
- hyp_vm = get_pkvm_unref_hyp_vm_locked(handle);
- if (!hyp_vm || !hyp_vm->kvm.arch.pkvm.is_dying) {
- err = -EINVAL;
- goto err_unlock;
+ scoped_guard(hyp_spinlock, &vm_table_lock) {
+ hyp_vm = get_pkvm_unref_hyp_vm_locked(handle);
+ if (!hyp_vm || !hyp_vm->kvm.arch.pkvm.is_dying)
+ return -EINVAL;
+
+ host_kvm = hyp_vm->host_kvm;
+
+ /* Ensure the VMID is clean before it can be reallocated */
+ __kvm_tlb_flush_vmid(&hyp_vm->kvm.arch.mmu);
+ remove_vm_table_entry(handle);
}
- host_kvm = hyp_vm->host_kvm;
-
- /* Ensure the VMID is clean before it can be reallocated */
- __kvm_tlb_flush_vmid(&hyp_vm->kvm.arch.mmu);
- remove_vm_table_entry(handle);
- hyp_spin_unlock(&vm_table_lock);
-
/* Reclaim guest pages (including page-table pages) */
mc = &host_kvm->arch.pkvm.teardown_mc;
stage2_mc = &host_kvm->arch.pkvm.stage2_teardown_mc;
@@ -1042,10 +1018,6 @@ int __pkvm_finalize_teardown_vm(pkvm_handle_t handle)
teardown_donated_memory(mc, hyp_vm, vm_size);
hyp_unpin_shared_mem(host_kvm, host_kvm + 1);
return 0;
-
-err_unlock:
- hyp_spin_unlock(&vm_table_lock);
- return err;
}
static u64 __pkvm_memshare_page_req(struct kvm_vcpu *vcpu, u64 ipa)
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [PATCH v1 03/11] KVM: arm64: Use guard()/scoped_guard() in arm64 KVM EL1 code
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260612065925.755562-1-tabba@google.com>
Convert the manual mutex_lock()/spin_lock() pairs in
arch/arm64/kvm/{pkvm,arm,mmu,reset,psci}.c to guard(mutex),
guard(spinlock) and scoped_guard(), dropping unlock-only goto labels in
favour of direct returns. Centralised cleanup gotos that still serve
other resources are preserved.
reset.c uses scoped_guard() rather than guard() so the lock covers only
the small read/update window inside kvm_reset_vcpu(), leaving the rest
of the function outside the critical section.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/arm.c | 14 +++-----
arch/arm64/kvm/mmu.c | 80 +++++++++++++++---------------------------
arch/arm64/kvm/pkvm.c | 26 ++++++--------
arch/arm64/kvm/psci.c | 17 ++++-----
arch/arm64/kvm/reset.c | 8 ++---
5 files changed, 53 insertions(+), 92 deletions(-)
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 9453321ef8c6..c9f36932c980 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -793,9 +793,7 @@ int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
struct kvm_mp_state *mp_state)
{
- int ret = 0;
-
- spin_lock(&vcpu->arch.mp_state_lock);
+ guard(spinlock)(&vcpu->arch.mp_state_lock);
switch (mp_state->mp_state) {
case KVM_MP_STATE_RUNNABLE:
@@ -808,12 +806,10 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
kvm_arm_vcpu_suspend(vcpu);
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&vcpu->arch.mp_state_lock);
-
- return ret;
+ return 0;
}
/**
@@ -1726,15 +1722,13 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
/*
* Handle the "start in power-off" case.
*/
- spin_lock(&vcpu->arch.mp_state_lock);
+ guard(spinlock)(&vcpu->arch.mp_state_lock);
if (power_off)
__kvm_arm_vcpu_power_off(vcpu);
else
WRITE_ONCE(vcpu->arch.mp_state.mp_state, KVM_MP_STATE_RUNNABLE);
- spin_unlock(&vcpu->arch.mp_state_lock);
-
return 0;
}
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 4da9281312eb..d18f4ce7ceae 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -391,13 +391,13 @@ static void stage2_flush_vm(struct kvm *kvm)
*/
void __init free_hyp_pgds(void)
{
- mutex_lock(&kvm_hyp_pgd_mutex);
- if (hyp_pgtable) {
- kvm_pgtable_hyp_destroy(hyp_pgtable);
- kfree(hyp_pgtable);
- hyp_pgtable = NULL;
- }
- mutex_unlock(&kvm_hyp_pgd_mutex);
+ guard(mutex)(&kvm_hyp_pgd_mutex);
+ if (!hyp_pgtable)
+ return;
+
+ kvm_pgtable_hyp_destroy(hyp_pgtable);
+ kfree(hyp_pgtable);
+ hyp_pgtable = NULL;
}
static bool kvm_host_owns_hyp_mappings(void)
@@ -424,16 +424,11 @@ static bool kvm_host_owns_hyp_mappings(void)
int __create_hyp_mappings(unsigned long start, unsigned long size,
unsigned long phys, enum kvm_pgtable_prot prot)
{
- int err;
-
if (WARN_ON(!kvm_host_owns_hyp_mappings()))
return -EINVAL;
- mutex_lock(&kvm_hyp_pgd_mutex);
- err = kvm_pgtable_hyp_map(hyp_pgtable, start, size, phys, prot);
- mutex_unlock(&kvm_hyp_pgd_mutex);
-
- return err;
+ guard(mutex)(&kvm_hyp_pgd_mutex);
+ return kvm_pgtable_hyp_map(hyp_pgtable, start, size, phys, prot);
}
static phys_addr_t kvm_kaddr_to_phys(void *kaddr)
@@ -481,56 +476,42 @@ static int share_pfn_hyp(u64 pfn)
{
struct rb_node **node, *parent;
struct hyp_shared_pfn *this;
- int ret = 0;
- mutex_lock(&hyp_shared_pfns_lock);
+ guard(mutex)(&hyp_shared_pfns_lock);
this = find_shared_pfn(pfn, &node, &parent);
if (this) {
this->count++;
- goto unlock;
+ return 0;
}
this = kzalloc_obj(*this);
- if (!this) {
- ret = -ENOMEM;
- goto unlock;
- }
+ if (!this)
+ return -ENOMEM;
this->pfn = pfn;
this->count = 1;
rb_link_node(&this->node, parent, node);
rb_insert_color(&this->node, &hyp_shared_pfns);
- ret = kvm_call_hyp_nvhe(__pkvm_host_share_hyp, pfn);
-unlock:
- mutex_unlock(&hyp_shared_pfns_lock);
-
- return ret;
+ return kvm_call_hyp_nvhe(__pkvm_host_share_hyp, pfn);
}
static int unshare_pfn_hyp(u64 pfn)
{
struct rb_node **node, *parent;
struct hyp_shared_pfn *this;
- int ret = 0;
- mutex_lock(&hyp_shared_pfns_lock);
+ guard(mutex)(&hyp_shared_pfns_lock);
this = find_shared_pfn(pfn, &node, &parent);
- if (WARN_ON(!this)) {
- ret = -ENOENT;
- goto unlock;
- }
+ if (WARN_ON(!this))
+ return -ENOENT;
this->count--;
if (this->count)
- goto unlock;
+ return 0;
rb_erase(&this->node, &hyp_shared_pfns);
kfree(this);
- ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_hyp, pfn);
-unlock:
- mutex_unlock(&hyp_shared_pfns_lock);
-
- return ret;
+ return kvm_call_hyp_nvhe(__pkvm_host_unshare_hyp, pfn);
}
int kvm_share_hyp(void *from, void *to)
@@ -655,7 +636,7 @@ int hyp_alloc_private_va_range(size_t size, unsigned long *haddr)
unsigned long base;
int ret = 0;
- mutex_lock(&kvm_hyp_pgd_mutex);
+ guard(mutex)(&kvm_hyp_pgd_mutex);
/*
* This assumes that we have enough space below the idmap
@@ -670,8 +651,6 @@ int hyp_alloc_private_va_range(size_t size, unsigned long *haddr)
base = io_map_base - size;
ret = __hyp_alloc_private_va_range(base);
- mutex_unlock(&kvm_hyp_pgd_mutex);
-
if (!ret)
*haddr = base;
@@ -714,17 +693,16 @@ int create_hyp_stack(phys_addr_t phys_addr, unsigned long *haddr)
size_t size;
int ret;
- mutex_lock(&kvm_hyp_pgd_mutex);
- /*
- * Efficient stack verification using the NVHE_STACK_SHIFT bit implies
- * an alignment of our allocation on the order of the size.
- */
- size = NVHE_STACK_SIZE * 2;
- base = ALIGN_DOWN(io_map_base - size, size);
+ scoped_guard(mutex, &kvm_hyp_pgd_mutex) {
+ /*
+ * Efficient stack verification using the NVHE_STACK_SHIFT bit implies
+ * an alignment of our allocation on the order of the size.
+ */
+ size = NVHE_STACK_SIZE * 2;
+ base = ALIGN_DOWN(io_map_base - size, size);
- ret = __hyp_alloc_private_va_range(base);
-
- mutex_unlock(&kvm_hyp_pgd_mutex);
+ ret = __hyp_alloc_private_va_range(base);
+ }
if (ret) {
kvm_err("Cannot allocate hyp stack guard page\n");
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 053e4f733e4b..a39111b70f9f 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -190,39 +190,33 @@ bool pkvm_hyp_vm_is_created(struct kvm *kvm)
int pkvm_create_hyp_vm(struct kvm *kvm)
{
- int ret = 0;
-
/*
* Synchronise with kvm_arch_prepare_memory_region(), as we
* prevent memslot modifications on a pVM that has been run.
*/
- mutex_lock(&kvm->slots_lock);
- mutex_lock(&kvm->arch.config_lock);
- if (!pkvm_hyp_vm_is_created(kvm))
- ret = __pkvm_create_hyp_vm(kvm);
- mutex_unlock(&kvm->arch.config_lock);
- mutex_unlock(&kvm->slots_lock);
+ guard(mutex)(&kvm->slots_lock);
+ guard(mutex)(&kvm->arch.config_lock);
- return ret;
+ if (!pkvm_hyp_vm_is_created(kvm))
+ return __pkvm_create_hyp_vm(kvm);
+
+ return 0;
}
int pkvm_create_hyp_vcpu(struct kvm_vcpu *vcpu)
{
- int ret = 0;
+ guard(mutex)(&vcpu->kvm->arch.config_lock);
- mutex_lock(&vcpu->kvm->arch.config_lock);
if (!vcpu_get_flag(vcpu, VCPU_PKVM_FINALIZED))
- ret = __pkvm_create_hyp_vcpu(vcpu);
- mutex_unlock(&vcpu->kvm->arch.config_lock);
+ return __pkvm_create_hyp_vcpu(vcpu);
- return ret;
+ return 0;
}
void pkvm_destroy_hyp_vm(struct kvm *kvm)
{
- mutex_lock(&kvm->arch.config_lock);
+ guard(mutex)(&kvm->arch.config_lock);
__pkvm_destroy_hyp_vm(kvm);
- mutex_unlock(&kvm->arch.config_lock);
}
int pkvm_init_host_vm(struct kvm *kvm, unsigned long type)
diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
index 3b5dbe9a0a0e..e1389c525e9d 100644
--- a/arch/arm64/kvm/psci.c
+++ b/arch/arm64/kvm/psci.c
@@ -62,7 +62,6 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
struct vcpu_reset_state *reset_state;
struct kvm *kvm = source_vcpu->kvm;
struct kvm_vcpu *vcpu = NULL;
- int ret = PSCI_RET_SUCCESS;
unsigned long cpu_id;
cpu_id = smccc_get_arg1(source_vcpu);
@@ -78,14 +77,13 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
if (!vcpu)
return PSCI_RET_INVALID_PARAMS;
- spin_lock(&vcpu->arch.mp_state_lock);
+ guard(spinlock)(&vcpu->arch.mp_state_lock);
+
if (!kvm_arm_vcpu_stopped(vcpu)) {
if (kvm_psci_version(source_vcpu) != KVM_ARM_PSCI_0_1)
- ret = PSCI_RET_ALREADY_ON;
+ return PSCI_RET_ALREADY_ON;
else
- ret = PSCI_RET_INVALID_PARAMS;
-
- goto out_unlock;
+ return PSCI_RET_INVALID_PARAMS;
}
reset_state = &vcpu->arch.reset_state;
@@ -113,9 +111,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
WRITE_ONCE(vcpu->arch.mp_state.mp_state, KVM_MP_STATE_RUNNABLE);
kvm_vcpu_wake_up(vcpu);
-out_unlock:
- spin_unlock(&vcpu->arch.mp_state_lock);
- return ret;
+ return PSCI_RET_SUCCESS;
}
static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu)
@@ -176,9 +172,8 @@ static void kvm_prepare_system_event(struct kvm_vcpu *vcpu, u32 type, u64 flags)
* re-initialized.
*/
kvm_for_each_vcpu(i, tmp, vcpu->kvm) {
- spin_lock(&tmp->arch.mp_state_lock);
+ guard(spinlock)(&tmp->arch.mp_state_lock);
WRITE_ONCE(tmp->arch.mp_state.mp_state, KVM_MP_STATE_STOPPED);
- spin_unlock(&tmp->arch.mp_state_lock);
}
kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index b963fd975aac..60969d90bdd3 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -193,10 +193,10 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu)
bool loaded;
u32 pstate;
- spin_lock(&vcpu->arch.mp_state_lock);
- reset_state = vcpu->arch.reset_state;
- vcpu->arch.reset_state.reset = false;
- spin_unlock(&vcpu->arch.mp_state_lock);
+ scoped_guard(spinlock, &vcpu->arch.mp_state_lock) {
+ reset_state = vcpu->arch.reset_state;
+ vcpu->arch.reset_state.reset = false;
+ }
preempt_disable();
loaded = (vcpu->cpu != -1);
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [PATCH v1 01/11] KVM: arm64: Add scoped resource management (guard) for hyp_spinlock
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260612065925.755562-1-tabba@google.com>
The nVHE hypervisor manages hyp_spinlock_t locks by hand across error
paths, where a missed unlock deadlocks the next CPU to take the lock.
Wire hyp_spinlock_t into <linux/cleanup.h> via DEFINE_LOCK_GUARD_1 so
callers can use guard(hyp_spinlock) and scoped_guard(hyp_spinlock),
letting later patches replace the manual lock/unlock pairs.
Signed-off-by: Fuad Tabba <tabba@google.com>
---
arch/arm64/kvm/hyp/include/nvhe/spinlock.h | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/kvm/hyp/include/nvhe/spinlock.h b/arch/arm64/kvm/hyp/include/nvhe/spinlock.h
index 7c7ea8c55405..63ba826d8e3d 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/spinlock.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/spinlock.h
@@ -13,6 +13,8 @@
#ifndef __ARM64_KVM_NVHE_SPINLOCK_H__
#define __ARM64_KVM_NVHE_SPINLOCK_H__
+#include <linux/cleanup.h>
+
#include <asm/alternative.h>
#include <asm/lse.h>
#include <asm/rwonce.h>
@@ -98,6 +100,10 @@ static inline void hyp_spin_unlock(hyp_spinlock_t *lock)
: "memory");
}
+DEFINE_LOCK_GUARD_1(hyp_spinlock, hyp_spinlock_t,
+ hyp_spin_lock(_T->lock),
+ hyp_spin_unlock(_T->lock))
+
static inline bool hyp_spin_is_locked(hyp_spinlock_t *lock)
{
hyp_spinlock_t lockval = READ_ONCE(*lock);
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [PATCH v1 00/11] KVM: arm64: Rework pKVM vCPU state synchronisation
From: tabba @ 2026-06-12 6:59 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Fuad Tabba, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
Hi folks,
Building on Will's pKVM infrastructure series [1], this series reworks
how pKVM moves vCPU state between the host and EL2, and stops copying a
non-protected guest's state on every world switch.
EL2 gains proper primitives for the state it transfers: vCPU lookup
helpers, and VGIC flush/sync that reduces how much host state EL2
dereferences. The series also moves some preparatory code (such as sys
reg access and PSCI helpers) to shared headers and HYP, and implements
lazy copying of a non-protected guest's register state back to the host
until the host actually needs it, instead of on every exit.
This is the first of two series moving pKVM vCPU state management to
EL2. The follow-up completes the job for protected VMs: state
isolation, PSCI handling at EL2, and the resulting API behaviour.
The series is structured as follows:
01-03: Guard/scoped-resource support for hyp_spinlock and KVM locking
(Marc asked for this to land as a prequel to a series that uses it).
04-07: Preparatory refactoring (MPIDR, sys reg access, vCPU reset, PSCI
helpers) to shared headers and HYP.
08: Host and hypervisor vCPU lookup primitives.
09-10: VGIC: reduce EL2's exposure to host state, add flush/sync primitives.
11: Lazy state sync for non-protected guests.
Based on v7.1-rc7.
[1] https://lore.kernel.org/all/20260105154939.11041-1-will@kernel.org/
Cheers,
/fuad
Fuad Tabba (8):
KVM: arm64: Add scoped resource management (guard) for hyp_spinlock
KVM: arm64: Use guard(hyp_spinlock) in pKVM hypervisor code
KVM: arm64: Use guard()/scoped_guard() in arm64 KVM EL1 code
KVM: arm64: Extract MPIDR computation into a shared header
KVM: arm64: Make vcpu_{read,write}_sys_reg available to HYP code
KVM: arm64: Factor out reusable vCPU reset helpers
KVM: arm64: Move PSCI helper functions to a shared header
KVM: arm64: Implement lazy vCPU state sync for non-protected guests
Marc Zyngier (3):
KVM: arm64: Add host and hypervisor vCPU lookup primitives
KVM: arm64: Minimise EL2's exposure of host VGIC state during world
switch
KVM: arm64: Add primitives to flush/sync the VGIC state at EL2
arch/arm64/include/asm/kvm_arm.h | 12 +
arch/arm64/include/asm/kvm_asm.h | 1 +
arch/arm64/include/asm/kvm_emulate.h | 80 ++++++-
arch/arm64/include/asm/kvm_host.h | 2 +
arch/arm64/kvm/arm.c | 21 +-
arch/arm64/kvm/handle_exit.c | 22 ++
arch/arm64/kvm/hyp/include/nvhe/spinlock.h | 6 +
arch/arm64/kvm/hyp/nvhe/ffa.c | 154 +++++--------
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 255 ++++++++++++++++++---
arch/arm64/kvm/hyp/nvhe/mm.c | 37 +--
arch/arm64/kvm/hyp/nvhe/page_alloc.c | 13 +-
arch/arm64/kvm/hyp/nvhe/pkvm.c | 86 +++----
arch/arm64/kvm/mmu.c | 80 +++----
arch/arm64/kvm/pkvm.c | 26 +--
arch/arm64/kvm/psci.c | 47 +---
arch/arm64/kvm/reset.c | 68 +-----
arch/arm64/kvm/sys_regs.c | 14 +-
arch/arm64/kvm/sys_regs.h | 19 ++
include/kvm/arm_psci.h | 28 +++
19 files changed, 562 insertions(+), 409 deletions(-)
base-commit: 4549871118cf616eecdd2d939f78e3b9e1dddc48
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply
* Re: [PATCH 0/3] ARM: dts: stm32: lxa: change stdout-path baud rate from 9600 to 115200
From: David Laight @ 2026-06-12 6:53 UTC (permalink / raw)
To: Ahmad Fatoum
Cc: Alexandre Torgue, Maxime Coquelin, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Leonard Göhrs,
Marc Kleine-Budde, Alexandre Torgue, devicetree, linux-stm32,
linux-arm-kernel, linux-kernel, kernel
In-Reply-To: <b4fd25b6-52e3-4b5e-8440-69545bce43e8@pengutronix.de>
On Thu, 11 Jun 2026 22:33:18 +0200
Ahmad Fatoum <a.fatoum@pengutronix.de> wrote:
> Hi David,
>
> On 6/11/26 21:43, David Laight wrote:
> > On Thu, 11 Jun 2026 20:12:32 +0200
> > Ahmad Fatoum <a.fatoum@pengutronix.de> wrote:
> >
> >> The LXA boards are the only STM32 boards that set stdout-path = &uart*
> >> instead of explicitly specifying a baud rate.
> >>
> >> This would mean the default of 9600 is used, but it goes unnoticed when
> >> booting normally as barebox fixes up a console= line that includes a
> >> baud rate.
> >>
> >> When EFI booting GRUB however, GRUB will not pass along the console=
> >> line and thus the board ends up with a 9600 baud Linux console,
> >> confusing users.
> >
> > Is it possible to determine the current baud rate (by reading the hardware
> > register) and default to that value.
> > Then if grub has initialised the uart the kernel will use the same
> > baud rate.
>
> I think so, yes. In addition to the register divider configuration, one
> would need the input clock rate as well, but that's not a problem.
>
> Do you know if any drivers already do this?
I've seen it done somewhere, certainly x86, but possibly NetBSD.
That would have been preserving the baud rate set by the bios.
You don't want the baud rate changing half way through the boot sequence.
David
>
> Nevertheless, I would like the LXA device trees changed, even if only
> to align them with all other existing STM32 device trees.
>
> Cheers,
> Ahmad
>
>
> >
> > David
> >
> >>
> >> This series fixes this. As the device trees were added at different
> >> times, they are fixed each in a separate commit with its own Fixes: tag.
> >>
> >> ---
> >> Ahmad Fatoum (3):
> >> ARM: dts: stm32: lxa-mc1: change stdout-path baud rate from 9600 to 115200
> >> ARM: dts: stm32: lxa-tac: change stdout-path baud rate from 9600 to 115200
> >> ARM: dts: stm32: fairytux2: change stdout-path baud rate from 9600 to 115200
> >>
> >> arch/arm/boot/dts/st/stm32mp153c-lxa-fairytux2.dtsi | 2 +-
> >> arch/arm/boot/dts/st/stm32mp157c-lxa-mc1.dts | 2 +-
> >> arch/arm/boot/dts/st/stm32mp15xc-lxa-tac.dtsi | 2 +-
> >> 3 files changed, 3 insertions(+), 3 deletions(-)
> >> ---
> >> base-commit: 4549871118cf616eecdd2d939f78e3b9e1dddc48
> >> change-id: 20260611-lxa-stdout-path-baudrate-7cf454cdae07
> >>
> >> Best regards,
> >> --
> >> Ahmad Fatoum <a.fatoum@pengutronix.de>
> >>
> >>
> >
> >
>
>
^ permalink raw reply
* [GIT PULL]arm64:Add eMMC controller support to the BST C1200 device tree
From: gordon.ge @ 2026-06-12 6:51 UTC (permalink / raw)
To: soc; +Cc: arnd, yangzh0906, linux-arm-kernel, bst-upstream
Hi Arnd, Thank you for pointing out the mistake.
---
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
https://github.com/BlackSesame-SoC/linux.git tags/bst-arm64-emmc-driver-dts-for-v7.2
for you to fetch changes up to 6191a61ec9d9d8f1d1d1d6bfcb6d303be76c2804:
arm64: dts: bst: enable eMMC controller in C1200 (2026-06-12 14:17:45 +0800)
----------------------------------------------------------------
arm64: BST C1200 eMMC DTS for v7.2
Black Sesame Technologies:
Enable eMMC controller on BST C1200 CDCU1.0 board:
- Add mmc0 node in bstc1200.dtsi (DWCMSHC SDHCI controller)
- Add fixed clock definition and reserved SRAM bounce buffer
- Enable mmc0 with 8-bit bus on CDCU1.0 ADAS 4C2G board
The MMC driver was merged via mmc-next in v7.1-rc1.
this is the remaining DTS piece.
Signed-off-by: Gordon Ge <gordon.ge@bst.ai>
----------------------------------------------------------------
Albert Yang (1):
arm64: dts: bst: enable eMMC controller in C1200
.../arm64/boot/dts/bst/bstc1200-cdcu1.0-adas_4c2g.dts | 19 +++++++++++++++++++
arch/arm64/boot/dts/bst/bstc1200.dtsi | 18 ++++++++++++++++++
2 files changed, 37 insertions(+)
^ permalink raw reply
* [GIT PULL]arm64: BST C1200 eMMC defconfig for v7.2
From: gordon.ge @ 2026-06-12 6:47 UTC (permalink / raw)
To: soc; +Cc: arnd, yangzh0906, linux-arm-kernel, bst-upstream
Hi Arnd, Thank you for pointing out the mistake.
---
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
https://github.com/BlackSesame-SoC/linux.git tags/bst-arm64-emmc-driver-defconfig-for-v7.2
for you to fetch changes up to 22ca5df7c9d25077e44e33fb5751583aa79ee21a:
arm64: defconfig: enable BST SDHCI controller (2026-06-12 14:21:04 +0800)
----------------------------------------------------------------
arm64: BST C1200 eMMC defconfig for v7.2
Black Sesame Technologies:
Enable eMMC controller on BST C1200 CDCU1.0 board:
- Enable CONFIG_MMC_SDHCI_BST=y in arm64 defconfig
The MMC driver was merged via mmc-next in v7.1-rc1.
This is the remaining defconfig piece.
Signed-off-by: Gordon Ge <gordon.ge@bst.ai>
----------------------------------------------------------------
Albert Yang (1):
arm64: defconfig: enable BST SDHCI controller
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)
^ permalink raw reply
* [GIT PULL]Add eMMC controller support to the BST C1200 device tree
From: gordon.ge @ 2026-06-12 6:46 UTC (permalink / raw)
To: soc; +Cc: arnd, yangzh0906, linux-arm-kernel, bst-upstream
Hi Arnd, Thank you for pointing out the mistake.
---
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
https://github.com/BlackSesame-SoC/linux.git tags/bst-arm64-emmc-driver-dts-for-v7.2
for you to fetch changes up to 6191a61ec9d9d8f1d1d1d6bfcb6d303be76c2804:
arm64: dts: bst: enable eMMC controller in C1200 (2026-06-12 14:17:45 +0800)
----------------------------------------------------------------
arm64: BST C1200 eMMC DTS for v7.2
Black Sesame Technologies:
Enable eMMC controller on BST C1200 CDCU1.0 board:
- Add mmc0 node in bstc1200.dtsi (DWCMSHC SDHCI controller)
- Add fixed clock definition and reserved SRAM bounce buffer
- Enable mmc0 with 8-bit bus on CDCU1.0 ADAS 4C2G board
The MMC driver was merged via mmc-next in v7.1-rc1.
this is the remaining DTS piece.
Signed-off-by: Gordon Ge <gordon.ge@bst.ai>
----------------------------------------------------------------
Albert Yang (1):
arm64: dts: bst: enable eMMC controller in C1200
.../arm64/boot/dts/bst/bstc1200-cdcu1.0-adas_4c2g.dts | 19 +++++++++++++++++++
arch/arm64/boot/dts/bst/bstc1200.dtsi | 18 ++++++++++++++++++
2 files changed, 37 insertions(+)
^ permalink raw reply
* Re: [PATCH] arm64: dts: aspeed: Fix duplicate pinctrl labels and address scheme
From: Andrew Jeffery @ 2026-06-12 6:42 UTC (permalink / raw)
To: Ryan Chen, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Joel Stanley, Arnd Bergmann
Cc: devicetree, linux-arm-kernel, linux-aspeed, linux-kernel
In-Reply-To: <20260611-dtsi_fix-v1-1-ef2b7cd86d6d@aspeedtech.com>
Hi Ryan,
On Thu, 2026-06-11 at 14:50 +0800, Ryan Chen wrote:
> Fix duplicate pinctrl_tach{0-15} and pinctrl_n{cts,dcd,dsr,ri}5 labels
> in aspeed-g7-soc1-pinctrl.dtsi.
>
> Drop the cpu-index from secondary/tertiary container nodes: reduce the
> "#address-cells" from 2 to 1 and update ssp_nvic/tsp_nvic unit-address
> and reg accordingly. Also remove URL comments from the DTS.
>
> Suggested-by: Andrew Jeffery <andrew@codeconstruct.com.au>
> Fixes: e77bb5dc5759 ("arm64: dts: aspeed: Add initial AST27xx SoC device tree")
> Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
> ---
> This series contains follow-up fixes for the AST27xx DTS support that
> was merged into linux-next (e77bb5dc5759).
>
> Two issues were identified after merge by Andrew Jeffery during review
> of the pending v11 series:
These were identified by the sashiko bot, not so much by me, as I
hadn't got around to looking at the patches at the time. I did comment
in the replies though:
https://lore.kernel.org/all/20260609025708.ADBFE1F00893@smtp.kernel.org/
Separately, the series at hand was v9, so any subsequent revision would
have been v10, not v11. This isn't significant on its own, but it is
another contribution to the collection of small errors that are
accumulating at this point, which concerns me. Please take care.
>
> 1. Duplicate pinctrl state labels in aspeed-g7-soc1-pinctrl.dtsi caused
> dtc to abort with fatal label-redefinition errors.
However, it didn't. soc/dt @ 564edaca1486 ("Merge tag 'sunxi-dt-for-
7.2-2' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux
into soc/dt"), which includes the v9 patches at e77bb5dc5759 ("arm64:
dts: aspeed: Add initial AST27xx SoC device tree"), builds without
error.
Why? Well, the report from sashiko appears misleading. Usually
duplicate labels do cause an error, for example:
$ cat dle.dts
/dts-v1/;
/ {
inner: test1 {
prop-inner;
};
inner: test1 {
prop-inner;
};
};
$ dtc -o /dev/null dle.dts
dle.dts:6.15-8.4: ERROR (duplicate_node_names): /test1: Duplicate node name
ERROR: Input tree has errors, aborting (use -f to force output)
$ cat dle-1.dts
/dts-v1/;
/ { };
&{/} {
inner: test0 {
prop-inner;
};
inner: test1 {
prop-inner;
};
};
$ dtc -o /dev/null dle-1.dts
dle-1.dts:8.15-10.4: ERROR (duplicate_label): /test1: Duplicate label 'inner' on /test1 and /test0
ERROR: Input tree has errors, aborting (use -f to force output)
However, a relatively minimal reproduction of the case at hand is:
$ cat dlu.dts
/dts-v1/;
/ { };
&{/} {
inner: test1 {
prop-inner;
};
inner: test1 {
prop-inner;
};
};
$ dtc -o /dev/null dlu.dts
$
This doesn't error out. I recommend not assuming reports from the bot
are entirely accurate. Please test that its claims make sense before
proceeding.
While it's not good that there were duplicate nodes and labels, it is
good that you've tidied them up.
If there are modifications to the aspeed-g7-soc*-pinctrl.dtsi files in
the future, I ask that you them sorted first so we can minimise the
chance of falling into this trap again. The current order seems fairly
haphazard and likely contributed to the oversight.
>
> 2. The synthetic container nodes (secondary, tertiary) for sub-processor
I'm not sure synthetic is the right word here. We're still describing
the hardware, just components that have their own distinct address
spaces.
On a separate note, if you feel the need to make a list when describing
the change (e.g. in the commit message or patch notes) it's usually an
indicator that the change should be split into separate commits. Please
keep this in mind for future changes.
> interrupt controllers used a 2-cell address scheme to encode a
> <cpu-index reg-base> tuple. Since the cpu-index adds no value for
> nodes that are purely phandle anchors, Andrew requested we drop it
> and use the bare register address instead.
> ---
> arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi | 14 ++-
> .../boot/dts/aspeed/aspeed-g7-soc1-pinctrl.dtsi | 102 ---------------------
> 2 files changed, 6 insertions(+), 110 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi
> index ef283d95649a..58193c3c3696 100644
> --- a/arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi
> +++ b/arch/arm64/boot/dts/aspeed/aspeed-g7-a35.dtsi
> @@ -84,32 +84,30 @@ l2: l2-cache0 {
> };
>
> secondary {
> - #address-cells = <2>;
> - /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/of/address.c?h=v6.16#n491 */
> + #address-cells = <1>;
> #size-cells = <0>;
> - /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/of/address.c?h=v6.16#n430 */
>
> - ssp_nvic: interrupt-controller@1,e000e100 {
> + ssp_nvic: interrupt-controller@e000e100 {
> compatible = "arm,v7m-nvic";
> #interrupt-cells = <2>;
> #address-cells = <0>;
> interrupt-controller;
> - reg = <1 0xe000e100>;
> + reg = <0xe000e100>;
Some other cleanups to consider are ensuring the property ordering
conforms to the DTS coding style:
https://docs.kernel.org/devicetree/bindings/dts-coding-style.html#order-of-properties-in-device-node
The following grep is likely helpful:
git grep -C1 -F 'compatible =' arch/arm64/boot/dts/aspeed
Andrew
^ permalink raw reply
* Re: [PATCH v7 6/6] coco: guest: arm64: Replace dummy CCA device with sysfs ABI
From: Aneesh Kumar K.V @ 2026-06-12 6:07 UTC (permalink / raw)
To: Dan Williams (nvidia), linux-coco, linux-arm-kernel, linux-kernel
Cc: Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
Lorenzo Pieralisi, Mark Rutland, Sudeep Holla, Will Deacon,
Steven Price, Suzuki K Poulose, Andre Przywara
In-Reply-To: <6a2b103d77596_344af1000@djbw-dev.notmuch>
"Dan Williams (nvidia)" <djbw@kernel.org> writes:
> Aneesh Kumar K.V (Arm) wrote:
>> The SMCCC firmware driver now creates the arm-smccc platform device and
>> instantiates the CCA RSI auxiliary devices once the RSI ABI is discovered.
>> The arm64-specific arm-cca-dev platform device stub is therefore no longer
>> needed.
>>
>> However, userspace has used the arm-cca-dev platform device to detect Arm
>> CCA Realm guests [1]. Removing it without a replacement would break that
>> detection and would also leave userspace depending on kernel device-model
>> details.
>>
>> Add /sys/firmware/cca/realm_guest as a stable, architecture-provided ABI
>> for detecting whether the kernel is running as an Arm CCA Realm guest. The
>> file returns 1 in Realm world and 0 otherwise, similar to the existing s390
>> /sys/firmware/uv/prot_virt_guest interface for protected virtualization
>> guests.
>>
>> Remove the dummy arm-cca-dev registration now that userspace has a
>> dedicated CCA Realm guest indicator, and document the new ABI in
>> Documentation/ABI/testing/sysfs-firmware-cca.
>
> I would have expected an attribute in /sys/class/tsm/tsmX to be the
> common protected guest indicator. Then, if you need to distinguish the
> architecture that registered that tsm it would be in the name of the
> parent device for the tsm class device.
>
It is not clear whether we need this capability early, for example in an
initrd configuration before loading the TSM driver, since
systemd-detect-virt reports the CC architecture.
Also, the general feedback was not to depend on device names or paths to
identify a confidential computing guest. Hence, parsing paths such as
../../devices/arm-rmi-dev-1/tsm/tsm0 may not be advisable.
>
> That also gives you the property that a uevent has signalled the arrival
> of tsm guest services. Otherwise, userspace still needs some custom
> device-model details to know when it can start issuing tsm requests.
>
> Is auxilliary device arrival too late in the flow for what systemd
> needs?
Systemd uses that to build part of its trust model.
static int import_credentials_qemu(ImportCredentialsContext *c) {
if (detect_container() > 0) /* don't access /sys/ in a container */
return 0;
if (detect_confidential_virtualization() > 0) /* don't trust firmware if confidential VMs */
return 0;
....
It also use that to build environment settings
cv = detect_confidential_virtualization();
if (cv > 0) {
r = strv_env_assign(&nl, "SYSTEMD_CONFIDENTIAL_VIRTUALIZATION", confidential_virtualization_to_string(cv));
IIUC, this would require the facility to be present even before we can
load the full set of modules.
-aneesh
^ permalink raw reply
* Re: [PATCH 2/2] phy: nuvoton: Add MA35D1 USB2 OTG PHY driver
From: Joey Lu @ 2026-06-12 5:53 UTC (permalink / raw)
To: Vinod Koul
Cc: Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jacky Huang, Shan-Chun Hung, linux-phy, devicetree,
linux-arm-kernel, linux-kernel
In-Reply-To: <aiqWSGCuhAK7hoY9@vaman>
On 6/11/2026 7:04 PM, Vinod Koul wrote:
> On 04-06-26, 18:12, Joey Lu wrote:
>> Add a PHY driver for the USB 2.0 PHYs in the Nuvoton MA35D1 SoC,
>> intended for use with the EHCI and OHCI host controllers.
>>
>> The MA35D1 SoC has two USB ports:
>>
>> - USB0: an OTG port shared between a DWC2 gadget controller and
>> EHCI0/OHCI0 host controllers. A hardware mux automatically routes
>> the physical USB0 signals to the appropriate controller based on the
>> USB ID pin state. The DWC2 IP is device-only in hardware,
>> so host-mode operation on USB0 is handled entirely by EHCI0/OHCI0.
>>
>> - USB1: a dedicated host-only port served by EHCI1/OHCI1.
>>
>> The driver implements:
>> - Power-On Reset sequence with a guard that skips re-initialization if
>> the PHY is already operational. This protects PHY0 when the DWC2
>> gadget driver has already run its own init before EHCI0 probes.
>> - Optional resistor calibration trim via nuvoton,rcalcode.
>> - Optional over-current detect polarity via nuvoton,oc-active-high.
>> - For PHY0 only: a USB role switch that exposes the hardware ID pin
>> state (PWRONOTP[16]).
>>
>> Signed-off-by: Joey Lu <a0987203069@gmail.com>
>> ---
>> drivers/phy/nuvoton/Kconfig | 15 ++
>> drivers/phy/nuvoton/Makefile | 1 +
>> drivers/phy/nuvoton/phy-ma35d1-otg.c | 264 +++++++++++++++++++++++++++
>> 3 files changed, 280 insertions(+)
>> create mode 100644 drivers/phy/nuvoton/phy-ma35d1-otg.c
>>
>> diff --git a/drivers/phy/nuvoton/Kconfig b/drivers/phy/nuvoton/Kconfig
>> index d02cae2db315..5fdd13f841e7 100644
>> --- a/drivers/phy/nuvoton/Kconfig
>> +++ b/drivers/phy/nuvoton/Kconfig
>> @@ -10,3 +10,18 @@ config PHY_MA35_USB
>> help
>> Enable this to support the USB2.0 PHY on the Nuvoton MA35
>> series SoCs.
>> +
>> +config PHY_MA35_USB_OTG
>> + tristate "Nuvoton MA35 USB2.0 OTG PHY driver"
>> + depends on ARCH_MA35 || COMPILE_TEST
>> + depends on OF
>> + select GENERIC_PHY
>> + select MFD_SYSCON
>> + select USB_ROLE_SWITCH
>> + help
>> + Enable this to support the USB2.0 OTG PHY on the Nuvoton MA35
>> + series SoCs. This driver handles PHY initialization for the
>> + EHCI/OHCI host controllers, including per-PHY power-on reset,
>> + resistor calibration trim, and over-current polarity
>> + configuration. For the OTG port (PHY0), it also monitors the
>> + USB ID pin and registers a USB role switch.
>> diff --git a/drivers/phy/nuvoton/Makefile b/drivers/phy/nuvoton/Makefile
>> index 2937e3921898..3ecd76f35d7c 100644
>> --- a/drivers/phy/nuvoton/Makefile
>> +++ b/drivers/phy/nuvoton/Makefile
>> @@ -1,3 +1,4 @@
>> # SPDX-License-Identifier: GPL-2.0
>>
>> obj-$(CONFIG_PHY_MA35_USB) += phy-ma35d1-usb2.o
>> +obj-$(CONFIG_PHY_MA35_USB_OTG) += phy-ma35d1-otg.o
> Have you considered reusing usb2 driver with a different power_on
> function? Or handle the differences internally in the driver. There are
> few similarities in two and some things are different
Thank you for the excellent suggestion regarding reusing the existing
USB2 driver.
After further evaluation and local testing, I verified that it is
entirely feasible to reuse the driver. Consequently, I will drop the
separate phy-ma35d1-otg.c patch series and submit a new patch set that
extends the existing phy-ma35d1-usb2.c mainline driver.
In the upcoming patch series, I will expand the driver's capability from
a single-port PHY0 peripheral driver to a dual-port manager supporting
both PHY0 and PHY1, while integrating OTG features.
BR,
Joey
>
^ permalink raw reply
* Re: [PATCH v7 5/6] firmware: smccc: arm-cca-guest: Bind the TSM provider to an SMCCC device
From: Aneesh Kumar K.V @ 2026-06-12 5:47 UTC (permalink / raw)
To: Suzuki K Poulose, linux-coco, linux-arm-kernel, linux-kernel
Cc: Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
Lorenzo Pieralisi, Mark Rutland, Sudeep Holla, Will Deacon,
Steven Price, Andre Przywara
In-Reply-To: <b1d4b888-bdbe-4a45-8561-4f27e0e9a1de@arm.com>
Suzuki K Poulose <suzuki.poulose@arm.com> writes:
..
>> diff --git a/include/linux/arm-smccc-rsi.h b/include/linux/arm-smccc-rsi.h
>> index fddb77986f70..ae663aa8fd7f 100644
>> --- a/include/linux/arm-smccc-rsi.h
>> +++ b/include/linux/arm-smccc-rsi.h
>> @@ -8,6 +8,8 @@
>>
>> #include <linux/arm-smccc.h>
>>
>> +#define RSI_DEV_NAME "arm-rsi-dev"
>
> This shouldn't be here ? This is not part of the SMCCC RSI standard, but
> a linux thing. May be in drivers/firmware/../rsi.h ?
>
The name is used by the Arm SMCCC firmware driver
(drivers/firmware/smccc/smccc.c) and the arm-cca-guest driver.
Since it is used by the Arm SMCCC firmware driver, I used the above
header. We do not currently have a generic placeholder for RSI/RMI
definitions under drivers/.
-aneesh
^ permalink raw reply
* Re: [PATCH v2 05/16] usb: hub: Associate port@ fwnode with USB port device
From: Chen-Yu Tsai @ 2026-06-12 5:46 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Heikki Krogerus, Bartosz Golaszewski, Greg Kroah-Hartman,
Daniel Scally, Sakari Ailus, Rafael J. Wysocki, Danilo Krummrich,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Alan Stern, linux-acpi, driver-core,
linux-pm, linux-usb, devicetree, linux-mediatek, linux-arm-kernel,
linux-kernel, Manivannan Sadhasivam
In-Reply-To: <aisEccAOm3qoXjxd@ashevche-desk.local>
On Fri, Jun 12, 2026 at 3:54 AM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Thu, Jun 11, 2026 at 06:48:56PM +0300, Heikki Krogerus wrote:
> > On Thu, Jun 11, 2026 at 11:35:13AM +0200, Bartosz Golaszewski wrote:
> > > On Thu, Jun 11, 2026 at 10:37 AM Andy Shevchenko
> > > <andriy.shevchenko@linux.intel.com> wrote:
> > > > On Thu, Jun 11, 2026 at 04:20:58AM -0400, Bartosz Golaszewski wrote:
> > > > > On Wed, 10 Jun 2026 16:16:12 +0200, Andy Shevchenko
> > > > > <andriy.shevchenko@linux.intel.com> said:
> > > > > > On Wed, Jun 10, 2026 at 04:40:39PM +0800, Chen-Yu Tsai wrote:
> > > > > >> When a USB hub port is connected to a connector in a firmware node
> > > > > >> graph, the port itself has a node in the graph.
> > > > > >>
> > > > > >> Associate the port's firmware node with the USB port's device,
> > > > > >> usb_port::dev. This is used in later changes for the M.2 slot power
> > > > > >> sequencing provider to match against the requesting port.
> > > > > >
> > > > > > Okay, would this affect ACPI-based systems? if so, how?
> > > > > > Can you elaborate on that, please?
> > > > >
> > > > > Is it possible that there's an ACPI device node associated with the port like
> > > > > on some DT systems? I don't think so and there should be no impact IMO but I
> > > > > also don't know enough about ACPI.
> >
> > There are device nodes for the USB ports in ACPI, and I think they get
> > always assigned in drivers/usb/core/usb-acpi.c.
> >
> > > > The API is agnostic. There is a possibility to have software nodes associated
> > > > with the port. I think the best is to be sure that ACPI-aware people who are
> > > > experts in USB will check this (Heikki?).
> >
> > I can't say what's the impact from this patch - I'm not an expert with
> > this side of USB. Is there a danger that we end up overwriting the
> > ACPI node for the port, or something else?
>
> Exactly this one is my worrying, but I haven't checked the actual flow.
Looking through ACPI code, ACPI_COMPANION_SET() is used, which boils
down to
set_primary_fwnode(dev, acpi_fwnode_handle(acpi_dev))
This is called through
usb_hub_create_port_device()
device_register()
device_add()
device_platform_notify()
acpi_device_notify()
usb_acpi_find_companion()
usb_acpi_find_companion_for_port()
acpi_bind_one()
ACPI_COMPANION_SET()
set_primary_fwnode()
Looking at device_add_software_node(), all swnodes are secondary.
set_primary_fwnode() seems to be able to make the ACPI handle / fwnode
the primary, keeping any existing fwnode as the secondary. However
if we do end up assigning a primary fwnode to the device using
device_set_node() as in this patch, set_primary_fwnode() is going
to complain loudly.
On another front, the ACPI representation of the USB ports looks nothing
like the OF graphs, at least on my X1 Carbon:
For a usb port device on the root hub such as
/sys/bus/usb/devices/4-0:1.0/usb4-port1/firmware_node/path
looks like
\_SB_.PC00.XHCI.RHUB.SS01
while a usb port's firmware node link
/sys/bus/usb/devices/4-0:1.0/usb4-port1//firmware_node
resolves to
/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:19/device:1a/device:29
Neither looks anything like the graph "port" / "endpoint" node names.
So maybe we're in the clear here.
Besides the loud warning from set_primary_fwnode(), the major issue stemming
from a wrong node is that power management (through ACPI) is likely to fail.
If we're still concerned, I think we can skip the assignment if the fwnode
is an ACPI node, i.e. check it with is_acpi_node().
I've never worked on ACPI systems, so this is just me checking the code.
Thanks
ChenYu
^ permalink raw reply
* Re: [PATCH v7 3/6] firmware: smccc: Move RSI definitions to include/linux
From: Aneesh Kumar K.V @ 2026-06-12 5:41 UTC (permalink / raw)
To: Suzuki K Poulose, linux-coco, linux-arm-kernel, linux-kernel
Cc: Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
Lorenzo Pieralisi, Mark Rutland, Sudeep Holla, Will Deacon,
Steven Price, Andre Przywara
In-Reply-To: <b009e840-6b79-415c-a3da-705ea569af38@arm.com>
Suzuki K Poulose <suzuki.poulose@arm.com> writes:
> On 11/06/2026 14:04, Aneesh Kumar K.V (Arm) wrote:
>> The RSI SMCCC function IDs describe a firmware ABI and are not arm64
>> architecture specific definitions. Follow-up changes need to use them from
>> non-arch code, including drivers/firmware/smccc and the Arm CCA guest
>> driver.
>>
>> Move the RSI SMCCC definitions from arch/arm64/include/asm/ to
>> include/linux/ so they can be shared with the driver code. This also
>> keeps the firmware interface outside architecture code, as requested [1].
>
> Please could we also mention about moving the "wrappers" only used by
> drivers accordingly ?
>
Added this
Not all helpers in rsi_cmds.h are used by architecture code. The
attestation token helper wrappers are only used by the Arm CCA guest
driver, so move them to a driver-private header under
drivers/virt/coco/arm-cca-guest/. Keep the remaining RSI command helpers,
which are shared by architecture code and drivers, in the arm64 header.
>
>>
>> [1] https://lore.kernel.org/all/agsNO9cc7H-b0H8L@willie-the-truck
>>
>> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
>> ---
>> arch/arm64/include/asm/rsi_cmds.h | 74 +---------------
>> .../virt/coco/arm-cca-guest/arm-cca-guest.c | 2 +
>> drivers/virt/coco/arm-cca-guest/rsi.h | 84 +++++++++++++++++++
>> .../linux/arm-smccc-rsi.h | 6 +-
>> 4 files changed, 90 insertions(+), 76 deletions(-)
>> create mode 100644 drivers/virt/coco/arm-cca-guest/rsi.h
>> rename arch/arm64/include/asm/rsi_smc.h => include/linux/arm-smccc-rsi.h (98%)
>>
>> diff --git a/arch/arm64/include/asm/rsi_cmds.h b/arch/arm64/include/asm/rsi_cmds.h
>> index 2c8763876dfb..633123a4e5d5 100644
>> --- a/arch/arm64/include/asm/rsi_cmds.h
>> +++ b/arch/arm64/include/asm/rsi_cmds.h
>> @@ -8,10 +8,9 @@
>>
>> #include <linux/arm-smccc.h>
>> #include <linux/string.h>
>> +#include <linux/arm-smccc-rsi.h>
>
> super minor nit: Please keep them in the alphabetical order.
>
> With that:
>
> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>
Thanks
-aneesh
^ permalink raw reply
* [RFC PATCH 2/2] kasan: hw_tags: Add boot option to elide free time poisoning
From: Dev Jain @ 2026-06-12 4:44 UTC (permalink / raw)
To: ryabinin.a.a, akpm, corbet
Cc: Dev Jain, glider, andreyknvl, dvyukov, vincenzo.frascino,
kasan-dev, linux-mm, linux-kernel, skhan, workflows, linux-doc,
linux-arm-kernel, ryan.roberts, anshuman.khandual, kaleshsingh,
21cnbao, david, will, catalin.marinas
In-Reply-To: <20260612044425.763060-1-dev.jain@arm.com>
Introduce a boot option to tag only at allocation time of the objects. This
reduces KASAN MTE overhead, the tradeoff being reduced ability
of catching bugs.
Now, when a memory object will be freed, it will retain the random tag it
had at allocation time. This compromises on catching UAF bugs, till the
time the object is not reallocated.
Hence, not catching "use-after-free-before-reallocation" and not catching
"double-free" will be the compromise for reduced KASAN overhead.
Keep this as a boot time feature to prevent building two kernel images.
To implement the feature, we need to effectively render kasan_poison()
redundant for hw tags case, but keep it working in the case where it is
used not in an object-freeing code path, but the redzoning path (which
means, poisoning the tail end of a vmalloc or kmalloc allocation).
We achieve this by overloading the poison values for the hw tags case: we
define the four poison values as 0x0E, 0x1E, 0x2E, 0x3E. In kasan_poison(),
if we arrive with KASAN_SLAB_REDZONE or KASAN_PAGE_REDZONE, do a bitwise
OR on the value of the tag to make it equal to KASAN_TAG_INVALID.
If not, then, if init is true, zero out the memory and bail out.
Signed-off-by: Dev Jain <dev.jain@arm.com>
---
Documentation/dev-tools/kasan.rst | 4 +++
mm/kasan/hw_tags.c | 43 ++++++++++++++++++++++++++++++-
mm/kasan/kasan.h | 23 ++++++++++++++++-
3 files changed, 68 insertions(+), 2 deletions(-)
diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst
index 4968b2aa60c80..b0c30584b5062 100644
--- a/Documentation/dev-tools/kasan.rst
+++ b/Documentation/dev-tools/kasan.rst
@@ -146,6 +146,10 @@ disabling KASAN altogether or controlling its features:
- ``kasan.vmalloc=off`` or ``=on`` disables or enables tagging of vmalloc
allocations (default: ``on``).
+- ``kasan.tag_only_on_alloc=off`` or ``=on`` disables or enables skipping
+ free-time tagging (poisoning) while keeping allocation-time tagging enabled
+ (default: ``off``).
+
- ``kasan.page_alloc.sample=<sampling interval>`` makes KASAN tag only every
Nth page_alloc allocation with the order equal or greater than
``kasan.page_alloc.sample.order``, where N is the value of the ``sample``
diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index c1a2b48808ed7..a392e34d11e3a 100644
--- a/mm/kasan/hw_tags.c
+++ b/mm/kasan/hw_tags.c
@@ -41,9 +41,16 @@ enum kasan_arg_vmalloc {
KASAN_ARG_VMALLOC_ON,
};
+enum kasan_arg_tag_only_on_alloc {
+ KASAN_ARG_TAG_ONLY_ON_ALLOC_DEFAULT,
+ KASAN_ARG_TAG_ONLY_ON_ALLOC_OFF,
+ KASAN_ARG_TAG_ONLY_ON_ALLOC_ON,
+};
+
static enum kasan_arg kasan_arg __ro_after_init;
static enum kasan_arg_mode kasan_arg_mode __ro_after_init;
static enum kasan_arg_vmalloc kasan_arg_vmalloc __initdata;
+static enum kasan_arg_tag_only_on_alloc kasan_arg_tag_only_on_alloc __initdata;
/*
* Whether the selected mode is synchronous, asynchronous, or asymmetric.
@@ -63,6 +70,10 @@ EXPORT_SYMBOL_GPL(kasan_flag_vmalloc);
/* Whether to check write accesses only. */
static bool kasan_flag_write_only = false;
+/* Whether to skip free-time tagging. */
+DEFINE_STATIC_KEY_FALSE(kasan_flag_tag_only_on_alloc);
+EXPORT_SYMBOL_GPL(kasan_flag_tag_only_on_alloc);
+
#define PAGE_ALLOC_SAMPLE_DEFAULT 1
#define PAGE_ALLOC_SAMPLE_ORDER_DEFAULT 3
@@ -154,6 +165,23 @@ static int __init early_kasan_flag_write_only(char *arg)
}
early_param("kasan.write_only", early_kasan_flag_write_only);
+/* kasan.tag_only_on_alloc=off/on */
+static int __init early_kasan_flag_tag_only_on_alloc(char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+
+ if (!strcmp(arg, "off"))
+ kasan_arg_tag_only_on_alloc = KASAN_ARG_TAG_ONLY_ON_ALLOC_OFF;
+ else if (!strcmp(arg, "on"))
+ kasan_arg_tag_only_on_alloc = KASAN_ARG_TAG_ONLY_ON_ALLOC_ON;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+early_param("kasan.tag_only_on_alloc", early_kasan_flag_tag_only_on_alloc);
+
static inline const char *kasan_mode_info(void)
{
if (kasan_mode == KASAN_MODE_ASYNC)
@@ -270,14 +298,27 @@ void __init kasan_init_hw_tags(void)
break;
}
+ switch (kasan_arg_tag_only_on_alloc) {
+ case KASAN_ARG_TAG_ONLY_ON_ALLOC_DEFAULT:
+ /* Default is specified by kasan_flag_tag_only_on_alloc. */
+ break;
+ case KASAN_ARG_TAG_ONLY_ON_ALLOC_OFF:
+ static_branch_disable(&kasan_flag_tag_only_on_alloc);
+ break;
+ case KASAN_ARG_TAG_ONLY_ON_ALLOC_ON:
+ static_branch_enable(&kasan_flag_tag_only_on_alloc);
+ break;
+ }
+
kasan_init_tags();
/* KASAN is now initialized, enable it. */
kasan_enable();
- pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s, vmalloc=%s, stacktrace=%s, write_only=%s)\n",
+ pr_info("KernelAddressSanitizer initialized (hw-tags, mode=%s, vmalloc=%s, tag_only_on_alloc=%s, stacktrace=%s, write_only=%s)\n",
kasan_mode_info(),
str_on_off(kasan_vmalloc_enabled()),
+ str_on_off(kasan_tag_only_on_alloc_enabled()),
str_on_off(kasan_stack_collection_enabled()),
str_on_off(kasan_flag_write_only));
}
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index fc9169a547662..4fa8abb312faa 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -33,6 +33,7 @@ static inline bool kasan_stack_collection_enabled(void)
#include "../slab.h"
DECLARE_STATIC_KEY_TRUE(kasan_flag_vmalloc);
+DECLARE_STATIC_KEY_FALSE(kasan_flag_tag_only_on_alloc);
enum kasan_mode {
KASAN_MODE_SYNC,
@@ -52,6 +53,11 @@ static inline bool kasan_vmalloc_enabled(void)
return static_branch_likely(&kasan_flag_vmalloc);
}
+static inline bool kasan_tag_only_on_alloc_enabled(void)
+{
+ return static_branch_unlikely(&kasan_flag_tag_only_on_alloc);
+}
+
static inline bool kasan_async_fault_possible(void)
{
return kasan_mode == KASAN_MODE_ASYNC || kasan_mode == KASAN_MODE_ASYMM;
@@ -145,12 +151,17 @@ static inline bool kasan_requires_meta(void)
#define KASAN_SLAB_REDZONE 0xFC /* redzone for slab object */
#define KASAN_SLAB_FREE 0xFB /* freed slab object */
#define KASAN_VMALLOC_INVALID 0xF8 /* inaccessible space in vmap area */
+#elif defined(CONFIG_KASAN_HW_TAGS)
+#define KASAN_PAGE_FREE 0x0E
+#define KASAN_PAGE_REDZONE 0x1E
+#define KASAN_SLAB_REDZONE 0x2E
+#define KASAN_SLAB_FREE 0x3E
#else
#define KASAN_PAGE_FREE KASAN_TAG_INVALID
#define KASAN_PAGE_REDZONE KASAN_TAG_INVALID
#define KASAN_SLAB_REDZONE KASAN_TAG_INVALID
#define KASAN_SLAB_FREE KASAN_TAG_INVALID
-#define KASAN_VMALLOC_INVALID KASAN_TAG_INVALID /* only used for SW_TAGS */
+#define KASAN_VMALLOC_INVALID KASAN_TAG_INVALID
#endif
#ifdef CONFIG_KASAN_GENERIC
@@ -478,6 +489,16 @@ static inline u8 kasan_random_tag(void) { return 0; }
static inline void kasan_poison(const void *addr, size_t size, u8 value, bool init)
{
+ if (kasan_tag_only_on_alloc_enabled()) {
+ if ((value != KASAN_SLAB_REDZONE) && (value != KASAN_PAGE_REDZONE)) {
+ if (init)
+ memset((void *)kasan_reset_tag(addr), 0, size);
+ return;
+ }
+ }
+
+ value |= 0xF0;
+
if (WARN_ON((unsigned long)addr & KASAN_GRANULE_MASK))
return;
if (WARN_ON(size & KASAN_GRANULE_MASK))
--
2.43.0
^ permalink raw reply related
* [RFC PATCH 1/2] kasan: hw_tags: Use KASAN_PAGE_REDZONE for vmalloc redzoning
From: Dev Jain @ 2026-06-12 4:44 UTC (permalink / raw)
To: ryabinin.a.a, akpm, corbet
Cc: Dev Jain, glider, andreyknvl, dvyukov, vincenzo.frascino,
kasan-dev, linux-mm, linux-kernel, skhan, workflows, linux-doc,
linux-arm-kernel, ryan.roberts, anshuman.khandual, kaleshsingh,
21cnbao, david, will, catalin.marinas
In-Reply-To: <20260612044425.763060-1-dev.jain@arm.com>
In preparation for adding "tag only on alloc" boot time option, use
KASAN_PAGE_REDZONE instead of KASAN_TAG_INVALID for poisoning the tail end
of the vmalloc allocation.
Although both values are the same for hw tags, KASAN_SLAB_REDZONE is used
for poisoning the tail end of a kmalloc object allocation, so maintain
the pattern.
Signed-off-by: Dev Jain <dev.jain@arm.com>
---
mm/kasan/hw_tags.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index cbef5e450954e..c1a2b48808ed7 100644
--- a/mm/kasan/hw_tags.c
+++ b/mm/kasan/hw_tags.c
@@ -375,7 +375,7 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
redzone_start = round_up((unsigned long)start + size,
KASAN_GRANULE_SIZE);
redzone_size = round_up(redzone_start, PAGE_SIZE) - redzone_start;
- kasan_poison((void *)redzone_start, redzone_size, KASAN_TAG_INVALID,
+ kasan_poison((void *)redzone_start, redzone_size, KASAN_PAGE_REDZONE,
flags & KASAN_VMALLOC_INIT);
/*
--
2.43.0
^ permalink raw reply related
* [RFC PATCH 0/2] kasan: hw_tags: Add option to tag only at allocation time
From: Dev Jain @ 2026-06-12 4:44 UTC (permalink / raw)
To: ryabinin.a.a, akpm, corbet
Cc: Dev Jain, glider, andreyknvl, dvyukov, vincenzo.frascino,
kasan-dev, linux-mm, linux-kernel, skhan, workflows, linux-doc,
linux-arm-kernel, ryan.roberts, anshuman.khandual, kaleshsingh,
21cnbao, david, will, catalin.marinas
Introduce a boot option to tag only at allocation time of the objects. This
reduces KASAN MTE overhead, the tradeoff being reduced ability of
catching bugs.
Now, when a memory object will be freed, it will retain the random tag it
had at allocation time. This compromises on catching UAF bugs, till the
time the object is not reallocated, at which point it will have a new
random tag.
Hence, not catching "use-after-free-before-reallocation" and not catching
"double-free" will be the compromise for reduced KASAN overhead.
This is an RFC because we are not clear about the performance benefit.
Android folks, please help with testing!
---
Applies on Linus master (9716c086c8e8).
Dev Jain (2):
kasan: hw_tags: Use KASAN_PAGE_REDZONE for vmalloc redzoning
kasan: hw_tags: Add boot option to elide free time poisoning
Documentation/dev-tools/kasan.rst | 4 +++
mm/kasan/hw_tags.c | 45 +++++++++++++++++++++++++++++--
mm/kasan/kasan.h | 23 +++++++++++++++-
3 files changed, 69 insertions(+), 3 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH 0/2] ACPM cpufreq with fast_switch support, fast path xfer in ACPM
From: Alexey Klimov @ 2026-06-12 4:36 UTC (permalink / raw)
To: Tudor Ambarus, Sam Protsenko, Krzysztof Kozlowski, Peter Griffin,
Alim Akhtar, Rafael J. Wysocki, Viresh Kumar
Cc: Sudeep Holla, linux-samsung-soc, linux-arm-kernel, linux-pm,
kernel-team, linux-kernel
In-Reply-To: <20260612-acpm-fast-xfer-v1-0-1aa6cd2268ba@linaro.org>
On Fri Jun 12, 2026 at 5:34 AM BST, Alexey Klimov wrote:
> The series implements acpm_do_xfer_fast() that does as little
> as possible to make it usabe in atomic context to eventually
Sorry, I forgot to add RFC tags. This is RFC/work-in-progress
series.
Thanks,
Alexey
^ permalink raw reply
* [PATCH 1/2] firmware: samsung: acpm: add fire-and-forget xfer support
From: Alexey Klimov @ 2026-06-12 4:34 UTC (permalink / raw)
To: Tudor Ambarus, Sam Protsenko, Krzysztof Kozlowski, Peter Griffin,
Alim Akhtar, Rafael J. Wysocki, Viresh Kumar
Cc: Sudeep Holla, linux-samsung-soc, linux-arm-kernel, linux-pm,
kernel-team, linux-kernel, Alexey Klimov
In-Reply-To: <20260612-acpm-fast-xfer-v1-0-1aa6cd2268ba@linaro.org>
The current ACPM IPC protocol relies on synchronous polling
(acpm_dequeue_by_polling) to process mailbox responses.
For CPU DVFS, cpufreqs schedutil governor requires ->fast_switch() to
execute in an atomic context. Waiting for firmware acknowledgments
in a loop in such case also using udelay(20) under spinlock doesn't
make a lot of sense. Experiemnts on Exynos850 showed that even with
removed udelay() or with it significantly decreased, the firmware
processing takes 15us...250us.
Introduce acpm_do_xfer_fast(), which implements a fire-and-forget
asynchronous path:
- utilizes spin_trylock() to exit without sleeping if the channel
is busy;
- adds/sends the message and kicks the mailbox doorbell;
- exits immediately, allowing fast_switch to complete quickly.
To prevent the unread asynchronous responses from permanently exhausting
the 63-slot sequence ring buffer, implement an acpm_drain_stale_rx().
This drains the RX queue during the fast path:
- copies payloads and sets completion flags for sleeping
synchronous users;
- explicitly acks 'is_async' messages.
Hooks it up in the right places of ACPM dvfs machinery.
The channels {tx,rx}_lock needs probably a bit of rework to
differentiate between channels that support or need fast xfer and
those that do not.
Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
---
drivers/firmware/samsung/exynos-acpm-dvfs.c | 14 ++
drivers/firmware/samsung/exynos-acpm-dvfs.h | 3 +
drivers/firmware/samsung/exynos-acpm.c | 142 +++++++++++++++++++--
drivers/firmware/samsung/exynos-acpm.h | 3 +
.../linux/firmware/samsung/exynos-acpm-protocol.h | 2 +
5 files changed, 154 insertions(+), 10 deletions(-)
diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.c b/drivers/firmware/samsung/exynos-acpm-dvfs.c
index 7266312ef5a6..5411aa121b73 100644
--- a/drivers/firmware/samsung/exynos-acpm-dvfs.c
+++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c
@@ -43,6 +43,20 @@ int acpm_dvfs_set_rate(struct acpm_handle *handle,
return acpm_do_xfer(handle, &xfer);
}
+int acpm_dvfs_set_rate_fast(struct acpm_handle *handle,
+ unsigned int acpm_chan_id, unsigned int clk_id,
+ unsigned long rate)
+{
+ struct acpm_xfer xfer = {0};
+ u32 cmd[4];
+
+ acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate);
+ acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, false);
+
+ return acpm_do_xfer_fast(handle, &xfer);
+}
+
+
static void acpm_dvfs_init_get_rate_cmd(u32 cmd[4], unsigned int clk_id)
{
cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id);
diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.h b/drivers/firmware/samsung/exynos-acpm-dvfs.h
index b37b15426102..107d9aa27690 100644
--- a/drivers/firmware/samsung/exynos-acpm-dvfs.h
+++ b/drivers/firmware/samsung/exynos-acpm-dvfs.h
@@ -14,6 +14,9 @@ struct acpm_handle;
int acpm_dvfs_set_rate(struct acpm_handle *handle,
unsigned int acpm_chan_id, unsigned int id,
unsigned long rate);
+int acpm_dvfs_set_rate_fast(struct acpm_handle *handle,
+ unsigned int acpm_chan_id, unsigned int id,
+ unsigned long rate);
unsigned long acpm_dvfs_get_rate(struct acpm_handle *handle,
unsigned int acpm_chan_id,
unsigned int clk_id);
diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c
index 942a2e9f02f5..3caab47adf26 100644
--- a/drivers/firmware/samsung/exynos-acpm.c
+++ b/drivers/firmware/samsung/exynos-acpm.c
@@ -20,13 +20,13 @@
#include <linux/mailbox/exynos-message.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/math.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/types.h>
#include "exynos-acpm.h"
@@ -109,12 +109,16 @@ struct acpm_queue {
* @rxcnt: expected length of the response in 32-bit words.
* @completed: flag indicating if the firmware response has been fully
* processed.
+ * @is_async: For fire-and-forget xfer. Set to true to just ack
+ * responses without processing.
+ * By default, set to false for regular senders.
*/
struct acpm_rx_data {
u32 *cmd __counted_by_ptr(cmdcnt);
size_t cmdcnt;
size_t rxcnt;
bool completed;
+ bool is_async;
};
#define ACPM_SEQNUM_MAX 64
@@ -148,8 +152,8 @@ struct acpm_chan {
struct acpm_info *acpm;
struct acpm_queue tx;
struct acpm_queue rx;
- struct mutex tx_lock;
- struct mutex rx_lock;
+ spinlock_t tx_lock;
+ spinlock_t rx_lock;
unsigned int qlen;
unsigned int mlen;
@@ -232,7 +236,7 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer,
*native_match = false;
- guard(mutex)(&achan->rx_lock);
+ guard(spinlock)(&achan->rx_lock);
rx_front = readl(achan->rx.front);
i = readl(achan->rx.rear);
@@ -297,6 +301,9 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer,
*native_match = true;
}
+ if (rx_data->is_async)
+ clear_bit_unlock(seqnum, achan->bitmap_seqnum);
+
i = (i + 1) % achan->qlen;
} while (i != rx_front);
@@ -306,6 +313,57 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer,
return 0;
}
+static void acpm_drain_stale_rx(struct acpm_chan *achan)
+{
+ u32 rx_front, seqnum, rx_seqnum;
+ const void __iomem *base = achan->rx.base;
+ struct acpm_rx_data *rx_data;
+ u32 i, val, mlen = achan->mlen;
+
+ if (!spin_trylock(&achan->rx_lock))
+ return;
+
+ rx_front = readl(achan->rx.front);
+ i = readl(achan->rx.rear);
+
+ /* Get out quick if we nothing to process */
+ if (i == rx_front) {
+ spin_unlock(&achan->rx_lock);
+ return;
+ }
+
+ do {
+ val = readl(base + mlen * i);
+ rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, val);
+
+ if (rx_seqnum) {
+ seqnum = rx_seqnum - 1;
+ rx_data = &achan->rx_data[seqnum];
+
+ if (rx_data->rxcnt)
+ __ioread32_copy(rx_data->cmd, base + mlen * i, rx_data->rxcnt);
+
+ /* Signal the waiting thread (if any). If it hasn't started
+ * spinning yet, it will see this instantly when it does. */
+ smp_store_release(&rx_data->completed, true);
+
+ /* Only free the sequence number if it belongs to an
+ * async request. Senders who use regular acpm_do_xfer()
+ * will free their own sequence numbers in
+ * acpm_dequeue_by_polling().
+ */
+ if (rx_data->is_async)
+ clear_bit_unlock(seqnum, achan->bitmap_seqnum);
+ }
+
+ i = (i + 1) % achan->qlen;
+ } while (i != rx_front);
+
+ writel(rx_front, achan->rx.rear);
+
+ spin_unlock(&achan->rx_lock);
+}
+
/**
* acpm_dequeue_by_polling() - RX dequeue by polling.
* @achan: ACPM channel info.
@@ -388,15 +446,15 @@ static int acpm_wait_for_queue_slots(struct acpm_chan *achan, u32 next_tx_front)
}
/**
- * acpm_prepare_xfer() - prepare a transfer before writing the message to the
+ * __acpm_prepare_xfer() - prepare a transfer before writing the message to the
* TX queue.
* @achan: ACPM channel info.
* @xfer: reference to the transfer being prepared.
*
* Return: 0 on success, -errno otherwise.
*/
-static int acpm_prepare_xfer(struct acpm_chan *achan,
- const struct acpm_xfer *xfer)
+static int __acpm_prepare_xfer(struct acpm_chan *achan,
+ const struct acpm_xfer *xfer, bool is_async)
{
struct acpm_rx_data *rx_data;
u32 *txd = (u32 *)xfer->txd;
@@ -429,6 +487,7 @@ static int acpm_prepare_xfer(struct acpm_chan *achan,
/* Clear data for upcoming responses */
rx_data = &achan->rx_data[bit];
rx_data->completed = false;
+ rx_data->is_async = is_async;
memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->cmdcnt);
/* zero means no response expected */
rx_data->rxcnt = xfer->rxcnt;
@@ -436,6 +495,12 @@ static int acpm_prepare_xfer(struct acpm_chan *achan,
return 0;
}
+static int acpm_prepare_xfer(struct acpm_chan *achan,
+ const struct acpm_xfer *xfer)
+{
+ return __acpm_prepare_xfer(achan, xfer, false);
+}
+
/**
* acpm_wait_for_message_response - an helper to group all possible ways of
* waiting for a synchronous message response.
@@ -452,6 +517,62 @@ static int acpm_wait_for_message_response(struct acpm_chan *achan,
return acpm_dequeue_by_polling(achan, xfer);
}
+int acpm_do_xfer_fast(struct acpm_handle *handle, const struct acpm_xfer *xfer)
+{
+ struct acpm_info *acpm = handle_to_acpm_info(handle);
+ struct exynos_mbox_msg msg;
+ struct acpm_chan *achan;
+ u32 idx, tx_front;
+ int ret;
+
+ if (xfer->acpm_chan_id >= acpm->num_chans)
+ return -EINVAL;
+
+ achan = &acpm->chans[xfer->acpm_chan_id];
+
+ msg.chan_id = xfer->acpm_chan_id;
+ msg.chan_type = EXYNOS_MBOX_CHAN_TYPE_DOORBELL;
+
+ /* Ideally should be a raw_spin_trylock.
+ * If we can't get it immediately, then give up.
+ */
+ if (!spin_trylock(&achan->tx_lock))
+ return -EBUSY;
+
+ /* clean up/ack previous responses */
+ acpm_drain_stale_rx(achan);
+
+ tx_front = readl(achan->tx.front);
+ idx = (tx_front + 1) % achan->qlen;
+
+ if (idx == readl(achan->tx.rear)) {
+ /* stalled; queue is full? */
+ spin_unlock(&achan->tx_lock);
+ return -EBUSY;
+ }
+
+ ret = __acpm_prepare_xfer(achan, xfer, true);
+ if (ret) {
+ spin_unlock(&achan->tx_lock);
+ return ret;
+ }
+
+ __iowrite32_copy(achan->tx.base + achan->mlen * tx_front,
+ xfer->txd, xfer->txcnt);
+
+ /* advance TX front */
+ writel(idx, achan->tx.front);
+
+ /* ring the doorbell */
+ ret = mbox_send_message(achan->chan, (void *)&msg);
+ if (ret >= 0)
+ mbox_client_txdone(achan->chan, 0);
+
+ spin_unlock(&achan->tx_lock);
+
+ return ret < 0 ? ret : 0;
+}
+
/**
* acpm_do_xfer() - do one transfer.
* @handle: pointer to the acpm handle.
@@ -485,7 +606,7 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer)
msg.chan_id = xfer->acpm_chan_id;
msg.chan_type = EXYNOS_MBOX_CHAN_TYPE_DOORBELL;
- scoped_guard(mutex, &achan->tx_lock) {
+ scoped_guard(spinlock, &achan->tx_lock) {
tx_front = readl(achan->tx.front);
idx = (tx_front + 1) % achan->qlen;
@@ -654,8 +775,8 @@ static int acpm_channels_init(struct acpm_info *acpm)
if (ret)
return ret;
- mutex_init(&achan->rx_lock);
- mutex_init(&achan->tx_lock);
+ spin_lock_init(&achan->rx_lock);
+ spin_lock_init(&achan->tx_lock);
cl->dev = dev;
@@ -675,6 +796,7 @@ static void acpm_clk_pdev_unregister(void *data)
static const struct acpm_ops exynos_acpm_driver_ops = {
.dvfs = {
.set_rate = acpm_dvfs_set_rate,
+ .set_rate_fast = acpm_dvfs_set_rate_fast,
.get_rate = acpm_dvfs_get_rate,
},
diff --git a/drivers/firmware/samsung/exynos-acpm.h b/drivers/firmware/samsung/exynos-acpm.h
index 708f6b0102ac..210709326a1f 100644
--- a/drivers/firmware/samsung/exynos-acpm.h
+++ b/drivers/firmware/samsung/exynos-acpm.h
@@ -22,4 +22,7 @@ void acpm_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdcnt,
int acpm_do_xfer(struct acpm_handle *handle,
const struct acpm_xfer *xfer);
+int acpm_do_xfer_fast(struct acpm_handle *handle,
+ const struct acpm_xfer *xfer);
+
#endif /* __EXYNOS_ACPM_H__ */
diff --git a/include/linux/firmware/samsung/exynos-acpm-protocol.h b/include/linux/firmware/samsung/exynos-acpm-protocol.h
index c6b35c0ff300..93c9b20517f8 100644
--- a/include/linux/firmware/samsung/exynos-acpm-protocol.h
+++ b/include/linux/firmware/samsung/exynos-acpm-protocol.h
@@ -16,6 +16,8 @@ struct device_node;
struct acpm_dvfs_ops {
int (*set_rate)(struct acpm_handle *handle, unsigned int acpm_chan_id,
unsigned int clk_id, unsigned long rate);
+ int (*set_rate_fast)(struct acpm_handle *handle, unsigned int acpm_chan_id,
+ unsigned int clk_id, unsigned long rate);
unsigned long (*get_rate)(struct acpm_handle *handle,
unsigned int acpm_chan_id,
unsigned int clk_id);
--
2.51.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox