diff options
82 files changed, 1242 insertions, 1068 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 16ce0c0537e8..9f4ddd4c69f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12428,13 +12428,13 @@ L: linux-crypto@vger.kernel.org S: Supported F: arch/powerpc/crypto/Kconfig F: arch/powerpc/crypto/Makefile -F: arch/powerpc/crypto/aes.c F: arch/powerpc/crypto/aes_cbc.c F: arch/powerpc/crypto/aes_ctr.c F: arch/powerpc/crypto/aes_xts.c -F: arch/powerpc/crypto/aesp8-ppc.* +F: arch/powerpc/crypto/aesp8-ppc.h F: arch/powerpc/crypto/ppc-xlate.pl F: arch/powerpc/crypto/vmx.c +F: lib/crypto/powerpc/aesp8-ppc.pl F: lib/crypto/powerpc/gf128hash.h F: lib/crypto/powerpc/ghashp8-ppc.pl diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index c99fd8335ddc..f7ce5fff81f0 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -206,6 +206,7 @@ config PPC select GENERIC_CPU_AUTOPROBE select GENERIC_CPU_VULNERABILITIES if PPC_BARRIER_NOSPEC select GENERIC_EARLY_IOREMAP + select GENERIC_ENTRY select GENERIC_GETTIMEOFDAY select GENERIC_IDLE_POLL_SETUP select GENERIC_IOREMAP diff --git a/arch/powerpc/boot/dts/Makefile b/arch/powerpc/boot/dts/Makefile index 0cd0d8558b47..1e61a951cebe 100644 --- a/arch/powerpc/boot/dts/Makefile +++ b/arch/powerpc/boot/dts/Makefile @@ -3,3 +3,76 @@ subdir-y += fsl dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(src)/%.dts,%.dtb, $(wildcard $(src)/*.dts)) + +# PPC44x platforms +dtb-$(CONFIG_PPC44x_SIMPLE) += arches.dtb bamboo.dtb bluestone.dtb glacier.dtb +dtb-$(CONFIG_PPC44x_SIMPLE) += eiger.dtb katmai.dtb rainier.dtb redwood.dtb +dtb-$(CONFIG_PPC44x_SIMPLE) += sequoia.dtb taishan.dtb yosemite.dtb icon.dtb +dtb-$(CONFIG_EBONY) += ebony.dtb +dtb-$(CONFIG_SAM440EP) += sam440ep.dtb +dtb-$(CONFIG_WARP) += warp.dtb +dtb-$(CONFIG_ISS4xx) += iss4xx.dtb iss4xx-mpic.dtb +dtb-$(CONFIG_CANYONLANDS) += canyonlands.dtb +dtb-$(CONFIG_CURRITUCK) += currituck.dtb +dtb-$(CONFIG_AKEBONO) += akebono.dtb +dtb-$(CONFIG_FSP2) += fsp2.dtb + +# Embedded 6xx platforms +dtb-$(CONFIG_LINKSTATION) += kuroboxHG.dtb kuroboxHD.dtb +dtb-$(CONFIG_STORCENTER) += storcenter.dtb +dtb-$(CONFIG_PPC_HOLLY) += holly.dtb +dtb-$(CONFIG_GAMECUBE) += gamecube.dtb +dtb-$(CONFIG_WII) += wii.dtb +dtb-$(CONFIG_MVME5100) += mvme5100.dtb + +# MPC8xx platforms +dtb-$(CONFIG_MPC885ADS) += mpc885ads.dtb +dtb-$(CONFIG_MPC86XADS) += mpc866ads.dtb +dtb-$(CONFIG_PPC_EP88XC) += ep88xc.dtb +dtb-$(CONFIG_PPC_ADDER875) += adder875-redboot.dtb adder875-uboot.dtb +dtb-$(CONFIG_TQM8XX) += tqm8xx.dtb + +# MPC512x platforms +dtb-$(CONFIG_MPC5121_ADS) += mpc5121ads.dtb +dtb-$(CONFIG_MPC512x_GENERIC) += mpc5125twr.dtb ac14xx.dts +dtb-$(CONFIG_PDM360NG) += pdm360ng.dtb + +# MPC5200 platforms +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += a3m071.dtb a4m072.dtb charon.dtb cm5200.dtb +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += digsy_mtc.dtb motionpro.dtb mucmc52.dtb +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += o2d.dtb o2d300.dtb o2dnt2.dtb o2i.dtb +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += o2mnt.dtb o3dnt.dtb pcm030.dtb pcm032.dtb +dtb-$(CONFIG_PPC_MPC5200_SIMPLE) += tqm5200.dtb uc101.dtb +dtb-$(CONFIG_PPC_LITE5200) += lite5200.dtb lite5200b.dtb +dtb-$(CONFIG_PPC_MEDIA5200) += media5200.dtb + +# MPC82xx platforms +dtb-$(CONFIG_EP8248E) += ep8248e.dtb +dtb-$(CONFIG_MGCOGE) += mgcoge.dtb + +# MPC83xx platforms +dtb-$(CONFIG_MPC830x_RDB) += mpc8308rdb.dtb mpc8308_p1m.dtb +dtb-$(CONFIG_MPC831x_RDB) += mpc8313erdb.dtb mpc8315erdb.dtb +dtb-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.dtb +dtb-$(CONFIG_MPC834x_ITX) += mpc8349emitx.dtb mpc8349emitxgp.dtb +dtb-$(CONFIG_ASP834x) += asp834x-redboot.dtb +dtb-$(CONFIG_MPC836x_RDK) += mpc836x_rdk.dtb +dtb-$(CONFIG_KMETER1) += kmeter1.dtb +dtb-$(CONFIG_MPC837x_RDB) += mpc8377_rdb.dtb mpc8378_rdb.dtb mpc8379_rdb.dtb +dtb-$(CONFIG_MPC837x_RDB) += mpc8377_wlan.dtb + +# MPC85xx platforms +dtb-$(CONFIG_STX_GP3) += stx_gp3_8560.dtb stxssa8555.dtb +dtb-$(CONFIG_TQM85xx) += tqm8540.dtb tqm8541.dtb tqm8548.dtb +dtb-$(CONFIG_TQM85xx) += tqm8548-bigflash.dtb tqm8555.dtb tqm8560.dtb +dtb-$(CONFIG_SOCRATES) += socrates.dtb +dtb-$(CONFIG_KSI8560) += ksi8560.dtb +dtb-$(CONFIG_XES_MPC85xx) += xcalibur1501.dtb xpedite5200.dtb +dtb-$(CONFIG_XES_MPC85xx) += xpedite5200_xmon.dtb xpedite5301.dtb +dtb-$(CONFIG_XES_MPC85xx) += xpedite5330.dtb xpedite5370.dtb +dtb-$(CONFIG_PPC_P2020) += turris1x.dtb + +# Misc. platforms +dtb-$(CONFIG_PPC_MICROWATT) += microwatt.dtb +dtb-$(CONFIG_AMIGAONE) += amigaone.dtb +dtb-$(CONFIG_PPC_PS3) += ps3.dtb diff --git a/arch/powerpc/boot/dts/fsl/Makefile b/arch/powerpc/boot/dts/fsl/Makefile index d3ecdf14bc42..d2cc8e1f007e 100644 --- a/arch/powerpc/boot/dts/fsl/Makefile +++ b/arch/powerpc/boot/dts/fsl/Makefile @@ -1,3 +1,46 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(src)/%.dts,%.dtb, $(wildcard $(src)/*.dts)) + +# MPC85xx platforms +dtb-$(CONFIG_BSC9131_RDB) += bsc9131rdb.dtb +dtb-$(CONFIG_BSC9132_QDS) += bsc9132qds.dtb +dtb-$(CONFIG_C293_PCIE) += c293pcie.dtb +dtb-$(CONFIG_MPC8536_DS) += mpc8536ds.dtb mpc8536ds_36b.dtb +dtb-$(CONFIG_MPC85xx_DS) += mpc8544ds.dtb mpc8572ds_camp_core0.dtb +dtb-$(CONFIG_MPC85xx_DS) += mpc8572ds_camp_core1.dtb mpc8572ds_36b.dtb +dtb-$(CONFIG_MPC85xx_DS) += mpc8572ds.dtb +dtb-$(CONFIG_MPC85xx_MDS) += mpc8568mds.dtb mpc8569mds.dtb p1021mds.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020mbg-pc_32b.dtb p1020mbg-pc_36b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020rdb_36b.dtb p1020rdb.dtb p1020rdb-pc_32b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020rdb-pc_36b.dtb p1020rdb-pc_camp_core0.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020rdb-pc_camp_core1.dtb p1020rdb-pd.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1020utm-pc_32b.dtb p1020utm-pc_36b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1021rdb-pc_32b.dtb p1021rdb-pc_36b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1024rdb_32b.dtb p1024rdb_36b.dtb p1025rdb_32b.dtb +dtb-$(CONFIG_MPC85xx_RDB) += p1025rdb_36b.dtb +dtb-$(CONFIG_P1010_RDB) += p1010rdb-pa_36b.dtb p1010rdb-pa.dtb +dtb-$(CONFIG_P1010_RDB) += p1010rdb-pb_36b.dtb p1010rdb-pb.dtb +dtb-$(CONFIG_P1022_DS) += p1022ds_32b.dtb p1022ds_36b.dtb +dtb-$(CONFIG_P1022_RDK) += p1022rdk.dtb +dtb-$(CONFIG_P1023_RDB) += p1023rdb.dtb +dtb-$(CONFIG_PPC_P2020) += p2020ds.dtb +dtb-$(CONFIG_TWR_P102x) += p1025twr.dtb +dtb-$(CONFIG_CORENET_GENERIC) += b4420qds.dtb b4860qds.dtb cyrus_p5020.dtb +dtb-$(CONFIG_CORENET_GENERIC) += kmcent2.dtb kmcoge4.dtb oca4080.dtb +dtb-$(CONFIG_CORENET_GENERIC) += p2041rdb.dtb p3041ds.dtb p4080ds.dtb +dtb-$(CONFIG_CORENET_GENERIC) += p5020ds.dtb p5040ds.dtb t1023rdb.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t1024qds.dtb t1024rdb.dtb t1040d4rdb.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t1040qds.dtb t1040rdb.dtb t1040rdb-rev-a.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t1042d4rdb.dtb t1042qds.dtb t1042rdb.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t1042rdb_pi.dtb t2080qds.dtb t2080rdb.dtb +dtb-$(CONFIG_CORENET_GENERIC) += t2081qds.dtb t4240qds.dtb t4240rdb.dtb +dtb-$(CONFIG_PPA8548) += ppa8548.dtb +dtb-$(CONFIG_GE_IMP3A) += ge_imp3a.dtb +dtb-$(CONFIG_MVME2500) += mvme2500.dtb + +# MPC86xx platforms +dtb-$(CONFIG_GEF_SBC310) += gef_sbc310.dtb +dtb-$(CONFIG_GEF_SBC610) += gef_sbc610.dtb +dtb-$(CONFIG_GEF_PPC9A) += gef_ppc9a.dtb +dtb-$(CONFIG_MVME7100) += mvme7100.dtb diff --git a/arch/powerpc/boot/dts/mpc8308_p1m.dts b/arch/powerpc/boot/dts/mpc8308_p1m.dts index 41f917f97dab..5af945e1743c 100644 --- a/arch/powerpc/boot/dts/mpc8308_p1m.dts +++ b/arch/powerpc/boot/dts/mpc8308_p1m.dts @@ -9,6 +9,7 @@ / { compatible = "denx,mpc8308_p1m"; + model = "denx,mpc8308_p1m"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8308rdb.dts b/arch/powerpc/boot/dts/mpc8308rdb.dts index 39ed26fba410..7b6fd58ffafa 100644 --- a/arch/powerpc/boot/dts/mpc8308rdb.dts +++ b/arch/powerpc/boot/dts/mpc8308rdb.dts @@ -10,6 +10,7 @@ / { compatible = "fsl,mpc8308rdb"; + model = "fsl,mpc8308rdb"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8315erdb.dts b/arch/powerpc/boot/dts/mpc8315erdb.dts index 7ba1159f8803..4757f0d24a2e 100644 --- a/arch/powerpc/boot/dts/mpc8315erdb.dts +++ b/arch/powerpc/boot/dts/mpc8315erdb.dts @@ -10,6 +10,7 @@ / { compatible = "fsl,mpc8315erdb"; + model = "MPC8315E-RDB"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc836x_rdk.dts b/arch/powerpc/boot/dts/mpc836x_rdk.dts index 4ff38e1a2185..d3d36fbf4e86 100644 --- a/arch/powerpc/boot/dts/mpc836x_rdk.dts +++ b/arch/powerpc/boot/dts/mpc836x_rdk.dts @@ -14,6 +14,7 @@ #address-cells = <1>; #size-cells = <1>; compatible = "fsl,mpc8360rdk"; + model = "fsl,mpc8360rdk"; aliases { serial0 = &serial0; diff --git a/arch/powerpc/boot/dts/mpc8377_rdb.dts b/arch/powerpc/boot/dts/mpc8377_rdb.dts index fb311a7eb9f2..f4244a7a3189 100644 --- a/arch/powerpc/boot/dts/mpc8377_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8377_rdb.dts @@ -9,6 +9,7 @@ / { compatible = "fsl,mpc8377rdb"; + model = "fsl,mpc8377rdb"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8377_wlan.dts b/arch/powerpc/boot/dts/mpc8377_wlan.dts index f736a15cceff..77ee5f8db14e 100644 --- a/arch/powerpc/boot/dts/mpc8377_wlan.dts +++ b/arch/powerpc/boot/dts/mpc8377_wlan.dts @@ -10,6 +10,7 @@ / { compatible = "fsl,mpc8377wlan"; + model = "fsl,mpc8377wlan"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8378_rdb.dts b/arch/powerpc/boot/dts/mpc8378_rdb.dts index 32c49622b404..9ae4ed5ffcf4 100644 --- a/arch/powerpc/boot/dts/mpc8378_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8378_rdb.dts @@ -9,6 +9,7 @@ / { compatible = "fsl,mpc8378rdb"; + model = "fsl,mpc8378rdb"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8379_rdb.dts b/arch/powerpc/boot/dts/mpc8379_rdb.dts index 07deb89c5a9b..214611dc6da9 100644 --- a/arch/powerpc/boot/dts/mpc8379_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8379_rdb.dts @@ -9,6 +9,7 @@ / { compatible = "fsl,mpc8379rdb"; + model = "fsl,mpc8379rdb"; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/hack-coff.c b/arch/powerpc/boot/hack-coff.c index a010e124ac4b..6bf0c94302f5 100644 --- a/arch/powerpc/boot/hack-coff.c +++ b/arch/powerpc/boot/hack-coff.c @@ -31,7 +31,7 @@ main(int ac, char **av) int i, nsect; int aoutsz; struct external_filehdr fhdr; - AOUTHDR aout; + struct aouthdr aout; struct external_scnhdr shdr; if (ac != 2) { diff --git a/arch/powerpc/boot/rs6000.h b/arch/powerpc/boot/rs6000.h index 16df8f3c43f1..970754a1f887 100644 --- a/arch/powerpc/boot/rs6000.h +++ b/arch/powerpc/boot/rs6000.h @@ -32,32 +32,30 @@ struct external_filehdr { /********************** AOUT "OPTIONAL HEADER" **********************/ -typedef struct -{ - unsigned char magic[2]; /* type of file */ - unsigned char vstamp[2]; /* version stamp */ - unsigned char tsize[4]; /* text size in bytes, padded to FW bdry */ - unsigned char dsize[4]; /* initialized data " " */ - unsigned char bsize[4]; /* uninitialized data " " */ - unsigned char entry[4]; /* entry pt. */ - unsigned char text_start[4]; /* base of text used for this file */ - unsigned char data_start[4]; /* base of data used for this file */ - unsigned char o_toc[4]; /* address of TOC */ - unsigned char o_snentry[2]; /* section number of entry point */ - unsigned char o_sntext[2]; /* section number of .text section */ - unsigned char o_sndata[2]; /* section number of .data section */ - unsigned char o_sntoc[2]; /* section number of TOC */ - unsigned char o_snloader[2]; /* section number of .loader section */ - unsigned char o_snbss[2]; /* section number of .bss section */ - unsigned char o_algntext[2]; /* .text alignment */ - unsigned char o_algndata[2]; /* .data alignment */ - unsigned char o_modtype[2]; /* module type (??) */ - unsigned char o_cputype[2]; /* cpu type */ - unsigned char o_maxstack[4]; /* max stack size (??) */ - unsigned char o_maxdata[4]; /* max data size (??) */ - unsigned char o_resv2[12]; /* reserved */ -} -AOUTHDR; +struct aouthdr { + unsigned char magic[2]; /* type of file */ + unsigned char vstamp[2]; /* version stamp */ + unsigned char tsize[4]; /* text size in bytes, padded to FW bdry */ + unsigned char dsize[4]; /* initialized data " " */ + unsigned char bsize[4]; /* uninitialized data " " */ + unsigned char entry[4]; /* entry pt. */ + unsigned char text_start[4]; /* base of text used for this file */ + unsigned char data_start[4]; /* base of data used for this file */ + unsigned char o_toc[4]; /* address of TOC */ + unsigned char o_snentry[2]; /* section number of entry point */ + unsigned char o_sntext[2]; /* section number of .text section */ + unsigned char o_sndata[2]; /* section number of .data section */ + unsigned char o_sntoc[2]; /* section number of TOC */ + unsigned char o_snloader[2]; /* section number of .loader section */ + unsigned char o_snbss[2]; /* section number of .bss section */ + unsigned char o_algntext[2]; /* .text alignment */ + unsigned char o_algndata[2]; /* .data alignment */ + unsigned char o_modtype[2]; /* module type (??) */ + unsigned char o_cputype[2]; /* cpu type */ + unsigned char o_maxstack[4]; /* max stack size (??) */ + unsigned char o_maxdata[4]; /* max data size (??) */ + unsigned char o_resv2[12]; /* reserved */ +}; #define AOUTSZ 72 #define SMALL_AOUTSZ (28) @@ -115,10 +113,10 @@ struct external_scnhdr { */ struct external_lineno { union { - char l_symndx[4]; /* function name symbol index, iff l_lnno == 0*/ + char l_symndx[4]; /* function name symbol index, iff l_lnno == 0 */ char l_paddr[4]; /* (physical) address of line number */ } l_addr; - char l_lnno[2]; /* line number */ + char l_lnno[2]; /* line number */ }; @@ -132,20 +130,19 @@ struct external_lineno { #define E_FILNMLEN 14 /* # characters in a file name */ #define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ -struct external_syment -{ - union { - char e_name[E_SYMNMLEN]; - struct { - char e_zeroes[4]; - char e_offset[4]; - } e; - } e; - char e_value[4]; - char e_scnum[2]; - char e_type[2]; - char e_sclass[1]; - char e_numaux[1]; +struct external_syment { + union { + char e_name[E_SYMNMLEN]; + struct { + char e_zeroes[4]; + char e_offset[4]; + } e; + } e; + char e_value[4]; + char e_scnum[2]; + char e_type[2]; + char e_sclass[1]; + char e_numaux[1]; }; @@ -187,7 +184,7 @@ union external_auxent { } x_file; struct { - char x_scnlen[4]; /* section length */ + char x_scnlen[4]; /* section length */ char x_nreloc[2]; /* # relocation entries */ char x_nlinno[2]; /* # line numbers */ } x_scn; @@ -207,7 +204,6 @@ union external_auxent { unsigned char x_stab[4]; unsigned char x_snstab[2]; } x_csect; - }; #define SYMENT struct external_syment diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index 1efd1206fcab..25321ce262e8 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -262,6 +262,7 @@ pseries) if [ "$format" != "elf32ppc" ]; then link_address= pie=-pie + notext='-z notext' fi make_space=n ;; diff --git a/arch/powerpc/include/asm/entry-common.h b/arch/powerpc/include/asm/entry-common.h new file mode 100644 index 000000000000..fc636c42e89a --- /dev/null +++ b/arch/powerpc/include/asm/entry-common.h @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _ASM_PPC_ENTRY_COMMON_H +#define _ASM_PPC_ENTRY_COMMON_H + +#include <asm/cputime.h> +#include <asm/interrupt.h> +#include <asm/runlatch.h> +#include <asm/stacktrace.h> +#include <asm/switch_to.h> +#include <asm/tm.h> + +#ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG +/* + * WARN/BUG is handled with a program interrupt so minimise checks here to + * avoid recursion and maximise the chance of getting the first oops handled. + */ +#define INT_SOFT_MASK_BUG_ON(regs, cond) \ +do { \ + if ((user_mode(regs) || (TRAP(regs) != INTERRUPT_PROGRAM))) \ + BUG_ON(cond); \ +} while (0) +#else +#define INT_SOFT_MASK_BUG_ON(regs, cond) +#endif + +#ifdef CONFIG_PPC_BOOK3S_64 +extern char __end_soft_masked[]; +bool search_kernel_soft_mask_table(unsigned long addr); +unsigned long search_kernel_restart_table(unsigned long addr); + +DECLARE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant); + +static inline bool is_implicit_soft_masked(struct pt_regs *regs) +{ + if (user_mode(regs)) + return false; + + if (regs->nip >= (unsigned long)__end_soft_masked) + return false; + + return search_kernel_soft_mask_table(regs->nip); +} + +static inline void srr_regs_clobbered(void) +{ + local_paca->srr_valid = 0; + local_paca->hsrr_valid = 0; +} +#else +static inline unsigned long search_kernel_restart_table(unsigned long addr) +{ + return 0; +} + +static inline bool is_implicit_soft_masked(struct pt_regs *regs) +{ + return false; +} + +static inline void srr_regs_clobbered(void) +{ +} +#endif + +static inline void nap_adjust_return(struct pt_regs *regs) +{ +#ifdef CONFIG_PPC_970_NAP + if (unlikely(test_thread_local_flags(_TLF_NAPPING))) { + /* Can avoid a test-and-clear because NMIs do not call this */ + clear_thread_local_flags(_TLF_NAPPING); + regs_set_return_ip(regs, (unsigned long)power4_idle_nap_return); + } +#endif +} + +static __always_inline void booke_load_dbcr0(void) +{ +#ifdef CONFIG_PPC_ADV_DEBUG_REGS + unsigned long dbcr0 = current->thread.debug.dbcr0; + + if (likely(!(dbcr0 & DBCR0_IDM))) + return; + + /* + * Check to see if the dbcr0 register is set up to debug. + * Use the internal debug mode bit to do this. + */ + mtmsr(mfmsr() & ~MSR_DE); + if (IS_ENABLED(CONFIG_PPC32)) { + isync(); + global_dbcr0[smp_processor_id()] = mfspr(SPRN_DBCR0); + } + mtspr(SPRN_DBCR0, dbcr0); + mtspr(SPRN_DBSR, -1); +#endif +} + +static inline void booke_restore_dbcr0(void) +{ +#ifdef CONFIG_PPC_ADV_DEBUG_REGS + unsigned long dbcr0 = current->thread.debug.dbcr0; + + if (IS_ENABLED(CONFIG_PPC32) && unlikely(dbcr0 & DBCR0_IDM)) { + mtspr(SPRN_DBSR, -1); + mtspr(SPRN_DBCR0, global_dbcr0[smp_processor_id()]); + } +#endif +} + +static inline void check_return_regs_valid(struct pt_regs *regs) +{ +#ifdef CONFIG_PPC_BOOK3S_64 + unsigned long trap, srr0, srr1; + static bool warned; + u8 *validp; + char *h; + + if (trap_is_scv(regs)) + return; + + trap = TRAP(regs); + // EE in HV mode sets HSRRs like 0xea0 + if (cpu_has_feature(CPU_FTR_HVMODE) && trap == INTERRUPT_EXTERNAL) + trap = 0xea0; + + switch (trap) { + case 0x980: + case INTERRUPT_H_DATA_STORAGE: + case 0xe20: + case 0xe40: + case INTERRUPT_HMI: + case 0xe80: + case 0xea0: + case INTERRUPT_H_FAC_UNAVAIL: + case 0x1200: + case 0x1500: + case 0x1600: + case 0x1800: + validp = &local_paca->hsrr_valid; + if (!READ_ONCE(*validp)) + return; + + srr0 = mfspr(SPRN_HSRR0); + srr1 = mfspr(SPRN_HSRR1); + h = "H"; + + break; + default: + validp = &local_paca->srr_valid; + if (!READ_ONCE(*validp)) + return; + + srr0 = mfspr(SPRN_SRR0); + srr1 = mfspr(SPRN_SRR1); + h = ""; + break; + } + + if (srr0 == regs->nip && srr1 == regs->msr) + return; + + /* + * A NMI / soft-NMI interrupt may have come in after we found + * srr_valid and before the SRRs are loaded. The interrupt then + * comes in and clobbers SRRs and clears srr_valid. Then we load + * the SRRs here and test them above and find they don't match. + * + * Test validity again after that, to catch such false positives. + * + * This test in general will have some window for false negatives + * and may not catch and fix all such cases if an NMI comes in + * later and clobbers SRRs without clearing srr_valid, but hopefully + * such things will get caught most of the time, statistically + * enough to be able to get a warning out. + */ + if (!READ_ONCE(*validp)) + return; + + if (!data_race(warned)) { + data_race(warned = true); + pr_warn("%sSRR0 was: %lx should be: %lx\n", h, srr0, regs->nip); + pr_warn("%sSRR1 was: %lx should be: %lx\n", h, srr1, regs->msr); + show_regs(regs); + } + + WRITE_ONCE(*validp, 0); /* fixup */ +#endif +} + +static inline void arch_interrupt_enter_prepare(struct pt_regs *regs) +{ +#ifdef CONFIG_PPC64 + irq_soft_mask_set(IRQS_ALL_DISABLED); + + /* + * If the interrupt was taken with HARD_DIS clear, then enable MSR[EE]. + * Asynchronous interrupts get here with HARD_DIS set (see below), so + * this enables MSR[EE] for synchronous interrupts. IRQs remain + * soft-masked. The interrupt handler may later call + * interrupt_cond_local_irq_enable() to achieve a regular process + * context. + */ + if (!(local_paca->irq_happened & PACA_IRQ_HARD_DIS)) { + INT_SOFT_MASK_BUG_ON(regs, !(regs->msr & MSR_EE)); + __hard_irq_enable(); + } else { + __hard_RI_enable(); + } + /* Enable MSR[RI] early, to support kernel SLB and hash faults */ +#endif + + if (!regs_irqs_disabled(regs)) + trace_hardirqs_off(); + + if (user_mode(regs)) { + kuap_lock(); + account_cpu_user_entry(); + account_stolen_time(); + } else { + kuap_save_and_lock(regs); + /* + * CT_WARN_ON comes here via program_check_exception, + * so avoid recursion. + */ + if (TRAP(regs) != INTERRUPT_PROGRAM) + CT_WARN_ON(ct_state() != CT_STATE_KERNEL && + ct_state() != CT_STATE_IDLE); + INT_SOFT_MASK_BUG_ON(regs, is_implicit_soft_masked(regs)); + INT_SOFT_MASK_BUG_ON(regs, regs_irqs_disabled(regs) && + search_kernel_restart_table(regs->nip)); + } + INT_SOFT_MASK_BUG_ON(regs, !regs_irqs_disabled(regs) && + !(regs->msr & MSR_EE)); + + booke_restore_dbcr0(); +} + +/* + * Care should be taken to note that arch_interrupt_exit_prepare and + * arch_interrupt_async_exit_prepare do not necessarily return immediately to + * regs context (e.g., if regs is usermode, we don't necessarily return to + * user mode). Other interrupts might be taken between here and return, + * context switch / preemption may occur in the exit path after this, or a + * signal may be delivered, etc. + * + * The real interrupt exit code is platform specific, e.g., + * interrupt_exit_user_prepare / interrupt_exit_kernel_prepare for 64s. + * + * However arch_interrupt_nmi_exit_prepare does return directly to regs, because + * NMIs do not do "exit work" or replay soft-masked interrupts. + */ +static inline void arch_interrupt_exit_prepare(struct pt_regs *regs) +{ + if (user_mode(regs)) { + BUG_ON(regs_is_unrecoverable(regs)); + BUG_ON(regs_irqs_disabled(regs)); + /* + * We don't need to restore AMR on the way back to userspace for KUAP. + * AMR can only have been unlocked if we interrupted the kernel. + */ + kuap_assert_locked(); + } + + /* irqentry_exit expects to be called with interrupts disabled */ + local_irq_disable(); +} + +static inline void arch_interrupt_async_enter_prepare(struct pt_regs *regs) +{ +#ifdef CONFIG_PPC64 + /* Ensure arch_interrupt_enter_prepare does not enable MSR[EE] */ + local_paca->irq_happened |= PACA_IRQ_HARD_DIS; +#endif + arch_interrupt_enter_prepare(regs); +#ifdef CONFIG_PPC_BOOK3S_64 + /* + * RI=1 is set by arch_interrupt_enter_prepare, so this thread flags access + * has to come afterward (it can cause SLB faults). + */ + if (cpu_has_feature(CPU_FTR_CTRL) && + !test_thread_local_flags(_TLF_RUNLATCH)) + __ppc64_runlatch_on(); +#endif +} + +static inline void arch_interrupt_async_exit_prepare(struct pt_regs *regs) +{ + /* + * Adjust at exit so the main handler sees the true NIA. This must + * come before irq_exit() because irq_exit can enable interrupts, and + * if another interrupt is taken before nap_adjust_return has run + * here, then that interrupt would return directly to idle nap return. + */ + nap_adjust_return(regs); + + arch_interrupt_exit_prepare(regs); +} + +struct interrupt_nmi_state { +#ifdef CONFIG_PPC64 + u8 irq_soft_mask; + u8 irq_happened; + u8 ftrace_enabled; + u64 softe; +#endif +}; + +static inline bool nmi_disables_ftrace(struct pt_regs *regs) +{ + /* Allow DEC and PMI to be traced when they are soft-NMI */ + if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) { + if (TRAP(regs) == INTERRUPT_DECREMENTER) + return false; + if (TRAP(regs) == INTERRUPT_PERFMON) + return false; + } + if (IS_ENABLED(CONFIG_PPC_BOOK3E_64)) { + if (TRAP(regs) == INTERRUPT_PERFMON) + return false; + } + + return true; +} + +static inline void arch_interrupt_nmi_enter_prepare(struct pt_regs *regs, + struct interrupt_nmi_state *state) +{ +#ifdef CONFIG_PPC64 + state->irq_soft_mask = local_paca->irq_soft_mask; + state->irq_happened = local_paca->irq_happened; + state->softe = regs->softe; + + /* + * Set IRQS_ALL_DISABLED unconditionally so irqs_disabled() does + * the right thing, and set IRQ_HARD_DIS. We do not want to reconcile + * because that goes through irq tracing which we don't want in NMI. + */ + local_paca->irq_soft_mask = IRQS_ALL_DISABLED; + local_paca->irq_happened |= PACA_IRQ_HARD_DIS; + + if (!(regs->msr & MSR_EE) || is_implicit_soft_masked(regs)) { + /* + * Adjust regs->softe to be soft-masked if it had not been + * reconcied (e.g., interrupt entry with MSR[EE]=0 but softe + * not yet set disabled), or if it was in an implicit soft + * masked state. This makes regs_irqs_disabled(regs) + * behave as expected. + */ + regs->softe = IRQS_ALL_DISABLED; + } + + __hard_RI_enable(); + + /* Don't do any per-CPU operations until interrupt state is fixed */ + + if (nmi_disables_ftrace(regs)) { + state->ftrace_enabled = this_cpu_get_ftrace_enabled(); + this_cpu_set_ftrace_enabled(0); + } +#endif +} + +static inline void arch_interrupt_nmi_exit_prepare(struct pt_regs *regs, + struct interrupt_nmi_state *state) +{ + /* + * nmi does not call nap_adjust_return because nmi should not create + * new work to do (must use irq_work for that). + */ + +#ifdef CONFIG_PPC64 +#ifdef CONFIG_PPC_BOOK3S + if (regs_irqs_disabled(regs)) { + unsigned long rst = search_kernel_restart_table(regs->nip); + + if (rst) + regs_set_return_ip(regs, rst); + } +#endif + + if (nmi_disables_ftrace(regs)) + this_cpu_set_ftrace_enabled(state->ftrace_enabled); + + /* Check we didn't change the pending interrupt mask. */ + WARN_ON_ONCE((state->irq_happened | PACA_IRQ_HARD_DIS) != local_paca->irq_happened); + regs->softe = state->softe; + local_paca->irq_happened = state->irq_happened; + local_paca->irq_soft_mask = state->irq_soft_mask; +#endif +} + +static __always_inline void arch_enter_from_user_mode(struct pt_regs *regs) +{ + kuap_lock(); + + if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) + BUG_ON(irq_soft_mask_return() != IRQS_ALL_DISABLED); + + BUG_ON(regs_is_unrecoverable(regs)); + BUG_ON(!user_mode(regs)); + BUG_ON(regs_irqs_disabled(regs)); + +#ifdef CONFIG_PPC_PKEY + if (mmu_has_feature(MMU_FTR_PKEY) && trap_is_syscall(regs)) { + unsigned long amr, iamr; + bool flush_needed = false; + /* + * When entering from userspace we mostly have the AMR/IAMR + * different from kernel default values. Hence don't compare. + */ + amr = mfspr(SPRN_AMR); + iamr = mfspr(SPRN_IAMR); + regs->amr = amr; + regs->iamr = iamr; + if (mmu_has_feature(MMU_FTR_KUAP)) { + mtspr(SPRN_AMR, AMR_KUAP_BLOCKED); + flush_needed = true; + } + if (mmu_has_feature(MMU_FTR_BOOK3S_KUEP)) { + mtspr(SPRN_IAMR, AMR_KUEP_BLOCKED); + flush_needed = true; + } + if (flush_needed) + isync(); + } +#endif + kuap_assert_locked(); + booke_restore_dbcr0(); + account_cpu_user_entry(); + account_stolen_time(); + + /* + * This is not required for the syscall exit path, but makes the + * stack frame look nicer. If this was initialised in the first stack + * frame, or if the unwinder was taught the first stack frame always + * returns to user with IRQS_ENABLED, this store could be avoided! + */ + irq_soft_mask_regs_set_state(regs, IRQS_ENABLED); + + /* + * If system call is called with TM active, set _TIF_RESTOREALL to + * prevent RFSCV being used to return to userspace, because POWER9 + * TM implementation has problems with this instruction returning to + * transactional state. Final register values are not relevant because + * the transaction will be aborted upon return anyway. Or in the case + * of unsupported_scv SIGILL fault, the return state does not much + * matter because it's an edge case. + */ + if (IS_ENABLED(CONFIG_PPC_TRANSACTIONAL_MEM) && + unlikely(MSR_TM_TRANSACTIONAL(regs->msr))) + set_bits(_TIF_RESTOREALL, ¤t_thread_info()->flags); + + /* + * If the system call was made with a transaction active, doom it and + * return without performing the system call. Unless it was an + * unsupported scv vector, in which case it's treated like an illegal + * instruction. + */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (unlikely(MSR_TM_TRANSACTIONAL(regs->msr)) && + !trap_is_unsupported_scv(regs)) { + /* Enable TM in the kernel, and disable EE (for scv) */ + hard_irq_disable(); + mtmsr(mfmsr() | MSR_TM); + + /* tabort, this dooms the transaction, nothing else */ + asm volatile(".long 0x7c00071d | ((%0) << 16)" + :: "r"(TM_CAUSE_SYSCALL | TM_CAUSE_PERSISTENT)); + + /* + * Userspace will never see the return value. Execution will + * resume after the tbegin. of the aborted transaction with the + * checkpointed register state. A context switch could occur + * or signal delivered to the process before resuming the + * doomed transaction context, but that should all be handled + * as expected. + */ + return; + } +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ +} + +#define arch_enter_from_user_mode arch_enter_from_user_mode + +static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs, + unsigned long ti_work) +{ + unsigned long mathflags; + + if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && IS_ENABLED(CONFIG_PPC_FPU)) { + if (IS_ENABLED(CONFIG_PPC_TRANSACTIONAL_MEM) && + unlikely((ti_work & _TIF_RESTORE_TM))) { + restore_tm_state(regs); + } else { + mathflags = MSR_FP; + + if (cpu_has_feature(CPU_FTR_VSX)) + mathflags |= MSR_VEC | MSR_VSX; + else if (cpu_has_feature(CPU_FTR_ALTIVEC)) + mathflags |= MSR_VEC; + + /* + * If userspace MSR has all available FP bits set, + * then they are live and no need to restore. If not, + * it means the regs were given up and restore_math + * may decide to restore them (to avoid taking an FP + * fault). + */ + if ((regs->msr & mathflags) != mathflags) + restore_math(regs); + } + } + + check_return_regs_valid(regs); +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + local_paca->tm_scratch = regs->msr; +#endif + /* Restore user access locks last */ + kuap_user_restore(regs); +} + +#define arch_exit_to_user_mode_prepare arch_exit_to_user_mode_prepare + +static __always_inline void arch_exit_to_user_mode(void) +{ + booke_load_dbcr0(); + + account_cpu_user_exit(); +} + +#define arch_exit_to_user_mode arch_exit_to_user_mode + +#endif /* _ASM_PPC_ENTRY_COMMON_H */ diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h index 9cd945f2acaf..b7eee6385ae5 100644 --- a/arch/powerpc/include/asm/hw_irq.h +++ b/arch/powerpc/include/asm/hw_irq.h @@ -393,7 +393,7 @@ static inline void do_hard_irq_enable(void) __hard_irq_enable(); } -static inline bool arch_irq_disabled_regs(struct pt_regs *regs) +static inline bool regs_irqs_disabled(struct pt_regs *regs) { return (regs->softe & IRQS_DISABLED); } @@ -466,7 +466,7 @@ static inline bool arch_irqs_disabled(void) #define hard_irq_disable() arch_local_irq_disable() -static inline bool arch_irq_disabled_regs(struct pt_regs *regs) +static inline bool regs_irqs_disabled(struct pt_regs *regs) { return !(regs->msr & MSR_EE); } diff --git a/arch/powerpc/include/asm/interrupt.h b/arch/powerpc/include/asm/interrupt.h index eb0e4a20b818..fb42a664ae54 100644 --- a/arch/powerpc/include/asm/interrupt.h +++ b/arch/powerpc/include/asm/interrupt.h @@ -66,11 +66,9 @@ #ifndef __ASSEMBLER__ -#include <linux/context_tracking.h> -#include <linux/hardirq.h> -#include <asm/cputime.h> -#include <asm/firmware.h> -#include <asm/ftrace.h> +#include <linux/sched/debug.h> /* for show_regs */ +#include <linux/irq-entry-common.h> + #include <asm/kprobes.h> #include <asm/runlatch.h> @@ -88,308 +86,6 @@ do { \ #define INT_SOFT_MASK_BUG_ON(regs, cond) #endif -#ifdef CONFIG_PPC_BOOK3S_64 -extern char __end_soft_masked[]; -bool search_kernel_soft_mask_table(unsigned long addr); -unsigned long search_kernel_restart_table(unsigned long addr); - -DECLARE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant); - -static inline bool is_implicit_soft_masked(struct pt_regs *regs) -{ - if (user_mode(regs)) - return false; - - if (regs->nip >= (unsigned long)__end_soft_masked) - return false; - - return search_kernel_soft_mask_table(regs->nip); -} - -static inline void srr_regs_clobbered(void) -{ - local_paca->srr_valid = 0; - local_paca->hsrr_valid = 0; -} -#else -static inline unsigned long search_kernel_restart_table(unsigned long addr) -{ - return 0; -} - -static inline bool is_implicit_soft_masked(struct pt_regs *regs) -{ - return false; -} - -static inline void srr_regs_clobbered(void) -{ -} -#endif - -static inline void nap_adjust_return(struct pt_regs *regs) -{ -#ifdef CONFIG_PPC_970_NAP - if (unlikely(test_thread_local_flags(_TLF_NAPPING))) { - /* Can avoid a test-and-clear because NMIs do not call this */ - clear_thread_local_flags(_TLF_NAPPING); - regs_set_return_ip(regs, (unsigned long)power4_idle_nap_return); - } -#endif -} - -static inline void booke_restore_dbcr0(void) -{ -#ifdef CONFIG_PPC_ADV_DEBUG_REGS - unsigned long dbcr0 = current->thread.debug.dbcr0; - - if (IS_ENABLED(CONFIG_PPC32) && unlikely(dbcr0 & DBCR0_IDM)) { - mtspr(SPRN_DBSR, -1); - mtspr(SPRN_DBCR0, global_dbcr0[smp_processor_id()]); - } -#endif -} - -static inline void interrupt_enter_prepare(struct pt_regs *regs) -{ -#ifdef CONFIG_PPC64 - irq_soft_mask_set(IRQS_ALL_DISABLED); - - /* - * If the interrupt was taken with HARD_DIS clear, then enable MSR[EE]. - * Asynchronous interrupts get here with HARD_DIS set (see below), so - * this enables MSR[EE] for synchronous interrupts. IRQs remain - * soft-masked. The interrupt handler may later call - * interrupt_cond_local_irq_enable() to achieve a regular process - * context. - */ - if (!(local_paca->irq_happened & PACA_IRQ_HARD_DIS)) { - INT_SOFT_MASK_BUG_ON(regs, !(regs->msr & MSR_EE)); - __hard_irq_enable(); - } else { - __hard_RI_enable(); - } - /* Enable MSR[RI] early, to support kernel SLB and hash faults */ -#endif - - if (!arch_irq_disabled_regs(regs)) - trace_hardirqs_off(); - - if (user_mode(regs)) { - kuap_lock(); - CT_WARN_ON(ct_state() != CT_STATE_USER); - user_exit_irqoff(); - - account_cpu_user_entry(); - account_stolen_time(); - } else { - kuap_save_and_lock(regs); - /* - * CT_WARN_ON comes here via program_check_exception, - * so avoid recursion. - */ - if (TRAP(regs) != INTERRUPT_PROGRAM) - CT_WARN_ON(ct_state() != CT_STATE_KERNEL && - ct_state() != CT_STATE_IDLE); - INT_SOFT_MASK_BUG_ON(regs, is_implicit_soft_masked(regs)); - INT_SOFT_MASK_BUG_ON(regs, arch_irq_disabled_regs(regs) && - search_kernel_restart_table(regs->nip)); - } - INT_SOFT_MASK_BUG_ON(regs, !arch_irq_disabled_regs(regs) && - !(regs->msr & MSR_EE)); - - booke_restore_dbcr0(); -} - -/* - * Care should be taken to note that interrupt_exit_prepare and - * interrupt_async_exit_prepare do not necessarily return immediately to - * regs context (e.g., if regs is usermode, we don't necessarily return to - * user mode). Other interrupts might be taken between here and return, - * context switch / preemption may occur in the exit path after this, or a - * signal may be delivered, etc. - * - * The real interrupt exit code is platform specific, e.g., - * interrupt_exit_user_prepare / interrupt_exit_kernel_prepare for 64s. - * - * However interrupt_nmi_exit_prepare does return directly to regs, because - * NMIs do not do "exit work" or replay soft-masked interrupts. - */ -static inline void interrupt_exit_prepare(struct pt_regs *regs) -{ -} - -static inline void interrupt_async_enter_prepare(struct pt_regs *regs) -{ -#ifdef CONFIG_PPC64 - /* Ensure interrupt_enter_prepare does not enable MSR[EE] */ - local_paca->irq_happened |= PACA_IRQ_HARD_DIS; -#endif - interrupt_enter_prepare(regs); -#ifdef CONFIG_PPC_BOOK3S_64 - /* - * RI=1 is set by interrupt_enter_prepare, so this thread flags access - * has to come afterward (it can cause SLB faults). - */ - if (cpu_has_feature(CPU_FTR_CTRL) && - !test_thread_local_flags(_TLF_RUNLATCH)) - __ppc64_runlatch_on(); -#endif - irq_enter(); -} - -static inline void interrupt_async_exit_prepare(struct pt_regs *regs) -{ - /* - * Adjust at exit so the main handler sees the true NIA. This must - * come before irq_exit() because irq_exit can enable interrupts, and - * if another interrupt is taken before nap_adjust_return has run - * here, then that interrupt would return directly to idle nap return. - */ - nap_adjust_return(regs); - - irq_exit(); - interrupt_exit_prepare(regs); -} - -struct interrupt_nmi_state { -#ifdef CONFIG_PPC64 - u8 irq_soft_mask; - u8 irq_happened; - u8 ftrace_enabled; - u64 softe; -#endif -}; - -static inline bool nmi_disables_ftrace(struct pt_regs *regs) -{ - /* Allow DEC and PMI to be traced when they are soft-NMI */ - if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) { - if (TRAP(regs) == INTERRUPT_DECREMENTER) - return false; - if (TRAP(regs) == INTERRUPT_PERFMON) - return false; - } - if (IS_ENABLED(CONFIG_PPC_BOOK3E_64)) { - if (TRAP(regs) == INTERRUPT_PERFMON) - return false; - } - - return true; -} - -static inline void interrupt_nmi_enter_prepare(struct pt_regs *regs, struct interrupt_nmi_state *state) -{ -#ifdef CONFIG_PPC64 - state->irq_soft_mask = local_paca->irq_soft_mask; - state->irq_happened = local_paca->irq_happened; - state->softe = regs->softe; - - /* - * Set IRQS_ALL_DISABLED unconditionally so irqs_disabled() does - * the right thing, and set IRQ_HARD_DIS. We do not want to reconcile - * because that goes through irq tracing which we don't want in NMI. - */ - local_paca->irq_soft_mask = IRQS_ALL_DISABLED; - local_paca->irq_happened |= PACA_IRQ_HARD_DIS; - - if (!(regs->msr & MSR_EE) || is_implicit_soft_masked(regs)) { - /* - * Adjust regs->softe to be soft-masked if it had not been - * reconcied (e.g., interrupt entry with MSR[EE]=0 but softe - * not yet set disabled), or if it was in an implicit soft - * masked state. This makes arch_irq_disabled_regs(regs) - * behave as expected. - */ - regs->softe = IRQS_ALL_DISABLED; - } - - __hard_RI_enable(); - - /* Don't do any per-CPU operations until interrupt state is fixed */ - - if (nmi_disables_ftrace(regs)) { - state->ftrace_enabled = this_cpu_get_ftrace_enabled(); - this_cpu_set_ftrace_enabled(0); - } -#endif - - /* If data relocations are enabled, it's safe to use nmi_enter() */ - if (mfmsr() & MSR_DR) { - nmi_enter(); - return; - } - - /* - * But do not use nmi_enter() for pseries hash guest taking a real-mode - * NMI because not everything it touches is within the RMA limit. - */ - if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && - firmware_has_feature(FW_FEATURE_LPAR) && - !radix_enabled()) - return; - - /* - * Likewise, don't use it if we have some form of instrumentation (like - * KASAN shadow) that is not safe to access in real mode (even on radix) - */ - if (IS_ENABLED(CONFIG_KASAN)) - return; - - /* - * Likewise, do not use it in real mode if percpu first chunk is not - * embedded. With CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK enabled there - * are chances where percpu allocation can come from vmalloc area. - */ - if (percpu_first_chunk_is_paged) - return; - - /* Otherwise, it should be safe to call it */ - nmi_enter(); -} - -static inline void interrupt_nmi_exit_prepare(struct pt_regs *regs, struct interrupt_nmi_state *state) -{ - if (mfmsr() & MSR_DR) { - // nmi_exit if relocations are on - nmi_exit(); - } else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && - firmware_has_feature(FW_FEATURE_LPAR) && - !radix_enabled()) { - // no nmi_exit for a pseries hash guest taking a real mode exception - } else if (IS_ENABLED(CONFIG_KASAN)) { - // no nmi_exit for KASAN in real mode - } else if (percpu_first_chunk_is_paged) { - // no nmi_exit if percpu first chunk is not embedded - } else { - nmi_exit(); - } - - /* - * nmi does not call nap_adjust_return because nmi should not create - * new work to do (must use irq_work for that). - */ - -#ifdef CONFIG_PPC64 -#ifdef CONFIG_PPC_BOOK3S - if (arch_irq_disabled_regs(regs)) { - unsigned long rst = search_kernel_restart_table(regs->nip); - if (rst) - regs_set_return_ip(regs, rst); - } -#endif - - if (nmi_disables_ftrace(regs)) - this_cpu_set_ftrace_enabled(state->ftrace_enabled); - - /* Check we didn't change the pending interrupt mask. */ - WARN_ON_ONCE((state->irq_happened | PACA_IRQ_HARD_DIS) != local_paca->irq_happened); - regs->softe = state->softe; - local_paca->irq_happened = state->irq_happened; - local_paca->irq_soft_mask = state->irq_soft_mask; -#endif -} - /* * Don't use noinstr here like x86, but rather add NOKPROBE_SYMBOL to each * function definition. The reason for this is the noinstr section is placed @@ -470,11 +166,14 @@ static __always_inline void ____##func(struct pt_regs *regs); \ \ interrupt_handler void func(struct pt_regs *regs) \ { \ - interrupt_enter_prepare(regs); \ - \ + irqentry_state_t state; \ + arch_interrupt_enter_prepare(regs); \ + state = irqentry_enter(regs); \ + instrumentation_begin(); \ ____##func (regs); \ - \ - interrupt_exit_prepare(regs); \ + instrumentation_end(); \ + arch_interrupt_exit_prepare(regs); \ + irqentry_exit(regs, state); \ } \ NOKPROBE_SYMBOL(func); \ \ @@ -504,12 +203,15 @@ static __always_inline long ____##func(struct pt_regs *regs); \ interrupt_handler long func(struct pt_regs *regs) \ { \ long ret; \ + irqentry_state_t state; \ \ - interrupt_enter_prepare(regs); \ - \ + arch_interrupt_enter_prepare(regs); \ + state = irqentry_enter(regs); \ + instrumentation_begin(); \ ret = ____##func (regs); \ - \ - interrupt_exit_prepare(regs); \ + instrumentation_end(); \ + arch_interrupt_exit_prepare(regs); \ + irqentry_exit(regs, state); \ \ return ret; \ } \ @@ -538,11 +240,16 @@ static __always_inline void ____##func(struct pt_regs *regs); \ \ interrupt_handler void func(struct pt_regs *regs) \ { \ - interrupt_async_enter_prepare(regs); \ - \ + irqentry_state_t state; \ + arch_interrupt_async_enter_prepare(regs); \ + state = irqentry_enter(regs); \ + instrumentation_begin(); \ + irq_enter_rcu(); \ ____##func (regs); \ - \ - interrupt_async_exit_prepare(regs); \ + irq_exit_rcu(); \ + instrumentation_end(); \ + arch_interrupt_async_exit_prepare(regs); \ + irqentry_exit(regs, state); \ } \ NOKPROBE_SYMBOL(func); \ \ @@ -572,14 +279,43 @@ ____##func(struct pt_regs *regs); \ \ interrupt_handler long func(struct pt_regs *regs) \ { \ - struct interrupt_nmi_state state; \ + irqentry_state_t state; \ + struct interrupt_nmi_state nmi_state; \ long ret; \ \ - interrupt_nmi_enter_prepare(regs, &state); \ - \ + arch_interrupt_nmi_enter_prepare(regs, &nmi_state); \ + if (mfmsr() & MSR_DR) { \ + /* nmi_entry if relocations are on */ \ + state = irqentry_nmi_enter(regs); \ + } else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && \ + firmware_has_feature(FW_FEATURE_LPAR) && \ + !radix_enabled()) { \ + /* no nmi_entry for a pseries hash guest \ + * taking a real mode exception */ \ + } else if (IS_ENABLED(CONFIG_KASAN)) { \ + /* no nmi_entry for KASAN in real mode */ \ + } else if (percpu_first_chunk_is_paged) { \ + /* no nmi_entry if percpu first chunk is not embedded */\ + } else { \ + state = irqentry_nmi_enter(regs); \ + } \ ret = ____##func (regs); \ - \ - interrupt_nmi_exit_prepare(regs, &state); \ + arch_interrupt_nmi_exit_prepare(regs, &nmi_state); \ + if (mfmsr() & MSR_DR) { \ + /* nmi_exit if relocations are on */ \ + irqentry_nmi_exit(regs, state); \ + } else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && \ + firmware_has_feature(FW_FEATURE_LPAR) && \ + !radix_enabled()) { \ + /* no nmi_exit for a pseries hash guest \ + * taking a real mode exception */ \ + } else if (IS_ENABLED(CONFIG_KASAN)) { \ + /* no nmi_exit for KASAN in real mode */ \ + } else if (percpu_first_chunk_is_paged) { \ + /* no nmi_exit if percpu first chunk is not embedded */ \ + } else { \ + irqentry_nmi_exit(regs, state); \ + } \ \ return ret; \ } \ @@ -661,7 +397,7 @@ void replay_soft_interrupts(void); static inline void interrupt_cond_local_irq_enable(struct pt_regs *regs) { - if (!arch_irq_disabled_regs(regs)) + if (!regs_irqs_disabled(regs)) local_irq_enable(); } diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h index 045804a86f98..a690e7da53c2 100644 --- a/arch/powerpc/include/asm/kasan.h +++ b/arch/powerpc/include/asm/kasan.h @@ -3,14 +3,19 @@ #define __ASM_KASAN_H #if defined(CONFIG_KASAN) && !defined(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) EXPORT_SYMBOL(__##fn) -#else +#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 +#endif /* CONFIG_KASAN && !CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX */ #ifndef __ASSEMBLER__ diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index f679a11a7e7f..f4991d10d89e 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -12,12 +12,14 @@ * Updates for PPC64 by Peter Bergner & David Engebretsen, IBM Corp. */ #include <linux/types.h> +#include <linux/sizes.h> #include <asm/firmware.h> struct device_node; struct property; -#define MIN_RMA 768 /* Minimum RMA (in MB) for CAS negotiation */ +/* Minimum RMA in bytes for CAS negotiation */ +#define MIN_RMA (768ULL * SZ_1M) #define OF_DT_BEGIN_NODE 0x1 /* Start of node, full name */ #define OF_DT_END_NODE 0x2 /* End node */ diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index 94aa1de2b06e..fdeb97421785 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -53,6 +53,9 @@ struct pt_regs unsigned long esr; }; unsigned long result; + unsigned long exit_flags; + /* Maintain 16 byte interrupt stack alignment */ + unsigned long __pt_regs_pad[3]; }; }; #if defined(CONFIG_PPC64) || defined(CONFIG_PPC_KUAP) @@ -174,9 +177,6 @@ extern unsigned long profile_pc(struct pt_regs *regs); #define profile_pc(regs) instruction_pointer(regs) #endif -long do_syscall_trace_enter(struct pt_regs *regs); -void do_syscall_trace_leave(struct pt_regs *regs); - static inline void set_return_regs_changed(void) { #ifdef CONFIG_PPC_BOOK3S_64 diff --git a/arch/powerpc/include/asm/rtas-types.h b/arch/powerpc/include/asm/rtas-types.h index 9d5b16803cbb..5d40d187b965 100644 --- a/arch/powerpc/include/asm/rtas-types.h +++ b/arch/powerpc/include/asm/rtas-types.h @@ -42,8 +42,9 @@ struct rtas_error_log { */ u8 byte3; /* General event or error*/ __be32 extended_log_length; /* length in bytes */ - unsigned char buffer[1]; /* Start of extended log */ - /* Variable length. */ + + /* Start of extended log, variable length */ + unsigned char buffer[] __counted_by_be(extended_log_length); }; /* RTAS general extended event log, Version 6. The extended log starts diff --git a/arch/powerpc/include/asm/signal.h b/arch/powerpc/include/asm/signal.h index 922d43700fb4..21af92cdb237 100644 --- a/arch/powerpc/include/asm/signal.h +++ b/arch/powerpc/include/asm/signal.h @@ -7,7 +7,6 @@ #include <uapi/asm/ptrace.h> struct pt_regs; -void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags); unsigned long get_min_sigframe_size_32(void); unsigned long get_min_sigframe_size_64(void); diff --git a/arch/powerpc/include/asm/stacktrace.h b/arch/powerpc/include/asm/stacktrace.h index 6149b53b3bc8..987f2e996262 100644 --- a/arch/powerpc/include/asm/stacktrace.h +++ b/arch/powerpc/include/asm/stacktrace.h @@ -10,4 +10,10 @@ void show_user_instructions(struct pt_regs *regs); +static __always_inline bool on_thread_stack(void) +{ + return !(((unsigned long)(current->stack) ^ current_stack_pointer) + & ~(THREAD_SIZE - 1)); +} + #endif /* _ASM_POWERPC_STACKTRACE_H */ diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h index 4b3c52ed6e9d..834fcc4f7b54 100644 --- a/arch/powerpc/include/asm/syscall.h +++ b/arch/powerpc/include/asm/syscall.h @@ -139,4 +139,9 @@ static inline int syscall_get_arch(struct task_struct *task) else return AUDIT_ARCH_PPC64; } + +static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs) +{ + return false; +} #endif /* _ASM_SYSCALL_H */ diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h index 97f35f9b1a96..ee3b9adb5b67 100644 --- a/arch/powerpc/include/asm/thread_info.h +++ b/arch/powerpc/include/asm/thread_info.h @@ -57,6 +57,7 @@ struct thread_info { #ifdef CONFIG_SMP unsigned int cpu; #endif + unsigned long syscall_work; /* SYSCALL_WORK_ flags */ unsigned long local_flags; /* private flags for thread */ #ifdef CONFIG_LIVEPATCH_64 unsigned long *livepatch_sp; diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index e98c628e3899..7b8c56962c31 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -18,9 +18,35 @@ /* Threshold above which VMX copy path is used */ #define VMX_COPY_THRESHOLD 3328 +#define __access_ok __access_ok + #include <asm-generic/access_ok.h> /* + * On powerpc64, TASK_SIZE_MAX is 0x0010000000000000 then even if both ptr and size + * are TASK_SIZE_MAX we are still inside the memory gap. So make it simple. + */ +static __always_inline int __access_ok(const void __user *ptr, unsigned long size) +{ + unsigned long addr = (unsigned long)ptr; + + if (IS_ENABLED(CONFIG_PPC64)) { + BUILD_BUG_ON(!is_power_of_2(TASK_SIZE_MAX)); + BUILD_BUG_ON(TASK_SIZE_MAX > 0x0010000000000000); + + if (statically_true(size > TASK_SIZE_MAX)) + return false; + if (statically_true(size <= TASK_SIZE_MAX)) + return !(addr & ~(TASK_SIZE_MAX - 1)); + return !((size | addr) & ~(TASK_SIZE_MAX - 1)); + } else { + if (statically_true(size <= SZ_128K)) + return addr < TASK_SIZE; + return size <= TASK_SIZE && addr <= TASK_SIZE - size; + } +} + +/* * These are the main single-value transfer routines. They automatically * use the right size if we just have the right pointer type. * diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h index 01e630149d48..a393b7f2760a 100644 --- a/arch/powerpc/include/uapi/asm/ptrace.h +++ b/arch/powerpc/include/uapi/asm/ptrace.h @@ -55,6 +55,8 @@ struct pt_regs unsigned long dar; /* Fault registers */ unsigned long dsisr; /* on 4xx/Book-E used for ESR */ unsigned long result; /* Result of a system call */ + unsigned long exit_flags; /* System call exit flags */ + unsigned long __pt_regs_pad[3]; /* Maintain 16 byte interrupt stack alignment */ }; #endif /* __ASSEMBLER__ */ @@ -114,10 +116,12 @@ struct pt_regs #define PT_DAR 41 #define PT_DSISR 42 #define PT_RESULT 43 -#define PT_DSCR 44 -#define PT_REGS_COUNT 44 +#define PT_EXIT_FLAGS 44 +#define PT_PAD 47 /* 3 times */ +#define PT_DSCR 48 +#define PT_REGS_COUNT 48 -#define PT_FPR0 48 /* each FP reg occupies 2 slots in this space */ +#define PT_FPR0 (PT_REGS_COUNT + 4) /* each FP reg occupies 2 slots in this space */ #ifndef __powerpc64__ @@ -129,7 +133,7 @@ struct pt_regs #define PT_FPSCR (PT_FPR0 + 32) /* each FP reg occupies 1 slot in 64-bit space */ -#define PT_VR0 82 /* each Vector reg occupies 2 slots in 64-bit */ +#define PT_VR0 (PT_FPSCR + 2) /* <82> each Vector reg occupies 2 slots in 64-bit */ #define PT_VSCR (PT_VR0 + 32*2 + 1) #define PT_VRSAVE (PT_VR0 + 33*2) @@ -137,7 +141,7 @@ struct pt_regs /* * Only store first 32 VSRs here. The second 32 VSRs in VR0-31 */ -#define PT_VSR0 150 /* each VSR reg occupies 2 slots in 64-bit */ +#define PT_VSR0 (PT_VRSAVE + 2) /* each VSR reg occupies 2 slots in 64-bit */ #define PT_VSR31 (PT_VSR0 + 2*31) #endif /* __powerpc64__ */ diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index 90d51d9b3ed2..04e5ea38bdc0 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -18,6 +18,7 @@ #include <linux/of.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <asm/cputhreads.h> #include <asm/smp.h> @@ -596,7 +597,7 @@ static ssize_t size_show(struct kobject *k, struct kobj_attribute *attr, char *b if (cache_size_kb(cache, &size_kb)) return -ENODEV; - return sprintf(buf, "%uK\n", size_kb); + return sysfs_emit(buf, "%uK\n", size_kb); } static struct kobj_attribute cache_size_attr = @@ -613,7 +614,7 @@ static ssize_t line_size_show(struct kobject *k, struct kobj_attribute *attr, ch if (cache_get_line_size(cache, &line_size)) return -ENODEV; - return sprintf(buf, "%u\n", line_size); + return sysfs_emit(buf, "%u\n", line_size); } static struct kobj_attribute cache_line_size_attr = @@ -629,7 +630,7 @@ static ssize_t nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char if (cache_nr_sets(cache, &nr_sets)) return -ENODEV; - return sprintf(buf, "%u\n", nr_sets); + return sysfs_emit(buf, "%u\n", nr_sets); } static struct kobj_attribute cache_nr_sets_attr = @@ -645,7 +646,7 @@ static ssize_t associativity_show(struct kobject *k, struct kobj_attribute *attr if (cache_associativity(cache, &associativity)) return -ENODEV; - return sprintf(buf, "%u\n", associativity); + return sysfs_emit(buf, "%u\n", associativity); } static struct kobj_attribute cache_assoc_attr = @@ -657,7 +658,7 @@ static ssize_t type_show(struct kobject *k, struct kobj_attribute *attr, char *b cache = index_kobj_to_cache(k); - return sprintf(buf, "%s\n", cache_type_string(cache)); + return sysfs_emit(buf, "%s\n", cache_type_string(cache)); } static struct kobj_attribute cache_type_attr = @@ -671,7 +672,7 @@ static ssize_t level_show(struct kobject *k, struct kobj_attribute *attr, char * index = kobj_to_cache_index_dir(k); cache = index->cache; - return sprintf(buf, "%d\n", cache->level); + return sysfs_emit(buf, "%d\n", cache->level); } static struct kobj_attribute cache_level_attr = diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c index 706e1eb95efe..b9785f105f75 100644 --- a/arch/powerpc/kernel/eeh_sysfs.c +++ b/arch/powerpc/kernel/eeh_sysfs.c @@ -9,6 +9,7 @@ #include <linux/of.h> #include <linux/pci.h> #include <linux/stat.h> +#include <linux/sysfs.h> #include <asm/ppc-pci.h> #include <asm/pci-bridge.h> @@ -31,7 +32,7 @@ static ssize_t eeh_show_##_name(struct device *dev, \ if (!edev) \ return 0; \ \ - return sprintf(buf, _format "\n", edev->_memb); \ + return sysfs_emit(buf, _format "\n", edev->_memb); \ } \ static DEVICE_ATTR(_name, 0444, eeh_show_##_name, NULL); @@ -49,8 +50,7 @@ static ssize_t eeh_pe_state_show(struct device *dev, return -ENODEV; state = eeh_ops->get_state(edev->pe, NULL); - return sprintf(buf, "0x%08x 0x%08x\n", - state, edev->pe->state); + return sysfs_emit(buf, "0x%08x 0x%08x\n", state, edev->pe->state); } static ssize_t eeh_pe_state_store(struct device *dev, @@ -87,7 +87,7 @@ static ssize_t eeh_notify_resume_show(struct device *dev, if (!edev || !edev->pe) return -ENODEV; - return sprintf(buf, "%d\n", pdn->last_allow_rc); + return sysfs_emit(buf, "%d\n", pdn->last_allow_rc); } static ssize_t eeh_notify_resume_store(struct device *dev, diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 501d43bf18f3..7f79c9aea4a9 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -1422,7 +1422,7 @@ static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", fw_dump.fadump_enabled); + return sysfs_emit(buf, "%d\n", fw_dump.fadump_enabled); } /* @@ -1434,28 +1434,28 @@ static ssize_t hotplug_ready_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", 1); + return sysfs_emit(buf, "%d\n", 1); } static ssize_t mem_reserved_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%ld\n", fw_dump.reserve_dump_area_size); + return sysfs_emit(buf, "%ld\n", fw_dump.reserve_dump_area_size); } static ssize_t registered_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", fw_dump.dump_registered); + return sysfs_emit(buf, "%d\n", fw_dump.dump_registered); } static ssize_t bootargs_append_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", (char *)__va(fw_dump.param_area)); + return sysfs_emit(buf, "%s\n", (char *)__va(fw_dump.param_area)); } static ssize_t bootargs_append_store(struct kobject *kobj, @@ -1759,10 +1759,10 @@ void __init fadump_setup_param_area(void) * 2. The range should be between MIN_RMA and RMA size (ppc64_rma_size) * 3. It must not overlap with the fadump reserved area. */ - if (ppc64_rma_size < MIN_RMA*1024*1024) + if (ppc64_rma_size < MIN_RMA) return; - range_start = MIN_RMA * 1024 * 1024; + range_start = MIN_RMA; range_end = min(ppc64_rma_size, fw_dump.boot_mem_top); } diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c index e63bfde13e03..f04978080837 100644 --- a/arch/powerpc/kernel/interrupt.c +++ b/arch/powerpc/kernel/interrupt.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <linux/context_tracking.h> +#include <linux/entry-common.h> #include <linux/err.h> #include <linux/compat.h> #include <linux/rseq.h> @@ -25,10 +26,6 @@ unsigned long global_dbcr0[NR_CPUS]; #endif -#if defined(CONFIG_PREEMPT_DYNAMIC) -DEFINE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched); -#endif - #ifdef CONFIG_PPC_BOOK3S_64 DEFINE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant); static inline bool exit_must_hard_disable(void) @@ -78,181 +75,6 @@ static notrace __always_inline bool prep_irq_for_enabled_exit(bool restartable) return true; } -static notrace void booke_load_dbcr0(void) -{ -#ifdef CONFIG_PPC_ADV_DEBUG_REGS - unsigned long dbcr0 = current->thread.debug.dbcr0; - - if (likely(!(dbcr0 & DBCR0_IDM))) - return; - - /* - * Check to see if the dbcr0 register is set up to debug. - * Use the internal debug mode bit to do this. - */ - mtmsr(mfmsr() & ~MSR_DE); - if (IS_ENABLED(CONFIG_PPC32)) { - isync(); - global_dbcr0[smp_processor_id()] = mfspr(SPRN_DBCR0); - } - mtspr(SPRN_DBCR0, dbcr0); - mtspr(SPRN_DBSR, -1); -#endif -} - -static notrace void check_return_regs_valid(struct pt_regs *regs) -{ -#ifdef CONFIG_PPC_BOOK3S_64 - unsigned long trap, srr0, srr1; - static bool warned; - u8 *validp; - char *h; - - if (trap_is_scv(regs)) - return; - - trap = TRAP(regs); - // EE in HV mode sets HSRRs like 0xea0 - if (cpu_has_feature(CPU_FTR_HVMODE) && trap == INTERRUPT_EXTERNAL) - trap = 0xea0; - - switch (trap) { - case 0x980: - case INTERRUPT_H_DATA_STORAGE: - case 0xe20: - case 0xe40: - case INTERRUPT_HMI: - case 0xe80: - case 0xea0: - case INTERRUPT_H_FAC_UNAVAIL: - case 0x1200: - case 0x1500: - case 0x1600: - case 0x1800: - validp = &local_paca->hsrr_valid; - if (!READ_ONCE(*validp)) - return; - - srr0 = mfspr(SPRN_HSRR0); - srr1 = mfspr(SPRN_HSRR1); - h = "H"; - - break; - default: - validp = &local_paca->srr_valid; - if (!READ_ONCE(*validp)) - return; - - srr0 = mfspr(SPRN_SRR0); - srr1 = mfspr(SPRN_SRR1); - h = ""; - break; - } - - if (srr0 == regs->nip && srr1 == regs->msr) - return; - - /* - * A NMI / soft-NMI interrupt may have come in after we found - * srr_valid and before the SRRs are loaded. The interrupt then - * comes in and clobbers SRRs and clears srr_valid. Then we load - * the SRRs here and test them above and find they don't match. - * - * Test validity again after that, to catch such false positives. - * - * This test in general will have some window for false negatives - * and may not catch and fix all such cases if an NMI comes in - * later and clobbers SRRs without clearing srr_valid, but hopefully - * such things will get caught most of the time, statistically - * enough to be able to get a warning out. - */ - if (!READ_ONCE(*validp)) - return; - - if (!data_race(warned)) { - data_race(warned = true); - printk("%sSRR0 was: %lx should be: %lx\n", h, srr0, regs->nip); - printk("%sSRR1 was: %lx should be: %lx\n", h, srr1, regs->msr); - show_regs(regs); - } - - WRITE_ONCE(*validp, 0); /* fixup */ -#endif -} - -static notrace unsigned long -interrupt_exit_user_prepare_main(unsigned long ret, struct pt_regs *regs) -{ - unsigned long ti_flags; - -again: - ti_flags = read_thread_flags(); - while (unlikely(ti_flags & (_TIF_USER_WORK_MASK & ~_TIF_RESTORE_TM))) { - local_irq_enable(); - if (ti_flags & (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)) { - schedule(); - } else { - /* - * SIGPENDING must restore signal handler function - * argument GPRs, and some non-volatiles (e.g., r1). - * Restore all for now. This could be made lighter. - */ - if (ti_flags & _TIF_SIGPENDING) - ret |= _TIF_RESTOREALL; - do_notify_resume(regs, ti_flags); - } - local_irq_disable(); - ti_flags = read_thread_flags(); - } - - if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && IS_ENABLED(CONFIG_PPC_FPU)) { - if (IS_ENABLED(CONFIG_PPC_TRANSACTIONAL_MEM) && - unlikely((ti_flags & _TIF_RESTORE_TM))) { - restore_tm_state(regs); - } else { - unsigned long mathflags = MSR_FP; - - if (cpu_has_feature(CPU_FTR_VSX)) - mathflags |= MSR_VEC | MSR_VSX; - else if (cpu_has_feature(CPU_FTR_ALTIVEC)) - mathflags |= MSR_VEC; - - /* - * If userspace MSR has all available FP bits set, - * then they are live and no need to restore. If not, - * it means the regs were given up and restore_math - * may decide to restore them (to avoid taking an FP - * fault). - */ - if ((regs->msr & mathflags) != mathflags) - restore_math(regs); - } - } - - check_return_regs_valid(regs); - - user_enter_irqoff(); - if (!prep_irq_for_enabled_exit(true)) { - user_exit_irqoff(); - local_irq_enable(); - local_irq_disable(); - goto again; - } - -#ifdef CONFIG_PPC_TRANSACTIONAL_MEM - local_paca->tm_scratch = regs->msr; -#endif - - booke_load_dbcr0(); - - account_cpu_user_exit(); - - /* Restore user access locks last */ - kuap_user_restore(regs); - - return ret; -} - /* * This should be called after a syscall returns, with r3 the return value * from the syscall. If this function returns non-zero, the system call @@ -267,17 +89,12 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, long scv) { unsigned long ti_flags; - unsigned long ret = 0; bool is_not_scv = !IS_ENABLED(CONFIG_PPC_BOOK3S_64) || !scv; - CT_WARN_ON(ct_state() == CT_STATE_USER); - kuap_assert_locked(); regs->result = r3; - - /* Check whether the syscall is issued inside a restartable sequence */ - rseq_syscall(regs); + regs->exit_flags = 0; ti_flags = read_thread_flags(); @@ -290,7 +107,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, if (unlikely(ti_flags & _TIF_PERSYSCALL_MASK)) { if (ti_flags & _TIF_RESTOREALL) - ret = _TIF_RESTOREALL; + regs->exit_flags = _TIF_RESTOREALL; else regs->gpr[3] = r3; clear_bits(_TIF_PERSYSCALL_MASK, ¤t_thread_info()->flags); @@ -299,18 +116,28 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, } if (unlikely(ti_flags & _TIF_SYSCALL_DOTRACE)) { - do_syscall_trace_leave(regs); - ret |= _TIF_RESTOREALL; + regs->exit_flags |= _TIF_RESTOREALL; } - local_irq_disable(); - ret = interrupt_exit_user_prepare_main(ret, regs); + syscall_exit_to_user_mode(regs); + +again: + user_enter_irqoff(); + if (!prep_irq_for_enabled_exit(true)) { + user_exit_irqoff(); + local_irq_enable(); + local_irq_disable(); + goto again; + } + + /* Restore user access locks last */ + kuap_user_restore(regs); #ifdef CONFIG_PPC64 - regs->exit_result = ret; + regs->exit_result = regs->exit_flags; #endif - return ret; + return regs->exit_flags; } #ifdef CONFIG_PPC64 @@ -330,13 +157,17 @@ notrace unsigned long syscall_exit_restart(unsigned long r3, struct pt_regs *reg set_kuap(AMR_KUAP_BLOCKED); #endif - trace_hardirqs_off(); - user_exit_irqoff(); - account_cpu_user_entry(); - - BUG_ON(!user_mode(regs)); +again: + user_enter_irqoff(); + if (!prep_irq_for_enabled_exit(true)) { + user_exit_irqoff(); + local_irq_enable(); + local_irq_disable(); + goto again; + } - regs->exit_result = interrupt_exit_user_prepare_main(regs->exit_result, regs); + kuap_user_restore(regs); + regs->exit_result |= regs->exit_flags; return regs->exit_result; } @@ -347,8 +178,7 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs) unsigned long ret; BUG_ON(regs_is_unrecoverable(regs)); - BUG_ON(arch_irq_disabled_regs(regs)); - CT_WARN_ON(ct_state() == CT_STATE_USER); + BUG_ON(regs_irqs_disabled(regs)); /* * We don't need to restore AMR on the way back to userspace for KUAP. @@ -357,8 +187,21 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs) kuap_assert_locked(); local_irq_disable(); + regs->exit_flags = 0; +again: + check_return_regs_valid(regs); + user_enter_irqoff(); + if (!prep_irq_for_enabled_exit(true)) { + user_exit_irqoff(); + local_irq_enable(); + local_irq_disable(); + goto again; + } + + /* Restore user access locks last */ + kuap_user_restore(regs); - ret = interrupt_exit_user_prepare_main(0, regs); + ret = regs->exit_flags; #ifdef CONFIG_PPC64 regs->exit_result = ret; @@ -396,17 +239,10 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs) local_irq_disable(); - if (!arch_irq_disabled_regs(regs)) { + if (!regs_irqs_disabled(regs)) { /* Returning to a kernel context with local irqs enabled. */ WARN_ON_ONCE(!(regs->msr & MSR_EE)); again: - if (need_irq_preemption()) { - /* Return to preemptible kernel context */ - if (unlikely(read_thread_flags() & _TIF_NEED_RESCHED)) { - if (preempt_count() == 0) - preempt_schedule_irq(); - } - } check_return_regs_valid(regs); @@ -479,7 +315,6 @@ notrace unsigned long interrupt_exit_user_restart(struct pt_regs *regs) #endif trace_hardirqs_off(); - user_exit_irqoff(); account_cpu_user_entry(); BUG_ON(!user_mode(regs)); diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index d122e8447831..ee1b5cb557c9 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/types.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/mm.h> #include <linux/spinlock.h> #include <linux/string.h> @@ -141,7 +142,7 @@ late_initcall(fail_iommu_debugfs); static ssize_t fail_iommu_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev->archdata.fail_iommu); + return sysfs_emit(buf, "%d\n", dev->archdata.fail_iommu); } static ssize_t fail_iommu_store(struct device *dev, diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index a0e8b998c9b5..f69de08ad347 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -370,10 +370,7 @@ int irq_choose_cpu(const struct cpumask *mask) do_round_robin: raw_spin_lock_irqsave(&irq_rover_lock, flags); - irq_rover = cpumask_next(irq_rover, cpu_online_mask); - if (irq_rover >= nr_cpu_ids) - irq_rover = cpumask_first(cpu_online_mask); - + irq_rover = cpumask_next_wrap(irq_rover, cpu_online_mask); cpuid = irq_rover; raw_spin_unlock_irqrestore(&irq_rover_lock, flags); diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index f26e80cbc615..53503937de0e 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1061,7 +1061,7 @@ static const struct ibm_arch_vec ibm_architecture_vec_template __initconst = { .virt_base = cpu_to_be32(0xffffffff), .virt_size = cpu_to_be32(0xffffffff), .load_base = cpu_to_be32(0xffffffff), - .min_rma = cpu_to_be32(MIN_RMA), + .min_rma = cpu_to_be32(MIN_RMA / SZ_1M), .min_load = cpu_to_be32(0xffffffff), /* full client load */ .min_rma_percent = 0, /* min RMA percentage of total RAM */ .max_pft_size = 48, /* max log_2(hash table size) */ diff --git a/arch/powerpc/kernel/ptrace/ptrace.c b/arch/powerpc/kernel/ptrace/ptrace.c index c6997df63287..316d4f5ead8e 100644 --- a/arch/powerpc/kernel/ptrace/ptrace.c +++ b/arch/powerpc/kernel/ptrace/ptrace.c @@ -21,9 +21,6 @@ #include <asm/switch_to.h> #include <asm/debug.h> -#define CREATE_TRACE_POINTS -#include <trace/events/syscalls.h> - #include "ptrace-decl.h" /* @@ -195,144 +192,6 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } -#ifdef CONFIG_SECCOMP -static int do_seccomp(struct pt_regs *regs) -{ - if (!test_thread_flag(TIF_SECCOMP)) - return 0; - - /* - * The ABI we present to seccomp tracers is that r3 contains - * the syscall return value and orig_gpr3 contains the first - * syscall parameter. This is different to the ptrace ABI where - * both r3 and orig_gpr3 contain the first syscall parameter. - */ - regs->gpr[3] = -ENOSYS; - - /* - * We use the __ version here because we have already checked - * TIF_SECCOMP. If this fails, there is nothing left to do, we - * have already loaded -ENOSYS into r3, or seccomp has put - * something else in r3 (via SECCOMP_RET_ERRNO/TRACE). - */ - if (__secure_computing()) - return -1; - - /* - * The syscall was allowed by seccomp, restore the register - * state to what audit expects. - * Note that we use orig_gpr3, which means a seccomp tracer can - * modify the first syscall parameter (in orig_gpr3) and also - * allow the syscall to proceed. - */ - regs->gpr[3] = regs->orig_gpr3; - - return 0; -} -#else -static inline int do_seccomp(struct pt_regs *regs) { return 0; } -#endif /* CONFIG_SECCOMP */ - -/** - * do_syscall_trace_enter() - Do syscall tracing on kernel entry. - * @regs: the pt_regs of the task to trace (current) - * - * Performs various types of tracing on syscall entry. This includes seccomp, - * ptrace, syscall tracepoints and audit. - * - * The pt_regs are potentially visible to userspace via ptrace, so their - * contents is ABI. - * - * One or more of the tracers may modify the contents of pt_regs, in particular - * to modify arguments or even the syscall number itself. - * - * It's also possible that a tracer can choose to reject the system call. In - * that case this function will return an illegal syscall number, and will put - * an appropriate return value in regs->r3. - * - * Return: the (possibly changed) syscall number. - */ -long do_syscall_trace_enter(struct pt_regs *regs) -{ - u32 flags; - - flags = read_thread_flags() & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE); - - if (flags) { - int rc = ptrace_report_syscall_entry(regs); - - if (unlikely(flags & _TIF_SYSCALL_EMU)) { - /* - * A nonzero return code from - * ptrace_report_syscall_entry() tells us to prevent - * the syscall execution, but we are not going to - * execute it anyway. - * - * Returning -1 will skip the syscall execution. We want - * to avoid clobbering any registers, so we don't goto - * the skip label below. - */ - return -1; - } - - if (rc) { - /* - * The tracer decided to abort the syscall. Note that - * the tracer may also just change regs->gpr[0] to an - * invalid syscall number, that is handled below on the - * exit path. - */ - goto skip; - } - } - - /* Run seccomp after ptrace; allow it to set gpr[3]. */ - if (do_seccomp(regs)) - return -1; - - /* Avoid trace and audit when syscall is invalid. */ - if (regs->gpr[0] >= NR_syscalls) - goto skip; - - if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) - trace_sys_enter(regs, regs->gpr[0]); - - if (!is_32bit_task()) - audit_syscall_entry(regs->gpr[0], regs->gpr[3], regs->gpr[4], - regs->gpr[5], regs->gpr[6]); - else - audit_syscall_entry(regs->gpr[0], - regs->gpr[3] & 0xffffffff, - regs->gpr[4] & 0xffffffff, - regs->gpr[5] & 0xffffffff, - regs->gpr[6] & 0xffffffff); - - /* Return the possibly modified but valid syscall number */ - return regs->gpr[0]; - -skip: - /* - * If we are aborting explicitly, or if the syscall number is - * now invalid, set the return value to -ENOSYS. - */ - regs->gpr[3] = -ENOSYS; - return -1; -} - -void do_syscall_trace_leave(struct pt_regs *regs) -{ - int step; - - audit_syscall_exit(regs); - - if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) - trace_sys_exit(regs, regs->result); - - step = test_thread_flag(TIF_SINGLESTEP); - if (step || test_thread_flag(TIF_SYSCALL_TRACE)) - ptrace_report_syscall_exit(regs, step); -} - void __init pt_regs_check(void); /* @@ -432,6 +291,7 @@ void __init pt_regs_check(void) CHECK_REG(PT_DAR, dar); CHECK_REG(PT_DSISR, dsisr); CHECK_REG(PT_RESULT, result); + CHECK_REG(PT_EXIT_FLAGS, exit_flags); #undef CHECK_REG BUILD_BUG_ON(PT_REGS_COUNT != sizeof(struct user_pt_regs) / sizeof(unsigned long)); diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c index fbb7ebd8aa08..600596cb4ffb 100644 --- a/arch/powerpc/kernel/security.c +++ b/arch/powerpc/kernel/security.c @@ -11,6 +11,7 @@ #include <linux/nospec.h> #include <linux/prctl.h> #include <linux/seq_buf.h> +#include <linux/sysfs.h> #include <linux/debugfs.h> #include <asm/asm-prototypes.h> @@ -163,13 +164,13 @@ ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, cha } if (thread_priv) - return sprintf(buf, "Vulnerable: L1D private per thread\n"); + return sysfs_emit(buf, "Vulnerable: L1D private per thread\n"); if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) - return sprintf(buf, "Not affected\n"); + return sysfs_emit(buf, "Not affected\n"); - return sprintf(buf, "Vulnerable\n"); + return sysfs_emit(buf, "Vulnerable\n"); } ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *buf) @@ -352,14 +353,14 @@ ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute * default: type = "unknown"; } - return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type); + return sysfs_emit(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type); } if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) - return sprintf(buf, "Not affected\n"); + return sysfs_emit(buf, "Not affected\n"); - return sprintf(buf, "Vulnerable\n"); + return sysfs_emit(buf, "Vulnerable\n"); } static int ssb_prctl_get(struct task_struct *task) diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 8a86b0efcb1c..67c545f61f0d 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -68,6 +68,7 @@ #include <asm/kasan.h> #include <asm/mce.h> #include <asm/systemcfg.h> +#include <linux/kmsg_dump.h> #include "setup.h" @@ -323,7 +324,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_putc(m, '\n'); /* If this is the last cpu, print the summary */ - if (cpumask_next(cpu_id, cpu_online_mask) >= nr_cpu_ids) + if (cpu_id == cpumask_last(cpu_online_mask)) show_cpuinfo_summary(m); return 0; @@ -331,10 +332,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) static void *c_start(struct seq_file *m, loff_t *pos) { - if (*pos == 0) /* just in case, cpu 0 is not the first */ - *pos = cpumask_first(cpu_online_mask); - else - *pos = cpumask_next(*pos - 1, cpu_online_mask); + *pos = cpumask_next(*pos - 1, cpu_online_mask); if ((*pos) < nr_cpu_ids) return (void *)(unsigned long)(*pos + 1); return NULL; @@ -745,6 +743,13 @@ static int ppc_panic_fadump_handler(struct notifier_block *this, hard_irq_disable(); /* + * Invoke kmsg_dump (e.g., pstore) before crash_fadump() as fadump + * runs before panic()'s kmsg_dump_desc() call. + */ + if (should_fadump_crash()) + kmsg_dump_desc(KMSG_DUMP_PANIC, (char *)ptr); + + /* * If firmware-assisted dump has been registered then trigger * its callback and let the firmware handles everything else. */ diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index aa17e62f3754..bb42a8b6c642 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -6,6 +6,7 @@ * Extracted from signal_32.c and signal_64.c */ +#include <linux/entry-common.h> #include <linux/resume_user_mode.h> #include <linux/signal.h> #include <linux/uprobes.h> @@ -292,23 +293,6 @@ static void do_signal(struct task_struct *tsk) signal_setup_done(ret, &ksig, test_thread_flag(TIF_SINGLESTEP)); } -void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) -{ - if (thread_info_flags & _TIF_UPROBE) - uprobe_notify_resume(regs); - - if (thread_info_flags & _TIF_PATCH_PENDING) - klp_update_patch_state(current); - - if (thread_info_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) { - BUG_ON(regs != current->thread.regs); - do_signal(current); - } - - if (thread_info_flags & _TIF_NOTIFY_RESUME) - resume_user_mode_work(regs); -} - static unsigned long get_tm_stackpointer(struct task_struct *tsk) { /* When in an active transaction that takes a signal, we need to be @@ -368,3 +352,10 @@ void signal_fault(struct task_struct *tsk, struct pt_regs *regs, printk_ratelimited(regs->msr & MSR_64BIT ? fm64 : fm32, tsk->comm, task_pid_nr(tsk), where, ptr, regs->nip, regs->link); } + +void arch_do_signal_or_restart(struct pt_regs *regs) +{ + BUG_ON(regs != current->thread.regs); + regs->exit_flags |= _TIF_RESTOREALL; + do_signal(current); +} diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c index b762677f8737..a9da2af6efa8 100644 --- a/arch/powerpc/kernel/syscall.c +++ b/arch/powerpc/kernel/syscall.c @@ -3,6 +3,7 @@ #include <linux/compat.h> #include <linux/context_tracking.h> #include <linux/randomize_kstack.h> +#include <linux/entry-common.h> #include <asm/interrupt.h> #include <asm/kup.h> @@ -18,124 +19,10 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0) long ret; syscall_fn f; - kuap_lock(); - - if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) - BUG_ON(irq_soft_mask_return() != IRQS_ALL_DISABLED); - - trace_hardirqs_off(); /* finish reconciling */ - - CT_WARN_ON(ct_state() == CT_STATE_KERNEL); - user_exit_irqoff(); - add_random_kstack_offset(); + r0 = syscall_enter_from_user_mode(regs, r0); - BUG_ON(regs_is_unrecoverable(regs)); - BUG_ON(!user_mode(regs)); - BUG_ON(arch_irq_disabled_regs(regs)); - -#ifdef CONFIG_PPC_PKEY - if (mmu_has_feature(MMU_FTR_PKEY)) { - unsigned long amr, iamr; - bool flush_needed = false; - /* - * When entering from userspace we mostly have the AMR/IAMR - * different from kernel default values. Hence don't compare. - */ - amr = mfspr(SPRN_AMR); - iamr = mfspr(SPRN_IAMR); - regs->amr = amr; - regs->iamr = iamr; - if (mmu_has_feature(MMU_FTR_KUAP)) { - mtspr(SPRN_AMR, AMR_KUAP_BLOCKED); - flush_needed = true; - } - if (mmu_has_feature(MMU_FTR_BOOK3S_KUEP)) { - mtspr(SPRN_IAMR, AMR_KUEP_BLOCKED); - flush_needed = true; - } - if (flush_needed) - isync(); - } else -#endif - kuap_assert_locked(); - - booke_restore_dbcr0(); - - account_cpu_user_entry(); - - account_stolen_time(); - - /* - * This is not required for the syscall exit path, but makes the - * stack frame look nicer. If this was initialised in the first stack - * frame, or if the unwinder was taught the first stack frame always - * returns to user with IRQS_ENABLED, this store could be avoided! - */ - irq_soft_mask_regs_set_state(regs, IRQS_ENABLED); - - /* - * If system call is called with TM active, set _TIF_RESTOREALL to - * prevent RFSCV being used to return to userspace, because POWER9 - * TM implementation has problems with this instruction returning to - * transactional state. Final register values are not relevant because - * the transaction will be aborted upon return anyway. Or in the case - * of unsupported_scv SIGILL fault, the return state does not much - * matter because it's an edge case. - */ - if (IS_ENABLED(CONFIG_PPC_TRANSACTIONAL_MEM) && - unlikely(MSR_TM_TRANSACTIONAL(regs->msr))) - set_bits(_TIF_RESTOREALL, ¤t_thread_info()->flags); - - /* - * If the system call was made with a transaction active, doom it and - * return without performing the system call. Unless it was an - * unsupported scv vector, in which case it's treated like an illegal - * instruction. - */ -#ifdef CONFIG_PPC_TRANSACTIONAL_MEM - if (unlikely(MSR_TM_TRANSACTIONAL(regs->msr)) && - !trap_is_unsupported_scv(regs)) { - /* Enable TM in the kernel, and disable EE (for scv) */ - hard_irq_disable(); - mtmsr(mfmsr() | MSR_TM); - - /* tabort, this dooms the transaction, nothing else */ - asm volatile(".long 0x7c00071d | ((%0) << 16)" - :: "r"(TM_CAUSE_SYSCALL|TM_CAUSE_PERSISTENT)); - - /* - * Userspace will never see the return value. Execution will - * resume after the tbegin. of the aborted transaction with the - * checkpointed register state. A context switch could occur - * or signal delivered to the process before resuming the - * doomed transaction context, but that should all be handled - * as expected. - */ - return -ENOSYS; - } -#endif // CONFIG_PPC_TRANSACTIONAL_MEM - - local_irq_enable(); - - if (unlikely(read_thread_flags() & _TIF_SYSCALL_DOTRACE)) { - if (unlikely(trap_is_unsupported_scv(regs))) { - /* Unsupported scv vector */ - _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); - return regs->gpr[3]; - } - /* - * We use the return value of do_syscall_trace_enter() as the - * syscall number. If the syscall was rejected for any reason - * do_syscall_trace_enter() returns an invalid syscall number - * and the test against NR_syscalls will fail and the return - * value to be used is in regs->gpr[3]. - */ - r0 = do_syscall_trace_enter(regs); - if (unlikely(r0 >= NR_syscalls)) - return regs->gpr[3]; - - } else if (unlikely(r0 >= NR_syscalls)) { + if (unlikely(r0 >= NR_syscalls)) { if (unlikely(trap_is_unsupported_scv(regs))) { /* Unsupported scv vector */ _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 6b3dd6decdf9..329c1690b5ed 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -5,6 +5,7 @@ #include <linux/percpu.h> #include <linux/init.h> #include <linux/sched.h> +#include <linux/sysfs.h> #include <linux/export.h> #include <linux/nodemask.h> #include <linux/cpumask.h> @@ -63,7 +64,7 @@ static ssize_t show_smt_snooze_delay(struct device *dev, { pr_warn_once("%s (%d) read from unsupported smt_snooze_delay\n", current->comm, current->pid); - return sprintf(buf, "100\n"); + return sysfs_emit(buf, "100\n"); } static DEVICE_ATTR(smt_snooze_delay, 0644, show_smt_snooze_delay, @@ -100,7 +101,7 @@ static ssize_t show_##NAME(struct device *dev, \ struct cpu *cpu = container_of(dev, struct cpu, dev); \ unsigned long val; \ smp_call_function_single(cpu->dev.id, read_##NAME, &val, 1); \ - return sprintf(buf, "%lx\n", val); \ + return sysfs_emit(buf, "%lx\n", val); \ } \ static ssize_t __used \ store_##NAME(struct device *dev, struct device_attribute *attr, \ @@ -183,7 +184,7 @@ static void add_write_permission_dev_attr(struct device_attribute *attr) static ssize_t show_dscr_default(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%lx\n", dscr_default); + return sysfs_emit(buf, "%lx\n", dscr_default); } /** @@ -272,7 +273,7 @@ static ssize_t show_pw20_state(struct device *dev, value &= PWRMGTCR0_PW20_WAIT; - return sprintf(buf, "%u\n", value ? 1 : 0); + return sysfs_emit(buf, "%u\n", value ? 1 : 0); } static void do_store_pw20_state(void *val) @@ -337,7 +338,7 @@ static ssize_t show_pw20_wait_time(struct device *dev, time = pw20_wt; } - return sprintf(buf, "%llu\n", time > 0 ? time : 0); + return sysfs_emit(buf, "%llu\n", time > 0 ? time : 0); } static void set_pw20_wait_entry_bit(void *val) @@ -394,7 +395,7 @@ static ssize_t show_altivec_idle(struct device *dev, value &= PWRMGTCR0_AV_IDLE_PD_EN; - return sprintf(buf, "%u\n", value ? 1 : 0); + return sysfs_emit(buf, "%u\n", value ? 1 : 0); } static void do_store_altivec_idle(void *val) @@ -459,7 +460,7 @@ static ssize_t show_altivec_idle_wait_time(struct device *dev, time = altivec_idle_wt; } - return sprintf(buf, "%llu\n", time > 0 ? time : 0); + return sysfs_emit(buf, "%llu\n", time > 0 ? time : 0); } static void set_altivec_idle_wait_entry_bit(void *val) @@ -746,7 +747,7 @@ static struct device_attribute pa6t_attrs[] = { #ifdef CONFIG_PPC_SVM static ssize_t show_svm(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", is_secure_guest()); + return sysfs_emit(buf, "%u\n", is_secure_guest()); } static DEVICE_ATTR(svm, 0444, show_svm, NULL); @@ -780,7 +781,7 @@ static ssize_t idle_purr_show(struct device *dev, u64 val; smp_call_function_single(cpu->dev.id, read_idle_purr, &val, 1); - return sprintf(buf, "%llx\n", val); + return sysfs_emit(buf, "%llx\n", val); } static DEVICE_ATTR(idle_purr, 0400, idle_purr_show, NULL); @@ -810,7 +811,7 @@ static ssize_t idle_spurr_show(struct device *dev, u64 val; smp_call_function_single(cpu->dev.id, read_idle_spurr, &val, 1); - return sprintf(buf, "%llx\n", val); + return sysfs_emit(buf, "%llx\n", val); } static DEVICE_ATTR(idle_spurr, 0400, idle_spurr_show, NULL); @@ -1143,7 +1144,7 @@ static ssize_t show_physical_id(struct device *dev, { struct cpu *cpu = container_of(dev, struct cpu, dev); - return sprintf(buf, "%d\n", get_hard_smp_processor_id(cpu->dev.id)); + return sysfs_emit(buf, "%d\n", get_hard_smp_processor_id(cpu->dev.id)); } static DEVICE_ATTR(physical_id, 0444, show_physical_id, NULL); diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index cb8e9357383e..629f2a2d4780 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1956,7 +1956,7 @@ DEFINE_INTERRUPT_HANDLER_RAW(performance_monitor_exception) * prevent hash faults on user addresses when reading callchains (and * looks better from an irq tracing perspective). */ - if (IS_ENABLED(CONFIG_PPC64) && unlikely(arch_irq_disabled_regs(regs))) + if (IS_ENABLED(CONFIG_PPC64) && unlikely(regs_irqs_disabled(regs))) performance_monitor_exception_nmi(regs); else performance_monitor_exception_async(regs); diff --git a/arch/powerpc/kernel/watchdog.c b/arch/powerpc/kernel/watchdog.c index 764001deb060..c40c69368476 100644 --- a/arch/powerpc/kernel/watchdog.c +++ b/arch/powerpc/kernel/watchdog.c @@ -376,7 +376,7 @@ DEFINE_INTERRUPT_HANDLER_NMI(soft_nmi_interrupt) u64 tb; /* should only arrive from kernel, with irqs disabled */ - WARN_ON_ONCE(!arch_irq_disabled_regs(regs)); + WARN_ON_ONCE(!regs_irqs_disabled(regs)); if (!cpumask_test_cpu(cpu, &wd_cpus_enabled)) return 0; diff --git a/arch/powerpc/kexec/core_64.c b/arch/powerpc/kexec/core_64.c index 825ab8a88f18..58c13a59b93b 100644 --- a/arch/powerpc/kexec/core_64.c +++ b/arch/powerpc/kexec/core_64.c @@ -169,7 +169,7 @@ static void kexec_prepare_cpus_wait(int wait_state) int my_cpu, i, notified=-1; hw_breakpoint_disable(); - my_cpu = get_cpu(); + my_cpu = raw_smp_processor_id(); /* Make sure each CPU has at least made it to the state we need. * * FIXME: There is a (slim) chance of a problem if not all of the CPUs @@ -267,8 +267,6 @@ static void kexec_prepare_cpus(void) /* after we tell the others to go down */ if (ppc_md.kexec_cpu_down) ppc_md.kexec_cpu_down(0, 0); - - put_cpu(); } #else /* ! SMP */ diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c index 9dc5889d6ecb..e4fcf929cb33 100644 --- a/arch/powerpc/mm/book3s64/hash_utils.c +++ b/arch/powerpc/mm/book3s64/hash_utils.c @@ -1299,9 +1299,7 @@ static void stress_hpt_timer_fn(struct timer_list *timer) if (!firmware_has_feature(FW_FEATURE_LPAR)) tlbiel_all(); - next_cpu = cpumask_next(raw_smp_processor_id(), cpu_online_mask); - if (next_cpu >= nr_cpu_ids) - next_cpu = cpumask_first(cpu_online_mask); + next_cpu = cpumask_next_wrap(raw_smp_processor_id(), cpu_online_mask); stress_hpt_timer.expires = jiffies + msecs_to_jiffies(10); add_timer_on(&stress_hpt_timer, next_cpu); } diff --git a/arch/powerpc/net/Makefile b/arch/powerpc/net/Makefile index 8e60af32e51e..7f9339bacef1 100644 --- a/arch/powerpc/net/Makefile +++ b/arch/powerpc/net/Makefile @@ -3,3 +3,7 @@ # Arch-specific network modules # obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o bpf_jit_comp$(BITS).o + +ifdef CONFIG_PPC64 +obj-$(CONFIG_BPF_JIT) += bpf_timed_may_goto.o +endif diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 6351a187ca61..d4a17e18c9fb 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -517,6 +517,11 @@ bool bpf_jit_supports_subprog_tailcalls(void) return IS_ENABLED(CONFIG_PPC64); } +bool bpf_jit_supports_timed_may_goto(void) +{ + return IS_ENABLED(CONFIG_PPC64); +} + bool bpf_jit_supports_kfunc_call(void) { return IS_ENABLED(CONFIG_PPC64); diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index db364d9083e7..dab106cae22b 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -451,10 +451,28 @@ void arch_bpf_stack_walk(bool (*consume_fn)(void *, u64, u64, u64), void *cookie } } +static int bpf_jit_emit_func_call(u32 *image, struct codegen_context *ctx, u64 func_addr, int reg) +{ + long reladdr = func_addr - kernel_toc_addr(); + + if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { + pr_err("eBPF: address of %ps out of range of kernel_toc.\n", (void *)func_addr); + return -ERANGE; + } + + EMIT(PPC_RAW_ADDIS(reg, _R2, PPC_HA(reladdr))); + EMIT(PPC_RAW_ADDI(reg, reg, PPC_LO(reladdr))); + EMIT(PPC_RAW_MTCTR(reg)); + EMIT(PPC_RAW_BCTRL()); + + return 0; +} + int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context *ctx, u64 func) { unsigned long func_addr = func ? ppc_function_entry((void *)func) : 0; - long reladdr; + long __maybe_unused reladdr; + int ret; /* bpf to bpf call, func is not known in the initial pass. Emit 5 nops as a placeholder */ if (!func) { @@ -507,16 +525,9 @@ int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context * EMIT(PPC_RAW_BCTRL()); #else if (core_kernel_text(func_addr)) { - reladdr = func_addr - kernel_toc_addr(); - if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { - pr_err("eBPF: address of %ps out of range of kernel_toc.\n", (void *)func); - return -ERANGE; - } - - EMIT(PPC_RAW_ADDIS(_R12, _R2, PPC_HA(reladdr))); - EMIT(PPC_RAW_ADDI(_R12, _R12, PPC_LO(reladdr))); - EMIT(PPC_RAW_MTCTR(_R12)); - EMIT(PPC_RAW_BCTRL()); + ret = bpf_jit_emit_func_call(image, ctx, func_addr, _R12); + if (ret) + return ret; } else { if (IS_ENABLED(CONFIG_PPC64_ELF_ABI_V1)) { /* func points to the function descriptor */ @@ -1755,6 +1766,35 @@ emit_clear: if (ret < 0) return ret; + /* + * Call to arch_bpf_timed_may_goto() is emitted by the + * verifier and called with custom calling convention with + * first argument and return value in BPF_REG_AX (_R12). + * + * The generic helper or bpf function call emission path + * may use the same scratch register as BPF_REG_AX to + * materialize the target address. This would clobber AX + * and break timed may_goto semantics. + * + * Emit a minimal indirect call sequence here using a temp + * register and skip the normal post-call return-value move. + */ + + if (func_addr == (u64)arch_bpf_timed_may_goto) { + ret = 0; + if (!IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) + ret = bpf_jit_emit_func_call(image, ctx, func_addr, + tmp1_reg); + + if (ret || IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) { + PPC_LI_ADDR(tmp1_reg, func_addr); + EMIT(PPC_RAW_MTCTR(tmp1_reg)); + EMIT(PPC_RAW_BCTRL()); + } + + break; + } + /* Take care of powerpc ABI requirements before kfunc call */ if (insn[i].src_reg == BPF_PSEUDO_KFUNC_CALL) { if (prepare_for_kfunc_call(fp, image, ctx, &insn[i])) diff --git a/arch/powerpc/net/bpf_timed_may_goto.S b/arch/powerpc/net/bpf_timed_may_goto.S new file mode 100644 index 000000000000..6fd8b1c9f4ac --- /dev/null +++ b/arch/powerpc/net/bpf_timed_may_goto.S @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2025 IBM Corporation, Saket Kumar Bhaskar <skb99@linux.ibm.com> */ + +#include <linux/linkage.h> +#include <asm/ppc_asm.h> + +/* + * arch_bpf_timed_may_goto() trampoline for powerpc64 + * + * Custom BPF convention (verifier/JIT): + * - input: stack offset in BPF_REG_AX (r12) + * - output: updated count in BPF_REG_AX (r12) + * + * Call bpf_check_timed_may_goto(ptr) with normal powerpc64 ABI: + * - r3 = ptr, return in r3 + * + * Preserve BPF regs R0-R5 (mapping: r8, r3-r7). + */ + +SYM_FUNC_START(arch_bpf_timed_may_goto) + /* Prologue: save LR, allocate frame */ + mflr r0 + std r0, 16(r1) + stdu r1, -112(r1) + + /* Save BPF registers R0 - R5 (r8, r3-r7) */ + std r3, 32(r1) + std r4, 40(r1) + std r5, 48(r1) + std r6, 56(r1) + std r7, 64(r1) + std r8, 72(r1) + + /* + * r3 = BPF_REG_FP + BPF_REG_AX + * BPF_REG_FP is r31; BPF_REG_AX is r12 (stack offset in bytes). + */ + add r3, r31, r12 + bl bpf_check_timed_may_goto + + /* Put return value back into AX */ + mr r12, r3 + + /* Restore BPF registers R0 - R5 (r8, r3-r7) */ + ld r3, 32(r1) + ld r4, 40(r1) + ld r5, 48(r1) + ld r6, 56(r1) + ld r7, 64(r1) + ld r8, 72(r1) + + /* Epilogue: pop frame, restore LR, return */ + addi r1, r1, 112 + ld r0, 16(r1) + mtlr r0 + blr +SYM_FUNC_END(arch_bpf_timed_may_goto) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 2e6adf5b95c4..720b1a500922 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -10,6 +10,7 @@ #include <linux/perf_event.h> #include <linux/percpu.h> #include <linux/hardirq.h> +#include <linux/sysfs.h> #include <linux/uaccess.h> #include <asm/reg.h> #include <asm/pmc.h> @@ -2204,7 +2205,7 @@ ssize_t power_events_sysfs_show(struct device *dev, pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); - return sprintf(page, "event=0x%02llx\n", pmu_attr->id); + return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id); } static struct pmu power_pmu = { @@ -2483,7 +2484,7 @@ static void __perf_event_interrupt(struct pt_regs *regs) * will trigger a PMI after waking up from idle. Since counter values are _not_ * saved/restored in idle path, can lead to below "Can't find PMC" message. */ - if (unlikely(!found) && !arch_irq_disabled_regs(regs)) + if (unlikely(!found) && !regs_irqs_disabled(regs)) printk_ratelimited(KERN_WARNING "Can't find PMC that caused IRQ\n"); /* diff --git a/arch/powerpc/perf/core-fsl-emb.c b/arch/powerpc/perf/core-fsl-emb.c index 7120ab20cbfe..02b5dd74c187 100644 --- a/arch/powerpc/perf/core-fsl-emb.c +++ b/arch/powerpc/perf/core-fsl-emb.c @@ -366,9 +366,10 @@ static void fsl_emb_pmu_del(struct perf_event *event, int flags) cpuhw->n_events--; + put_cpu_var(cpu_hw_events); + out: perf_pmu_enable(event->pmu); - put_cpu_var(cpu_hw_events); } static void fsl_emb_pmu_start(struct perf_event *event, int ef_flags) diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 243c0a1c8cda..abb4cfb11fcc 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -12,6 +12,7 @@ #include <linux/rbtree.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/vmalloc.h> #include <asm/cputhreads.h> @@ -434,19 +435,19 @@ static ssize_t cpumask_show(struct device *dev, static ssize_t sockets_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", phys_sockets); + return sysfs_emit(buf, "%d\n", phys_sockets); } static ssize_t chipspersocket_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", phys_chipspersocket); + return sysfs_emit(buf, "%d\n", phys_chipspersocket); } static ssize_t coresperchip_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", phys_coresperchip); + return sysfs_emit(buf, "%d\n", phys_coresperchip); } static struct attribute *device_str_attr_create_(char *name, char *str) @@ -1061,7 +1062,7 @@ e_free: static ssize_t domains_show(struct device *dev, struct device_attribute *attr, char *page) { - int d, n, count = 0; + int d, count = 0; const char *str; for (d = 0; d < HV_PERF_DOMAIN_MAX; d++) { @@ -1069,12 +1070,7 @@ static ssize_t domains_show(struct device *dev, struct device_attribute *attr, if (!str) continue; - n = sprintf(page, "%d: %s\n", d, str); - if (n < 0) - break; - - count += n; - page += n; + count += sysfs_emit_at(page, count, "%d: %s\n", d, str); } return count; } @@ -1095,7 +1091,7 @@ static ssize_t _name##_show(struct device *dev, \ ret = -EIO; \ goto e_free; \ } \ - ret = sprintf(buf, _fmt, _expr); \ + ret = sysfs_emit(buf, _fmt, _expr); \ e_free: \ kmem_cache_free(hv_page_cache, page); \ return ret; \ diff --git a/arch/powerpc/perf/hv-gpci.c b/arch/powerpc/perf/hv-gpci.c index 10c82cf8f5b3..7269273d3aa8 100644 --- a/arch/powerpc/perf/hv-gpci.c +++ b/arch/powerpc/perf/hv-gpci.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/perf_event.h> +#include <linux/sysfs.h> #include <asm/firmware.h> #include <asm/hvcall.h> #include <asm/io.h> @@ -85,7 +86,7 @@ static ssize_t _name##_show(struct device *dev, \ if (hret) \ return -EIO; \ \ - return sprintf(page, _format, caps._name); \ + return sysfs_emit(page, _format, caps._name); \ } \ static struct device_attribute hv_caps_attr_##_name = __ATTR_RO(_name) @@ -93,7 +94,7 @@ static ssize_t kernel_version_show(struct device *dev, struct device_attribute *attr, char *page) { - return sprintf(page, "0x%x\n", COUNTER_INFO_VERSION_CURRENT); + return sysfs_emit(page, "0x%x\n", COUNTER_INFO_VERSION_CURRENT); } static ssize_t cpumask_show(struct device *dev, diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c index c1563b4eaa94..e3822f36c419 100644 --- a/arch/powerpc/perf/imc-pmu.c +++ b/arch/powerpc/perf/imc-pmu.c @@ -421,7 +421,6 @@ static int ppc_nest_imc_cpu_offline(unsigned int cpu) static int ppc_nest_imc_cpu_online(unsigned int cpu) { const struct cpumask *l_cpumask; - static struct cpumask tmp_mask; int res; /* Get the cpumask of this node */ @@ -431,7 +430,7 @@ static int ppc_nest_imc_cpu_online(unsigned int cpu) * If this is not the first online CPU on this node, then * just return. */ - if (cpumask_and(&tmp_mask, l_cpumask, &nest_imc_cpumask)) + if (cpumask_intersects(l_cpumask, &nest_imc_cpumask)) return 0; /* @@ -647,14 +646,13 @@ static bool is_core_imc_mem_inited(int cpu) static int ppc_core_imc_cpu_online(unsigned int cpu) { const struct cpumask *l_cpumask; - static struct cpumask tmp_mask; int ret = 0; /* Get the cpumask for this core */ l_cpumask = cpu_sibling_mask(cpu); /* If a cpu for this core is already set, then, don't do anything */ - if (cpumask_and(&tmp_mask, l_cpumask, &core_imc_cpumask)) + if (cpumask_intersects(l_cpumask, &core_imc_cpumask)) return 0; if (!is_core_imc_mem_inited(cpu)) { diff --git a/arch/powerpc/perf/kvm-hv-pmu.c b/arch/powerpc/perf/kvm-hv-pmu.c index ae264c9080ef..aa72b96b5a8c 100644 --- a/arch/powerpc/perf/kvm-hv-pmu.c +++ b/arch/powerpc/perf/kvm-hv-pmu.c @@ -16,6 +16,7 @@ #include <linux/perf_event.h> #include <linux/spinlock_types.h> #include <linux/spinlock.h> +#include <linux/sysfs.h> #include <asm/types.h> #include <asm/kvm_ppc.h> @@ -48,7 +49,7 @@ static ssize_t kvmppc_events_sysfs_show(struct device *dev, struct perf_pmu_events_attr *pmu_attr; pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); - return sprintf(page, "event=0x%02llx\n", pmu_attr->id); + return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id); } /* Holds the hostwide stats */ diff --git a/arch/powerpc/perf/vpa-pmu.c b/arch/powerpc/perf/vpa-pmu.c index 840733468959..bff4cfab7b94 100644 --- a/arch/powerpc/perf/vpa-pmu.c +++ b/arch/powerpc/perf/vpa-pmu.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/perf_event.h> +#include <linux/sysfs.h> #include <asm/kvm_ppc.h> #include <asm/kvm_book3s_64.h> @@ -26,7 +27,7 @@ static ssize_t vpa_pmu_events_sysfs_show(struct device *dev, pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); - return sprintf(page, "event=0x%02llx\n", pmu_attr->id); + return sysfs_emit(page, "event=0x%02llx\n", pmu_attr->id); } #define VPA_PMU_EVENT_ATTR(_name, _id) \ diff --git a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c index 9b693594a5f7..c3fbec1f1d24 100644 --- a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c +++ b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c @@ -15,6 +15,7 @@ #include <linux/i2c.h> #include <linux/gpio/driver.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/kthread.h> #include <linux/property.h> #include <linux/reboot.h> @@ -77,7 +78,7 @@ static ssize_t show_status(struct device *d, return -ENODEV; mcu->reg_ctrl = ret; - return sprintf(buf, "%02x\n", ret); + return sysfs_emit(buf, "%02x\n", ret); } static DEVICE_ATTR(status, 0444, show_status, NULL); diff --git a/arch/powerpc/platforms/8xx/cpm1.c b/arch/powerpc/platforms/8xx/cpm1.c index f00734f0590c..b31376bf6778 100644 --- a/arch/powerpc/platforms/8xx/cpm1.c +++ b/arch/powerpc/platforms/8xx/cpm1.c @@ -472,6 +472,18 @@ static int cpm1_gpio16_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int cpm1_gpio16_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(gc); + struct cpm_ioport16 __iomem *iop = cpm1_gc->regs; + u16 pin_mask = 1 << (15 - gpio); + + if (in_be16(&iop->dir) & pin_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + int cpm1_gpiochip_add16(struct device *dev) { struct device_node *np = dev->of_node; @@ -498,6 +510,7 @@ int cpm1_gpiochip_add16(struct device *dev) gc->ngpio = 16; gc->direction_input = cpm1_gpio16_dir_in; gc->direction_output = cpm1_gpio16_dir_out; + gc->get_direction = cpm1_gpio16_get_direction; gc->get = cpm1_gpio16_get; gc->set = cpm1_gpio16_set; gc->to_irq = cpm1_gpio16_to_irq; @@ -604,6 +617,18 @@ static int cpm1_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int cpm1_gpio32_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(gc); + struct cpm_ioport32b __iomem *iop = cpm1_gc->regs; + u32 pin_mask = 1 << (31 - gpio); + + if (in_be32(&iop->dir) & pin_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + int cpm1_gpiochip_add32(struct device *dev) { struct device_node *np = dev->of_node; @@ -621,6 +646,7 @@ int cpm1_gpiochip_add32(struct device *dev) gc->ngpio = 32; gc->direction_input = cpm1_gpio32_dir_in; gc->direction_output = cpm1_gpio32_dir_out; + gc->get_direction = cpm1_gpio32_get_direction; gc->get = cpm1_gpio32_get; gc->set = cpm1_gpio32_set; gc->parent = dev; diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 0ec7b3bdda56..8452153d4650 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -20,6 +20,7 @@ #include <linux/mutex.h> #include <linux/linux_logo.h> #include <linux/syscore_ops.h> +#include <linux/sysfs.h> #include <asm/spu.h> #include <asm/spu_priv1.h> #include <asm/spu_csa.h> @@ -638,8 +639,8 @@ static ssize_t spu_stat_show(struct device *dev, { struct spu *spu = container_of(dev, struct spu, dev); - return sprintf(buf, "%s %llu %llu %llu %llu " - "%llu %llu %llu %llu %llu %llu %llu %llu\n", + return sysfs_emit(buf, + "%s %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", spu_state_names[spu->stats.util_state], spu_acct_time(spu, SPU_UTIL_USER), spu_acct_time(spu, SPU_UTIL_SYSTEM), diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c index 6cd461f82968..33103a98cfd5 100644 --- a/arch/powerpc/platforms/powernv/idle.c +++ b/arch/powerpc/platforms/powernv/idle.c @@ -8,6 +8,7 @@ #include <linux/types.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/of.h> #include <linux/device.h> #include <linux/cpu.h> @@ -171,7 +172,7 @@ static u8 fastsleep_workaround_applyonce; static ssize_t show_fastsleep_workaround_applyonce(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", fastsleep_workaround_applyonce); + return sysfs_emit(buf, "%u\n", fastsleep_workaround_applyonce); } static ssize_t store_fastsleep_workaround_applyonce(struct device *dev, diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c index 2e4bffa74163..0586821d7af4 100644 --- a/arch/powerpc/platforms/powernv/opal-dump.c +++ b/arch/powerpc/platforms/powernv/opal-dump.c @@ -8,6 +8,7 @@ #include <linux/kobject.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> #include <linux/delay.h> @@ -40,7 +41,7 @@ static ssize_t dump_id_show(struct dump_obj *dump_obj, struct dump_attribute *attr, char *buf) { - return sprintf(buf, "0x%x\n", dump_obj->id); + return sysfs_emit(buf, "0x%x\n", dump_obj->id); } static const char* dump_type_to_string(uint32_t type) @@ -58,15 +59,15 @@ static ssize_t dump_type_show(struct dump_obj *dump_obj, char *buf) { - return sprintf(buf, "0x%x %s\n", dump_obj->type, - dump_type_to_string(dump_obj->type)); + return sysfs_emit(buf, "0x%x %s\n", dump_obj->type, + dump_type_to_string(dump_obj->type)); } static ssize_t dump_ack_show(struct dump_obj *dump_obj, struct dump_attribute *attr, char *buf) { - return sprintf(buf, "ack - acknowledge dump\n"); + return sysfs_emit(buf, "ack - acknowledge dump\n"); } /* @@ -114,7 +115,7 @@ static ssize_t init_dump_show(struct dump_obj *dump_obj, struct dump_attribute *attr, char *buf) { - return sprintf(buf, "1 - initiate Service Processor(FSP) dump\n"); + return sysfs_emit(buf, "1 - initiate Service Processor(FSP) dump\n"); } static int64_t dump_fips_init(uint8_t type) diff --git a/arch/powerpc/platforms/powernv/opal-elog.c b/arch/powerpc/platforms/powernv/opal-elog.c index 2b8331922ab9..6cacd3fd3cd5 100644 --- a/arch/powerpc/platforms/powernv/opal-elog.c +++ b/arch/powerpc/platforms/powernv/opal-elog.c @@ -40,7 +40,7 @@ static ssize_t elog_id_show(struct elog_obj *elog_obj, struct elog_attribute *attr, char *buf) { - return sprintf(buf, "0x%llx\n", elog_obj->id); + return sysfs_emit(buf, "0x%llx\n", elog_obj->id); } static const char *elog_type_to_string(uint64_t type) @@ -55,16 +55,15 @@ static ssize_t elog_type_show(struct elog_obj *elog_obj, struct elog_attribute *attr, char *buf) { - return sprintf(buf, "0x%llx %s\n", - elog_obj->type, - elog_type_to_string(elog_obj->type)); + return sysfs_emit(buf, "0x%llx %s\n", elog_obj->type, + elog_type_to_string(elog_obj->type)); } static ssize_t elog_ack_show(struct elog_obj *elog_obj, struct elog_attribute *attr, char *buf) { - return sprintf(buf, "ack - acknowledge log message\n"); + return sysfs_emit(buf, "ack - acknowledge log message\n"); } static ssize_t elog_ack_store(struct elog_obj *elog_obj, diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c index a3f7a2928767..5ca5f6329a2d 100644 --- a/arch/powerpc/platforms/powernv/opal-flash.c +++ b/arch/powerpc/platforms/powernv/opal-flash.c @@ -238,7 +238,7 @@ static ssize_t manage_show(struct kobject *kobj, struct manage_flash_t *const args_buf = &manage_flash_data; int rc; - rc = sprintf(buf, "%d\n", args_buf->status); + rc = sysfs_emit(buf, "%d\n", args_buf->status); /* Set status to default*/ args_buf->status = FLASH_NO_OP; return rc; @@ -321,7 +321,7 @@ static ssize_t update_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct update_flash_t *const args_buf = &update_flash_data; - return sprintf(buf, "%d\n", args_buf->status); + return sysfs_emit(buf, "%d\n", args_buf->status); } /* diff --git a/arch/powerpc/platforms/powernv/opal-powercap.c b/arch/powerpc/platforms/powernv/opal-powercap.c index 9bb73cb42a65..bf18b333281e 100644 --- a/arch/powerpc/platforms/powernv/opal-powercap.c +++ b/arch/powerpc/platforms/powernv/opal-powercap.c @@ -10,6 +10,7 @@ #include <linux/of.h> #include <linux/kobject.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <asm/opal.h> @@ -56,16 +57,11 @@ static ssize_t powercap_show(struct kobject *kobj, struct kobj_attribute *attr, goto out; } ret = opal_error_code(opal_get_async_rc(msg)); - if (!ret) { - ret = sprintf(buf, "%u\n", be32_to_cpu(pcap)); - if (ret < 0) - ret = -EIO; - } + if (!ret) + ret = sysfs_emit(buf, "%u\n", be32_to_cpu(pcap)); break; case OPAL_SUCCESS: - ret = sprintf(buf, "%u\n", be32_to_cpu(pcap)); - if (ret < 0) - ret = -EIO; + ret = sysfs_emit(buf, "%u\n", be32_to_cpu(pcap)); break; default: ret = opal_error_code(ret); diff --git a/arch/powerpc/platforms/powernv/opal-psr.c b/arch/powerpc/platforms/powernv/opal-psr.c index 24d0a894d965..19228181cb6f 100644 --- a/arch/powerpc/platforms/powernv/opal-psr.c +++ b/arch/powerpc/platforms/powernv/opal-psr.c @@ -10,6 +10,7 @@ #include <linux/of.h> #include <linux/kobject.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <asm/opal.h> @@ -50,16 +51,11 @@ static ssize_t psr_show(struct kobject *kobj, struct kobj_attribute *attr, goto out; } ret = opal_error_code(opal_get_async_rc(msg)); - if (!ret) { - ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); - if (ret < 0) - ret = -EIO; - } + if (!ret) + ret = sysfs_emit(buf, "%u\n", be32_to_cpu(psr)); break; case OPAL_SUCCESS: - ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); - if (ret < 0) - ret = -EIO; + ret = sysfs_emit(buf, "%u\n", be32_to_cpu(psr)); break; default: ret = opal_error_code(ret); diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 4dbb47ddbdcc..06ed5e2aa265 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -396,7 +396,8 @@ static void pnv_kexec_wait_secondaries_down(void) { int my_cpu, i, notified = -1; - my_cpu = get_cpu(); + /* Called with interrupts disabled, so the CPU is pinned. */ + my_cpu = raw_smp_processor_id(); for_each_online_cpu(i) { uint8_t status; diff --git a/arch/powerpc/platforms/powernv/subcore.c b/arch/powerpc/platforms/powernv/subcore.c index 393e747541fb..f7668ef1ac1f 100644 --- a/arch/powerpc/platforms/powernv/subcore.c +++ b/arch/powerpc/platforms/powernv/subcore.c @@ -12,6 +12,7 @@ #include <linux/gfp.h> #include <linux/smp.h> #include <linux/stop_machine.h> +#include <linux/sysfs.h> #include <asm/cputhreads.h> #include <asm/cpuidle.h> @@ -409,7 +410,7 @@ out: static ssize_t show_subcores_per_core(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%x\n", subcores_per_core); + return sysfs_emit(buf, "%x\n", subcores_per_core); } static DEVICE_ATTR(subcores_per_core, 0644, diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c index 150c09b58ae8..ca2608a70f4d 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/fs.h> #include <linux/root_dev.h> +#include <linux/sysfs.h> #include <linux/console.h> #include <linux/export.h> #include <linux/memblock.h> @@ -183,7 +184,7 @@ static int ps3_set_dabr(unsigned long dabr, unsigned long dabrx) static ssize_t ps3_fw_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%s", ps3_firmware_version_str); + return sysfs_emit(buf, "%s\n", ps3_firmware_version_str); } static int __init ps3_setup_sysfs(void) diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index 8d83df12430f..38e22125b96f 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -18,6 +18,7 @@ #include <linux/sched.h> #include <linux/stringify.h> #include <linux/swap.h> +#include <linux/sysfs.h> #include <linux/device.h> #include <linux/balloon.h> #include <asm/firmware.h> @@ -333,7 +334,7 @@ static int cmm_thread(void *dummy) struct device_attribute *attr, \ char *buf) \ { \ - return sprintf(buf, format, ##args); \ + return sysfs_emit(buf, format, ##args); \ } \ static DEVICE_ATTR(name, 0444, show_##name, NULL) @@ -343,7 +344,7 @@ CMM_SHOW(loaned_target_kb, "%lu\n", PAGES2KB(loaned_pages_target)); static ssize_t show_oom_pages(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", PAGES2KB(oom_freed_pages)); + return sysfs_emit(buf, "%lu\n", PAGES2KB(oom_freed_pages)); } static ssize_t store_oom_pages(struct device *dev, diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index a7c451c2507d..f4d33b8dffd8 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -14,6 +14,7 @@ #include <linux/spinlock.h> #include <linux/cpu.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/of.h> #include "of_helpers.h" @@ -798,7 +799,7 @@ dlpar_store_out: static ssize_t dlpar_show(const struct class *class, const struct class_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", "memory,cpu,dt"); + return sysfs_emit(buf, "%s\n", "memory,cpu,dt"); } static CLASS_ATTR_RW(dlpar); diff --git a/arch/powerpc/platforms/pseries/ibmebus.c b/arch/powerpc/platforms/pseries/ibmebus.c index cad2deb7e70d..2d0f991da2c8 100644 --- a/arch/powerpc/platforms/pseries/ibmebus.c +++ b/arch/powerpc/platforms/pseries/ibmebus.c @@ -46,6 +46,7 @@ #include <linux/of.h> #include <linux/slab.h> #include <linux/stat.h> +#include <linux/sysfs.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <asm/ibmebus.h> @@ -399,7 +400,7 @@ static ssize_t devspec_show(struct device *dev, struct platform_device *ofdev; ofdev = to_platform_device(dev); - return sprintf(buf, "%pOF\n", ofdev->dev.of_node); + return sysfs_emit(buf, "%pOF\n", ofdev->dev.of_node); } static DEVICE_ATTR_RO(devspec); @@ -409,7 +410,7 @@ static ssize_t name_show(struct device *dev, struct platform_device *ofdev; ofdev = to_platform_device(dev); - return sprintf(buf, "%pOFn\n", ofdev->dev.of_node); + return sysfs_emit(buf, "%pOFn\n", ofdev->dev.of_node); } static DEVICE_ATTR_RO(name); diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c index 8821c378bfff..54b7ecf375b5 100644 --- a/arch/powerpc/platforms/pseries/lparcfg.c +++ b/arch/powerpc/platforms/pseries/lparcfg.c @@ -22,6 +22,7 @@ #include <asm/papr-sysparm.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/uaccess.h> #include <linux/hugetlb.h> #include <asm/lppaca.h> @@ -431,17 +432,18 @@ static void parse_system_parameter_string(struct seq_file *m) w_idx = 0; } else if (local_buffer[idx] == '=') { /* code here to replace workbuffer contents - with different keyword strings */ - if (0 == strcmp(workbuffer, "MaxEntCap")) { - strcpy(workbuffer, - "partition_max_entitled_capacity"); - w_idx = strlen(workbuffer); - } - if (0 == strcmp(workbuffer, "MaxPlatProcs")) { - strcpy(workbuffer, - "system_potential_processors"); - w_idx = strlen(workbuffer); - } + * with different keyword strings. Truncation + * by strscpy is deliberately ignored because + * SPLPAR_MAXLENGTH >= maximum string size. + */ + if (!strcmp(workbuffer, "MaxEntCap")) + w_idx = strscpy(workbuffer, + "partition_max_entitled_capacity", + SPLPAR_MAXLENGTH); + if (!strcmp(workbuffer, "MaxPlatProcs")) + w_idx = strscpy(workbuffer, + "system_potential_processors", + SPLPAR_MAXLENGTH); } } kfree(workbuffer); diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c index 63eca4ebb5e5..75da96c08cdd 100644 --- a/arch/powerpc/platforms/pseries/papr_scm.c +++ b/arch/powerpc/platforms/pseries/papr_scm.c @@ -8,6 +8,7 @@ #include <linux/ioport.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/ndctl.h> #include <linux/sched.h> #include <linux/libnvdimm.h> @@ -1062,8 +1063,8 @@ static ssize_t health_bitmap_inject_show(struct device *dev, struct nvdimm *dimm = to_nvdimm(dev); struct papr_scm_priv *p = nvdimm_provider_data(dimm); - return sprintf(buf, "%#llx\n", - READ_ONCE(p->health_bitmap_inject_mask)); + return sysfs_emit(buf, "%#llx\n", + READ_ONCE(p->health_bitmap_inject_mask)); } static DEVICE_ATTR_ADMIN_RO(health_bitmap_inject); diff --git a/arch/powerpc/platforms/pseries/power.c b/arch/powerpc/platforms/pseries/power.c index 3676cb297767..7b9dfe829f25 100644 --- a/arch/powerpc/platforms/pseries/power.c +++ b/arch/powerpc/platforms/pseries/power.c @@ -11,6 +11,7 @@ #include <linux/kobject.h> #include <linux/string.h> +#include <linux/sysfs.h> #include <linux/errno.h> #include <linux/init.h> #include <asm/machdep.h> @@ -22,7 +23,7 @@ unsigned long rtas_poweron_auto; /* default and normal state is 0 */ static ssize_t auto_poweron_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", rtas_poweron_auto); + return sysfs_emit(buf, "%lu\n", rtas_poweron_auto); } static ssize_t auto_poweron_store(struct kobject *kobj, diff --git a/arch/powerpc/platforms/pseries/pseries_energy.c b/arch/powerpc/platforms/pseries/pseries_energy.c index 2c661b798235..fdaf85ecd39b 100644 --- a/arch/powerpc/platforms/pseries/pseries_energy.c +++ b/arch/powerpc/platforms/pseries/pseries_energy.c @@ -12,6 +12,7 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/seq_file.h> +#include <linux/sysfs.h> #include <linux/device.h> #include <linux/cpu.h> #include <linux/of.h> @@ -242,7 +243,7 @@ static ssize_t get_best_energy_data(struct device *dev, if (rc != H_SUCCESS) return -EINVAL; - return sprintf(page, "%lu\n", retbuf[1] >> 32); + return sysfs_emit(page, "%lu\n", retbuf[1] >> 32); } /* Wrapper functions */ diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.c b/arch/powerpc/platforms/pseries/rtas-fadump.c index eceb3289383e..3bb4ac2ab6cc 100644 --- a/arch/powerpc/platforms/pseries/rtas-fadump.c +++ b/arch/powerpc/platforms/pseries/rtas-fadump.c @@ -179,9 +179,42 @@ static u64 rtas_fadump_get_bootmem_min(void) return RTAS_FADUMP_MIN_BOOT_MEM; } +/* + * Helper to make an ibm,configure-kernel-dump RTAS call with a bounded + * busy-wait loop. Returns the RTAS return code on completion, or + * -ETIMEDOUT if firmware keeps returning a busy status beyond + * RTAS_FADUMP_MAX_WAIT_MS milliseconds. + */ +static int rtas_fadump_call(struct fw_dump *fadump_conf, int operation, + void *fdm_ptr, unsigned int fdm_size, + const char *op_name) +{ + unsigned int wait_time, total_wait = 0; + int rc; + + do { + rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, + NULL, operation, fdm_ptr, fdm_size); + wait_time = rtas_busy_delay_time(rc); + if (wait_time) { + pr_debug("Firmware busy during fadump %s, waiting %ums (total %ums)\n", + op_name, wait_time, total_wait); + if (total_wait >= RTAS_FADUMP_MAX_WAIT_MS) { + pr_err("Timed out waiting for firmware to complete fadump %s\n", + op_name); + return -ETIMEDOUT; + } + total_wait += wait_time; + mdelay(wait_time); + } + } while (wait_time); + + return rc; +} + static int rtas_fadump_register(struct fw_dump *fadump_conf) { - unsigned int wait_time, fdm_size; + unsigned int fdm_size; int rc, err = -EIO; /* @@ -192,16 +225,10 @@ static int rtas_fadump_register(struct fw_dump *fadump_conf) fdm_size = sizeof(struct rtas_fadump_section_header); fdm_size += be16_to_cpu(fdm.header.dump_num_sections) * sizeof(struct rtas_fadump_section); - /* TODO: Add upper time limit for the delay */ - do { - rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, - NULL, FADUMP_REGISTER, &fdm, fdm_size); - - wait_time = rtas_busy_delay_time(rc); - if (wait_time) - mdelay(wait_time); - - } while (wait_time); + rc = rtas_fadump_call(fadump_conf, FADUMP_REGISTER, &fdm, fdm_size, + "register"); + if (rc == -ETIMEDOUT) + return -ETIMEDOUT; switch (rc) { case 0: @@ -234,19 +261,12 @@ static int rtas_fadump_register(struct fw_dump *fadump_conf) static int rtas_fadump_unregister(struct fw_dump *fadump_conf) { - unsigned int wait_time; int rc; - /* TODO: Add upper time limit for the delay */ - do { - rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, - NULL, FADUMP_UNREGISTER, &fdm, - sizeof(struct rtas_fadump_mem_struct)); - - wait_time = rtas_busy_delay_time(rc); - if (wait_time) - mdelay(wait_time); - } while (wait_time); + rc = rtas_fadump_call(fadump_conf, FADUMP_UNREGISTER, &fdm, + sizeof(struct rtas_fadump_mem_struct), "unregister"); + if (rc == -ETIMEDOUT) + return -ETIMEDOUT; if (rc) { pr_err("Failed to un-register - unexpected error(%d).\n", rc); @@ -259,19 +279,13 @@ static int rtas_fadump_unregister(struct fw_dump *fadump_conf) static int rtas_fadump_invalidate(struct fw_dump *fadump_conf) { - unsigned int wait_time; int rc; - /* TODO: Add upper time limit for the delay */ - do { - rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, - NULL, FADUMP_INVALIDATE, fdm_active, - sizeof(struct rtas_fadump_mem_struct)); - - wait_time = rtas_busy_delay_time(rc); - if (wait_time) - mdelay(wait_time); - } while (wait_time); + rc = rtas_fadump_call(fadump_conf, FADUMP_INVALIDATE, + (void *)fdm_active, + sizeof(struct rtas_fadump_mem_struct), "invalidate"); + if (rc == -ETIMEDOUT) + return -ETIMEDOUT; if (rc) { pr_err("Failed to invalidate - unexpected error (%d).\n", rc); diff --git a/arch/powerpc/platforms/pseries/rtas-fadump.h b/arch/powerpc/platforms/pseries/rtas-fadump.h index c109abf6befd..65fdab7b5b8d 100644 --- a/arch/powerpc/platforms/pseries/rtas-fadump.h +++ b/arch/powerpc/platforms/pseries/rtas-fadump.h @@ -41,6 +41,12 @@ #define MAX_SECTIONS 10 #define RTAS_FADUMP_MAX_BOOT_MEM_REGS 7 +/* + * Maximum time to wait for firmware to respond to an + * ibm,configure-kernel-dump RTAS call before giving up. + */ +#define RTAS_FADUMP_MAX_WAIT_MS 60000U + /* Kernel Dump section info */ struct rtas_fadump_section { __be32 request_flag; diff --git a/arch/powerpc/platforms/pseries/suspend.c b/arch/powerpc/platforms/pseries/suspend.c index c51db63d3e88..a9928d75624a 100644 --- a/arch/powerpc/platforms/pseries/suspend.c +++ b/arch/powerpc/platforms/pseries/suspend.c @@ -7,6 +7,7 @@ #include <linux/delay.h> #include <linux/suspend.h> #include <linux/stat.h> +#include <linux/sysfs.h> #include <asm/firmware.h> #include <asm/hvcall.h> #include <asm/machdep.h> @@ -121,7 +122,7 @@ static ssize_t show_hibernate(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", KERN_DT_UPDATE); + return sysfs_emit(buf, "%d\n", KERN_DT_UPDATE); } static DEVICE_ATTR(hibernate, 0644, show_hibernate, store_hibernate); diff --git a/arch/powerpc/platforms/pseries/vas-sysfs.c b/arch/powerpc/platforms/pseries/vas-sysfs.c index 4f6fbbb672ae..00c6ffd3ef39 100644 --- a/arch/powerpc/platforms/pseries/vas-sysfs.c +++ b/arch/powerpc/platforms/pseries/vas-sysfs.c @@ -10,6 +10,7 @@ #include <linux/miscdevice.h> #include <linux/kobject.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/mm.h> #include "vas.h" @@ -58,7 +59,7 @@ static ssize_t update_total_credits_store(struct vas_cop_feat_caps *caps, #define sysfs_caps_entry_read(_name) \ static ssize_t _name##_show(struct vas_cop_feat_caps *caps, char *buf) \ { \ - return sprintf(buf, "%d\n", atomic_read(&caps->_name)); \ + return sysfs_emit(buf, "%d\n", atomic_read(&caps->_name)); \ } struct vas_sysfs_entry { diff --git a/arch/powerpc/platforms/pseries/vio.c b/arch/powerpc/platforms/pseries/vio.c index 08e2add48adb..572bdf42335e 100644 --- a/arch/powerpc/platforms/pseries/vio.c +++ b/arch/powerpc/platforms/pseries/vio.c @@ -14,6 +14,7 @@ #include <linux/types.h> #include <linux/delay.h> #include <linux/stat.h> +#include <linux/sysfs.h> #include <linux/device.h> #include <linux/init.h> #include <linux/slab.h> @@ -942,14 +943,14 @@ static ssize_t cmo_##name##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - return sprintf(buf, "%lu\n", to_vio_dev(dev)->cmo.name); \ + return sysfs_emit(buf, "%lu\n", to_vio_dev(dev)->cmo.name); \ } static ssize_t cmo_allocs_failed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct vio_dev *viodev = to_vio_dev(dev); - return sprintf(buf, "%d\n", atomic_read(&viodev->cmo.allocs_failed)); + return sysfs_emit(buf, "%d\n", atomic_read(&viodev->cmo.allocs_failed)); } static ssize_t cmo_allocs_failed_store(struct device *dev, @@ -998,7 +999,7 @@ static DEVICE_ATTR_RW(cmo_allocs_failed); #define viobus_cmo_rd_attr(name) \ static ssize_t cmo_bus_##name##_show(const struct bus_type *bt, char *buf) \ { \ - return sprintf(buf, "%lu\n", vio_cmo.name); \ + return sysfs_emit(buf, "%lu\n", vio_cmo.name); \ } \ static struct bus_attribute bus_attr_cmo_bus_##name = \ __ATTR(cmo_##name, S_IRUGO, cmo_bus_##name##_show, NULL) @@ -1007,7 +1008,7 @@ static struct bus_attribute bus_attr_cmo_bus_##name = \ static ssize_t \ cmo_##name##_##var##_show(const struct bus_type *bt, char *buf) \ { \ - return sprintf(buf, "%lu\n", vio_cmo.name.var); \ + return sysfs_emit(buf, "%lu\n", vio_cmo.name.var); \ } \ static BUS_ATTR_RO(cmo_##name##_##var) @@ -1022,7 +1023,7 @@ viobus_cmo_pool_rd_attr(excess, free); static ssize_t cmo_high_show(const struct bus_type *bt, char *buf) { - return sprintf(buf, "%lu\n", vio_cmo.high); + return sysfs_emit(buf, "%lu\n", vio_cmo.high); } static ssize_t cmo_high_store(const struct bus_type *bt, const char *buf, @@ -1535,7 +1536,7 @@ machine_device_initcall(pseries, vio_device_init); static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", to_vio_dev(dev)->name); + return sysfs_emit(buf, "%s\n", to_vio_dev(dev)->name); } static DEVICE_ATTR_RO(name); @@ -1544,7 +1545,7 @@ static ssize_t devspec_show(struct device *dev, { struct device_node *of_node = dev->of_node; - return sprintf(buf, "%pOF\n", of_node); + return sysfs_emit(buf, "%pOF\n", of_node); } static DEVICE_ATTR_RO(devspec); @@ -1556,17 +1557,13 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, const char *cp; dn = dev->of_node; - if (!dn) { - strcpy(buf, "\n"); - return strlen(buf); - } + if (!dn) + return sysfs_emit(buf, "\n"); cp = of_get_property(dn, "compatible", NULL); - if (!cp) { - strcpy(buf, "\n"); - return strlen(buf); - } + if (!cp) + return sysfs_emit(buf, "\n"); - return sprintf(buf, "vio:T%sS%s\n", vio_dev->type, cp); + return sysfs_emit(buf, "vio:T%sS%s\n", vio_dev->type, cp); } static DEVICE_ATTR_RO(modalias); diff --git a/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c index f9e64f54dc14..f63b89adf9f3 100644 --- a/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c +++ b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c @@ -7,6 +7,7 @@ #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -61,7 +62,7 @@ static ssize_t fsl_timer_wakeup_show(struct device *dev, } mutex_unlock(&sysfs_lock); - return sprintf(buf, "%lld\n", interval); + return sysfs_emit(buf, "%lld\n", interval); } static ssize_t fsl_timer_wakeup_store(struct device *dev, diff --git a/arch/powerpc/sysdev/xive/common.c b/arch/powerpc/sysdev/xive/common.c index c120be73d149..dadd1f46ec93 100644 --- a/arch/powerpc/sysdev/xive/common.c +++ b/arch/powerpc/sysdev/xive/common.c @@ -564,6 +564,7 @@ static int xive_find_target_in_mask(const struct cpumask *mask, return cpu; } + WARN_ONCE(1, "target CPU not found in mask: %*pbl\n", cpumask_pr_args(mask)); return -1; } |
