summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-07-24 11:50:48 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-07-24 11:50:48 -0700
commit0c452fbdf41374ff418cb069e59d141eb73f374a (patch)
treeb59bd868744235eb49ecebf52d720b9b61873f64
parenta93212b3d1f517859ec8f540cd8d587155edafc8 (diff)
parentf73a8edc2ccc6ec72c37d5c578e7592d2e1f9922 (diff)
Merge tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
Pull arm64 fixes from Will Deacon: "It's a bit all over the place, as I was hoping to fix a decade-old bug in our seccomp handling on syscall entry and ended up collecting other fixes in the meantime. You'll see the failed attempt (+revert) here but I didn't want to hold off on the others any longer. Hopefully we'll get that one squashed next week... - Fix early_ioremap() of unaligned ACPI tables - Remove bogus information from data abort diagnostics - Fix kprobes recursion during single-step - Fix incorrect constant in ESR address size fault macro - Fix OOB page-table walk in memory hot-unplug notifier - Fix OOB access to the linear map when retrieving an unaligned huge pte - Fix MPAM register reset values - Fix MPAM NULL dereference on teardown" * tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: arm64: make huge_ptep_get handled unaligned addresses arm64/mm: Check the requested PFN range during memory removal arm64: Correct value returned by ESR_ELx_FSC_ADDRSZ_nL() arm64: kprobes: Allow reentering kprobes while single-stepping arm64: kprobes: Only handle faults originating from XOL slot drivers/virt: pkvm: Fix end calculation in mmio_guard_ioremap_hook() Revert "arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates" arm64: mm: When logging data aborts only decode Xs when ISV=1 arm64: fixmap: Allow 256K early_ioremap() at any offset arm_mpam: guard MBWU state before adding it to garbage arm_mpam: Fix MPAMCFG_MBW_PBM register setting arm_mpam: Fix software reset values of MPAMCFG_PRI arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates
-rw-r--r--arch/arm64/include/asm/esr.h2
-rw-r--r--arch/arm64/include/asm/fixmap.h6
-rw-r--r--arch/arm64/include/asm/kprobes.h6
-rw-r--r--arch/arm64/kernel/probes/kprobes.c45
-rw-r--r--arch/arm64/mm/fault.c7
-rw-r--r--arch/arm64/mm/hugetlbpage.c2
-rw-r--r--arch/arm64/mm/mmu.c2
-rw-r--r--drivers/resctrl/mpam_devices.c36
-rw-r--r--drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c2
9 files changed, 85 insertions, 23 deletions
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 81c17320a588..f816f5d77f1a 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -131,7 +131,7 @@
* Annoyingly, the negative levels for Address size faults aren't laid out
* contiguously (or in the desired order)
*/
-#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x25 : 0x2C)
+#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x29 : 0x2C)
#define ESR_ELx_FSC_ADDRSZ_L(n) ((n) < 0 ? ESR_ELx_FSC_ADDRSZ_nL(n) : \
(ESR_ELx_FSC_ADDRSZ + (n)))
diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h
index 65555284446e..7075a3bd2c61 100644
--- a/arch/arm64/include/asm/fixmap.h
+++ b/arch/arm64/include/asm/fixmap.h
@@ -78,8 +78,12 @@ enum fixed_addresses {
/*
* Temporary boot-time mappings, used by early_ioremap(),
* before ioremap() is functional.
+ *
+ * Reserve one extra page so a 256K mapping may start at any
+ * offset within a page. early_ioremap() maps the page-aligned
+ * physical range, so the initial offset can consume an extra page.
*/
-#define NR_FIX_BTMAPS (SZ_256K / PAGE_SIZE)
+#define NR_FIX_BTMAPS ((SZ_256K / PAGE_SIZE) + 1)
#define FIX_BTMAPS_SLOTS 7
#define TOTAL_FIX_BTMAPS (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS)
diff --git a/arch/arm64/include/asm/kprobes.h b/arch/arm64/include/asm/kprobes.h
index f2782560647b..35ce2c94040e 100644
--- a/arch/arm64/include/asm/kprobes.h
+++ b/arch/arm64/include/asm/kprobes.h
@@ -26,6 +26,12 @@
struct prev_kprobe {
struct kprobe *kp;
unsigned int status;
+
+ /*
+ * The original DAIF state of the outer kprobe, saved here before
+ * a nested kprobe overwrites kcb->saved_irqflag during reentry.
+ */
+ unsigned long saved_irqflag;
};
/* per-cpu kprobe control block */
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index 43a0361a8bf0..4e0efad5caf2 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -174,12 +174,27 @@ static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
{
kcb->prev_kprobe.kp = kprobe_running();
kcb->prev_kprobe.status = kcb->kprobe_status;
+
+ /*
+ * Save the outer kprobe's original DAIF flags before the nested
+ * kprobe calls kprobes_save_local_irqflag() and overwrites
+ * kcb->saved_irqflag. Without this, the outer kprobe will restore
+ * the wrong DAIF state and leave interrupts permanently masked.
+ */
+ kcb->prev_kprobe.saved_irqflag = kcb->saved_irqflag;
}
static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
{
__this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
kcb->kprobe_status = kcb->prev_kprobe.status;
+
+ /*
+ * Restore the outer kprobe's saved_irqflag so that when its
+ * single-step completes, kprobes_restore_local_irqflag() uses
+ * the correct original DAIF value.
+ */
+ kcb->saved_irqflag = kcb->prev_kprobe.saved_irqflag;
}
static void __kprobes set_current_kprobe(struct kprobe *p)
@@ -240,10 +255,16 @@ static int __kprobes reenter_kprobe(struct kprobe *p,
switch (kcb->kprobe_status) {
case KPROBE_HIT_SSDONE:
case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SS:
+ /*
+ * A probe can be hit while another kprobe is preparing or
+ * executing its XOL single-step instruction. This is still a
+ * recoverable one-level reentry, so handle it in the same way as
+ * reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
+ */
kprobes_inc_nmissed_count(p);
setup_singlestep(p, regs, kcb, 1);
break;
- case KPROBE_HIT_SS:
case KPROBE_REENTER:
pr_warn("Failed to recover from reentered kprobes.\n");
dump_kprobe(p);
@@ -282,10 +303,32 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
struct kprobe *cur = kprobe_running();
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ /*
+ * Simulated kprobes execute in the debug trap context and have no
+ * XOL slot. Any page fault taken while a simulated kprobe is in
+ * progress cannot have been caused by kprobe single-stepping and
+ * must be left alone for the normal page fault handler, including
+ * fixup_exception.
+ */
+ if (cur && !cur->ainsn.xol_insn)
+ return 0;
+
switch (kcb->kprobe_status) {
case KPROBE_HIT_SS:
case KPROBE_REENTER:
/*
+ * A page fault taken while in KPROBE_HIT_SS or
+ * KPROBE_REENTER state is only attributable to kprobe
+ * single-stepping if the faulting PC points to the
+ * current kprobe's XOL instruction. If the fault occurred
+ * elsewhere (e.g. in perf or tracing code invoked from the
+ * debug exception path), leave it for the normal page fault
+ * handler to process.
+ */
+ if (instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
+ break;
+
+ /*
* We are here because the instruction being single
* stepped caused a page fault. We reset the current
* kprobe and the ip points back to the probe address
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 85e23388f9bb..0b52557652be 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -76,6 +76,8 @@ static void data_abort_decode(unsigned long esr)
pr_alert(" SF = %lu, AR = %lu\n",
(esr & ESR_ELx_SF) >> ESR_ELx_SF_SHIFT,
(esr & ESR_ELx_AR) >> ESR_ELx_AR_SHIFT);
+ pr_alert(" Xs = %llu\n",
+ (iss2 & ESR_ELx_Xs_MASK) >> ESR_ELx_Xs_SHIFT);
} else {
pr_alert(" ISV = 0, ISS = 0x%08lx, ISS2 = 0x%08lx\n",
esr & ESR_ELx_ISS_MASK, iss2);
@@ -87,11 +89,10 @@ static void data_abort_decode(unsigned long esr)
(iss2 & ESR_ELx_TnD) >> ESR_ELx_TnD_SHIFT,
(iss2 & ESR_ELx_TagAccess) >> ESR_ELx_TagAccess_SHIFT);
- pr_alert(" GCS = %ld, Overlay = %lu, DirtyBit = %lu, Xs = %llu\n",
+ pr_alert(" GCS = %ld, Overlay = %lu, DirtyBit = %lu\n",
(iss2 & ESR_ELx_GCS) >> ESR_ELx_GCS_SHIFT,
(iss2 & ESR_ELx_Overlay) >> ESR_ELx_Overlay_SHIFT,
- (iss2 & ESR_ELx_DirtyBit) >> ESR_ELx_DirtyBit_SHIFT,
- (iss2 & ESR_ELx_Xs_MASK) >> ESR_ELx_Xs_SHIFT);
+ (iss2 & ESR_ELx_DirtyBit) >> ESR_ELx_DirtyBit_SHIFT);
}
static void mem_abort_decode(unsigned long esr)
diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index 30772a909aea..8e799c1fe0aa 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -87,7 +87,7 @@ static int find_num_contig(struct mm_struct *mm, unsigned long addr,
p4dp = p4d_offset(pgdp, addr);
pudp = pud_offset(p4dp, addr);
pmdp = pmd_offset(pudp, addr);
- if ((pte_t *)pmdp == ptep) {
+ if ((pte_t *)PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS) == ptep) {
*pgsize = PMD_SIZE;
return CONT_PMDS;
}
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index a25d8beacc83..18a8b0d3714e 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -2194,7 +2194,7 @@ static int prevent_memory_remove_notifier(struct notifier_block *nb,
}
}
- if (!can_unmap_without_split(pfn, arg->nr_pages))
+ if (!can_unmap_without_split(arg->start_pfn, arg->nr_pages))
return NOTIFY_BAD;
return NOTIFY_OK;
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index b69f99488111..2f09f4b78bd3 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1552,12 +1552,9 @@ static u16 mpam_wa_t241_calc_min_from_max(struct mpam_props *props,
static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
struct mpam_config *cfg)
{
- u32 pri_val = 0;
u16 cmax = MPAMCFG_CMAX_CMAX;
struct mpam_msc *msc = ris->vmsc->msc;
struct mpam_props *rprops = &ris->props;
- u16 dspri = GENMASK(rprops->dspri_wd, 0);
- u16 intpri = GENMASK(rprops->intpri_wd, 0);
mutex_lock(&msc->part_sel_lock);
__mpam_part_sel(ris->ris_idx, partid, msc);
@@ -1583,9 +1580,9 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
if (mpam_has_feature(mpam_feat_mbw_part, rprops)) {
if (mpam_has_feature(mpam_feat_mbw_part, cfg))
- mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits);
- else
mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm);
+ else
+ mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits);
}
if (mpam_has_feature(mpam_feat_mbw_min, rprops)) {
@@ -1622,16 +1619,25 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
if (mpam_has_feature(mpam_feat_intpri_part, rprops) ||
mpam_has_feature(mpam_feat_dspri_part, rprops)) {
- /* aces high? */
- if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops))
- intpri = 0;
- if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops))
- dspri = 0;
+ u32 pri_val = 0;
+
+ if (mpam_has_feature(mpam_feat_intpri_part, rprops)) {
+ u16 intpri = GENMASK(rprops->intpri_wd - 1, 0);
+
+ /* aces high? */
+ if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops))
+ intpri = 0;
- if (mpam_has_feature(mpam_feat_intpri_part, rprops))
pri_val |= FIELD_PREP(MPAMCFG_PRI_INTPRI, intpri);
- if (mpam_has_feature(mpam_feat_dspri_part, rprops))
+ }
+ if (mpam_has_feature(mpam_feat_dspri_part, rprops)) {
+ u16 dspri = GENMASK(rprops->dspri_wd - 1, 0);
+
+ if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops))
+ dspri = 0;
+
pri_val |= FIELD_PREP(MPAMCFG_PRI_DSPRI, dspri);
+ }
mpam_write_partsel_reg(msc, PRI, pri_val);
}
@@ -2606,8 +2612,10 @@ static void __destroy_component_cfg(struct mpam_component *comp)
msc = vmsc->msc;
if (mpam_mon_sel_lock(msc)) {
- list_for_each_entry(ris, &vmsc->ris, vmsc_list)
- add_to_garbage(ris->mbwu_state);
+ list_for_each_entry(ris, &vmsc->ris, vmsc_list) {
+ if (ris->mbwu_state)
+ add_to_garbage(ris->mbwu_state);
+ }
mpam_mon_sel_unlock(msc);
}
}
diff --git a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
index 4230b817a80b..d66291def0f4 100644
--- a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
+++ b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
@@ -82,8 +82,8 @@ static int mmio_guard_ioremap_hook(phys_addr_t phys, size_t size,
if (protval != PROT_DEVICE_nGnRE && protval != PROT_DEVICE_nGnRnE)
return 0;
+ end = PAGE_ALIGN(phys + size);
phys = PAGE_ALIGN_DOWN(phys);
- end = phys + PAGE_ALIGN(size);
while (phys < end) {
const int func_id = ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_FUNC_ID;