From a7ea67879af09438d38032107b89a62739b09e96 Mon Sep 17 00:00:00 2001 From: Bui Nguyen Nhu Quynh Date: Fri, 18 Sep 2026 21:31:24 +0700 Subject: KVM: PPC: Fix callback check in kvmppc_gsm_refresh_info The kvmppc_gsm_refresh_info() function incorrectly checks for the presence of the fill_info callback instead of refresh_info. This can lead to: - NULL pointer dereference if refresh_info is NULL but fill_info is present - EINVAL return if refresh_info is present but fill_info is NULL Fix this by checking the correct callback. Fixes: 6ccbbc33f06ad ("KVM: PPC: Add helper library for Guest State Buffers") Signed-off-by: Bui Nguyen Nhu Quynh Tested-by: Gautam Menghani Reviewed-by: Amit Machhiwal Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260918143124.9913-1-kaylovefoss@gmail.com --- arch/powerpc/kvm/guest-state-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kvm/guest-state-buffer.c b/arch/powerpc/kvm/guest-state-buffer.c index 42843eca6727..aeb586ba4523 100644 --- a/arch/powerpc/kvm/guest-state-buffer.c +++ b/arch/powerpc/kvm/guest-state-buffer.c @@ -598,7 +598,7 @@ EXPORT_SYMBOL_GPL(kvmppc_gsm_fill_info); int kvmppc_gsm_refresh_info(struct kvmppc_gs_msg *gsm, struct kvmppc_gs_buff *gsb) { - if (!gsm->ops->fill_info) + if (!gsm->ops->refresh_info) return -EINVAL; return gsm->ops->refresh_info(gsm, gsb); -- cgit v1.2.3 From adc7dcd36f46cf5963a093d558e0f1c1d58fb36a Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Mon, 21 Sep 2026 14:31:51 +0530 Subject: powerpc/irq: Move __softirq_pending out of irq_stat __softirq_pending isn't part of arch specific irq_stats. It is used by softirq core for various decision making such as whether to kick off ksoftirqd. Move it out of irq_cpustat_t. This makes it simple to make irq_cpustat_t array based approach. Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260921090153.54745-2-sshegde@linux.ibm.com --- arch/powerpc/include/asm/hardirq.h | 3 ++- arch/powerpc/kernel/irq.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h index f133b5930ae1..bf3926a0c69c 100644 --- a/arch/powerpc/include/asm/hardirq.h +++ b/arch/powerpc/include/asm/hardirq.h @@ -6,7 +6,6 @@ #include typedef struct { - unsigned int __softirq_pending; unsigned int timer_irqs_event; unsigned int broadcast_irqs_event; unsigned int timer_irqs_others; @@ -23,6 +22,8 @@ typedef struct { } ____cacheline_aligned irq_cpustat_t; DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); +DECLARE_PER_CPU(unsigned int, __softirq_pending); +#define local_softirq_pending_ref __softirq_pending #define __ARCH_IRQ_STAT #define __ARCH_IRQ_EXIT_IRQS_DISABLED diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 15a3c3fd8e70..0a4daceee293 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -73,6 +73,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); EXPORT_PER_CPU_SYMBOL(irq_stat); +DEFINE_PER_CPU(unsigned int, __softirq_pending); #ifdef CONFIG_PPC32 atomic_t ppc_n_lost_interrupts; -- cgit v1.2.3 From fbb22ceaaf8f9c6786e6ff5da1962fe6ac0fc904 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Mon, 21 Sep 2026 14:31:52 +0530 Subject: powerpc/irq: Make irqstats array based Current irq_cpustat_t has separate member for handling each arch specific interrupt type. The same can be achieved with array instead indexed by corresponding irq counter type. This helps to, - Make it easy to integrate into genirq improvements by calling genirq provided irq_proc_emit_counts. That speeds up quite a bit by printing all 0's once as much as possible. - Adding a new vector or software counter only requires to update the table and everything just works - Remove ifdef usage a bit. - Instead of going through each member, it simply becomes an array traversal. Time taken to read /proc/interrupts 1000 times. Base and v6 details can be found in cover-letter. Base : 103us v6 : 63us v6+this_patch : 57us A Decent 10% reduction can be seen in a system 240 CPUs. As the system size increases the gain would be more as emitting 0 would reduce more and more. Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260921090153.54745-3-sshegde@linux.ibm.com --- arch/powerpc/include/asm/hardirq.h | 27 ++++++---- arch/powerpc/kernel/dbell.c | 2 +- arch/powerpc/kernel/irq.c | 107 +++++++++++++------------------------ arch/powerpc/kernel/time.c | 6 +-- arch/powerpc/kernel/traps.c | 11 ++-- arch/powerpc/kernel/watchdog.c | 2 +- 6 files changed, 64 insertions(+), 91 deletions(-) diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h index bf3926a0c69c..38098e35b241 100644 --- a/arch/powerpc/include/asm/hardirq.h +++ b/arch/powerpc/include/asm/hardirq.h @@ -5,26 +5,33 @@ #include #include -typedef struct { - unsigned int timer_irqs_event; - unsigned int broadcast_irqs_event; - unsigned int timer_irqs_others; - unsigned int pmu_irqs; - unsigned int mce_exceptions; - unsigned int spurious_irqs; - unsigned int sreset_irqs; +enum irq_stat_counts { + IRQ_COUNT_LOC_TIMER, + IRQ_COUNT_BCT_TIMER, + IRQ_COUNT_OTHER_TIMER, + IRQ_COUNT_SPURIOUS, + IRQ_COUNT_PMI, + IRQ_COUNT_MCE, + IRQ_COUNT_NMI_SRESET, #ifdef CONFIG_PPC_WATCHDOG - unsigned int soft_nmi_irqs; + IRQ_COUNT_WATCHDOG, #endif #ifdef CONFIG_PPC_DOORBELL - unsigned int doorbell_irqs; + IRQ_COUNT_DOORBELL, #endif + IRQ_COUNT_MAX, +}; + +typedef struct { + unsigned int counts[IRQ_COUNT_MAX]; } ____cacheline_aligned irq_cpustat_t; DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); DECLARE_PER_CPU(unsigned int, __softirq_pending); #define local_softirq_pending_ref __softirq_pending +#define inc_irq_stat(index) __this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index]) + #define __ARCH_IRQ_STAT #define __ARCH_IRQ_EXIT_IRQS_DISABLED diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c index 5712dd846263..f5e298a4c4c0 100644 --- a/arch/powerpc/kernel/dbell.c +++ b/arch/powerpc/kernel/dbell.c @@ -31,7 +31,7 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(doorbell_exception) do_hard_irq_enable(); kvmppc_clear_host_ipi(smp_processor_id()); - __this_cpu_inc(irq_stat.doorbell_irqs); + inc_irq_stat(DOORBELL); smp_ipi_demux_relaxed(); /* already performed the barrier */ diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 0a4daceee293..cfbec8363f99 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -84,79 +84,57 @@ u32 tau_interrupts(unsigned long cpu); #endif #endif /* CONFIG_PPC32 */ +struct irq_stat_info { + const char *symbol; + const char *text; +}; + +#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt} + +static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { + ISE(LOC_TIMER, "LOC", " Local timer interrupts for timer event device\n"), + ISE(BCT_TIMER, "BCT", " Broadcast timer interrupts for timer event device\n"), + ISE(OTHER_TIMER, "LOC", " Local timer interrupts for others\n"), + ISE(SPURIOUS, "SPU", " Spurious interrupts\n"), + ISE(PMI, "PMI", " Performance monitoring interrupts\n"), + ISE(MCE, "MCE", " Machine check exceptions\n"), + ISE(NMI_SRESET, "NMI", " System Reset interrupts\n"), +#ifdef CONFIG_PPC_WATCHDOG + ISE(WATCHDOG, "WDG", " Watchdog soft-NMI interrupts\n"), +#endif +#ifdef CONFIG_PPC_DOORBELL + ISE(DOORBELL, "DBL", " Doorbell interrupts\n"), +#endif +}; + int arch_show_interrupts(struct seq_file *p, int prec) { - int j; + const struct irq_stat_info *info = irq_stat_info; + + for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) { + seq_printf(p, "%*s:", prec, info->symbol); + irq_proc_emit_counts(p, &irq_stat.counts[i]); + seq_puts(p, info->text); + } #if defined(CONFIG_PPC32) && defined(CONFIG_TAU_INT) if (tau_initialized) { + int j; seq_printf(p, "%*s:", prec, "TAU"); for_each_online_cpu(j) seq_put_decimal_ull_width(p, " ", tau_interrupts(j), 10); seq_puts(p, " PowerPC Thermal Assist (cpu temp)\n"); } #endif /* CONFIG_PPC32 && CONFIG_TAU_INT */ - - seq_printf(p, "%*s:", prec, "LOC"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).timer_irqs_event, 10); - seq_printf(p, " Local timer interrupts for timer event device\n"); - - seq_printf(p, "%*s:", prec, "BCT"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).broadcast_irqs_event, 10); - seq_printf(p, " Broadcast timer interrupts for timer event device\n"); - - seq_printf(p, "%*s:", prec, "LOC"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).timer_irqs_others, 10); - seq_printf(p, " Local timer interrupts for others\n"); - - seq_printf(p, "%*s:", prec, "SPU"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).spurious_irqs, 10); - seq_printf(p, " Spurious interrupts\n"); - - seq_printf(p, "%*s:", prec, "PMI"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).pmu_irqs, 10); - seq_printf(p, " Performance monitoring interrupts\n"); - - seq_printf(p, "%*s:", prec, "MCE"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).mce_exceptions, 10); - seq_printf(p, " Machine check exceptions\n"); - #ifdef CONFIG_PPC_BOOK3S_64 if (cpu_has_feature(CPU_FTR_HVMODE)) { + int j; seq_printf(p, "%*s:", prec, "HMI"); for_each_online_cpu(j) seq_put_decimal_ull_width(p, " ", paca_ptrs[j]->hmi_irqs, 10); seq_printf(p, " Hypervisor Maintenance Interrupts\n"); } #endif - - seq_printf(p, "%*s:", prec, "NMI"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).sreset_irqs, 10); - seq_printf(p, " System Reset interrupts\n"); - -#ifdef CONFIG_PPC_WATCHDOG - seq_printf(p, "%*s:", prec, "WDG"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).soft_nmi_irqs, 10); - seq_printf(p, " Watchdog soft-NMI interrupts\n"); -#endif - -#ifdef CONFIG_PPC_DOORBELL - if (cpu_has_feature(CPU_FTR_DBELL)) { - seq_printf(p, "%*s:", prec, "DBL"); - for_each_online_cpu(j) - seq_put_decimal_ull_width(p, " ", per_cpu(irq_stat, j).doorbell_irqs, 10); - seq_printf(p, " Doorbell interrupts\n"); - } -#endif - return 0; } @@ -165,24 +143,15 @@ int arch_show_interrupts(struct seq_file *p, int prec) */ u64 arch_irq_stat_cpu(unsigned int cpu) { - u64 sum = per_cpu(irq_stat, cpu).timer_irqs_event; + irq_cpustat_t *p = per_cpu_ptr(&irq_stat, cpu); + u64 sum = 0; + + for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++) + sum += p->counts[i]; - sum += per_cpu(irq_stat, cpu).broadcast_irqs_event; - sum += per_cpu(irq_stat, cpu).pmu_irqs; - sum += per_cpu(irq_stat, cpu).mce_exceptions; - sum += per_cpu(irq_stat, cpu).spurious_irqs; - sum += per_cpu(irq_stat, cpu).timer_irqs_others; #ifdef CONFIG_PPC_BOOK3S_64 sum += paca_ptrs[cpu]->hmi_irqs; #endif - sum += per_cpu(irq_stat, cpu).sreset_irqs; -#ifdef CONFIG_PPC_WATCHDOG - sum += per_cpu(irq_stat, cpu).soft_nmi_irqs; -#endif -#ifdef CONFIG_PPC_DOORBELL - sum += per_cpu(irq_stat, cpu).doorbell_irqs; -#endif - return sum; } @@ -252,7 +221,7 @@ static void __do_irq(struct pt_regs *regs, unsigned long oldsp) /* And finally process it */ if (unlikely(!irq)) - __this_cpu_inc(irq_stat.spurious_irqs); + inc_irq_stat(SPURIOUS); else generic_handle_irq(irq); diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 11145c40183d..e00dcda8240b 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -615,13 +615,13 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(timer_interrupt) now = get_tb(); if (now >= *next_tb) { evt->event_handler(evt); - __this_cpu_inc(irq_stat.timer_irqs_event); + inc_irq_stat(LOC_TIMER); } else { now = *next_tb - now; if (now > decrementer_max) now = decrementer_max; set_dec_or_work(now); - __this_cpu_inc(irq_stat.timer_irqs_others); + inc_irq_stat(OTHER_TIMER); } trace_timer_interrupt_exit(regs); @@ -634,7 +634,7 @@ EXPORT_SYMBOL(timer_interrupt); void timer_broadcast_interrupt(void) { tick_receive_broadcast(); - __this_cpu_inc(irq_stat.broadcast_irqs_event); + inc_irq_stat(BCT_TIMER); } #endif diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 629f2a2d4780..0d605600dee6 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -459,8 +459,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(system_reset_exception) } hv_nmi_check_nonrecoverable(regs); - - __this_cpu_inc(irq_stat.sreset_irqs); + inc_irq_stat(NMI_SRESET); /* See if any machine dependent calls */ if (ppc_md.system_reset_exception) { @@ -817,7 +816,7 @@ static void __machine_check_exception(struct pt_regs *regs) { int recover = 0; - __this_cpu_inc(irq_stat.mce_exceptions); + inc_irq_stat(MCE); add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); @@ -1932,8 +1931,7 @@ DEFINE_INTERRUPT_HANDLER(vsx_unavailable_tm) DECLARE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi); DEFINE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi) { - __this_cpu_inc(irq_stat.pmu_irqs); - + inc_irq_stat(PMI); perf_irq(regs); return 0; @@ -1943,8 +1941,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(performance_monitor_exception_nmi) DECLARE_INTERRUPT_HANDLER_ASYNC(performance_monitor_exception_async); DEFINE_INTERRUPT_HANDLER_ASYNC(performance_monitor_exception_async) { - __this_cpu_inc(irq_stat.pmu_irqs); - + inc_irq_stat(PMI); perf_irq(regs); } diff --git a/arch/powerpc/kernel/watchdog.c b/arch/powerpc/kernel/watchdog.c index c40c69368476..582ee3fd71c1 100644 --- a/arch/powerpc/kernel/watchdog.c +++ b/arch/powerpc/kernel/watchdog.c @@ -381,7 +381,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(soft_nmi_interrupt) if (!cpumask_test_cpu(cpu, &wd_cpus_enabled)) return 0; - __this_cpu_inc(irq_stat.soft_nmi_irqs); + inc_irq_stat(WATCHDOG); tb = get_tb(); if (tb - per_cpu(wd_timer_tb, cpu) >= wd_panic_timeout_tb) { -- cgit v1.2.3 From 2d97c8498a9fca1a876b7a8b4fda024f8e234361 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Mon, 21 Sep 2026 14:31:53 +0530 Subject: powerpc/irq: Suppress unlikely interrupt stats by default Some interrupts are always zero and that is expected since they occur very rarely and are mostly error indications. Don't print them by default. "MCE" - "Machine check exceptions" "NMI" - "System Reset interrupts" Print them if they occur once. Maintain a bitmap to know which interrupts are to be printed. Time taken to read /proc/interrupts 1000 times. Base and v6 details can be found in cover-letter. Base : 103us v6 : 63us v6+patch 1+2 : 57us v6+patch 1+2+3 : 54us Patch 3 shows an additional 5% gain compared to patch 1+2. So it does make sense to print them only if they are ever set. Note: Since /proc/interrupts depend on kconfig and arch dependent, userspace tools don't make explicit assumptions. Signed-off-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260921090153.54745-4-sshegde@linux.ibm.com --- arch/powerpc/include/asm/hardirq.h | 1 + arch/powerpc/kernel/irq.c | 37 ++++++++++++++++++++++++++++++++++--- arch/powerpc/kernel/traps.c | 4 ++-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h index 38098e35b241..be6cd5aab016 100644 --- a/arch/powerpc/include/asm/hardirq.h +++ b/arch/powerpc/include/asm/hardirq.h @@ -31,6 +31,7 @@ DECLARE_PER_CPU(unsigned int, __softirq_pending); #define local_softirq_pending_ref __softirq_pending #define inc_irq_stat(index) __this_cpu_inc(irq_stat.counts[IRQ_COUNT_##index]) +void inc_irq_stat_and_enable(enum irq_stat_counts which); #define __ARCH_IRQ_STAT #define __ARCH_IRQ_EXIT_IRQS_DISABLED diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index cfbec8363f99..bce546931a13 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -87,9 +87,13 @@ u32 tau_interrupts(unsigned long cpu); struct irq_stat_info { const char *symbol; const char *text; + int optional; }; -#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt} +/* ISE - IRQ STAT ENABLED, ISC - IRQ STAT CONDITIONAL */ +#define ISE(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt, .optional = 0} +#define ISC(idx, sym, txt)[IRQ_COUNT_##idx] = { .symbol = sym, .text = txt, .optional = 1} + static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { ISE(LOC_TIMER, "LOC", " Local timer interrupts for timer event device\n"), @@ -97,8 +101,8 @@ static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { ISE(OTHER_TIMER, "LOC", " Local timer interrupts for others\n"), ISE(SPURIOUS, "SPU", " Spurious interrupts\n"), ISE(PMI, "PMI", " Performance monitoring interrupts\n"), - ISE(MCE, "MCE", " Machine check exceptions\n"), - ISE(NMI_SRESET, "NMI", " System Reset interrupts\n"), + ISC(MCE, "MCE", " Machine check exceptions\n"), + ISC(NMI_SRESET, "NMI", " System Reset interrupts\n"), #ifdef CONFIG_PPC_WATCHDOG ISE(WATCHDOG, "WDG", " Watchdog soft-NMI interrupts\n"), #endif @@ -107,11 +111,25 @@ static struct irq_stat_info irq_stat_info[IRQ_COUNT_MAX] __ro_after_init = { #endif }; +/* + * Used for default disabled counters to increment the stats and to enable the + * entry for /proc/interrupts output. + */ +static DECLARE_BITMAP(irq_stat_count_show, IRQ_COUNT_MAX) __read_mostly; +void inc_irq_stat_and_enable(enum irq_stat_counts which) +{ + __this_cpu_inc(irq_stat.counts[which]); + set_bit(which, irq_stat_count_show); +} + int arch_show_interrupts(struct seq_file *p, int prec) { const struct irq_stat_info *info = irq_stat_info; for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) { + if (info->optional && !test_bit(i, irq_stat_count_show)) + continue; + seq_printf(p, "%*s:", prec, info->symbol); irq_proc_emit_counts(p, &irq_stat.counts[i]); seq_puts(p, info->text); @@ -138,6 +156,19 @@ int arch_show_interrupts(struct seq_file *p, int prec) return 0; } +static int __init irq_init_stats(void) +{ + struct irq_stat_info *info = irq_stat_info; + + for (unsigned int i = 0; i < ARRAY_SIZE(irq_stat_info); i++, info++) { + if (info->optional == 0) + set_bit(i, irq_stat_count_show); + } + + return 0; +} +late_initcall(irq_init_stats); + /* * /proc/stat helpers */ diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 0d605600dee6..3a8f5416e3f6 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -459,7 +459,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(system_reset_exception) } hv_nmi_check_nonrecoverable(regs); - inc_irq_stat(NMI_SRESET); + inc_irq_stat_and_enable(IRQ_COUNT_NMI_SRESET); /* See if any machine dependent calls */ if (ppc_md.system_reset_exception) { @@ -816,7 +816,7 @@ static void __machine_check_exception(struct pt_regs *regs) { int recover = 0; - inc_irq_stat(MCE); + inc_irq_stat_and_enable(IRQ_COUNT_MCE); add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); -- cgit v1.2.3 From a3b88f816f1b87c6dbcbb858d153adb82fec2e15 Mon Sep 17 00:00:00 2001 From: Link Mauve Date: Thu, 30 Jul 2026 08:50:05 +0200 Subject: powerpc/sstep: Consistently use one define to check arch bit width MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, both CONFIG_PPC64 and __powerpc64__ were being used to check whether we built a 32-bit or a 64-bit kernel, sometimes in a single function. I went with CONFIG_PPC64 because it’s the most used in the kernel, and the preferred way to check whether we’re building a 64-bit kernel, not just 64-bit code, as recommended by Christophe Leroy. Signed-off-by: Link Mauve Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260730065007.964703-2-linkmauve@linkmauve.fr --- arch/powerpc/lib/sstep.c | 102 +++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index f0d6aa657c1a..fd57b748cd2b 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -47,7 +47,7 @@ extern void conv_sp_to_dp(const float *sp, double *dp); extern void conv_dp_to_sp(const double *dp, float *sp); #endif -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 /* * Functions in quad.S */ @@ -134,7 +134,7 @@ static nokprobe_inline unsigned long dform_ea(unsigned int instr, return ea; } -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 /* * Calculate effective address for a DS-form instruction */ @@ -168,7 +168,7 @@ static nokprobe_inline unsigned long dqform_ea(unsigned int instr, return ea; } -#endif /* __powerpc64 */ +#endif /* CONFIG_PPC64 */ /* * Calculate effective address for an X-form instruction @@ -250,7 +250,7 @@ static nokprobe_inline unsigned long byterev_4(unsigned long x) ((x & 0xff00) << 8) | ((x & 0xff) << 24); } -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 static nokprobe_inline unsigned long byterev_8(unsigned long x) { return (byterev_4(x) << 32) | byterev_4(x >> 32); @@ -266,7 +266,7 @@ static nokprobe_inline void do_byte_reverse(void *ptr, int nb) case 4: *(u32 *)ptr = byterev_4(*(u32 *)ptr); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 8: *(unsigned long *)ptr = byterev_8(*(unsigned long *)ptr); break; @@ -312,7 +312,7 @@ __read_mem_aligned(unsigned long *dest, unsigned long ea, int nb, struct pt_regs case 4: unsafe_get_user(x, (unsigned int __user *)ea, Efault); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 8: unsafe_get_user(x, (unsigned long __user *)ea, Efault); break; @@ -364,7 +364,7 @@ static __always_inline int __copy_mem_in(u8 *dest, unsigned long ea, int nb, str case 4: unsafe_get_user(*(u32 *)dest, (u32 __user *)ea, Efault); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 8: unsafe_get_user(*(u64 *)dest, (u64 __user *)ea, Efault); break; @@ -443,7 +443,7 @@ __write_mem_aligned(unsigned long val, unsigned long ea, int nb, struct pt_regs case 4: unsafe_put_user(val, (unsigned int __user *)ea, Efault); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 8: unsafe_put_user(val, (unsigned long __user *)ea, Efault); break; @@ -494,7 +494,7 @@ static __always_inline int __copy_mem_out(u8 *dest, unsigned long ea, int nb, st case 4: unsafe_put_user(*(u32 *)dest, (u32 __user *)ea, Efault); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 8: unsafe_put_user(*(u64 *)dest, (u64 __user *)ea, Efault); break; @@ -722,7 +722,7 @@ static nokprobe_inline int do_vec_store(int rn, unsigned long ea, } #endif /* CONFIG_ALTIVEC */ -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 static nokprobe_inline int emulate_lq(struct pt_regs *regs, unsigned long ea, int reg, bool cross_endian) { @@ -765,7 +765,7 @@ static nokprobe_inline int emulate_stq(struct pt_regs *regs, unsigned long ea, err = write_mem(vals[IS_BE], ea + 8, 8, regs); return err; } -#endif /* __powerpc64 */ +#endif /* CONFIG_PPC64 */ #ifdef CONFIG_VSX static nokprobe_inline void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg, @@ -1316,7 +1316,7 @@ static nokprobe_inline int trap_compare(long v1, long v2) */ #define MASK32(mb, me) ((0xffffffffUL >> (mb)) + \ ((signed long)-0x80000000L >> (me)) + ((me) >= (mb))) -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 #define MASK64_L(mb) (~0UL >> (mb)) #define MASK64_R(me) ((signed long)-0x8000000000000000L >> (me)) #define MASK64(mb, me) (MASK64_L(mb) + MASK64_R(me) + ((me) >= (mb))) @@ -1447,7 +1447,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, switch ((word >> 1) & 0x3ff) { case 598: /* sync */ op->type = BARRIER + BARRIER_SYNC; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 switch ((word >> 21) & 3) { case 1: /* lwsync */ op->type = BARRIER + BARRIER_LWSYNC; @@ -1472,7 +1472,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, rc = (word >> 6) & 0x1f; switch (opcode) { -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 1: if (!cpu_has_feature(CPU_FTR_ARCH_31)) goto unknown_opcode; @@ -1506,7 +1506,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, goto trap; return 1; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 4: /* * There are very many instructions with this primary opcode @@ -1555,7 +1555,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, case 10: /* cmpli */ imm = (unsigned short) word; val = regs->gpr[ra]; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 if ((rd & 1) == 0) val = (unsigned int) val; #endif @@ -1565,7 +1565,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, case 11: /* cmpi */ imm = (short) word; val = regs->gpr[ra]; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 if ((rd & 1) == 0) val = (int) val; #endif @@ -1662,7 +1662,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, set_cr0(regs, op); goto logical_done_nocc; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 30: /* rld* */ mb = ((word >> 6) & 0x1f) | (word & 0x20); val = regs->gpr[rd]; @@ -1720,7 +1720,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, (int)regs->gpr[rb]))) goto trap; return 1; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 68: /* td */ if (rd & trap_compare(regs->gpr[ra], regs->gpr[rb])) goto trap; @@ -1826,7 +1826,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, case 0: /* cmp */ val = regs->gpr[ra]; val2 = regs->gpr[rb]; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 if ((rd & 1) == 0) { /* word (32-bit) compare */ val = (int) val; @@ -1839,7 +1839,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, case 32: /* cmpl */ val = regs->gpr[ra]; val2 = regs->gpr[rb]; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 if ((rd & 1) == 0) { /* word (32-bit) compare */ val = (unsigned int) val; @@ -1860,7 +1860,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, add_with_carry(regs, op, rd, ~regs->gpr[ra], regs->gpr[rb], 1); goto arith_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 9: /* mulhdu */ asm("mulhdu %0,%1,%2" : "=r" (op->val) : "r" (regs->gpr[ra]), "r" (regs->gpr[rb])); @@ -1879,7 +1879,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, case 40: /* subf */ op->val = regs->gpr[rb] - regs->gpr[ra]; goto arith_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 73: /* mulhd */ asm("mulhd %0,%1,%2" : "=r" (op->val) : "r" (regs->gpr[ra]), "r" (regs->gpr[rb])); @@ -1918,7 +1918,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, add_with_carry(regs, op, rd, ~regs->gpr[ra], -1L, regs->xer & XER_CA); goto arith_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 233: /* mulld */ op->val = regs->gpr[ra] * regs->gpr[rb]; goto arith_done; @@ -1933,7 +1933,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, (int) regs->gpr[rb]; goto arith_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 265: /* modud */ if (!cpu_has_feature(CPU_FTR_ARCH_300)) goto unknown_opcode; @@ -1950,7 +1950,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, op->val = (unsigned int) regs->gpr[ra] % (unsigned int) regs->gpr[rb]; goto compute_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 457: /* divdu */ op->val = regs->gpr[ra] / regs->gpr[rb]; goto arith_done; @@ -1959,7 +1959,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, op->val = (unsigned int) regs->gpr[ra] / (unsigned int) regs->gpr[rb]; goto arith_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 489: /* divd */ op->val = (long int) regs->gpr[ra] / (long int) regs->gpr[rb]; @@ -1969,7 +1969,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, op->val = (int) regs->gpr[ra] / (int) regs->gpr[rb]; goto arith_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 425: /* divde[.] */ asm volatile(PPC_DIVDE(%0, %1, %2) : "=r" (op->val) : "r" (regs->gpr[ra]), @@ -2002,7 +2002,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, } goto unknown_opcode; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 777: /* modsd */ if (!cpu_has_feature(CPU_FTR_ARCH_300)) goto unknown_opcode; @@ -2025,7 +2025,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, val = (unsigned int) regs->gpr[rd]; op->val = ( val ? __builtin_clz(val) : 32 ); goto logical_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 58: /* cntlzd */ val = regs->gpr[rd]; op->val = ( val ? __builtin_clzl(val) : 64 ); @@ -2093,7 +2093,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, val = (unsigned int) regs->gpr[rd]; op->val = (val ? __builtin_ctz(val) : 32); goto logical_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 570: /* cnttzd */ if (!cpu_has_feature(CPU_FTR_ARCH_300)) goto unknown_opcode; @@ -2108,7 +2108,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, case 954: /* extsb */ op->val = (signed char) regs->gpr[rd]; goto logical_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 986: /* extsw */ op->val = (signed int) regs->gpr[rd]; goto logical_done; @@ -2159,7 +2159,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, set_ca32(op, op->xerval & XER_CA); goto logical_done; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 27: /* sld */ sh = regs->gpr[rb] & 0x7f; if (sh < 64) @@ -2216,7 +2216,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, op->val = val; goto logical_done; -#endif /* __powerpc64__ */ +#endif /* CONFIG_PPC64 */ /* * Cache instructions @@ -2296,7 +2296,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, op->type = MKOP(STCX, 0, 2); break; #endif -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 84: /* ldarx */ op->type = MKOP(LARX, 0, 8); break; @@ -2373,7 +2373,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, break; #endif /* CONFIG_ALTIVEC */ -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 21: /* ldx */ case 53: /* ldux */ op->type = MKOP(LOAD, u, 8); @@ -2400,7 +2400,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, op->type = MKOP(LOAD, u, 2); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 341: /* lwax */ case 373: /* lwaux */ op->type = MKOP(LOAD, SIGNEXT | u, 4); @@ -2417,7 +2417,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, op->type = MKOP(STORE, u, 2); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 532: /* ldbrx */ op->type = MKOP(LOAD, BYTEREV, 8); break; @@ -2459,7 +2459,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, op->type = MKOP(STORE_FP, u, 8); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 791: /* lfdpx */ op->type = MKOP(LOAD_FP, 0, 16); break; @@ -2479,10 +2479,10 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, case 983: /* stfiwx */ op->type = MKOP(STORE_FP, 0, 4); break; -#endif /* __powerpc64 */ +#endif /* CONFIG_PPC64 */ #endif /* CONFIG_PPC_FPU */ -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 660: /* stdbrx */ op->type = MKOP(STORE, BYTEREV, 8); op->val = byterev_8(regs->gpr[rd]); @@ -2820,7 +2820,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, break; #endif -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 56: /* lq */ if (!((rd & 1) || (rd == ra))) op->type = MKOP(LOAD, 0, 16); @@ -2857,7 +2857,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, break; #endif /* CONFIG_VSX */ -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 58: /* ld[u], lwa */ op->ea = dsform_ea(word, regs); switch (word & 3) { @@ -2946,7 +2946,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, break; #endif /* CONFIG_VSX */ -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 62: /* std[u] */ op->ea = dsform_ea(word, regs); switch (word & 3) { @@ -3099,7 +3099,7 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, case 3: /* Type 11 Modified Register-to-Register */ break; } -#endif /* __powerpc64__ */ +#endif /* CONFIG_PPC64 */ } @@ -3200,7 +3200,7 @@ static nokprobe_inline void do_byterev(unsigned long *valp, int size) case 4: *valp = byterev_4(*valp); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 8: *valp = byterev_8(*valp); break; @@ -3341,7 +3341,7 @@ int emulate_loadstore(struct pt_regs *regs, struct instruction_op *op) case 4: __get_user_asmx(val, ea, err, "lwarx"); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 8: __get_user_asmx(val, ea, err, "ldarx"); break; @@ -3367,7 +3367,7 @@ int emulate_loadstore(struct pt_regs *regs, struct instruction_op *op) return -EFAULT; err = 0; switch (size) { -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 1: __put_user_asmx(op->val, ea, err, "stbcx.", cr); break; @@ -3378,7 +3378,7 @@ int emulate_loadstore(struct pt_regs *regs, struct instruction_op *op) case 4: __put_user_asmx(op->val, ea, err, "stwcx.", cr); break; -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 case 8: __put_user_asmx(op->val, ea, err, "stdcx.", cr); break; @@ -3399,7 +3399,7 @@ int emulate_loadstore(struct pt_regs *regs, struct instruction_op *op) break; case LOAD: -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 if (size == 16) { err = emulate_lq(regs, ea, op->reg, cross_endian); break; @@ -3473,7 +3473,7 @@ int emulate_loadstore(struct pt_regs *regs, struct instruction_op *op) break; case STORE: -#ifdef __powerpc64__ +#ifdef CONFIG_PPC64 if (size == 16) { err = emulate_stq(regs, ea, op->reg, cross_endian); break; -- cgit v1.2.3 From 16736e0587530a2ad074ef716a6a2f1d45f63827 Mon Sep 17 00:00:00 2001 From: Link Mauve Date: Thu, 30 Jul 2026 08:50:06 +0200 Subject: =?UTF-8?q?powerpc/sstep:=20Don=E2=80=99t=20define=20variables=20w?= =?UTF-8?q?e=20won=E2=80=99t=20be=20using?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling with W=1 on 32-bit PowerPC, -Wunused-but-set-variable was set, and alongside -Werror caused the build to fail with this error: ``` ../arch/powerpc/lib/sstep.c: In function 'analyse_instr': ../arch/powerpc/lib/sstep.c:1350:28: error: variable 'suffix' set but not used [-Werror=unused-but-set-variable] 1350 | unsigned int word, suffix; | ^~~~~~ ../arch/powerpc/lib/sstep.c:1346:38: error: variable 'rc' set but not used [-Werror=unused-but-set-variable] 1346 | unsigned int opcode, ra, rb, rc, rd, spr, u; | ^~ cc1: all warnings being treated as errors ``` Signed-off-by: Link Mauve Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260730065007.964703-3-linkmauve@linkmauve.fr --- arch/powerpc/lib/sstep.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index fd57b748cd2b..d49b2ef5d763 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -1342,16 +1342,19 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, { #ifdef CONFIG_PPC64 unsigned int suffixopcode, prefixtype, prefix_r; + unsigned int rc, suffix; #endif - unsigned int opcode, ra, rb, rc, rd, spr, u; + unsigned int opcode, ra, rb, rd, spr, u; unsigned long int imm; unsigned long int val, val2; unsigned int mb, me, sh; - unsigned int word, suffix; + unsigned int word; long ival; word = ppc_inst_val(instr); +#ifdef CONFIG_PPC64 suffix = ppc_inst_suffix(instr); +#endif op->type = COMPUTE; @@ -1469,7 +1472,9 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, rd = (word >> 21) & 0x1f; ra = (word >> 16) & 0x1f; rb = (word >> 11) & 0x1f; +#ifdef CONFIG_PPC64 rc = (word >> 6) & 0x1f; +#endif switch (opcode) { #ifdef CONFIG_PPC64 -- cgit v1.2.3 From dcc3091bde54bbf870b56edb3c91a420c1bcdb29 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 19 Jul 2026 22:37:16 -0700 Subject: powerpc/dts: mpc5121: Move GPIO controller properties to SoC dtsi Define gpio@1100 with label gpio_pic as a GPIO controller and interrupt controller directly in mpc5121.dtsi so all MPC5121 boards inherit the correct controller capabilities. Remove the redundant local controller property declaration block from ac14xx.dts. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260719-ads7846-pdm360ng-v2-1-1198bd108502@gmail.com --- arch/powerpc/boot/dts/ac14xx.dts | 13 ------------- arch/powerpc/boot/dts/mpc5121.dtsi | 6 +++++- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/boot/dts/ac14xx.dts b/arch/powerpc/boot/dts/ac14xx.dts index 5d8877e1f4ad..05fcf08df1ca 100644 --- a/arch/powerpc/boot/dts/ac14xx.dts +++ b/arch/powerpc/boot/dts/ac14xx.dts @@ -148,19 +148,6 @@ compatible = "fsl,mpc5121rev2-clock", "fsl,mpc5121-clock"; }; - /* - * GPIO PIC: - * interrupts cell = - * sense == 8: Level, low assertion - * sense == 2: Edge, high-to-low change - */ - gpio_pic: gpio@1100 { - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - }; - sdhc@1500 { cd-gpios = <&gpio_pic 23 0>; /* card detect */ wp-gpios = <&gpio_pic 24 0>; /* write protect */ diff --git a/arch/powerpc/boot/dts/mpc5121.dtsi b/arch/powerpc/boot/dts/mpc5121.dtsi index a278fb7b9e71..9c5dacb94b92 100644 --- a/arch/powerpc/boot/dts/mpc5121.dtsi +++ b/arch/powerpc/boot/dts/mpc5121.dtsi @@ -146,10 +146,14 @@ interrupts = <83 0x8>; }; - gpio@1100 { + gpio_pic: gpio@1100 { compatible = "fsl,mpc5121-gpio"; reg = <0x1100 0x100>; interrupts = <78 0x8>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; }; can@1300 { -- cgit v1.2.3 From 4e3187c830221460f1a7d9389c3e8662ad28d4f4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 19 Jul 2026 22:37:17 -0700 Subject: powerpc/dts: pdm360ng: Convert ADS7845 touchscreen to DT bindings The PDM360NG board is equipped with an ADS7845 touchscreen controller. Previously, pdm360ng.dts used "ti,ads7846" as a placeholder and relied on platform data in pdm360ng.c to override the model to 7845. Update the ts@0 touchscreen node in pdm360ng.dts to use the correct "ti,ads7845" compatible string along with pendown-gpio (using GPIO_ACTIVE_LOW) and interrupt-parent properties referencing gpio_pic. Note that changing the compatible string to "ti,ads7845" causes the legacy platform data notifier in pdm360ng.c to skip injecting platform data, allowing ads7846 to parse DT properties natively. Although pdm360ng_penirq_init() in pdm360ng.c still runs on boot prior to removing pdm360ng.c, its direct GPIO register writes are harmless because the gpio-mpc8xxx driver resets and re-unmasks pin 25 on demand when the ads7846 driver requests its interrupt. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260719-ads7846-pdm360ng-v2-2-1198bd108502@gmail.com --- arch/powerpc/boot/dts/pdm360ng.dts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/boot/dts/pdm360ng.dts b/arch/powerpc/boot/dts/pdm360ng.dts index 67c3b9db75d7..05e93ca294ee 100644 --- a/arch/powerpc/boot/dts/pdm360ng.dts +++ b/arch/powerpc/boot/dts/pdm360ng.dts @@ -9,6 +9,7 @@ * Copyright 2008 Freescale Semiconductor Inc. */ +#include #include "mpc5121.dtsi" / { @@ -176,11 +177,12 @@ /* ADS7845 touch screen controller */ ts@0 { - compatible = "ti,ads7846"; + compatible = "ti,ads7845"; reg = <0x0>; spi-max-frequency = <3000000>; - /* pen irq is GPIO25 */ - interrupts = <78 0x8>; + interrupt-parent = <&gpio_pic>; + interrupts = <25 0x8>; + pendown-gpio = <&gpio_pic 25 GPIO_ACTIVE_LOW>; }; }; -- cgit v1.2.3 From 5e78442b76844e4d46b9cac5ed123b76807f30db Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 19 Jul 2026 22:37:18 -0700 Subject: powerpc/512x: Remove pdm360ng platform setup in favor of mpc512x_generic Now that the ADS7845 touchscreen on PDM360NG is fully described in the device tree, custom platform setup in pdm360ng.c is no longer needed. Add ifm,pdm360ng to the board match list in mpc512x_generic.c, update Kconfig to select MPC512x_GENERIC for PDM360NG, and remove pdm360ng.c. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260719-ads7846-pdm360ng-v2-3-1198bd108502@gmail.com --- arch/powerpc/platforms/512x/Kconfig | 3 +- arch/powerpc/platforms/512x/Makefile | 1 - arch/powerpc/platforms/512x/mpc512x_generic.c | 1 + arch/powerpc/platforms/512x/pdm360ng.c | 126 -------------------------- 4 files changed, 3 insertions(+), 128 deletions(-) delete mode 100644 arch/powerpc/platforms/512x/pdm360ng.c diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig index deecede78776..9d46d791af05 100644 --- a/arch/powerpc/platforms/512x/Kconfig +++ b/arch/powerpc/platforms/512x/Kconfig @@ -32,11 +32,12 @@ config MPC512x_GENERIC which do not need custom platform specific setup. Compatible boards include: Protonic LVT base boards (ZANMCU - and VICVT2), Freescale MPC5125 Tower system. + and VICVT2), Freescale MPC5125 Tower system, IFM PDM360NG. config PDM360NG bool "ifm PDM360NG board" depends on PPC_MPC512x + select MPC512x_GENERIC select DEFAULT_UIMAGE help This option enables support for the PDM360NG board. diff --git a/arch/powerpc/platforms/512x/Makefile b/arch/powerpc/platforms/512x/Makefile index 2daf22ee26a0..4da6f6923d8f 100644 --- a/arch/powerpc/platforms/512x/Makefile +++ b/arch/powerpc/platforms/512x/Makefile @@ -7,4 +7,3 @@ obj-y += mpc512x_shared.o obj-$(CONFIG_MPC5121_ADS) += mpc5121_ads.o mpc5121_ads_cpld.o obj-$(CONFIG_MPC512x_GENERIC) += mpc512x_generic.o obj-$(CONFIG_MPC512x_LPBFIFO) += mpc512x_lpbfifo.o -obj-$(CONFIG_PDM360NG) += pdm360ng.o diff --git a/arch/powerpc/platforms/512x/mpc512x_generic.c b/arch/powerpc/platforms/512x/mpc512x_generic.c index d4fa6c302ccf..d330d3d85887 100644 --- a/arch/powerpc/platforms/512x/mpc512x_generic.c +++ b/arch/powerpc/platforms/512x/mpc512x_generic.c @@ -24,6 +24,7 @@ static const char * const board[] __initconst = { "prt,prtlvt", "fsl,mpc5125ads", "ifm,ac14xx", + "ifm,pdm360ng", NULL }; diff --git a/arch/powerpc/platforms/512x/pdm360ng.c b/arch/powerpc/platforms/512x/pdm360ng.c deleted file mode 100644 index 8bbbf78bb42b..000000000000 --- a/arch/powerpc/platforms/512x/pdm360ng.c +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2010 DENX Software Engineering - * - * Anatolij Gustschin, - * - * PDM360NG board setup - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "mpc512x.h" - -#if defined(CONFIG_TOUCHSCREEN_ADS7846) || \ - defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE) -#include -#include -#include -#include - -static void *pdm360ng_gpio_base; - -static int pdm360ng_get_pendown_state(void) -{ - u32 reg; - - reg = in_be32(pdm360ng_gpio_base + 0xc); - if (reg & 0x40) - setbits32(pdm360ng_gpio_base + 0xc, 0x40); - - reg = in_be32(pdm360ng_gpio_base + 0x8); - - /* return 1 if pen is down */ - return (reg & 0x40) == 0; -} - -static struct ads7846_platform_data pdm360ng_ads7846_pdata = { - .model = 7845, - .get_pendown_state = pdm360ng_get_pendown_state, - .irq_flags = IRQF_TRIGGER_LOW, -}; - -static int __init pdm360ng_penirq_init(void) -{ - struct device_node *np; - - np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-gpio"); - if (!np) { - pr_err("%s: Can't find 'mpc5121-gpio' node\n", __func__); - return -ENODEV; - } - - pdm360ng_gpio_base = of_iomap(np, 0); - of_node_put(np); - if (!pdm360ng_gpio_base) { - pr_err("%s: Can't map gpio regs.\n", __func__); - return -ENODEV; - } - out_be32(pdm360ng_gpio_base + 0xc, 0xffffffff); - setbits32(pdm360ng_gpio_base + 0x18, 0x2000); - setbits32(pdm360ng_gpio_base + 0x10, 0x40); - - return 0; -} - -static int pdm360ng_touchscreen_notifier_call(struct notifier_block *nb, - unsigned long event, void *__dev) -{ - struct device *dev = __dev; - - if ((event == BUS_NOTIFY_ADD_DEVICE) && - of_device_is_compatible(dev->of_node, "ti,ads7846")) { - dev->platform_data = &pdm360ng_ads7846_pdata; - return NOTIFY_OK; - } - return NOTIFY_DONE; -} - -static struct notifier_block pdm360ng_touchscreen_nb = { - .notifier_call = pdm360ng_touchscreen_notifier_call, -}; - -static void __init pdm360ng_touchscreen_init(void) -{ - if (pdm360ng_penirq_init()) - return; - - bus_register_notifier(&spi_bus_type, &pdm360ng_touchscreen_nb); -} -#else -static inline void __init pdm360ng_touchscreen_init(void) -{ -} -#endif /* CONFIG_TOUCHSCREEN_ADS7846 */ - -static void __init pdm360ng_init(void) -{ - mpc512x_init(); - pdm360ng_touchscreen_init(); -} - -static int __init pdm360ng_probe(void) -{ - mpc512x_init_early(); - - return 1; -} - -define_machine(pdm360ng) { - .name = "PDM360NG", - .compatible = "ifm,pdm360ng", - .probe = pdm360ng_probe, - .setup_arch = mpc512x_setup_arch, - .init = pdm360ng_init, - .init_IRQ = mpc512x_init_IRQ, - .get_irq = ipic_get_irq, - .restart = mpc512x_restart, -}; -- cgit v1.2.3 From af5f0870883ae4aea7f668d06cbb7ad0e12824c8 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Mon, 21 Sep 2026 12:51:50 +0530 Subject: powerpc/ftrace: Don't restore r13 during ftrace_regs_caller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Michal reported a stack-protector failure and subsequent panic when running kernel builds. This was observed with full/lazy preemption. Initially it was suspected as KVM, but later turned out to be due to a bcc tool running in parallel. Issue was recreated using a bcc tool. For example, running below in parallel leads to crash. ./funccount sched* -d 100 and make -j 64 The same crash was observed when running kprobe for schedule() function, while simpler function tracer for schedule() didn't cause the crash. This helped to narrow it down to ftrace backed kprobes area. The crash occurs as follows: ftrace_regs_caller entry on CPU A | +-> save r13 = CPU A PACA into pt_regs | +-> call kprobe_ftrace_handler() | +-> ftrace_test_recursion_unlock() | +-> preempt_enable +-> task can schedule and migrate to CPU B +-> task resumes with live r13 = CPU B PACA | +-> REST_GPRS(2, 31) | +-> restore saved r13 = CPU A PACA | |-> The task then continues running on CPU B with r13 pointing | to CPU A's PACA. The stack-protector canary is accessed through the PACA. After the task migrates, CPU A may run a different task and update its PACA with that task's canary. Restoring the saved r13 then causes the migrated task's saved stack canary to be compared against the canary in CPU A's PACA, resulting in a stack-protector failure. Similarly, current is resolved through the PACA. With a stale r13, preempt_count() can access the state of the task referenced by CPU A's PACA instead of the task running on CPU B. This results in corrupted preempt-count warnings and scheduling-while-atomic failures. This path for example is called when using kprobes and parallel kernel builds can cause preemptions during ftrace_test_recursion_unlock. Do not restore r13 from the saved register frame. If the task did not migrate, the live r13 already has the saved value. If it migrated, the live r13 contains the correct PACA pointer for the CPU on which the task resumed. On PPC32, r13 is regular register. So do this fix only for PPC64 Fixes: 153086644fd1 ("powerpc/ftrace: Add support for -mprofile-kernel ftrace ABI") Reported-by: Michal Suchánek Closes: https://lore.kernel.org/all/aqKfsVArHHaIK6M9@kunlun.suse.cz/ Reviewed-by: Christophe Leroy (CS GROUP) Reviewed-by: Hari Bathini Signed-off-by: Shrikanth Hegde Tested-by: Samir Mulani Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260921072151.35531-1-sshegde@linux.ibm.com --- arch/powerpc/kernel/trace/ftrace_entry.S | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/powerpc/kernel/trace/ftrace_entry.S b/arch/powerpc/kernel/trace/ftrace_entry.S index 6599fe3c6234..5eb8eee32549 100644 --- a/arch/powerpc/kernel/trace/ftrace_entry.S +++ b/arch/powerpc/kernel/trace/ftrace_entry.S @@ -220,7 +220,13 @@ /* Restore gprs */ .if \allregs == 1 +#ifdef CONFIG_PPC64 + REST_GPRS(2, 12, r1) + /* Do not restore a stale PACA pointer if the task migrated */ + REST_GPRS(14, 31, r1) +#else REST_GPRS(2, 31, r1) +#endif .else REST_GPRS(3, 10, r1) #if defined(CONFIG_LIVEPATCH_64) || defined(CONFIG_PPC_FTRACE_OUT_OF_LINE) -- cgit v1.2.3 From cb3502d2e57ab2b73cee7d69756becbe241db0e1 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sat, 22 Aug 2026 10:12:53 +0200 Subject: docs: ABI: Fix powerpc memtrace Kconfig symbol The memtrace interface is controlled by CONFIG_PPC_MEMTRACE. The documented CONFIG_PPC64_HARDWARE_TRACING symbol does not exist. Fixes: 1f84c2624a2d ("Add documentation for the powerpc memtrace debugfs files") Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Karl Mehltretter Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260822081253.86256-1-kmehltretter@gmail.com --- Documentation/ABI/testing/ppc-memtrace | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/ppc-memtrace b/Documentation/ABI/testing/ppc-memtrace index 9606aed33137..2de9fd18715e 100644 --- a/Documentation/ABI/testing/ppc-memtrace +++ b/Documentation/ABI/testing/ppc-memtrace @@ -3,7 +3,7 @@ Date: Aug 2017 KernelVersion: 4.14 Contact: linuxppc-dev@lists.ozlabs.org Description: This folder contains the relevant debugfs files for the - hardware trace macro to use. CONFIG_PPC64_HARDWARE_TRACING + hardware trace macro to use. CONFIG_PPC_MEMTRACE must be set. What: /sys/kernel/debug/powerpc/memtrace/enable -- cgit v1.2.3 From 4d9f2ca2e37ce7fe7567761b2392748456e1ce43 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 14 Sep 2026 23:59:00 +0200 Subject: powerpc/ps3: Simplify control flow in ps3_{virq,irq_plug}_setup() In ps3_virq_setup() and ps3_irq_plug_setup(), remove the goto statements and handle errors directly. Signed-off-by: Thorsten Blum Reviewed-by: Amit Machhiwal Reviewed-by: Geert Uytterhoeven Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260914215900.84712-2-blum@kernel.org --- arch/powerpc/platforms/ps3/interrupt.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index a4ad4b49eef7..358611638258 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -184,8 +184,7 @@ static int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, if (!*virq) { FAIL("%s:%d: irq_create_mapping failed: outlet %lu\n", __func__, __LINE__, outlet); - result = -ENOMEM; - goto fail_create; + return -ENOMEM; } DBG("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, @@ -196,17 +195,13 @@ static int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, if (result) { FAIL("%s:%d: irq_set_chip_data failed\n", __func__, __LINE__); - goto fail_set; + irq_dispose_mapping(*virq); + return result; } ps3_chip_mask(irq_get_irq_data(*virq)); return result; - -fail_set: - irq_dispose_mapping(*virq); -fail_create: - return result; } /** @@ -250,7 +245,7 @@ int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, if (result) { FAIL("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); - goto fail_setup; + return result; } pd = irq_get_chip_data(*virq); @@ -263,16 +258,11 @@ int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, if (result) { FAIL("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", __func__, __LINE__, ps3_result(result)); - result = -EPERM; - goto fail_connect; + ps3_virq_destroy(*virq); + return -EPERM; } return result; - -fail_connect: - ps3_virq_destroy(*virq); -fail_setup: - return result; } EXPORT_SYMBOL_GPL(ps3_irq_plug_setup); -- cgit v1.2.3 From 12d238d584ba485a99e02e033746100fc7099184 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 16 Sep 2026 17:12:54 +0200 Subject: powerpc/sysfs: Remove redundant wait time clamps The wait times are u64 and cannot be negative. Remove the redundant clamps. Signed-off-by: Thorsten Blum Reviewed-by: Amit Machhiwal Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260916151254.120629-2-blum@kernel.org --- arch/powerpc/kernel/sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 329c1690b5ed..2100c5e2c821 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -338,7 +338,7 @@ static ssize_t show_pw20_wait_time(struct device *dev, time = pw20_wt; } - return sysfs_emit(buf, "%llu\n", time > 0 ? time : 0); + return sysfs_emit(buf, "%llu\n", time); } static void set_pw20_wait_entry_bit(void *val) @@ -460,7 +460,7 @@ static ssize_t show_altivec_idle_wait_time(struct device *dev, time = altivec_idle_wt; } - return sysfs_emit(buf, "%llu\n", time > 0 ? time : 0); + return sysfs_emit(buf, "%llu\n", time); } static void set_altivec_idle_wait_entry_bit(void *val) -- cgit v1.2.3 From 3ebf4ae6d8afac11fe6732418dd378e031f8f4ed Mon Sep 17 00:00:00 2001 From: Abhishek Dubey Date: Mon, 28 Sep 2026 10:43:50 +0530 Subject: powerpc64/bpf: fix compare instruction emitted for tailcall The tail_call_info field can contain either a scalar counter value or a 64-bit pointer to the counter, using a 32-bit compare (cmplwi) only checks the lower 32 bits, which can lead to incorrect comparisions when location of counter is near 4GB boundary. Use instruction cmpldi/cmplwi for accurate comparision in corresponding cases. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/bpf/20260517191450.85AE6C2BCB8@smtp.kernel.org/ Fixes: 2ed2d8f6fb38 ("powerpc64/bpf: Support tailcalls with subprogs") Signed-off-by: Abhishek Dubey Signed-off-by: Saket Kumar Bhaskar Tested-by: Yeswanth Krishna Tellakula Tested-by: R Nageswara Sastry Acked-by: Hari Bathini Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/961601491716ef9e00f79e3aa9f7f4ef4440d4d3.1789982239.git.skb99@linux.ibm.com --- arch/powerpc/net/bpf_jit.h | 6 ++++++ arch/powerpc/net/bpf_jit_comp.c | 2 +- arch/powerpc/net/bpf_jit_comp64.c | 8 ++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index f32de8704d4d..35015d7ecb76 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h @@ -188,6 +188,12 @@ struct codegen_context { #define bpf_to_ppc(r) (ctx->b2p[r]) +#ifdef CONFIG_PPC64 +#define PPC_RAW_CMPLLI(a, i) PPC_RAW_CMPLDI(a, i) +#else +#define PPC_RAW_CMPLLI(a, i) PPC_RAW_CMPLWI(a, i) +#endif + #ifdef CONFIG_PPC32 #define BPF_FIXUP_LEN 3 /* Three instructions => 12 bytes */ #else diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 7b07b43575f1..a875521ff904 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -739,7 +739,7 @@ static void bpf_trampoline_setup_tail_call_info(u32 *image, struct codegen_conte * Setting the tail_call_info in trampoline's frame * depending on if previous frame had value or reference. */ - EMIT(PPC_RAW_CMPLWI(_R3, MAX_TAIL_CALL_CNT)); + EMIT(PPC_RAW_CMPLLI(_R3, MAX_TAIL_CALL_CNT)); PPC_BCC_CONST_SHORT(COND_GT, 8); EMIT(PPC_RAW_ADDI(_R3, _R4, -BPF_PPC_TAILCALL)); diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index fc9db691e820..53f67b3d2906 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -276,7 +276,7 @@ void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx) */ EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_2), _R1, 0)); EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_1), bpf_to_ppc(TMP_REG_2), -(BPF_PPC_TAILCALL))); - EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT)); + EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT)); PPC_BCC_CONST_SHORT(COND_GT, 8); EMIT(PPC_RAW_ADDI(bpf_to_ppc(TMP_REG_1), bpf_to_ppc(TMP_REG_2), -(BPF_PPC_TAILCALL))); @@ -662,7 +662,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o PPC_BCC_SHORT(COND_GE, out); EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_1), _R1, bpf_jit_stack_tailcallinfo_offset(ctx))); - EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT)); + EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT)); PPC_BCC_CONST_SHORT(COND_LE, 8); /* dereference TMP_REG_1 */ @@ -672,7 +672,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o * if (tail_call_info == MAX_TAIL_CALL_CNT) * goto out; */ - EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT)); + EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT)); PPC_BCC_SHORT(COND_EQ, out); /* @@ -707,7 +707,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o * tail_call_info. */ EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_2), _R1, bpf_jit_stack_tailcallinfo_offset(ctx))); - EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_2), MAX_TAIL_CALL_CNT)); + EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_2), MAX_TAIL_CALL_CNT)); PPC_BCC_CONST_SHORT(COND_GT, 8); /* First get address of tail_call_info */ -- cgit v1.2.3 From 24affb84d98e6b37b276aa57e5dd25ad93458cea Mon Sep 17 00:00:00 2001 From: Abhishek Dubey Date: Mon, 28 Sep 2026 10:43:51 +0530 Subject: powerpc64/bpf: fix percpu private stack leak on JIT failure The existing conditional statement in bpf_int_jit_compile() frees the percpu private stack at out_addrs only when the image buffer was never allocated. If bpf_jit_build_body() fails during a code-generation pass, the image buffer has already been allocated, so !image is false and the percpu stack is not freed. Because JIT compilation failed, fp->jited remains at 0. The subsequent bpf_jit_free() path only frees priv_stack_ptr when fp->jited is set, so freeing is skipped here too, leaking the percpu allocation. Fix implements freeing the private stack whenever fp->jited was not set, i.e. compilation did not succeed, instead of keying off !image. !fp->jited already covers the !image case, since image is only NULL on early-failure paths where fp->jited is likewise 0. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/bpf/20260616135426.A06B71F000E9@smtp.kernel.org Fixes: 156d985123b6 ("powerpc64/bpf: Implement JIT support for private stack") Cc: stable@vger.kernel.org Signed-off-by: Abhishek Dubey Signed-off-by: Saket Kumar Bhaskar Tested-by: Yeswanth Krishna Tellakula Tested-by: R Nageswara Sastry Acked-by: Hari Bathini Link: https://lore.kernel.org/bpf/20260616135426.A06B71F000E9@smtp.kernel.org Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/a827e3ef81cd43f2aee7e9bdfe8683f2c77065da.1789982239.git.skb99@linux.ibm.com --- arch/powerpc/net/bpf_jit_comp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index a875521ff904..8f7501954d9f 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -357,7 +357,7 @@ skip_init_ctx: (void *)fimage + FUNCTION_DESCR_SIZE); out_addrs: - if (!image && priv_stack_ptr) { + if (!fp->jited && priv_stack_ptr) { fp->aux->priv_stack_ptr = NULL; free_percpu(priv_stack_ptr); } -- cgit v1.2.3 From 78ce55b7913a78e1687306668e078b86bc6508ee Mon Sep 17 00:00:00 2001 From: Abhishek Dubey Date: Mon, 28 Sep 2026 10:43:52 +0530 Subject: powerpc/bpf: fix buffer overflow in JIT for large BPF programs During size calculation in pass-0, exit_addr is 0 since addrs[fp->len] is not yet populated. bpf_jit_emit_exit_insn() treats a zero exit_addr as in-range and skips bpf_jit_build_epilogue(), so the alternate inline epilogue instructions are not counted in alloclen. In later passes, if the real exit_addr falls outside the 32MB branch range, the full inline epilogue is emitted into the already-allocated buffer, writing past its end and corrupting adjacent memory. Fix by ensuring exit_addr is non-zero before treating it as in-range, so pass-0 always falls through to bpf_jit_build_epilogue() and conservatively accounts for all epilogue instructions in alloclen. Also range check alt_exit_addr directly in the else-if condition. Since exit_addr handling now falls through to the epilogue, two related issues in bpf_int_jit_compile() must also be addressed: 1. Reset cgctx.alt_exit_addr before the second size-calculation pass. Without this, a stale alt_exit_addr from the first pass causes the second pass to emit a single jump instead of the full epilogue, undercounting alloclen and reintroducing the overflow. 2. Recompute addrs[fp->len] at the end of each code-generation pass. The larger pass-0 body can shrink in later passes as out-of-range exits settle into in-range jumps; a stale addrs[fp->len] would leave exit branches targeting past the real (shrunken) epilogue. Because shrinkage in a later pass can move the epilogue offset, the fixed two-pass loop is no longer sufficient: an exit that was out of range in an earlier pass may fall in range once the epilogue offset shrinks, shrinking the body further and overwriting the start of the epilogue. Convert the code-generation loop to iterate until the program size converges, bounded by CODEGEN_MAX_PASSES, and fail the JIT if it does not converge. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/bpf/20260529015855.364704-2-adubey@linux.ibm.com/T/#mfcb23909d977b949727cca4f59ee56a13fd69b92 Fixes: 0ffdbce6f4a8 ("powerpc/bpf: Handle large branch ranges with BPF_EXIT") Cc: stable@vger.kernel.org Signed-off-by: Hari Bathini Signed-off-by: Abhishek Dubey Signed-off-by: Saket Kumar Bhaskar Tested-by: Yeswanth Krishna Tellakula Tested-by: R Nageswara Sastry Link: https://lore.kernel.org/bpf/20260529015855.364704-2-adubey@linux.ibm.com/T/#mfcb23909d977b949727cca4f59ee56a13fd69b92 Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/eebffa997e07781b384c4480e2b03c5b87c1aeb1.1789982239.git.skb99@linux.ibm.com --- arch/powerpc/net/bpf_jit.h | 7 +++++++ arch/powerpc/net/bpf_jit_comp.c | 34 ++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index 35015d7ecb76..6d58df361648 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h @@ -14,6 +14,13 @@ #include #include +/* + * We need at least 2 passes for proper code generation, and may need + * additional passes if code size changes between passes. + */ +#define CODEGEN_MIN_PASSES 2 +#define CODEGEN_MAX_PASSES 3 + #ifdef CONFIG_PPC64_ELF_ABI_V1 #define FUNCTION_DESCR_SIZE 24 #else diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 8f7501954d9f..11981d2270a9 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -99,11 +99,10 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx) int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr) { - if (!exit_addr || is_offset_in_branch_range(exit_addr - (ctx->idx * 4))) { + if (exit_addr && is_offset_in_branch_range(exit_addr - (long)(ctx->idx * 4))) { PPC_JMP(exit_addr); - } else if (ctx->alt_exit_addr) { - if (WARN_ON(!is_offset_in_branch_range((long)ctx->alt_exit_addr - (ctx->idx * 4)))) - return -1; + } else if (ctx->alt_exit_addr && is_offset_in_branch_range( + (long)(ctx->alt_exit_addr) - (long)(ctx->idx * 4))) { PPC_JMP(ctx->alt_exit_addr); } else { ctx->alt_exit_addr = ctx->idx * 4; @@ -274,6 +273,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr */ if (cgctx.seen & SEEN_TAILCALL || !is_offset_in_branch_range((long)cgctx.idx * 4)) { cgctx.idx = 0; + cgctx.alt_exit_addr = 0; if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) goto out_err; } @@ -306,10 +306,13 @@ skip_init_ctx: code_base = (u32 *)(image + FUNCTION_DESCR_SIZE); fcode_base = (u32 *)(fimage + FUNCTION_DESCR_SIZE); - /* Code generation passes 1-2 */ - for (pass = 1; pass < 3; pass++) { + /* Code generation passes 1-2+, loop until program size converges. */ + for (pass = 1; pass <= CODEGEN_MAX_PASSES; pass++) { + u32 prev_proglen = proglen; + /* Now build the prologue, body code & epilogue for real. */ cgctx.idx = 0; + cgctx.exentry_idx = 0; cgctx.alt_exit_addr = 0; bpf_jit_build_prologue(code_base, &cgctx); if (bpf_jit_build_body(fp, code_base, fcode_base, &cgctx, addrs, pass, @@ -318,11 +321,26 @@ skip_init_ctx: bpf_jit_binary_pack_free(fhdr, hdr); goto out_err; } + addrs[fp->len] = cgctx.idx * 4; bpf_jit_build_epilogue(code_base, &cgctx); + proglen = cgctx.idx * 4; + if (bpf_jit_enable > 1) pr_info("Pass %d: shrink = %d, seen = 0x%x\n", pass, - proglen - (cgctx.idx * 4), cgctx.seen); + prev_proglen - proglen, cgctx.seen); + + /* Check if program size has converged, but ensure minimum passes */ + if (pass >= CODEGEN_MIN_PASSES && proglen == prev_proglen) + break; + + if (pass == CODEGEN_MAX_PASSES && proglen != prev_proglen) { + pr_err("BPF JIT: Program did not converge after %d passes\n", + CODEGEN_MAX_PASSES); + bpf_arch_text_copy(&fhdr->size, &hdr->size, sizeof(hdr->size)); + bpf_jit_binary_pack_free(fhdr, hdr); + goto out_err; + } } if (bpf_jit_enable > 1) @@ -399,7 +417,7 @@ int bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, u32 *fimage, int pass u32 *fixup; /* Populate extable entries only in the last pass */ - if (pass != 2) + if (pass < CODEGEN_MIN_PASSES) return 0; if (!fp->aux->extable || -- cgit v1.2.3 From 04001e8ddb979f463f1c3ca8ac4d6ac59fb48921 Mon Sep 17 00:00:00 2001 From: Abhishek Dubey Date: Mon, 28 Sep 2026 10:43:53 +0530 Subject: powerpc/bpf: fix alignment of long branch trampoline address Ensure the dummy trampoline address field present between the OOL stub and the long branch stub is 4/8-byte aligned, for memory compatibility when content loaded to a register. Reported-by: Hari Bathini Fixes: d243b62b7bd3 ("powerpc64/bpf: Add support for bpf trampolines") Cc: stable@vger.kernel.org Signed-off-by: Abhishek Dubey Signed-off-by: Saket Kumar Bhaskar Tested-by: Yeswanth Krishna Tellakula Tested-by: R Nageswara Sastry Reviewed-by: Hari Bathini Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/a8257022057cbb71928c960be589e10b89fc0899.1789982239.git.skb99@linux.ibm.com --- arch/powerpc/net/bpf_jit.h | 7 ++++--- arch/powerpc/net/bpf_jit_comp.c | 38 ++++++++++++++++++++++++++++++++------ arch/powerpc/net/bpf_jit_comp32.c | 7 ++++--- arch/powerpc/net/bpf_jit_comp64.c | 7 ++++--- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index 6d58df361648..4da8bde92e1e 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h @@ -227,10 +227,11 @@ int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context * int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct codegen_context *ctx, u32 *addrs, int pass, bool extra_pass); void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx); -void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx); -void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx); +void bpf_jit_build_epilogue(u32 *image, u32 *fimage, struct codegen_context *ctx); +void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context *ctx); void bpf_jit_realloc_regs(struct codegen_context *ctx); -int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr); +int bpf_jit_emit_exit_insn(u32 *image, u32 *fimage, struct codegen_context *ctx, int tmp_reg, + long exit_addr); void prepare_for_fsession_fentry(u32 *image, struct codegen_context *ctx, int cookie_cnt, int cookie_off, int retval_off); void store_func_meta(u32 *image, struct codegen_context *ctx, u64 func_meta, int func_meta_off); diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 11981d2270a9..8ca36a933c7a 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -49,11 +49,35 @@ asm ( " .popsection ;" ); -void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx) +void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context *ctx) { int ool_stub_idx, long_branch_stub_idx; + int ool_stub_sz; /* + * Align the mis-aligned dummy_tramp_addr field in the fimage. + * The alignment NOP must appear before OOL stub, to make + * ool_stub_idx & long_branch_stub_idx constant from end. + * + * The fimage can be non 8-byte aligned, so final alignment depends + * on start of fimage and the stub's instruction count offset. The + * OOL stub size is 4 instructions (with CONFIG_PPC_FTRACE_OUT_OF_LINE) + * or 3 instructions (without) before dummy_tramp_addr. + * + * Emit a NOP here if address is not SZL aligned. + * + * In pass=0 when image==NULL, conservatively account for space + * required to accommodate alignment NOP. In case final pass skips + * emitting alignment NOP, the image buffer have 4 spare bytes and + * jited_len signifies correct program size. + */ + + ool_stub_sz = IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) ? 16 : 12; + if (!image || !IS_ALIGNED((unsigned long)fimage + ctx->idx*4 + ool_stub_sz, SZL)) + EMIT(PPC_RAW_NOP()); + + /* + * nop // optional, for alignment of dummy_tramp_addr * Out-of-line stub: * mflr r0 * [b|bl] tramp @@ -70,7 +94,7 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx) /* * Long branch stub: - * .long + * .long // 8-byte aligned * mflr r11 * bcl 20,31,$+4 * mflr r12 @@ -81,6 +105,7 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx) */ if (image) *((unsigned long *)&image[ctx->idx]) = (unsigned long)dummy_tramp; + ctx->idx += SZL / 4; long_branch_stub_idx = ctx->idx; EMIT(PPC_RAW_MFLR(_R11)); @@ -97,7 +122,8 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx) } } -int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr) +int bpf_jit_emit_exit_insn(u32 *image, u32 *fimage, struct codegen_context *ctx, + int tmp_reg, long exit_addr) { if (exit_addr && is_offset_in_branch_range(exit_addr - (long)(ctx->idx * 4))) { PPC_JMP(exit_addr); @@ -106,7 +132,7 @@ int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, PPC_JMP(ctx->alt_exit_addr); } else { ctx->alt_exit_addr = ctx->idx * 4; - bpf_jit_build_epilogue(image, ctx); + bpf_jit_build_epilogue(image, fimage, ctx); } return 0; @@ -286,7 +312,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr */ bpf_jit_build_prologue(NULL, &cgctx); addrs[fp->len] = cgctx.idx * 4; - bpf_jit_build_epilogue(NULL, &cgctx); + bpf_jit_build_epilogue(NULL, NULL, &cgctx); fixup_len = fp->aux->num_exentries * BPF_FIXUP_LEN * 4; extable_len = fp->aux->num_exentries * sizeof(struct exception_table_entry); @@ -322,7 +348,7 @@ skip_init_ctx: goto out_err; } addrs[fp->len] = cgctx.idx * 4; - bpf_jit_build_epilogue(code_base, &cgctx); + bpf_jit_build_epilogue(code_base, fcode_base, &cgctx); proglen = cgctx.idx * 4; diff --git a/arch/powerpc/net/bpf_jit_comp32.c b/arch/powerpc/net/bpf_jit_comp32.c index bfdc50740da8..f5b9441cf46a 100644 --- a/arch/powerpc/net/bpf_jit_comp32.c +++ b/arch/powerpc/net/bpf_jit_comp32.c @@ -229,7 +229,7 @@ static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx } -void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) +void bpf_jit_build_epilogue(u32 *image, u32 *fimage, struct codegen_context *ctx) { EMIT(PPC_RAW_MR(_R3, bpf_to_ppc(BPF_REG_0))); @@ -237,7 +237,7 @@ void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) EMIT(PPC_RAW_BLR()); - bpf_jit_build_fentry_stubs(image, ctx); + bpf_jit_build_fentry_stubs(image, fimage, ctx); } /* Relative offset needs to be calculated based on final image location */ @@ -1149,7 +1149,8 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code * we'll just fall through to the epilogue. */ if (i != flen - 1) { - ret = bpf_jit_emit_exit_insn(image, ctx, _R0, exit_addr); + ret = bpf_jit_emit_exit_insn(image, fimage, + ctx, _R0, exit_addr); if (ret) return ret; } diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index 53f67b3d2906..c1e86fe0bd79 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -398,7 +398,7 @@ static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx } } -void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) +void bpf_jit_build_epilogue(u32 *image, u32 *fimage, struct codegen_context *ctx) { bpf_jit_emit_common_epilogue(image, ctx); @@ -407,7 +407,7 @@ void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) EMIT(PPC_RAW_BLR()); - bpf_jit_build_fentry_stubs(image, ctx); + bpf_jit_build_fentry_stubs(image, fimage, ctx); } /* @@ -1737,7 +1737,8 @@ emit_clear: * we'll just fall through to the epilogue. */ if (i != flen - 1) { - ret = bpf_jit_emit_exit_insn(image, ctx, tmp1_reg, exit_addr); + ret = bpf_jit_emit_exit_insn(image, fimage, ctx, + tmp1_reg, exit_addr); if (ret) return ret; } -- cgit v1.2.3 From 6652b9941a6c9b6814ca6d2eb153dbf162b89a02 Mon Sep 17 00:00:00 2001 From: Abhishek Dubey Date: Mon, 28 Sep 2026 10:43:54 +0530 Subject: powerpc/bpf: Move out dummy_tramp_addr after Long branch stub Move the long branch address field to the bottom of the long branch stub. This allows uninterrupted disassembly until the last 4/8 bytes. The last bytes exclusion is logically necessary to prevent disassembly failure, otherwise the actual program layout is never altered. Hence no effect on overall program size. Also, align dummy_tramp_addr field with 8-byte boundary. Following is disassembler output for test program with moved down dummy_tramp_addr field: ..... ..... pc:68 left:44 a6 03 08 7c : mtlr 0 pc:72 left:40 bc ff ff 4b : b .-68 pc:76 left:36 a6 02 68 7d : mflr 11 pc:80 left:32 05 00 9f 42 : bcl 20, 31, .+4 pc:84 left:28 a6 02 88 7d : mflr 12 pc:88 left:24 14 00 8c e9 : ld 12, 20(12) pc:92 left:20 a6 03 89 7d : mtctr 12 pc:96 left:16 a6 03 68 7d : mtlr 11 pc:100 left:12 20 04 80 4e : bctr pc:104 left:8 c0 34 1d 00 : Failure log: Can't disasm instruction at offset 104: c0 34 1d 00 00 00 00 c0 Disassembly logic can truncate at 104, ignoring last 8 bytes. Update the dummy_tramp_addr field offset calculation from the end of the program to reflect its new location, for bpf_arch_text_poke() to update the actual trampoline's address in this field. All BPF trampoline selftests continue to pass with this patch applied. Fixes: d243b62b7bd3 ("powerpc64/bpf: Add support for bpf trampolines") Signed-off-by: Abhishek Dubey Signed-off-by: Saket Kumar Bhaskar Tested-by: Yeswanth Krishna Tellakula Tested-by: R Nageswara Sastry Reviewed-by: Hari Bathini Acked-by: Hari Bathini Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/552225fc257381fb83d282b3040c6ad363246465.1789982239.git.skb99@linux.ibm.com --- arch/powerpc/net/bpf_jit_comp.c | 44 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 8ca36a933c7a..c2717f0d9cdd 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -52,19 +52,19 @@ asm ( void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context *ctx) { int ool_stub_idx, long_branch_stub_idx; - int ool_stub_sz; + int stub_sz; /* + * The dummy_tramp_addr field is placed at bottom of Long branch stub. * Align the mis-aligned dummy_tramp_addr field in the fimage. * The alignment NOP must appear before OOL stub, to make * ool_stub_idx & long_branch_stub_idx constant from end. * * The fimage can be non 8-byte aligned, so final alignment depends - * on start of fimage and the stub's instruction count offset. The - * OOL stub size is 4 instructions (with CONFIG_PPC_FTRACE_OUT_OF_LINE) - * or 3 instructions (without) before dummy_tramp_addr. - * - * Emit a NOP here if address is not SZL aligned. + * on start of fimage and the stub's instruction count. The + * stubs block has 11 instructions (with CONFIG_PPC_FTRACE_OUT_OF_LINE) + * or 10 instructions (without) before dummy_tramp_addr field. Emit a + * NOP if the address of dummy_tramp_addr is non aligned. * * In pass=0 when image==NULL, conservatively account for space * required to accommodate alignment NOP. In case final pass skips @@ -72,8 +72,8 @@ void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context * jited_len signifies correct program size. */ - ool_stub_sz = IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) ? 16 : 12; - if (!image || !IS_ALIGNED((unsigned long)fimage + ctx->idx*4 + ool_stub_sz, SZL)) + stub_sz = IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) ? 44 : 40; + if (!image || !IS_ALIGNED((unsigned long)fimage + ctx->idx*4 + stub_sz, SZL)) EMIT(PPC_RAW_NOP()); /* @@ -94,28 +94,29 @@ void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context /* * Long branch stub: - * .long // 8-byte aligned * mflr r11 * bcl 20,31,$+4 - * mflr r12 - * ld r12, -8-SZL(r12) + * mflr r12 // lr/r12 stores pc of current(this) inst. + * ld r12, 20(r12) // offset(dummy_tramp_addr) from prev inst. is 20 * mtctr r12 - * mtlr r11 // needed to retain ftrace ABI + * mtlr r11 // needed to retain ftrace ABI * bctr + * .long // SZL bytes aligned */ - if (image) - *((unsigned long *)&image[ctx->idx]) = (unsigned long)dummy_tramp; - - ctx->idx += SZL / 4; long_branch_stub_idx = ctx->idx; EMIT(PPC_RAW_MFLR(_R11)); EMIT(PPC_RAW_BCL4()); EMIT(PPC_RAW_MFLR(_R12)); - EMIT(PPC_RAW_LL(_R12, _R12, -8-SZL)); + EMIT(PPC_RAW_LL(_R12, _R12, 20)); EMIT(PPC_RAW_MTCTR(_R12)); EMIT(PPC_RAW_MTLR(_R11)); EMIT(PPC_RAW_BCTR()); + if (image) + *((unsigned long *)&image[ctx->idx]) = (unsigned long)dummy_tramp; + + ctx->idx += SZL / 4; + if (!bpf_jit_ool_stub) { bpf_jit_ool_stub = (ctx->idx - ool_stub_idx) * 4; bpf_jit_long_branch_stub = (ctx->idx - long_branch_stub_idx) * 4; @@ -1309,6 +1310,7 @@ static void do_isync(void *info __maybe_unused) * bpf_func: * [nop|b] ool_stub * 2. Out-of-line stub: + * nop // optional nop for alignment * ool_stub: * mflr r0 * [b|bl] / @@ -1316,14 +1318,14 @@ static void do_isync(void *info __maybe_unused) * b bpf_func + 4 * 3. Long branch stub: * long_branch_stub: - * .long / * mflr r11 * bcl 20,31,$+4 * mflr r12 - * ld r12, -16(r12) + * ld r12, 20(r12) * mtctr r12 * mtlr r11 // needed to retain ftrace ABI * bctr + * .long / * * dummy_tramp is used to reduce synchronization requirements. * @@ -1425,10 +1427,12 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t, * 1. Update the address in the long branch stub: * If new_addr is out of range, we will have to use the long branch stub, so patch new_addr * here. Otherwise, revert to dummy_tramp, but only if we had patched old_addr here. + * + * dummy_tramp_addr moved to bottom of long branch stub. */ if ((new_addr && !is_offset_in_branch_range(new_addr - ip)) || (old_addr && !is_offset_in_branch_range(old_addr - ip))) - ret = patch_ulong((void *)(bpf_func_end - bpf_jit_long_branch_stub - SZL), + ret = patch_ulong((void *)(bpf_func_end - SZL), /* SZL: dummy_tramp_addr offset */ (new_addr && !is_offset_in_branch_range(new_addr - ip)) ? (unsigned long)new_addr : (unsigned long)dummy_tramp); if (ret) -- cgit v1.2.3 From d52a1659cb1718f93166ab8dd8621237e564a1c4 Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:05 +0530 Subject: pseries/plpks: fix error handling in plpks_read_var() When a PLPKS variable is initialized without a policy and used to read an object with the PLPKS_WRAPPINGKEY policy set, the hypervisor returns H_AUTHORITY along with the object's policy. However, plpks_read_var() currently treats this the same as any other H_AUTHORITY failure and returns an error without propagating the policy information to the caller. Distinguish this case from other H_AUTHORITY failures and propagate the returned policy to the caller while preserving the authorization error. Remove the explicit assignment of zero to rc on H_SUCCESS, as it is redundant. Also validate the PLPKS variable pointer before dereferencing it. Fixes: 2454a7af0f2a ("powerpc/pseries: define driver for Platform KeyStore") Cc: stable@vger.kernel.org # 6.0 Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-2-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c index 23e4e2a922fc..7e56b3dcacbc 100644 --- a/arch/powerpc/platforms/pseries/plpks.c +++ b/arch/powerpc/platforms/pseries/plpks.c @@ -822,6 +822,9 @@ static int plpks_read_var(u8 consumer, struct plpks_var *var) u8 *output; int rc; + if (!var) + return -EINVAL; + if (var->namelen > PLPKS_MAX_NAME_SIZE) return -EINVAL; @@ -856,22 +859,21 @@ static int plpks_read_var(u8 consumer, struct plpks_var *var) virt_to_phys(var->name), var->namelen, virt_to_phys(output), maxobjsize); - if (rc != H_SUCCESS) { rc = pseries_status_to_err(rc); - goto out_free_output; + if (rc != -EPERM || !retbuf[1]) + goto out_free_output; + goto out_copy_policy; } if (!var->data || var->datalen > retbuf[0]) var->datalen = retbuf[0]; - var->policy = retbuf[1]; - if (var->data) memcpy(var->data, output, var->datalen); - rc = 0; - +out_copy_policy: + var->policy = retbuf[1]; out_free_output: kfree(output); out_free_label: -- cgit v1.2.3 From 0d0eafedcaf4939819d38ede6ad355ea37acd978 Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:06 +0530 Subject: pseries/plpks: fix error code for wrapping key reads PLPKS variables with the PLPKS_WRAPPINGKEY policy bit set are not readable. Return -EPERM instead of -EINVAL in this case to better reflect the access restriction being enforced. Fixes: 133aa79e211d ("pseries/plpks: add HCALLs for PowerVM Key Wrapping Module") Cc: stable@vger.kernel.org # 7.0 Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-3-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c index 7e56b3dcacbc..ccf5765a907b 100644 --- a/arch/powerpc/platforms/pseries/plpks.c +++ b/arch/powerpc/platforms/pseries/plpks.c @@ -829,7 +829,7 @@ static int plpks_read_var(u8 consumer, struct plpks_var *var) return -EINVAL; if (var->policy & PLPKS_WRAPPINGKEY) - return -EINVAL; + return -EPERM; auth = construct_auth(consumer); if (IS_ERR(auth)) -- cgit v1.2.3 From b59ff2d50b817eeec1259f357aae821c7834007f Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:07 +0530 Subject: pseries/plpks: validate input to plpks_signed_update_var() plpks_signed_update_var() dereferences the supplied PLPKS variable pointer without validating it. A NULL argument could therefore result in a NULL pointer dereference. Reject a NULL pointer argument with -EINVAL. Fixes: 899d9b8fee66 ("powerpc/pseries: Implement signed update for PLPKS objects") Cc: stable@vger.kernel.org # 6.3 Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-4-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c index ccf5765a907b..e3ae2cd78247 100644 --- a/arch/powerpc/platforms/pseries/plpks.c +++ b/arch/powerpc/platforms/pseries/plpks.c @@ -637,6 +637,9 @@ int plpks_signed_update_var(struct plpks_var *var, u64 flags) u64 continuetoken = 0; u64 timeout = 0; + if (!var) + return -EINVAL; + if (!var->data || var->datalen <= 0 || var->namelen > PLPKS_MAX_NAME_SIZE) return -EINVAL; -- cgit v1.2.3 From fe18716ff314eb32299048fd64242d31b4e852cf Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:08 +0530 Subject: pseries/plpks: clear sensitive PLPKS buffers PLPKS read, remove, and write operations use authentication buffers that contain passwords. The read output buffer may also contain sensitive data. Freeing these buffers with kfree(), without wiping the contents, can leave sensitive data in memory. Use kfree_sensitive() to wipe the buffers when freeing them. Fixes: 2454a7af0f2a ("powerpc/pseries: define driver for Platform KeyStore") Cc: stable@vger.kernel.org # 6.0 Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Reviewed-by: Nayna Jain Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-5-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c index e3ae2cd78247..8dbf85e9714c 100644 --- a/arch/powerpc/platforms/pseries/plpks.c +++ b/arch/powerpc/platforms/pseries/plpks.c @@ -754,7 +754,7 @@ int plpks_write_var(struct plpks_var var) rc = pseries_status_to_err(rc); kfree(label); out: - kfree(auth); + kfree_sensitive(auth); return rc; } @@ -812,7 +812,7 @@ int plpks_remove_var(char *component, u8 varos, struct plpks_var_name vname) rc = pseries_status_to_err(rc); kfree(label); out: - kfree(auth); + kfree_sensitive(auth); return rc; } @@ -878,11 +878,11 @@ static int plpks_read_var(u8 consumer, struct plpks_var *var) out_copy_policy: var->policy = retbuf[1]; out_free_output: - kfree(output); + kfree_sensitive(output); out_free_label: kfree(label); out_free_auth: - kfree(auth); + kfree_sensitive(auth); return rc; } -- cgit v1.2.3 From 203695c109e37d8fe3531844218f75b98eef8e1f Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:09 +0530 Subject: pseries/plpks: clear sensitive buffers in wrapping operations PLPKS wrapping key generation, object wrapping, and object unwrapping use authentication buffers that contain passwords. An object unwrapping failure also frees an output buffer that may contain sensitive data. Freeing these buffers with kfree(), without wiping the contents, can leave sensitive data in memory. Use kfree_sensitive() to wipe the buffers when freeing them. Fixes: 133aa79e211d ("pseries/plpks: add HCALLs for PowerVM Key Wrapping Module") Cc: stable@vger.kernel.org # 7.0 Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Reviewed-by: Nayna Jain Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-6-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c index 8dbf85e9714c..17a73a04d45b 100644 --- a/arch/powerpc/platforms/pseries/plpks.c +++ b/arch/powerpc/platforms/pseries/plpks.c @@ -975,7 +975,7 @@ int plpks_gen_wrapping_key(void) kfree(label); out: - kfree(auth); + kfree_sensitive(auth); return rc; } EXPORT_SYMBOL_GPL(plpks_gen_wrapping_key); @@ -1100,7 +1100,7 @@ int plpks_wrap_object(u8 **input_buf, u32 input_len, u16 wrap_flags, out_free_label: kfree(label); out: - kfree(auth); + kfree_sensitive(auth); return rc; } EXPORT_SYMBOL_GPL(plpks_wrap_object); @@ -1177,14 +1177,14 @@ int plpks_unwrap_object(u8 **input_buf, u32 input_len, u8 **output_buf, if (rc) { pr_err("H_PKS_UNWRAP_OBJECT failed. pseries_status=%d, rc=%d", pseries_status, rc); - kfree(*output_buf); + kfree_sensitive(*output_buf); *output_buf = NULL; } else { *output_len = retbuf[1]; } out: - kfree(auth); + kfree_sensitive(auth); return rc; } EXPORT_SYMBOL_GPL(plpks_unwrap_object); -- cgit v1.2.3 From 63de55024e7c85975f4410380323edb05b186d7c Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:10 +0530 Subject: pseries/plpks: clear signed update authentication buffer The PLPKS signed update operation uses an authentication buffer that contains a password. Freeing this buffer with kfree(), without wiping the contents, can leave sensitive data in memory. Use kfree_sensitive() to wipe the buffer when freeing it. Fixes: 899d9b8fee66 ("powerpc/pseries: Implement signed update for PLPKS objects") Cc: stable@vger.kernel.org # 6.3 Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Reviewed-by: Nayna Jain Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-7-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c index 17a73a04d45b..b057e378a6b3 100644 --- a/arch/powerpc/platforms/pseries/plpks.c +++ b/arch/powerpc/platforms/pseries/plpks.c @@ -685,7 +685,7 @@ int plpks_signed_update_var(struct plpks_var *var, u64 flags) kfree(label); out: - kfree(auth); + kfree_sensitive(auth); return rc; } -- cgit v1.2.3 From 217ba01e5b9096091acc2c1a3776cb3eb88c00ff Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:11 +0530 Subject: pseries/plpks: clear SED key data after use Explicitly clear the temporary buffer containing SED key data after read and write operations. Fixes: ec8cf230cecc ("powerpc/pseries: PLPKS SED Opal keystore support") Cc: stable@vger.kernel.org # 6.7 Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Reviewed-by: Greg Joyce Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-8-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks_sed_ops.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/pseries/plpks_sed_ops.c b/arch/powerpc/platforms/pseries/plpks_sed_ops.c index 7c873c9589ef..ac3ec2ca7fbe 100644 --- a/arch/powerpc/platforms/pseries/plpks_sed_ops.c +++ b/arch/powerpc/platforms/pseries/plpks_sed_ops.c @@ -84,14 +84,16 @@ int sed_read_key(char *keyname, char *key, u_int *keylen) ret = plpks_read_os_var(&var); if (ret != 0) - return ret; + goto out; len = min_t(u16, be32_to_cpu(data.key_len), var.datalen); memcpy(key, data.key, len); key[len] = '\0'; *keylen = len; - return 0; +out: + memzero_explicit(&data, sizeof(data)); + return ret; } /* @@ -102,6 +104,7 @@ int sed_write_key(char *keyname, char *key, u_int keylen) struct plpks_var var; struct plpks_sed_object_data data; struct plpks_var_name vname; + int ret; plpks_init_var(&var, keyname); @@ -127,5 +130,8 @@ int sed_write_key(char *keyname, char *key, u_int keylen) vname.name = var.name; plpks_remove_var(var.component, var.os, vname); - return plpks_write_var(var); + ret = plpks_write_var(var); + memzero_explicit(&data, sizeof(data)); + + return ret; } -- cgit v1.2.3 From abcccecf4aa4b681f50eb7d7de93fc9a0a61c181 Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:12 +0530 Subject: pseries/plpks: fix self-reference in plpks_var initializer In plpks_gen_wrapping_key() and plpks_wrap_object(), strlen(var.name) is used to initialize var.namelen within the same struct initializer. This references a member of var before initialization of var is complete. Use the compile-time length of PLPKS_WRAPKEY_NAME instead. Reported-by: R Nageswara Sastry Fixes: 133aa79e211d ("pseries/plpks: add HCALLs for PowerVM Key Wrapping Module") Cc: stable@vger.kernel.org # 7.0 Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-9-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c index b057e378a6b3..896eda3eb65f 100644 --- a/arch/powerpc/platforms/pseries/plpks.c +++ b/arch/powerpc/platforms/pseries/plpks.c @@ -939,7 +939,7 @@ int plpks_gen_wrapping_key(void) int rc = 0, pseries_status = 0; struct plpks_var var = { .name = PLPKS_WRAPKEY_NAME, - .namelen = strlen(var.name), + .namelen = sizeof(PLPKS_WRAPKEY_NAME) - 1, .policy = PLPKS_WRAPPINGKEY, .os = PLPKS_VAR_LINUX, .component = PLPKS_WRAPKEY_COMPONENT @@ -1034,7 +1034,7 @@ int plpks_wrap_object(u8 **input_buf, u32 input_len, u16 wrap_flags, bool sb_enforce_bit = wrap_flags & BIT(1); struct plpks_var var = { .name = PLPKS_WRAPKEY_NAME, - .namelen = strlen(var.name), + .namelen = sizeof(PLPKS_WRAPKEY_NAME) - 1, .os = PLPKS_VAR_LINUX, .component = PLPKS_WRAPKEY_COMPONENT }; -- cgit v1.2.3 From 46b8411b2ee948b1786bc4af76e7feede0c2ddff Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:13 +0530 Subject: pseries/plpks: hide wrapping_features when unsupported The PLPKS wrapping features config is only valid on systems that support the "Key Wrapping" feature. Currently the config is always exposed, even when the feature is unavailable. Add an is_visible() callback to suppress the wrapping_features sysfs attribute when the "Key Wrapping" feature is not supported. Declare the attribute group static const, as it is not modified after initialization. Fixes: 447eb1d5ef00 ("pseries/plpks: expose PowerVM wrapping features via the sysfs") Cc: stable@vger.kernel.org # 7.0 Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-10-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks-sysfs.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/pseries/plpks-sysfs.c b/arch/powerpc/platforms/pseries/plpks-sysfs.c index c2ebcbb41ae3..f2436229f323 100644 --- a/arch/powerpc/platforms/pseries/plpks-sysfs.c +++ b/arch/powerpc/platforms/pseries/plpks-sysfs.c @@ -45,6 +45,16 @@ static const struct attribute *config_attrs[] = { static struct kobject *plpks_kobj, *plpks_config_kobj; +static umode_t plpks_config_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + if (attr == &attr_wrapping_features.attr && + !plpks_wrapping_is_supported()) + return 0; + + return attr->mode; +} + int plpks_config_create_softlink(struct kobject *from) { if (!plpks_config_kobj) @@ -52,13 +62,13 @@ int plpks_config_create_softlink(struct kobject *from) return sysfs_create_link(from, plpks_config_kobj, "config"); } +static const struct attribute_group config_group = { + .attrs = (struct attribute **)config_attrs, + .is_visible = plpks_config_attr_is_visible, +}; + static __init int plpks_sysfs_config(struct kobject *kobj) { - struct attribute_group config_group = { - .name = NULL, - .attrs = (struct attribute **)config_attrs, - }; - return sysfs_create_group(kobj, &config_group); } -- cgit v1.2.3 From e0645be900a856a8ec462b1c47a4977ca19808e7 Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:14 +0530 Subject: pseries/plpks: make narrowing conversions explicit Add explicit casts when assigning values from the hcall return buffers to narrower fields to make the intended conversions clear. Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-11-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c index 896eda3eb65f..eff968fe5ae0 100644 --- a/arch/powerpc/platforms/pseries/plpks.c +++ b/arch/powerpc/platforms/pseries/plpks.c @@ -576,7 +576,7 @@ static int plpks_confirm_object_flushed(struct label *label, virt_to_phys(auth), virt_to_phys(label), label->size); - status = retbuf[0]; + status = (u8)retbuf[0]; if (rc) { timed_out = false; if (rc == H_NOT_FOUND && status == 1) @@ -870,13 +870,13 @@ static int plpks_read_var(u8 consumer, struct plpks_var *var) } if (!var->data || var->datalen > retbuf[0]) - var->datalen = retbuf[0]; + var->datalen = (u16)retbuf[0]; if (var->data) memcpy(var->data, output, var->datalen); out_copy_policy: - var->policy = retbuf[1]; + var->policy = (u32)retbuf[1]; out_free_output: kfree_sensitive(output); out_free_label: -- cgit v1.2.3 From a2df535d9dd9029987fd9339e5fe668d53a459fa Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:15 +0530 Subject: pseries/plpks: rename the default wrapping key macro Rename PLPKS_WRAPKEY_NAME to PLPKS_DEFAULT_WRAPKEY_LABEL to clarify that the macro represents the default wrapping key label used by PKWM. Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-12-ssrish@linux.ibm.com --- arch/powerpc/platforms/pseries/plpks.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c index eff968fe5ae0..240cd5632446 100644 --- a/arch/powerpc/platforms/pseries/plpks.c +++ b/arch/powerpc/platforms/pseries/plpks.c @@ -10,7 +10,7 @@ #define pr_fmt(fmt) "plpks: " fmt #define PLPKS_WRAPKEY_COMPONENT "PLPKSWR" -#define PLPKS_WRAPKEY_NAME "default-wrapping-key" +#define PLPKS_DEFAULT_WRAPKEY_LABEL "default-wrapping-key" /* * To 4K align the {input, output} buffers to the {UN}WRAP H_CALLs @@ -938,8 +938,8 @@ int plpks_gen_wrapping_key(void) struct label *label; int rc = 0, pseries_status = 0; struct plpks_var var = { - .name = PLPKS_WRAPKEY_NAME, - .namelen = sizeof(PLPKS_WRAPKEY_NAME) - 1, + .name = PLPKS_DEFAULT_WRAPKEY_LABEL, + .namelen = sizeof(PLPKS_DEFAULT_WRAPKEY_LABEL) - 1, .policy = PLPKS_WRAPPINGKEY, .os = PLPKS_VAR_LINUX, .component = PLPKS_WRAPKEY_COMPONENT @@ -1033,8 +1033,8 @@ int plpks_wrap_object(u8 **input_buf, u32 input_len, u16 wrap_flags, bool sb_audit_or_enforce_bit = wrap_flags & BIT(0); bool sb_enforce_bit = wrap_flags & BIT(1); struct plpks_var var = { - .name = PLPKS_WRAPKEY_NAME, - .namelen = sizeof(PLPKS_WRAPKEY_NAME) - 1, + .name = PLPKS_DEFAULT_WRAPKEY_LABEL, + .namelen = sizeof(PLPKS_DEFAULT_WRAPKEY_LABEL) - 1, .os = PLPKS_VAR_LINUX, .component = PLPKS_WRAPKEY_COMPONENT }; -- cgit v1.2.3 From 0499ab96527ac4c55be381d7417ff542d4ea24f6 Mon Sep 17 00:00:00 2001 From: Srish Srinivasan Date: Wed, 23 Sep 2026 23:58:16 +0530 Subject: pseries/plpks: update PKS documentation and MAINTAINERS entry Wrap long PKS hcall return-value lines consistently and fix a typo in the H_PKS_UNWRAP_OBJECT description. Also update the MAINTAINERS entry from KEYS-TRUSTED-PLPKS to KEYS-TRUSTED-PKWM to reflect the PowerVM Key Wrapping Module naming. Signed-off-by: Srish Srinivasan Tested-by: R Nageswara Sastry Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260923182816.151451-13-ssrish@linux.ibm.com --- Documentation/arch/powerpc/papr_hcalls.rst | 16 ++++++++-------- MAINTAINERS | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Documentation/arch/powerpc/papr_hcalls.rst b/Documentation/arch/powerpc/papr_hcalls.rst index 14e39f095a1c..44c9c8b32ae3 100644 --- a/Documentation/arch/powerpc/papr_hcalls.rst +++ b/Documentation/arch/powerpc/papr_hcalls.rst @@ -305,8 +305,8 @@ like core instruction, core LLAT and nest. | Input: authorization, objectlabel, objectlabellen, policy, out, outlen | Out: *Hypervisor Generated Key, or None when the wrapping key policy is set* | Return Value: *H_SUCCESS, H_Function, H_State, H_R_State, H_Parameter, H_P2, - H_P3, H_P4, H_P5, H_P6, H_Authority, H_Nomem, H_Busy, H_Resource, - H_Aborted* + H_P3, H_P4, H_P5, H_P6, H_Authority, H_Nomem, H_Busy, + H_Resource, H_Aborted* H_PKS_GEN_KEY is used to have the hypervisor generate a new random key. This key is stored as an object in the Power LPAR Platform KeyStore with @@ -321,8 +321,8 @@ the user. Generation of wrapping keys is supported only for a key size of | inlen, out, outlen, continue-token | Out: *continue-token, byte size of wrapped object, wrapped object* | Return Value: *H_SUCCESS, H_Function, H_State, H_R_State, H_Parameter, H_P2, - H_P3, H_P4, H_P5, H_P6, H_P7, H_P8, H_P9, H_Authority, H_Invalid_Key, - H_NOT_FOUND, H_Busy, H_LongBusy, H_Aborted* + H_P3, H_P4, H_P5, H_P6, H_P7, H_P8, H_P9, H_Authority, + H_Invalid_Key, H_NOT_FOUND, H_Busy, H_LongBusy, H_Aborted* H_PKS_WRAP_OBJECT is used to wrap an object using a wrapping key stored in the Power LPAR Platform KeyStore and return the wrapped object to the caller. The @@ -331,16 +331,16 @@ which must have been previously created with H_PKS_GEN_KEY. The provided object is then encrypted with the wrapping key and additional metadata and the result is returned to the caller. - **H_PKS_UNWRAP_OBJECT** | Input: authorization, objectwrapflags, in, inlen, out, outlen, continue-token | Out: *continue-token, byte size of unwrapped object, unwrapped object* | Return Value: *H_SUCCESS, H_Function, H_State, H_R_State, H_Parameter, H_P2, - H_P3, H_P4, H_P5, H_P6, H_P7, H_Authority, H_Unsupported, H_Bad_Data, - H_NOT_FOUND, H_Invalid_Key, H_Busy, H_LongBusy, H_Aborted* + H_P3, H_P4, H_P5, H_P6, H_P7, H_Authority, H_Unsupported, + H_Bad_Data, H_NOT_FOUND, H_Invalid_Key, H_Busy, H_LongBusy, + H_Aborted* -H_PKS_UNWRAP_OBJECT is used to unwrap an object that was previously warapped with +H_PKS_UNWRAP_OBJECT is used to unwrap an object that was previously wrapped with H_PKS_WRAP_OBJECT. References diff --git a/MAINTAINERS b/MAINTAINERS index cc3cae2e378b..932eb463428c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14485,7 +14485,7 @@ S: Supported F: include/keys/trusted_dcp.h F: security/keys/trusted-keys/trusted_dcp.c -KEYS-TRUSTED-PLPKS +KEYS-TRUSTED-PKWM M: Srish Srinivasan M: Nayna Jain L: linux-integrity@vger.kernel.org -- cgit v1.2.3 From 61a39797bb136243c48b46e2586d8a9e2cea6a89 Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Mon, 28 Sep 2026 17:58:35 +0530 Subject: KVM: PPC: Book3S HV: Add SRCU protection for virtual-mode HPT hcalls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The virtual-mode HPT hcall handlers (H_ENTER, H_REMOVE, H_BULK_REMOVE, H_CLEAR_REF, H_CLEAR_MOD) call into book3s_hv_rm_mmu.c, which accesses memslots via kvm_memslots_raw(). kvm_memslots_raw() uses rcu_dereference_raw_check() to bypass SRCU lockdep annotation checking. This is safe in real mode because the entire guest entry/exit is wrapped in srcu_read_lock/unlock inside kvmppc_run_core() and kvmhv_run_single_vcpu(). However, since commit 6165d5dd99db ("KVM: PPC: Book3S HV: add virtual mode handlers for HPT hcalls and page faults"), these same handlers are also executed in virtual mode via kvmppc_pseries_do_hcall(), which runs after SRCU has already been released on guest exit. A concurrent KVM_SET_USER_MEMORY_REGION deletion or move can therefore call synchronize_srcu_expedited() — which does not wait for this thread — and then kfree(slot) and vfree(slot->arch.rmap) while the hcall handler still holds a raw pointer to the memslot. lock_rmap() then writes to freed vmalloc memory, leading to use-after-free and memory corruption. Because kvm_memslots_raw() suppresses lockdep checks, this race is entirely silent. Fix this by factoring out the 7 HPT hcall handlers (H_REMOVE, H_ENTER, H_READ, H_CLEAR_MOD, H_CLEAR_REF, H_PROTECT, H_BULK_REMOVE) into a helper function, kvmppc_pseries_do_hpt_hcall(), and wrapping its call site in srcu_read_lock(&kvm->srcu) / srcu_read_unlock(&kvm->srcu, idx). Targeting only the HPT hcalls avoids wrapping non-HPT hcalls that sleep (such as H_CONFER, H_REGISTER_VPA, H_PAGE_INIT) or handlers that already acquire SRCU internally (such as H_RTAS). Fixes: 6165d5dd99db ("KVM: PPC: Book3S HV: add virtual mode handlers for HPT hcalls and page faults") Cc: stable@vger.kernel.org # v5.14+ Suggested-by: Ritesh Harjani (IBM) Signed-off-by: Amit Machhiwal Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260928122837.8782-2-amachhiw@linux.ibm.com --- arch/powerpc/kvm/book3s_hv.c | 70 ++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index dbac3573b2c8..aa51968e206a 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1159,6 +1159,38 @@ static long kvmppc_h_rpt_invalidate(struct kvm_vcpu *vcpu, return H_SUCCESS; } +static long kvmppc_pseries_do_hpt_hcall(struct kvm_vcpu *vcpu, unsigned long req) +{ + switch (req) { + case H_REMOVE: + return kvmppc_h_remove(vcpu, kvmppc_get_gpr(vcpu, 4), + kvmppc_get_gpr(vcpu, 5), + kvmppc_get_gpr(vcpu, 6)); + case H_ENTER: + return kvmppc_h_enter(vcpu, kvmppc_get_gpr(vcpu, 4), + kvmppc_get_gpr(vcpu, 5), + kvmppc_get_gpr(vcpu, 6), + kvmppc_get_gpr(vcpu, 7)); + case H_READ: + return kvmppc_h_read(vcpu, kvmppc_get_gpr(vcpu, 4), + kvmppc_get_gpr(vcpu, 5)); + case H_CLEAR_MOD: + return kvmppc_h_clear_mod(vcpu, kvmppc_get_gpr(vcpu, 4), + kvmppc_get_gpr(vcpu, 5)); + case H_CLEAR_REF: + return kvmppc_h_clear_ref(vcpu, kvmppc_get_gpr(vcpu, 4), + kvmppc_get_gpr(vcpu, 5)); + case H_PROTECT: + return kvmppc_h_protect(vcpu, kvmppc_get_gpr(vcpu, 4), + kvmppc_get_gpr(vcpu, 5), + kvmppc_get_gpr(vcpu, 6)); + case H_BULK_REMOVE: + return kvmppc_h_bulk_remove(vcpu); + } + + return H_FUNCTION; +} + int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu) { struct kvm *kvm = vcpu->kvm; @@ -1174,47 +1206,15 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu) switch (req) { case H_REMOVE: - ret = kvmppc_h_remove(vcpu, kvmppc_get_gpr(vcpu, 4), - kvmppc_get_gpr(vcpu, 5), - kvmppc_get_gpr(vcpu, 6)); - if (ret == H_TOO_HARD) - return RESUME_HOST; - break; case H_ENTER: - ret = kvmppc_h_enter(vcpu, kvmppc_get_gpr(vcpu, 4), - kvmppc_get_gpr(vcpu, 5), - kvmppc_get_gpr(vcpu, 6), - kvmppc_get_gpr(vcpu, 7)); - if (ret == H_TOO_HARD) - return RESUME_HOST; - break; case H_READ: - ret = kvmppc_h_read(vcpu, kvmppc_get_gpr(vcpu, 4), - kvmppc_get_gpr(vcpu, 5)); - if (ret == H_TOO_HARD) - return RESUME_HOST; - break; case H_CLEAR_MOD: - ret = kvmppc_h_clear_mod(vcpu, kvmppc_get_gpr(vcpu, 4), - kvmppc_get_gpr(vcpu, 5)); - if (ret == H_TOO_HARD) - return RESUME_HOST; - break; case H_CLEAR_REF: - ret = kvmppc_h_clear_ref(vcpu, kvmppc_get_gpr(vcpu, 4), - kvmppc_get_gpr(vcpu, 5)); - if (ret == H_TOO_HARD) - return RESUME_HOST; - break; case H_PROTECT: - ret = kvmppc_h_protect(vcpu, kvmppc_get_gpr(vcpu, 4), - kvmppc_get_gpr(vcpu, 5), - kvmppc_get_gpr(vcpu, 6)); - if (ret == H_TOO_HARD) - return RESUME_HOST; - break; case H_BULK_REMOVE: - ret = kvmppc_h_bulk_remove(vcpu); + idx = srcu_read_lock(&kvm->srcu); + ret = kvmppc_pseries_do_hpt_hcall(vcpu, req); + srcu_read_unlock(&kvm->srcu, idx); if (ret == H_TOO_HARD) return RESUME_HOST; break; -- cgit v1.2.3 From abbbe5ff83e7c9ed3a4d61b73011f1df7bf87907 Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Mon, 28 Sep 2026 17:58:36 +0530 Subject: KVM: PPC: Book3S HV: Add preempt_disable() around virtual-mode HPTE bit-lock users kvmppc_hv_find_lock_hpte() requires virtual-mode callers to run with preemption disabled, because it can return with HPTE_V_HVLOCK still held until the caller later unlocks the HPTE. Existing virtual-mode callers in book3s_64_mmu_hv.c already follow that rule. Two virtual-mode call paths do not: 1. kvmppc_handle_exit_hv() calls kvmppc_hpte_hv_fault() for hash-mode data-side and instruction-side faults after guest exit, with preemption already enabled. kvmppc_hpte_hv_fault() calls kvmppc_hv_find_lock_hpte() directly. 2. kvmppc_pseries_do_hcall() executes virtual-mode handlers for HPT hcalls via kvmppc_pseries_do_hpt_hcall() with preemption enabled. The handlers for H_ENTER, H_REMOVE, H_READ, H_CLEAR_MOD, H_CLEAR_REF, H_PROTECT, and H_BULK_REMOVE spin on try_lock_hpte() or lock_rmap(), and H_ENTER also reaches kvmppc_do_h_enter(), which uses arch_spin_lock() on kvm->mmu_lock. That raw lock choice is intentional because kvmppc_do_h_enter() is also used by real-mode callers, so the correct fix is to establish the proper preemption context at the virtual-mode caller boundary. If a vCPU thread is preempted while holding HPTE_V_HVLOCK, any other thread on the same CPU trying to acquire the same bit-lock can spin indefinitely. The same applies to lock_rmap() users and to the H_ENTER path while holding the raw mmu_lock. Fix this by adding preempt_disable()/preempt_enable() pairs around the two kvmppc_hpte_hv_fault() call sites in kvmppc_handle_exit_hv(), and around the kvmppc_pseries_do_hpt_hcall() invocation in kvmppc_pseries_do_hcall(). Targeting only the HPT hcall helper avoids wrapping non-HPT hcalls (such as H_RTAS, H_CONFER, H_REGISTER_VPA) that perform page faults, blocking memory allocations, or context switches. Fixes: 6165d5dd99db ("KVM: PPC: Book3S HV: add virtual mode handlers for HPT hcalls and page faults") Cc: stable@vger.kernel.org # v5.14+ Signed-off-by: Amit Machhiwal Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260928122837.8782-3-amachhiw@linux.ibm.com --- arch/powerpc/kvm/book3s_hv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index aa51968e206a..52f72f30baf0 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1212,9 +1212,11 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu) case H_CLEAR_REF: case H_PROTECT: case H_BULK_REMOVE: + preempt_disable(); idx = srcu_read_lock(&kvm->srcu); ret = kvmppc_pseries_do_hpt_hcall(vcpu, req); srcu_read_unlock(&kvm->srcu, idx); + preempt_enable(); if (ret == H_TOO_HARD) return RESUME_HOST; break; @@ -1834,8 +1836,10 @@ static int kvmppc_handle_exit_hv(struct kvm_vcpu *vcpu, else vsid = vcpu->arch.fault_gpa; + preempt_disable(); err = kvmppc_hpte_hv_fault(vcpu, vcpu->arch.fault_dar, vsid, vcpu->arch.fault_dsisr, true); + preempt_enable(); if (err == 0) { r = RESUME_GUEST; } else if (err == -1 || err == -2) { @@ -1881,8 +1885,10 @@ static int kvmppc_handle_exit_hv(struct kvm_vcpu *vcpu, else vsid = vcpu->arch.fault_gpa; + preempt_disable(); err = kvmppc_hpte_hv_fault(vcpu, vcpu->arch.fault_dar, vsid, vcpu->arch.fault_dsisr, false); + preempt_enable(); if (err == 0) { r = RESUME_GUEST; } else if (err == -1) { -- cgit v1.2.3 From 31895b0ff96778b524c74b43a108090581271603 Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Mon, 28 Sep 2026 17:58:37 +0530 Subject: KVM: PPC: Fix get_d_signext() 12-bit displacement for paired-single D-form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_d_signext() has two compounding bugs since its introduction in 2010: 1. The extraction mask 0x8FF silently drops bits 8-10 of the 12-bit D field (PPC ISA bits 21-23), corrupting any displacement that has any of those bits set. 2. The sign-magnitude idiom "-(d & 0x7ff)" is wrong for two's-complement: for D=0xFFC (encoding of -4) it returns -252 instead of -4. Together these errors produce a wrong effective address for psq_l, psq_lu, psq_st, and psq_stu whenever the displacement is negative or is a positive value >= 0x100 with bits 8-10 set — essentially any real-world paired- single stack-relative access. The D field occupies the bottom 12 bits of the instruction word (confirmed by the adjacent W and I extractions via inst_get_field(inst,16,16) and inst_get_field(inst,17,19)). Replace the open-coded logic with the standard sign_extend32(inst & 0xfff, 11), which correctly performs two's-complement sign extension from 12 bits to 32 bits. Fixes: 831317b605e7 ("KVM: PPC: Implement Paired Single emulation") Cc: stable@vger.kernel.org Signed-off-by: Amit Machhiwal Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260928122837.8782-4-amachhiw@linux.ibm.com --- arch/powerpc/kvm/book3s_paired_singles.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/arch/powerpc/kvm/book3s_paired_singles.c b/arch/powerpc/kvm/book3s_paired_singles.c index bc39c76c9d9f..532f96293de0 100644 --- a/arch/powerpc/kvm/book3s_paired_singles.c +++ b/arch/powerpc/kvm/book3s_paired_singles.c @@ -479,12 +479,7 @@ static bool kvmppc_inst_is_paired_single(struct kvm_vcpu *vcpu, u32 inst) static int get_d_signext(u32 inst) { - int d = inst & 0x8ff; - - if (d & 0x800) - return -(d & 0x7ff); - - return (d & 0x7ff); + return sign_extend32(inst & 0xfff, 11); } static int kvmppc_ps_three_in(struct kvm_vcpu *vcpu, bool rc, -- cgit v1.2.3 From 644ffb02746690c0ebfe143e066f81c9bde2975e Mon Sep 17 00:00:00 2001 From: Venkat Rao Bagalkote Date: Wed, 9 Sep 2026 18:02:40 +0530 Subject: powerpc/interrupt: Use early_radix_enabled() in NMI real-mode guard radix_enabled() uses a jump label which is only valid after setup_feature_keys() is called. Before that point, on a pSeries hash guest, early_check_vec5() clears MMU_FTR_TYPE_RADIX in cur_cpu_spec->mmu_features, but the jump label has not yet been patched, so radix_enabled() incorrectly returns true. The dangerous usage window where it goes wrong in early_setup(): early_setup: configure_exceptions(); setup_feature_keys(); If an NMI occurs in this window, !radix_enabled() evaluates to false in DEFINE_INTERRUPT_HANDLER_NMI. The handler fails to skip NMI entry in real mode and attempts to access memory outside the Real Mode Area (RMA), hanging the boot. Since common interrupt wrappers do not have the context of early or late, using early_radix_enabled() is the safer option. It does a plain bitmask check against cur_cpu_spec->mmu_features and is correct at all times. Console logs from a pSeries HASH guest showing values across boot stages: [ 0.000000] DEBUG: after early_init_devtree: early_radix_enabled=0 radix_enabled=1 (mismatch means NMI real-mode check is unsafe!) [ 0.000000] DEBUG: after configure_exceptions (DANGEROUS WINDOW): early_radix_enabled=0 radix_enabled=1 [ 0.000000] DEBUG: after setup_feature_keys (jump labels initialized): early_radix_enabled=0 radix_enabled=0 (should now match!) [ 0.057124] DEBUG: post secondary CPU bringup: early_radix_enabled=0 radix_enabled=0 (should match!) Console logs from a RADIX guest showing values across boot stages: [ 0.000000] DEBUG: after early_init_devtree: early_radix_enabled=1 radix_enabled=1 (mismatch means NMI real-mode check is unsafe!) [ 0.000000] DEBUG: after configure_exceptions (DANGEROUS WINDOW): early_radix_enabled=1 radix_enabled=1 [ 0.000000] DEBUG: after setup_feature_keys (jump labels initialized): early_radix_enabled=1 radix_enabled=1 (should now match!) [ 0.057016] DEBUG: post secondary CPU bringup: early_radix_enabled=1 radix_enabled=1 (should match!) Add the missing #include in alphabetical order since early_radix_enabled() is declared there. Fixes: 8d0e21012743 ("powerpc/mce: Avoid nmi_enter/exit in real mode on pseries hash") Signed-off-by: Venkat Rao Bagalkote Reviewed-by: Mukesh Kumar Chaurasiya Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260909123240.58786-1-venkat88@linux.ibm.com --- arch/powerpc/include/asm/interrupt.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/interrupt.h b/arch/powerpc/include/asm/interrupt.h index 1b45a49e9bed..355f6bbf9894 100644 --- a/arch/powerpc/include/asm/interrupt.h +++ b/arch/powerpc/include/asm/interrupt.h @@ -70,6 +70,7 @@ #include #include +#include #include #ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG @@ -290,7 +291,7 @@ interrupt_handler long func(struct pt_regs *regs) \ state = irqentry_nmi_enter(regs); \ } else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && \ firmware_has_feature(FW_FEATURE_LPAR) && \ - !radix_enabled()) { \ + !early_radix_enabled()) { \ /* no nmi_entry for a pseries hash guest \ * taking a real mode exception */ \ } else if (IS_ENABLED(CONFIG_KASAN)) { \ @@ -307,7 +308,7 @@ interrupt_handler long func(struct pt_regs *regs) \ irqentry_nmi_exit(regs, state); \ } else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && \ firmware_has_feature(FW_FEATURE_LPAR) && \ - !radix_enabled()) { \ + !early_radix_enabled()) { \ /* no nmi_exit for a pseries hash guest \ * taking a real mode exception */ \ } else if (IS_ENABLED(CONFIG_KASAN)) { \ -- cgit v1.2.3 From bda16261848768b8c54bfa22dea369d7133d8241 Mon Sep 17 00:00:00 2001 From: Gautam Menghani Date: Tue, 12 May 2026 17:27:24 +0530 Subject: KVM: PPC: Book3S HV: Add missing mappings for tracing exits The macro kvm_trace_symbol_exit is used for providing the mappings for the trap vectors and their names. Add mappings for H_FAC_UNAVAIL and RETURN_TO_HOST so that trap reasons are displayed as string instead of vector numbers when using the kvm_guest_exit tracepoint. Reviewed-by: Harsh Prateek Bora Reviewed-by: Amit Machhiwal Reviewed-by: Vaibhav Jain (IBM) Signed-off-by: Gautam Menghani Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260512115724.59299-1-gautam@linux.ibm.com --- arch/powerpc/kvm/trace_book3s.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kvm/trace_book3s.h b/arch/powerpc/kvm/trace_book3s.h index 9260ddbd557f..4ee35c6d01aa 100644 --- a/arch/powerpc/kvm/trace_book3s.h +++ b/arch/powerpc/kvm/trace_book3s.h @@ -7,6 +7,7 @@ */ #define kvm_trace_symbol_exit \ + {0x0, "RETURN_TO_HOST"}, \ {0x100, "SYSTEM_RESET"}, \ {0x200, "MACHINE_CHECK"}, \ {0x300, "DATA_STORAGE"}, \ @@ -28,6 +29,7 @@ {0xea0, "H_VIRT"}, \ {0xf00, "PERFMON"}, \ {0xf20, "ALTIVEC"}, \ - {0xf40, "VSX"} + {0xf40, "VSX"}, \ + {0xf80, "H_FAC_UNAVAIL"} #endif -- cgit v1.2.3 From daf5a5a82ca37ccf5b03c103ca44717b0b2f3e0b Mon Sep 17 00:00:00 2001 From: Gautam Menghani Date: Thu, 17 Sep 2026 12:58:38 +0530 Subject: KVM: PPC: Book3S HV: Free the queue pages in error path If the xive_native_configure_queue() call fails, the qpage allocated is not freed, which results in wasted memory. Fix this by freeing the qpage in the error path. Fixes: 5af50993850a ("KVM: PPC: Book3S HV: Native usage of the XIVE interrupt controller") Reviewed-by: Amit Machhiwal Signed-off-by: Gautam Menghani Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260917072839.78857-1-gautam@linux.ibm.com --- arch/powerpc/kvm/book3s_xive.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kvm/book3s_xive.c b/arch/powerpc/kvm/book3s_xive.c index 1d67237783b7..d348365df72d 100644 --- a/arch/powerpc/kvm/book3s_xive.c +++ b/arch/powerpc/kvm/book3s_xive.c @@ -961,9 +961,11 @@ static int xive_provision_queue(struct kvm_vcpu *vcpu, u8 prio) */ rc = xive_native_configure_queue(xc->vp_id, q, prio, qpage, xive->q_order, true); - if (rc) + if (rc) { pr_err("Failed to configure queue %d for VCPU %d\n", prio, xc->server_num); + free_pages((unsigned long)qpage, xive->q_page_order); + } return rc; } -- cgit v1.2.3 From 0b8b0c17a8998709ecb2852d56442ea981102941 Mon Sep 17 00:00:00 2001 From: Gaurav Batra Date: Thu, 6 Aug 2026 15:50:27 -0500 Subject: powerpc/pseries/iommu: Add TCEs for 16GB pages when RAM is pre-mapped In powerPC, if Dynamic DMA Window is big enough, RAM is pre-mapped. To determine the size of RAM, a PAPR+ property "ibm,lrdr-capacity" is used. This OF property dictates what is the max size of RAM an LPAR can have, including DR added memory. In PowerPC, 16GB pages can be allocated at machine level and then assigned to LPARs. These 16GB pages are added to LPAR memory at the time of boot. The address range for these 16GB pages is above MAX RAM an LPAR can have (ibm,lrdr-capacity). In the current implementation, these 16GB pages are being excluded from pre-mapped TCEs. A driver can have DMA buffers allocated from 16GB pages. This results in platform to raise an EEH when DMA is attempted on buffers in 16GB memory range. commit 6aa989ab2bd0 ("powerpc/pseries/iommu: memory notifier incorrectly adds TCEs for pmemory") Prior to the above patch, memblock_end_of_DRAM() was being used to determine the MAX memory of an LPAR. This included 16GB pages as well. The issue with using memblock_end_of_DRAM() is that when pmemory is converted to RAM via daxctl command, the DDW engine will incorrectly try to add TCEs for pmemory as well. Below is the address distribution of RAM, 16GB pages and pmemory for an LPAR with max memory of 256GB, memory allocated 64GB, 2 16GB pages and assigned pmemory of 8GB. RANGE SIZE STATE REMOVABLE BLOCK 0x0000000000000000-0x0000000fffffffff 64G online yes 0-255 0x0000004000000000-0x00000047ffffffff 32G online yes 1024-1151 cat /sys/bus/nd/devices/region0/resource 0x40100000000 cat /sys/bus/nd/devices/region0/size 8589934592 The approach to fix this problem is to revert back the code changes introduced by the above patch and to stash away the MAX memory of an LPAR, including 16GB pages, at the LPAR boot time. This value is then used whenever TCEs are needed to be pre-mapped - enable_DDW() or, iommu_mem_notifier() Fixes: 6aa989ab2bd0 ("powerpc/pseries/iommu: memory notifier incorrectly adds TCEs for pmemory") Cc: stable@vger.kernel.org Signed-off-by: Gaurav Batra Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260806205027.86428-1-gbatra@linux.ibm.com --- arch/powerpc/platforms/pseries/iommu.c | 67 +++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 272e9aab66d4..32d414c2cb84 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -69,6 +69,8 @@ static struct iommu_table *iommu_pseries_alloc_table(int node) return tbl; } +static phys_addr_t pseries_ddw_max_ram __ro_after_init; + #ifdef CONFIG_IOMMU_API static struct iommu_table_group_ops spapr_tce_table_group_ops; #endif @@ -1284,15 +1286,19 @@ struct failed_ddw_pdn { static LIST_HEAD(failed_ddw_pdn_list); -static phys_addr_t ddw_memory_hotplug_max(void) +static phys_addr_t __init ddw_memory_hotplug_max(void) { - resource_size_t max_addr; + resource_size_t max_addr = memory_hotplug_max(); + struct device_node *memory; -#if defined(CONFIG_NUMA) && defined(CONFIG_MEMORY_HOTPLUG) - max_addr = hot_add_drconf_memory_max(); -#else - max_addr = memblock_end_of_DRAM(); -#endif + for_each_node_by_type(memory, "memory") { + struct resource res; + + if (of_address_to_resource(memory, 0, &res)) + continue; + + max_addr = max_t(resource_size_t, max_addr, res.end + 1); + } return max_addr; } @@ -1447,7 +1453,7 @@ static struct property *ddw_property_create(const char *propname, u32 liobn, u64 static bool enable_ddw(struct pci_dev *dev, struct device_node *pdn, u64 dma_mask) { int len = 0, ret; - int max_ram_len = order_base_2(ddw_memory_hotplug_max()); + int max_ram_len = order_base_2(pseries_ddw_max_ram); struct ddw_query_response query; struct ddw_create_response create; int page_shift; @@ -1617,12 +1623,12 @@ static bool enable_ddw(struct pci_dev *dev, struct device_node *pdn, u64 dma_mas if (default_win_removed && pmem_present && !direct_mapping) { /* DDW is big enough to be split */ if ((1ULL << ddw_sz) >= - MIN_DDW_VPMEM_DMA_WINDOW + (1ULL << max_ram_len)) { + MIN_DDW_VPMEM_DMA_WINDOW + pseries_ddw_max_ram) { direct_mapping = true; /* offset of the Dynamic part of DDW */ - dynamic_offset = 1ULL << max_ram_len; + dynamic_offset = pseries_ddw_max_ram; } /* DDW will at least have dynamic allocation */ @@ -1669,7 +1675,7 @@ static bool enable_ddw(struct pci_dev *dev, struct device_node *pdn, u64 dma_mas if (direct_mapping) { /* DDW maps the whole partition, so enable direct DMA mapping */ - ret = walk_system_ram_range(0, ddw_memory_hotplug_max() >> PAGE_SHIFT, + ret = walk_system_ram_range(0, pseries_ddw_max_ram >> PAGE_SHIFT, win64->value, tce_setrange_multi_pSeriesLP_walk); if (ret) { dev_info(&dev->dev, "failed to map DMA window for %pOF: %d\n", @@ -1772,8 +1778,7 @@ out_unlock: /* For pre-mapped memory, set bus_dma_limit to the max RAM */ if (direct_mapping) - dev->dev.bus_dma_limit = dev->dev.archdata.dma_offset + - (1ULL << max_ram_len); + dev->dev.bus_dma_limit = dev->dev.archdata.dma_offset + pseries_ddw_max_ram; dev_info(&dev->dev, "lsa_required: %x, lsa_enabled: %x, direct mapping: %x\n", limited_addr_req, limited_addr_enabled, direct_mapping); @@ -2420,23 +2425,35 @@ static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action, { struct dma_win *window; struct memory_notify *arg = data; + unsigned long limit = arg->nr_pages; + unsigned long max_ram_pages = pseries_ddw_max_ram >> PAGE_SHIFT; int ret = 0; /* This notifier can get called when onlining persistent memory as well. * TCEs are not pre-mapped for persistent memory. Persistent memory will - * always be above ddw_memory_hotplug_max() + * always be above pseries_ddw_max_ram */ + if (arg->start_pfn >= max_ram_pages) + return NOTIFY_OK; + + /* RAM is being DLPAR'ed. The range should never exceed max ram. + * Just in case, clamp the range and throw a warning. + */ + if (arg->start_pfn + limit > max_ram_pages) { + limit = max_ram_pages - arg->start_pfn; + WARN_ONCE(1, "Limiting Page Range %lx - %lx to Max Mem Pages: %lx\n", + arg->start_pfn, arg->start_pfn + arg->nr_pages, + max_ram_pages); + } switch (action) { case MEM_GOING_ONLINE: spin_lock(&dma_win_list_lock); list_for_each_entry(window, &dma_win_list, list) { - if (window->direct && (arg->start_pfn << PAGE_SHIFT) < - ddw_memory_hotplug_max()) { + if (window->direct) { ret |= tce_setrange_multi_pSeriesLP(arg->start_pfn, - arg->nr_pages, window->prop); + limit, window->prop); } - /* XXX log error */ } spin_unlock(&dma_win_list_lock); break; @@ -2444,12 +2461,10 @@ static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action, case MEM_OFFLINE: spin_lock(&dma_win_list_lock); list_for_each_entry(window, &dma_win_list, list) { - if (window->direct && (arg->start_pfn << PAGE_SHIFT) < - ddw_memory_hotplug_max()) { + if (window->direct) { ret |= tce_clearrange_multi_pSeriesLP(arg->start_pfn, - arg->nr_pages, window->prop); + limit, window->prop); } - /* XXX log error */ } spin_unlock(&dma_win_list_lock); break; @@ -2533,6 +2548,14 @@ void __init iommu_init_early_pSeries(void) register_memory_notifier(&iommu_mem_nb); set_pci_dma_ops(&dma_iommu_ops); + + /* During init determine the max memory an LPAR can have and set it. This + * will be used for pre-mapping RAM in DDW. memblock_end_of_DRAM() can + * change during the running of LPAR - daxctl can add pmemory as + * "system-ram". This memory range should not be pre-mapped in DDW since + * the address of pmemory can be much higher than the DDW size. + */ + pseries_ddw_max_ram = ddw_memory_hotplug_max(); } static int __init disable_multitce(char *str) -- cgit v1.2.3 From 8fa763bc3ee9bd215476ab91ee480c649eaa4b29 Mon Sep 17 00:00:00 2001 From: "Mukesh Kumar Chaurasiya (IBM)" Date: Sat, 3 Oct 2026 15:15:09 +0530 Subject: powerpc: fix NMI exit ordering: restore soft masks after irqentry_nmi_exit() When running with CONFIG_PPC_IRQ_SOFT_MASK_DEBUG=y and LOCKDEP=y, an Oops is triggered during NMI exit: [ 202.258332] Oops: Exception in kernel mode, sig: 4 [#1] NIP [000000000003e178] 0x3e178 LR [c00000000003e174] arch_local_irq_restore+0x2a4/0x328 TRAP: 0700 MSR: 0000000000081000 The faulting instruction at NIP is a 'trap' (0fe00000) inside the rfi_flush_fallback trampoline at a userspace-looking low address, reached after a bad rfid caused by an incorrect SRR0 value. Root cause: DEFINE_INTERRUPT_HANDLER_NMI used to call arch_interrupt_nmi_exit_prepare() *before* irqentry_nmi_exit(), giving this sequence: arch_interrupt_nmi_enter_prepare() // saves irq_soft_mask, irq_happened irqentry_nmi_enter() // __nmi_enter() -> nmi_count++, in_nmi()=true ____func(regs) // NMI handler body arch_interrupt_nmi_exit_prepare() // RESTORES irq_soft_mask=0 (IRQS_ENABLED) // RESTORES irq_happened with pending bits irqentry_nmi_exit() // instrumentation block runs here... trace_hardirqs_on_prepare() // ...with in_nmi()==true still lockdep_hardirqs_on_prepare() // ...and irq_soft_mask already restored to 0 -> arch_local_irq_restore(0) // mask=0 -> enable path entered WARN_ON_ONCE(in_nmi()) // FIRES: __nmi_exit() hasn't run yet __nmi_exit() // nmi_count-- (too late) arch_interrupt_nmi_exit_prepare() restores irq_soft_mask to whatever the interrupted thread had (IRQS_ENABLED / 0) and restores irq_happened with any pending deferred-interrupt bits that were present when the NMI fired. This is correct and necessary before rfid back to the interrupted context, but it must not happen until irqentry_nmi_exit() has fully completed. With irq_soft_mask=0 and pending bits in irq_happened, the trace_hardirqs_on_prepare() call inside irqentry_nmi_exit()'s instrumentation block reaches lockdep_hardirqs_on_prepare() which calls __trace_hardirqs_on_caller(), which eventually calls arch_local_irq_restore(0) (the IRQS_ENABLED / enable path). arch_local_irq_restore() with mask=0 hits the CONFIG_PPC_IRQ_SOFT_MASK_DEBUG guards at irq_64.c:217: WARN_ON_ONCE(in_nmi()); // TRUE: __nmi_exit() not yet called The WARN_ON_ONCE fires a 'trap' instruction (tw 31,0,0 / 0fe00000) in kernel mode with MSR_PR=0, MSR_IR=0 (real-mode). The program check handler (TRAP 0700) takes the sig:4 (SIGILL) path via do_program_check() and calls die(), producing the Oops. The RESTART_TABLE mechanism in arch_local_irq_restore then causes the bad rfid that lands at the userspace-looking address visible in the NIP. The bug is invisible with LOCKDEP=n because trace_hardirqs_on_prepare() is a no-op when CONFIG_TRACE_IRQFLAGS is not set (TRACE_IRQFLAGS is selected by PROVE_LOCKING which selects LOCKDEP). Fix: Move arch_interrupt_nmi_exit_prepare() to after the irqentry_nmi_exit() block, so that: irqentry_nmi_exit() // __nmi_exit() -> nmi_count--, in_nmi()=false arch_interrupt_nmi_exit_prepare() // now safe to restore soft mask state The instrumentation inside irqentry_nmi_exit() now executes while in_nmi() is still true (nmi_count not yet decremented), but irq_soft_mask is still IRQS_ALL_DISABLED (NMI-entry value), so arch_local_irq_restore() is never called with mask=0 from that path. After __nmi_exit() returns, arch_interrupt_nmi_exit_prepare() restores the pre-NMI soft mask and irq_happened, which is the correct state for the rfid back to the interrupted context. Reviewed-by: Shrikanth Hegde Tested-by: Venkat Rao Bagalkote Reported-by: Venkat Rao Bagalkote Closes: https://lore.kernel.org/all/d4cccba9-ce5c-424b-864c-2248fb0d99e6@linux.ibm.com/ Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") Signed-off-by: Mukesh Kumar Chaurasiya (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20261003094509.3408190-1-mkchauras@gmail.com --- arch/powerpc/include/asm/interrupt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/interrupt.h b/arch/powerpc/include/asm/interrupt.h index 355f6bbf9894..c7d0e71eac83 100644 --- a/arch/powerpc/include/asm/interrupt.h +++ b/arch/powerpc/include/asm/interrupt.h @@ -302,7 +302,6 @@ interrupt_handler long func(struct pt_regs *regs) \ state = irqentry_nmi_enter(regs); \ } \ ret = ____##func (regs); \ - arch_interrupt_nmi_exit_prepare(regs, &nmi_state); \ if (mfmsr() & MSR_DR) { \ /* nmi_exit if relocations are on */ \ irqentry_nmi_exit(regs, state); \ @@ -318,6 +317,7 @@ interrupt_handler long func(struct pt_regs *regs) \ } else { \ irqentry_nmi_exit(regs, state); \ } \ + arch_interrupt_nmi_exit_prepare(regs, &nmi_state); \ \ return ret; \ } \ -- cgit v1.2.3 From de5a7fedf5061bdf6823eb509353bd7c128e34b5 Mon Sep 17 00:00:00 2001 From: "Mukesh Kumar Chaurasiya (IBM)" Date: Sat, 3 Oct 2026 15:10:06 +0530 Subject: powerpc/kasan: fix r2 corruption in mem*() on GENERIC_ENTRY with old compilers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") introduced a bug that, when CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX is not set, expand to two consecutive ELFv2 global entry points for the same function body: _GLOBAL_TOC_KASAN(memcpy) expands to: _GLOBAL_TOC(memcpy); _GLOBAL_TOC(__memcpy) On powerpc64le this produces: memcpy: addis r2,r12,(.TOC.-0b)@ha <- global entry, sets up r2 addi r2,r2,(.TOC.-0b)@l .localentry memcpy,.-memcpy <- local entry at memcpy+8 __memcpy: addis r2,r12,(.TOC.-0b)@ha <- memcpy+8: __memcpy global entry addi r2,r2,(.TOC.-0b)@l .localentry __memcpy,.-__memcpy The assembler sets memcpy's st_other field so its local entry offset is 8 bytes past the global entry point. The linker resolves same-TOC calls to the local entry, so every same-TOC caller of memcpy() lands at memcpy+8 — which is the beginning of the __memcpy global-entry TOC prologue. That prologue uses r12 as the base to recompute r2, but r12 holds whatever the caller left in it (not the address of memcpy+8). The result is r2 pointing at garbage on return. Same-TOC callers do not reload r2 after the call, so the first TOC-relative access after returning from memcpy() or memmove() crashes or silently corrupts data. With GENERIC_ENTRY selected, mm/kasan/shadow.c no longer provides its own memcpy()/memmove()/memset() wrappers, so on a toolchain without -mllvm -asan-kernel-mem-intrinsic-prefix=1 (e.g. GCC 9) every instrumented file resolves mem*() directly to the assembly symbols and hits this corrupt-r2 path. This manifests as an early boot hang unique to powerpc with GCC 9 and CONFIG_KASAN=y. Fix this by removing the dual-entry macros entirely. The assembly files in mem_64.S, memcpy_64.S and copy_32.S now use plain _GLOBAL()/_GLOBAL_TOC() for memset/memcpy/memmove, emitting a single correct ELFv2 entry point. The __mem* aliases required by mm/kasan (used in mm/kasan/shadow.c and mm/kasan/generic.c as raw backends to bypass KASAN checking) are provided entirely through the #define redirections in asm/string.h, which already handles both the CC_HAS_KASAN_MEMINTRINSIC_PREFIX and non-prefix cases in C. As a consequence the _GLOBAL_KASAN(), _GLOBAL_TOC_KASAN() and EXPORT_SYMBOL_KASAN() macros have no remaining users and are removed from asm/kasan.h. The has_renamed_memintrinsics() branch in prom_init_check.sh, which conditionally whitelisted __memcpy/__memset when CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX was absent, is dead code after this change and is removed; the whitelist now unconditionally lists memcpy and memset. Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") Reported-by: Venkat Rao Bagalkote Closes: https://lore.kernel.org/all/96dae110-f79b-4e54-8a9b-514369019b5d@linux.ibm.com Tested-by: Venkat Rao Bagalkote Signed-off-by: Mukesh Kumar Chaurasiya (IBM) [Maddy: Fixed commit message to avoid checkpatch error] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20261003094006.3404731-1-mkchauras@gmail.com --- arch/powerpc/include/asm/kasan.h | 15 --------------- arch/powerpc/include/asm/string.h | 21 +-------------------- arch/powerpc/kernel/cputable.c | 14 +++----------- arch/powerpc/kernel/prom_init.c | 10 ++-------- arch/powerpc/kernel/prom_init_check.sh | 15 +-------------- arch/powerpc/lib/copy_32.S | 10 +++------- arch/powerpc/lib/mem_64.S | 7 ++----- arch/powerpc/lib/memcpy_64.S | 4 +--- 8 files changed, 13 insertions(+), 83 deletions(-) diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h index a690e7da53c2..599a9e02af02 100644 --- a/arch/powerpc/include/asm/kasan.h +++ b/arch/powerpc/include/asm/kasan.h @@ -2,21 +2,6 @@ #ifndef __ASM_KASAN_H #define __ASM_KASAN_H -#if defined(CONFIG_KASAN) && !defined(CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX) -#define _GLOBAL_KASAN(fn) \ - _GLOBAL(fn); \ - _GLOBAL(__##fn) -#define _GLOBAL_TOC_KASAN(fn) \ - _GLOBAL_TOC(fn); \ - _GLOBAL_TOC(__##fn) -#define EXPORT_SYMBOL_KASAN(fn) \ - EXPORT_SYMBOL(__##fn) -#else /* CONFIG_KASAN && !CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX */ -#define _GLOBAL_KASAN(fn) _GLOBAL(fn) -#define _GLOBAL_TOC_KASAN(fn) _GLOBAL_TOC(fn) -#define EXPORT_SYMBOL_KASAN(fn) -#endif /* CONFIG_KASAN && !CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX */ - #ifndef __ASSEMBLER__ #include diff --git a/arch/powerpc/include/asm/string.h b/arch/powerpc/include/asm/string.h index 1981bd4036b5..161b949683b3 100644 --- a/arch/powerpc/include/asm/string.h +++ b/arch/powerpc/include/asm/string.h @@ -29,29 +29,10 @@ extern void * memchr(const void *,int,__kernel_size_t); void memcpy_flushcache(void *dest, const void *src, size_t size); #ifdef CONFIG_KASAN -/* __mem variants are used by KASAN to implement instrumented meminstrinsics. */ -#ifdef CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX +/* Used by mm/kasan as raw backends to bypass KASAN checking. */ #define __memset memset #define __memcpy memcpy #define __memmove memmove -#else /* CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX */ -void *__memset(void *s, int c, __kernel_size_t count); -void *__memcpy(void *to, const void *from, __kernel_size_t n); -void *__memmove(void *to, const void *from, __kernel_size_t n); -#ifndef __SANITIZE_ADDRESS__ -/* - * For files that are not instrumented (e.g. mm/slub.c) we - * should use not instrumented version of mem* functions. - */ -#define memcpy(dst, src, len) __memcpy(dst, src, len) -#define memmove(dst, src, len) __memmove(dst, src, len) -#define memset(s, c, n) __memset(s, c, n) - -#ifndef __NO_FORTIFY -#define __NO_FORTIFY /* FORTIFY_SOURCE uses __builtin_memcpy, etc. */ -#endif -#endif /* !__SANITIZE_ADDRESS__ */ -#endif /* CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX */ #endif /* CONFIG_KASAN */ #ifdef CONFIG_PPC64 diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index 6f6801da9dc1..4731e27bb7bc 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -35,12 +35,7 @@ void __init set_cur_cpu_spec(struct cpu_spec *s) struct cpu_spec *t = &the_cpu_spec; t = PTRRELOC(t); - /* - * use memcpy() instead of *t = *s so that GCC replaces it - * by __memcpy() when KASAN is active - */ - memcpy(t, s, sizeof(*t)); - + *t = *s; *PTRRELOC(&cur_cpu_spec) = &the_cpu_spec; } @@ -53,11 +48,8 @@ static struct cpu_spec * __init setup_cpu_spec(unsigned long offset, t = PTRRELOC(t); old = *t; - /* - * Copy everything, then do fixups. Use memcpy() instead of *t = *s - * so that GCC replaces it by __memcpy() when KASAN is active - */ - memcpy(t, s, sizeof(*t)); + /* Copy everything, then do fixups. */ + *t = *s; /* * If we are overriding a previous value derived from the real diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index eb9f556b0937..e8e071024da5 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1362,14 +1362,8 @@ static void __init prom_check_platform_support(void) int prop_len = prom_getproplen(prom.chosen, "ibm,arch-vec-5-platform-support"); - /* - * First copy the architecture vec template - * - * use memcpy() instead of *vec = *vec_template so that GCC replaces it - * by __memcpy() when KASAN is active - */ - memcpy(&ibm_architecture_vec, &ibm_architecture_vec_template, - sizeof(ibm_architecture_vec)); + /* First copy the architecture vec template */ + ibm_architecture_vec = ibm_architecture_vec_template; prom_strscpy_pad(ibm_architecture_vec.vec7.os_id, linux_banner, 256); diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh index 3090b97258ae..3155cc722e48 100644 --- a/arch/powerpc/kernel/prom_init_check.sh +++ b/arch/powerpc/kernel/prom_init_check.sh @@ -13,21 +13,8 @@ # If you really need to reference something from prom_init.o add # it to the list below: -has_renamed_memintrinsics() -{ - grep -q "^CONFIG_KASAN=y$" "${KCONFIG_CONFIG}" && \ - ! grep -q "^CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX=y" "${KCONFIG_CONFIG}" -} - -if has_renamed_memintrinsics -then - MEM_FUNCS="__memcpy __memset" -else - MEM_FUNCS="memcpy memset" -fi - WHITELIST="add_reloc_offset __bss_start __bss_stop copy_and_flush -_end enter_prom $MEM_FUNCS reloc_offset __secondary_hold +_end enter_prom memcpy memset reloc_offset __secondary_hold __secondary_hold_acknowledge __secondary_hold_spinloop __start logo_linux_clut224 btext_prepare_BAT reloc_got2 kernstart_addr memstart_addr linux_banner _stext diff --git a/arch/powerpc/lib/copy_32.S b/arch/powerpc/lib/copy_32.S index 933b685e7ab6..97eb9ca0cc23 100644 --- a/arch/powerpc/lib/copy_32.S +++ b/arch/powerpc/lib/copy_32.S @@ -10,7 +10,6 @@ #include #include #include -#include #define COPY_16_BYTES \ lwz r7,4(r4); \ @@ -87,7 +86,7 @@ EXPORT_SYMBOL(memset16) * We therefore skip the optimised bloc that uses dcbz. This jump is * replaced by a nop once cache is active. This is done in machine_init() */ -_GLOBAL_KASAN(memset) +_GLOBAL(memset) cmplwi 0,r5,4 blt 7f @@ -147,7 +146,6 @@ _GLOBAL_KASAN(memset) bdnz 9b blr EXPORT_SYMBOL(memset) -EXPORT_SYMBOL_KASAN(memset) /* * This version uses dcbz on the complete cache lines in the @@ -160,12 +158,12 @@ EXPORT_SYMBOL_KASAN(memset) * We therefore jump to generic_memcpy which doesn't use dcbz. This jump is * replaced by a nop once cache is active. This is done in machine_init() */ -_GLOBAL_KASAN(memmove) +_GLOBAL(memmove) cmplw 0,r3,r4 bgt backwards_memcpy /* fall through */ -_GLOBAL_KASAN(memcpy) +_GLOBAL(memcpy) 1: b generic_memcpy patch_site 1b, patch__memcpy_nocache @@ -241,8 +239,6 @@ _GLOBAL_KASAN(memcpy) 65: blr EXPORT_SYMBOL(memcpy) EXPORT_SYMBOL(memmove) -EXPORT_SYMBOL_KASAN(memcpy) -EXPORT_SYMBOL_KASAN(memmove) generic_memcpy: srwi. r7,r5,3 diff --git a/arch/powerpc/lib/mem_64.S b/arch/powerpc/lib/mem_64.S index 6fd06cd20faa..40eaedd31486 100644 --- a/arch/powerpc/lib/mem_64.S +++ b/arch/powerpc/lib/mem_64.S @@ -8,7 +8,6 @@ #include #include #include -#include #ifndef CONFIG_KASAN _GLOBAL(__memset16) @@ -29,7 +28,7 @@ EXPORT_SYMBOL(__memset32) EXPORT_SYMBOL(__memset64) #endif -_GLOBAL_KASAN(memset) +_GLOBAL(memset) neg r0,r3 rlwimi r4,r4,8,16,23 andi. r0,r0,7 /* # bytes to be 8-byte aligned */ @@ -95,9 +94,8 @@ _GLOBAL_KASAN(memset) stb r4,0(r6) blr EXPORT_SYMBOL(memset) -EXPORT_SYMBOL_KASAN(memset) -_GLOBAL_TOC_KASAN(memmove) +_GLOBAL_TOC(memmove) cmplw 0,r3,r4 bgt backwards_memcpy b memcpy @@ -139,4 +137,3 @@ _GLOBAL(backwards_memcpy) mtctr r7 b 1b EXPORT_SYMBOL(memmove) -EXPORT_SYMBOL_KASAN(memmove) diff --git a/arch/powerpc/lib/memcpy_64.S b/arch/powerpc/lib/memcpy_64.S index b5a67e20143f..0cedd455231a 100644 --- a/arch/powerpc/lib/memcpy_64.S +++ b/arch/powerpc/lib/memcpy_64.S @@ -7,7 +7,6 @@ #include #include #include -#include #ifndef SELFTEST_CASE /* For big-endian, 0 == most CPUs, 1 == POWER6, 2 == Cell */ @@ -15,7 +14,7 @@ #endif .align 7 -_GLOBAL_TOC_KASAN(memcpy) +_GLOBAL_TOC(memcpy) BEGIN_FTR_SECTION #ifdef __LITTLE_ENDIAN__ cmpdi cr7,r5,0 @@ -227,4 +226,3 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) blr #endif EXPORT_SYMBOL(memcpy) -EXPORT_SYMBOL_KASAN(memcpy) -- cgit v1.2.3 From 66236558dfac63d9bea5b0b6a66a4d717529c118 Mon Sep 17 00:00:00 2001 From: "Mukesh Kumar Chaurasiya (IBM)" Date: Tue, 29 Sep 2026 12:47:44 +0530 Subject: powerpc/syscall: Avoid atomic test_and_clear_thread_flag on fast path In system_call_exception(), test_and_clear_thread_flag(TIF_SYSCALL_RET) performs an atomic bit test-and-clear operation on every system call entry. This introduces an unnecessary performance regression on the syscall fast path. Combine the TIF_SYSCALL_RET check with the unlikely error condition from syscall_enter_from_user_mode_randomize_stack() using non-atomic test_thread_flag(). If the flag is set or entry failed, only then clear the flag via clear_thread_flag() and return the error value. Signed-off-by: Mukesh Kumar Chaurasiya (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260929071744.2965058-1-mkchauras@gmail.com --- arch/powerpc/kernel/syscall.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c index fbefe1927b10..8529827d697f 100644 --- a/arch/powerpc/kernel/syscall.c +++ b/arch/powerpc/kernel/syscall.c @@ -18,14 +18,12 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0) long ret; syscall_fn f; - if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0))) { + if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0) || + test_thread_flag(TIF_SYSCALL_RET))) { clear_thread_flag(TIF_SYSCALL_RET); return syscall_get_error(current, regs); } - if (unlikely(test_and_clear_thread_flag(TIF_SYSCALL_RET))) - return syscall_get_error(current, regs); - if (unlikely(r0 >= NR_syscalls)) { if (unlikely(trap_is_unsupported_scv(regs))) { /* Unsupported scv vector */ -- cgit v1.2.3 From b3d75e2bc299a759de2fd11649c7ed275b1b2067 Mon Sep 17 00:00:00 2001 From: Shivang Upadhyay Date: Mon, 28 Sep 2026 15:46:46 +0530 Subject: powerpc/fadump: collect dump when CPU_STATE_DATA is less than reserved During Fadump in Qemu VM, when maxcpus value is set to more than current cpus, following failure is observed. [ 0.045806] [ T1] rtas fadump: Dump taken by platform is incomplete (0) This is because the CPU_STATE_DATA is allocated for maxcpus, while the data is only filled for current cpus. As per current implementation of Fadump, dumped_bytes and source_len for a region have to match, Which is failing for qemu's case, Even tough the dumped bytes are reported correctly for only the current cpus. After this /proc/vmcore generatition also fails. Allowing dumped_bytes to be lesser than or equal to allocated length, for CPU_STATE_DATA Fadump region. Reported-by: Anushree Mathur Closes: https://lore.kernel.org/all/5e66daf4-3f55-4044-94a5-4f50bb040849@linux.ibm.com/T/#u Tested-by: Anushree Mathur Reviewed-by: Sourabh Jain Signed-off-by: Shivang Upadhyay [Maddy: Fixed ppc -> powerpc in title] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260928101646.415278-1-shivangu@linux.ibm.com --- arch/powerpc/platforms/pseries/rtas-fadump.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.c b/arch/powerpc/platforms/pseries/rtas-fadump.c index 3bb4ac2ab6cc..9b9e09f3d900 100644 --- a/arch/powerpc/platforms/pseries/rtas-fadump.c +++ b/arch/powerpc/platforms/pseries/rtas-fadump.c @@ -459,6 +459,8 @@ static int __init rtas_fadump_process(struct fw_dump *fadump_conf) /* Check if the dump data is valid. */ for (int i = 0; i < be16_to_cpu(fdm_active->header.dump_num_sections); i++) { int type = be16_to_cpu(fdm_active->rgn[i].source_data_type); + uint64_t bytes_dumped = be64_to_cpu(fdm_active->rgn[i].bytes_dumped); + uint64_t source_len = be64_to_cpu(fdm_active->rgn[i].source_len); int rc = 0; switch (type) { @@ -469,10 +471,20 @@ static int __init rtas_fadump_process(struct fw_dump *fadump_conf) pr_err("Dump taken by platform is not valid (%d)\n", i); rc = -EINVAL; } - if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len) { - pr_err("Dump taken by platform is incomplete (%d)\n", i); + + /* + * Make sure that dump is collected for entire region. + * CPU_STATE_DATA region is allowed to dump less than allocated space. + */ + if (!(bytes_dumped == source_len || + (type == RTAS_FADUMP_CPU_STATE_DATA && bytes_dumped <= source_len))) { + pr_err("Dump taken by platform is %s (%d)\n", + (bytes_dumped < source_len ? "incomplete" : "corrupted"), i); + pr_debug("type -> %d, bytes_dumped -> %llx, source_len -> %llx\n", + type, bytes_dumped, source_len); rc = -EINVAL; } + if (rc) { pr_warn("Region type: %u src addr: 0x%llx dest addr: 0x%llx\n", be16_to_cpu(fdm_active->rgn[i].source_data_type), @@ -482,7 +494,7 @@ static int __init rtas_fadump_process(struct fw_dump *fadump_conf) } break; case RTAS_FADUMP_PARAM_AREA: - if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len || + if (bytes_dumped != source_len || fdm_active->rgn[i].error_flags != 0) { pr_warn("Failed to process additional parameters! Proceeding anyway..\n"); fadump_conf->param_area = 0; -- cgit v1.2.3 From ff9d9df1deb10c657ff00fc7b94f154fb4552721 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 2 Sep 2026 18:05:00 +0530 Subject: powerpc/numa: Simplify find_primary_domain_index Initialize the return value once and use a single exit path in find_primary_domain_index(). This is a small cleanup that keeps the existing behavior unchanged while making the control flow easier to follow. Signed-off-by: Srikar Dronamraju Reviewed-by: Shrikanth Hegde Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260902123458.340456-6-srikar@linux.ibm.com --- arch/powerpc/mm/numa.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index f4cf3ae036de..c44a80d8fc11 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -432,7 +432,7 @@ static void __init initialize_form2_numa_distance_lookup_table(void) static int __init find_primary_domain_index(void) { - int index; + int index = -1; struct device_node *root; /* @@ -502,12 +502,9 @@ static int __init find_primary_domain_index(void) distance_ref_points_depth = MAX_DISTANCE_REF_POINTS; } - of_node_put(root); - return index; - err: of_node_put(root); - return -1; + return index; } static void __init get_n_mem_cells(int *n_addr_cells, int *n_size_cells) -- cgit v1.2.3 From e18351513a8e7ba08d9735056864f0c0095676d0 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 2 Sep 2026 18:05:01 +0530 Subject: powerpc/numa: Allow cpu_to_coregroup_id without PPC_SPLPAR Make cpu_to_coregroup_id() available outside PPC_SPLPAR so it can be used by platforms that do not rely on the SPLPAR-specific VPHN path. Keep the existing fallback behavior by returning the core ID when coregroup information is unavailable. Signed-off-by: Srikar Dronamraju Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260902123458.340456-7-srikar@linux.ibm.com --- arch/powerpc/include/asm/topology.h | 15 +++++++-------- arch/powerpc/mm/numa.c | 22 +++++++++++++++------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 44ec416fa489..02c3c9ab3ea9 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -71,6 +71,7 @@ extern void map_cpu_to_node(int cpu, int node); extern void unmap_cpu_from_node(unsigned long cpu); #endif /* CONFIG_HOTPLUG_CPU */ +int cpu_to_coregroup_id(int cpu); #else static inline int early_cpu_to_node(int cpu) { return 0; } @@ -107,14 +108,6 @@ static inline void map_cpu_to_node(int cpu, int node) {} static inline void unmap_cpu_from_node(unsigned long cpu) {} #endif /* CONFIG_HOTPLUG_CPU */ #endif /* CONFIG_SMP */ - -#endif /* CONFIG_NUMA */ - -#if defined(CONFIG_NUMA) && defined(CONFIG_PPC_SPLPAR) -void find_and_update_cpu_nid(int cpu); -extern int cpu_to_coregroup_id(int cpu); -#else -static inline void find_and_update_cpu_nid(int cpu) {} static inline int cpu_to_coregroup_id(int cpu) { #ifdef CONFIG_SMP @@ -124,6 +117,12 @@ static inline int cpu_to_coregroup_id(int cpu) #endif } +#endif /* CONFIG_NUMA */ + +#if defined(CONFIG_NUMA) && defined(CONFIG_PPC_SPLPAR) +void find_and_update_cpu_nid(int cpu); +#else +static inline void find_and_update_cpu_nid(int cpu) {} #endif /* CONFIG_NUMA && CONFIG_PPC_SPLPAR */ #include diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index c44a80d8fc11..5f326b005a2a 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1428,6 +1428,21 @@ void find_and_update_cpu_nid(int cpu) pr_debug("%s:%d cpu %d nid %d\n", __func__, __LINE__, cpu, new_nid); } +static int topology_update_init(void) +{ + topology_inited = 1; + return 0; +} +device_initcall(topology_update_init); + +#else +static long vphn_get_associativity(unsigned long cpu, + __be32 *associativity) +{ + return -1; +} +#endif /* CONFIG_PPC_SPLPAR */ + int cpu_to_coregroup_id(int cpu) { __be32 associativity[VPHN_ASSOC_BUFSIZE] = {0}; @@ -1453,10 +1468,3 @@ out: return cpu_to_core_id(cpu); } -static int topology_update_init(void) -{ - topology_inited = 1; - return 0; -} -device_initcall(topology_update_init); -#endif /* CONFIG_PPC_SPLPAR */ -- cgit v1.2.3 From 129f5d395fd66b6587a5ac156b232cca060d6512 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 2 Sep 2026 18:05:02 +0530 Subject: powerpc/numa: Support coregroup on PowerNV Coregroup support on powerpc has so far been limited to PowerVM LPARs. However, PowerNV can also support coregroups when firmware exposes the required coregroup information through the associativity hierarchy. Detect coregroup support by checking whether primary_domain_index is the penultimate domain in the CPU node's ibm,associativity property. On PowerNV, a non-penultimate primary_domain_index indicates that firmware provides an additional level for coregroup information. This keeps the logic compatible with PowerVM systems, where primary_domain_index is likewise not the penultimate associativity domain. Signed-off-by: Srikar Dronamraju Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260902123458.340456-8-srikar@linux.ibm.com --- arch/powerpc/mm/numa.c | 58 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 5f326b005a2a..e97b624203ea 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -889,12 +889,32 @@ static int __init numa_setup_drmem_lmb(struct drmem_lmb *lmb, return 0; } +/* + * If hierarchy extends beyond primary_domain_index + 1, then next + * level corresponds to coregroup. + */ +static int detect_and_enable_coregroup(const __be32 *associativity, int index) +{ + if (!associativity || index == -1) + goto out; + + index = of_read_number(associativity, 1); + + if (index > primary_domain_index + 1) { + coregroup_enabled = 1; + return index; + } +out: + coregroup_enabled = 0; + return -1; +} + static int __init parse_numa_properties(void) { struct device_node *memory, *pci; - int default_nid = 0; - unsigned long i; + int default_nid = 0, index = 0; const __be32 *associativity; + unsigned long i; if (numa_enabled == 0) { pr_warn("disabled by user\n"); @@ -927,7 +947,6 @@ static int __init parse_numa_properties(void) */ for_each_present_cpu(i) { __be32 vphn_assoc[VPHN_ASSOC_BUFSIZE]; - struct device_node *cpu; int nid = NUMA_NO_NODE; memset(vphn_assoc, 0, VPHN_ASSOC_BUFSIZE * sizeof(__be32)); @@ -935,7 +954,9 @@ static int __init parse_numa_properties(void) if (__vphn_get_associativity(i, vphn_assoc) == 0) { nid = associativity_to_nid(vphn_assoc); initialize_form1_numa_distance(vphn_assoc); + index = detect_and_enable_coregroup(vphn_assoc, index); } else { + struct device_node *cpu; /* * Don't fall back to default_nid yet -- we will plug @@ -948,6 +969,7 @@ static int __init parse_numa_properties(void) associativity = of_get_associativity(cpu); if (associativity) { nid = associativity_to_nid(associativity); + index = detect_and_enable_coregroup(associativity, index); initialize_form1_numa_distance(associativity); } of_node_put(cpu); @@ -1445,7 +1467,9 @@ static long vphn_get_associativity(unsigned long cpu, int cpu_to_coregroup_id(int cpu) { - __be32 associativity[VPHN_ASSOC_BUFSIZE] = {0}; + int coregroup_id = cpu_to_core_id(cpu); + struct device_node *cpunode = NULL; + const __be32 *associativity; int index; if (cpu < 0 || cpu > nr_cpu_ids) @@ -1454,17 +1478,31 @@ int cpu_to_coregroup_id(int cpu) if (!coregroup_enabled) goto out; - if (!firmware_has_feature(FW_FEATURE_VPHN)) - goto out; + if (firmware_has_feature(FW_FEATURE_VPHN)) { + __be32 tmp[VPHN_ASSOC_BUFSIZE] = {0}; - if (vphn_get_associativity(cpu, associativity)) + if (vphn_get_associativity(cpu, tmp)) + goto out; + + associativity = tmp; + + } else { + cpunode = of_get_cpu_node(cpu, NULL); + if (!cpunode) + goto out; + + associativity = of_get_associativity(cpunode); + } + if (!associativity) goto out; index = of_read_number(associativity, 1); if (index > primary_domain_index + 1) - return of_read_number(&associativity[index - 1], 1); + coregroup_id = of_read_number(&associativity[index - 1], 1); out: - return cpu_to_core_id(cpu); -} + if (cpunode) + of_node_put(cpunode); + return coregroup_id; +} -- cgit v1.2.3