diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index e3003a241d5b..3660d7478fb9 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -6566,6 +6566,83 @@ KVM_S390_KEYOP_SSKE Sets the storage key for the guest address ``guest_addr`` to the key specified in ``key``, returning the previous value in ``key``. +4.145 KVM_PPC_GET_COMPAT_CAPS +----------------------------- +:Capability: KVM_CAP_PPC_COMPAT_CAPS +:Architectures: powerpc +:Type: vm ioctl +:Parameters: struct kvm_ppc_compat_caps (in/out) +:Returns: 0 on success, negative value on failure + +Errors include: + + ======== ============================================================ + EFAULT if ``struct kvm_ppc_compat_caps`` cannot be read from or + written to userspace + EINVAL if the ``size`` field is smaller than + ``KVM_PPC_COMPAT_CAPS_SIZE_VER0``, if the ``flags`` field + is non-zero, or if the backend fails to retrieve or map + CPU compatibility capabilities + E2BIG if ``size`` exceeds ``PAGE_SIZE`` (pathological input guard), + or if ``size`` is larger than the kernel's struct size and + the unknown trailing bytes are non-zero (new userspace on + old kernel with non-default fields set); in the latter case + the kernel writes back its own struct size into the ``size`` + field so userspace can retry with the correct size + ENOTTY if the backend does not implement the ``get_compat_caps`` + operation (e.g., on non-HV KVM implementations where the + required KVM operations are not available) + ======== ============================================================ + +IBM POWER system server-based processors provide a compatibility mode feature +where an Nth generation processor can operate in modes consistent with earlier +generations such as (N-1) and (N-2). + +This ioctl provides userspace with information about the CPU compatibility modes +supported by the current host processor for booting the nested KVM guests on +KVM on PowerNV (nested API v1) and KVM on PowerVM (nested API v2) platforms. + +:: + + struct kvm_ppc_compat_caps { + __u64 size; /* Size of this structure */ + __u64 flags; /* Reserved for future use, must be 0 */ + __u64 compat_capabilities; /* Capabilities supported by the host */ + }; + +Before calling this ioctl, userspace must set the ``size`` field to +``sizeof(struct kvm_ppc_compat_caps)`` and zero the ``flags`` field. +The kernel rejects non-zero ``flags`` with ``-EINVAL`` to prevent +uninitialized stack values from being silently accepted, keeping the +field available for future use without ABI ambiguity. + +The ioctl uses ``copy_struct_from_user()`` and ``copy_struct_to_user()`` +to support extensible versioning. + +``KVM_PPC_COMPAT_CAPS_SIZE_VER0`` (24) is a frozen constant marking the +size of the initial struct version. + +The ``compat_capabilities`` bit field describes the processor compatibility +modes supported by the host. The following bits indicate support for specific +processor modes (using IBM's MSB-0 convention where bit 0 is the most +significant bit): + +- ``KVM_PPC_COMPAT_CAP_POWER9`` (bit 1) -- KVM guests can run in Power9 processor mode +- ``KVM_PPC_COMPAT_CAP_POWER10`` (bit 2) -- KVM guests can run in Power10 processor mode +- ``KVM_PPC_COMPAT_CAP_POWER11`` (bit 3) -- KVM guests can run in Power11 processor mode + +.. note:: + + The bit numbering above uses IBM's MSB-0 convention (bit 0 is the most + significant bit). In the actual implementation, these are defined as: + + - ``KVM_PPC_COMPAT_CAP_POWER9`` = ``(1ULL << 62)`` + - ``KVM_PPC_COMPAT_CAP_POWER10`` = ``(1ULL << 61)`` + - ``KVM_PPC_COMPAT_CAP_POWER11`` = ``(1ULL << 60)`` + + Userspace should use the defined constants from ```` rather + than hardcoding bit positions. + .. _kvm_run: 5. The kvm_run structure diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index f7ce5fff81f0..da35126d93ac 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -281,10 +281,13 @@ config PPC select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP select HAVE_PREEMPT_DYNAMIC_KEY + select HAVE_POSIX_CPU_TIMERS_TASK_WORK select HAVE_RETHOOK if KPROBES select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RELIABLE_STACKTRACE select HAVE_RSEQ + select HAVE_RUST if PPC32 + select HAVE_RUST if PPC64 && CPU_LITTLE_ENDIAN select HAVE_SAMPLE_FTRACE_DIRECT if HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS select HAVE_SAMPLE_FTRACE_DIRECT_MULTI if HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS select HAVE_SETUP_PER_CPU_AREA if PPC64 diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index a58b1029592c..9385db478c59 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -61,6 +61,13 @@ else KBUILD_LDFLAGS_MODULE += $(objtree)/arch/powerpc/lib/crtsavres.o endif +ifdef CONFIG_PPC64 +KBUILD_RUSTFLAGS += --target=powerpc64le-unknown-linux-gnu +KBUILD_RUSTFLAGS += -Ctarget-feature=-mma,-vsx,-hard-float,-altivec +else +KBUILD_RUSTFLAGS += --target=powerpc-unknown-linux-gnu +endif + ifdef CONFIG_CPU_LITTLE_ENDIAN KBUILD_CPPFLAGS += -mlittle-endian KBUILD_LDFLAGS += -EL diff --git a/arch/powerpc/boot/serial.c b/arch/powerpc/boot/serial.c index c6d32a8c3612..1d221ff420fd 100644 --- a/arch/powerpc/boot/serial.c +++ b/arch/powerpc/boot/serial.c @@ -37,7 +37,6 @@ static void serial_edit_cmdline(char *buf, int len, unsigned int timeout) char ch, *cp; struct serial_console_data *scdp = console_ops.data; - cp = buf; count = strlen(buf); cp = &buf[count]; count++; diff --git a/arch/powerpc/boot/stdio.c b/arch/powerpc/boot/stdio.c index 31eece29f56d..b12aa2c0b8e7 100644 --- a/arch/powerpc/boot/stdio.c +++ b/arch/powerpc/boot/stdio.c @@ -326,17 +326,6 @@ int vsprintf(char *buf, const char *fmt, va_list args) return str-buf; } -int sprintf(char * buf, const char *fmt, ...) -{ - va_list args; - int i; - - va_start(args, fmt); - i=vsprintf(buf,fmt,args); - va_end(args); - return i; -} - static char sprint_buf[1024]; int diff --git a/arch/powerpc/boot/stdio.h b/arch/powerpc/boot/stdio.h index 884d5959a9ae..d5a7a0f54453 100644 --- a/arch/powerpc/boot/stdio.h +++ b/arch/powerpc/boot/stdio.h @@ -12,9 +12,6 @@ extern int printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); #define fprintf(fmt, args...) printf(args) -extern int sprintf(char *buf, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); - extern int vsprintf(char *buf, const char *fmt, va_list args); #endif /* _PPC_BOOT_STDIO_H_ */ diff --git a/arch/powerpc/configs/44x/warp_defconfig b/arch/powerpc/configs/44x/warp_defconfig index 5757625469c4..d6014b9c5708 100644 --- a/arch/powerpc/configs/44x/warp_defconfig +++ b/arch/powerpc/configs/44x/warp_defconfig @@ -12,7 +12,7 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_EBONY is not set CONFIG_WARP=y -CONFIG_PPC4xx_GPIO=y +CONFIG_GPIO_PPC44X=y CONFIG_HZ_1000=y CONFIG_CMDLINE="ip=on" # CONFIG_PCI is not set diff --git a/arch/powerpc/configs/85xx-hw.config b/arch/powerpc/configs/85xx-hw.config index 2b19c20a9a2c..6b8471810143 100644 --- a/arch/powerpc/configs/85xx-hw.config +++ b/arch/powerpc/configs/85xx-hw.config @@ -90,6 +90,7 @@ CONFIG_PPC_EPAPR_HV_BYTECHAN=y CONFIG_QE_GPIO=y CONFIG_QUICC_ENGINE=y CONFIG_RAPIDIO=y +CONFIG_RAS=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_CMOS=y CONFIG_RTC_DRV_DS1307=y diff --git a/arch/powerpc/configs/ppc44x_defconfig b/arch/powerpc/configs/ppc44x_defconfig index 0dc537f6aff3..d351551e266f 100644 --- a/arch/powerpc/configs/ppc44x_defconfig +++ b/arch/powerpc/configs/ppc44x_defconfig @@ -22,7 +22,7 @@ CONFIG_GLACIER=y CONFIG_REDWOOD=y CONFIG_EIGER=y CONFIG_YOSEMITE=y -CONFIG_PPC4xx_GPIO=y +CONFIG_GPIO_PPC44X=y CONFIG_MATH_EMULATION=y CONFIG_NET=y CONFIG_PACKET=y diff --git a/arch/powerpc/include/asm/cpu_setup.h b/arch/powerpc/include/asm/cpu_setup.h index 30e2fe389502..26d2c0e2c99c 100644 --- a/arch/powerpc/include/asm/cpu_setup.h +++ b/arch/powerpc/include/asm/cpu_setup.h @@ -9,10 +9,12 @@ void __setup_cpu_power7(unsigned long offset, struct cpu_spec *spec); void __setup_cpu_power8(unsigned long offset, struct cpu_spec *spec); void __setup_cpu_power9(unsigned long offset, struct cpu_spec *spec); void __setup_cpu_power10(unsigned long offset, struct cpu_spec *spec); +void __setup_cpu_power12(unsigned long offset, struct cpu_spec *spec); void __restore_cpu_power7(void); void __restore_cpu_power8(void); void __restore_cpu_power9(void); void __restore_cpu_power10(void); +void __restore_cpu_power12(void); void __setup_cpu_e500v1(unsigned long offset, struct cpu_spec *spec); void __setup_cpu_e500v2(unsigned long offset, struct cpu_spec *spec); diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index ec16c12296da..a3be81c47df3 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -194,6 +194,7 @@ static inline void cpu_feature_keys_init(void) { } #define CPU_FTR_DAWR1 LONG_ASM_CONST(0x0008000000000000) #define CPU_FTR_DEXCR_NPHIE LONG_ASM_CONST(0x0010000000000000) #define CPU_FTR_P11_PVR LONG_ASM_CONST(0x0020000000000000) +#define CPU_FTR_ARCH_32 LONG_ASM_CONST(0x0040000000000000) #ifndef __ASSEMBLER__ @@ -457,6 +458,18 @@ static inline void cpu_feature_keys_init(void) { } #define CPU_FTRS_POWER11 (CPU_FTRS_POWER10 | CPU_FTR_P11_PVR) +#define CPU_FTRS_POWER12 (CPU_FTR_LWSYNC | \ + CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | CPU_FTR_ARCH_206 |\ + CPU_FTR_MMCRA | CPU_FTR_SMT | \ + CPU_FTR_COHERENT_ICACHE | \ + CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ + CPU_FTR_DSCR | \ + CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ + CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ + CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_ARCH_207S | \ + CPU_FTR_ARCH_300 | CPU_FTR_ARCH_31 | CPU_FTR_ARCH_32 | \ + CPU_FTR_DAWR | CPU_FTR_DAWR1) + #define CPU_FTRS_CELL (CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ @@ -476,7 +489,7 @@ static inline void cpu_feature_keys_init(void) { } (CPU_FTRS_POWER7 | CPU_FTRS_POWER8E | CPU_FTRS_POWER8 | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_VSX_COMP | CPU_FTRS_POWER9 | \ CPU_FTRS_POWER9_DD2_1 | CPU_FTRS_POWER9_DD2_2 | \ - CPU_FTRS_POWER9_DD2_3 | CPU_FTRS_POWER10 | CPU_FTRS_POWER11) + CPU_FTRS_POWER9_DD2_3 | CPU_FTRS_POWER10 | CPU_FTRS_POWER11 | CPU_FTRS_POWER12) #else #define CPU_FTRS_POSSIBLE \ (CPU_FTRS_PPC970 | CPU_FTRS_POWER5 | \ @@ -484,7 +497,7 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTRS_POWER8 | CPU_FTRS_CELL | CPU_FTRS_PA6T | \ CPU_FTR_VSX_COMP | CPU_FTR_ALTIVEC_COMP | CPU_FTRS_POWER9 | \ CPU_FTRS_POWER9_DD2_1 | CPU_FTRS_POWER9_DD2_2 | \ - CPU_FTRS_POWER9_DD2_3 | CPU_FTRS_POWER10 | CPU_FTRS_POWER11) + CPU_FTRS_POWER9_DD2_3 | CPU_FTRS_POWER10 | CPU_FTRS_POWER11 | CPU_FTRS_POWER12) #endif /* CONFIG_CPU_LITTLE_ENDIAN */ #endif #else diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h index d4eaba459a0e..3016e9c8d6bc 100644 --- a/arch/powerpc/include/asm/jump_label.h +++ b/arch/powerpc/include/asm/jump_label.h @@ -15,14 +15,20 @@ #define JUMP_ENTRY_TYPE stringify_in_c(FTR_ENTRY_LONG) #define JUMP_LABEL_NOP_SIZE 4 +#define JUMP_TABLE_ENTRY(key, label) \ + ".pushsection __jump_table, \"aw\" \n\t" \ + ".long 1b - ., " label " - . \n\t" \ + JUMP_ENTRY_TYPE key " - . \n\t" \ + ".popsection \n\t" + +#define ARCH_STATIC_BRANCH_ASM(key, label) \ + "1: nop \n\t" \ + JUMP_TABLE_ENTRY(key, label) + static __always_inline bool arch_static_branch(struct static_key *key, bool branch) { - asm goto("1:\n\t" - "nop # arch_static_branch\n\t" - ".pushsection __jump_table, \"aw\"\n\t" - ".long 1b - ., %l[l_yes] - .\n\t" - JUMP_ENTRY_TYPE "%c0 - .\n\t" - ".popsection \n\t" + asm goto( + ARCH_STATIC_BRANCH_ASM("%c0", "%l[l_yes]") : : "i" (&((char *)key)[branch]) : : l_yes); return false; @@ -34,10 +40,7 @@ static __always_inline bool arch_static_branch_jump(struct static_key *key, bool { asm goto("1:\n\t" "b %l[l_yes] # arch_static_branch_jump\n\t" - ".pushsection __jump_table, \"aw\"\n\t" - ".long 1b - ., %l[l_yes] - .\n\t" - JUMP_ENTRY_TYPE "%c0 - .\n\t" - ".popsection \n\t" + JUMP_TABLE_ENTRY("%c0", "%l[l_yes]") : : "i" (&((char *)key)[branch]) : : l_yes); return false; diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 0953f2daa466..169ea6a7fbad 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -319,6 +319,7 @@ struct kvmppc_ops { bool (*hash_v3_possible)(void); int (*create_vm_debugfs)(struct kvm *kvm); int (*create_vcpu_debugfs)(struct kvm_vcpu *vcpu, struct dentry *debugfs_dentry); + int (*get_compat_caps)(struct kvm_ppc_compat_caps *host_caps); }; extern struct kvmppc_ops *kvmppc_hv_ops; diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h index 5f9c5d436e17..4fd7f81a2930 100644 --- a/arch/powerpc/include/asm/mmu.h +++ b/arch/powerpc/include/asm/mmu.h @@ -133,6 +133,7 @@ #define MMU_FTRS_POWER9 MMU_FTRS_POWER6 #define MMU_FTRS_POWER10 MMU_FTRS_POWER6 #define MMU_FTRS_POWER11 MMU_FTRS_POWER6 +#define MMU_FTRS_POWER12 MMU_FTRS_POWER6 #define MMU_FTRS_CELL MMU_FTRS_DEFAULT_HPTE_ARCH_V2 | \ MMU_FTR_CI_LARGE_PAGE #define MMU_FTRS_PA6T MMU_FTRS_DEFAULT_HPTE_ARCH_V2 | \ diff --git a/arch/powerpc/include/asm/papr-watchdog.h b/arch/powerpc/include/asm/papr-watchdog.h new file mode 100644 index 000000000000..bf876fc2caae --- /dev/null +++ b/arch/powerpc/include/asm/papr-watchdog.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_POWERPC_PAPR_WATCHDOG_H +#define _ASM_POWERPC_PAPR_WATCHDOG_H + +/* + * H_WATCHDOG Input + * + * R4: "flags": + * + * Bits 48-55: "operation" + */ +#define PSERIES_WDTF_OP_START 0x100UL /* start timer */ +#define PSERIES_WDTF_OP_STOP 0x200UL /* stop timer */ +#define PSERIES_WDTF_OP_QUERY 0x300UL /* query timer capabilities */ + +/* + * Bits 56-63: "timeoutAction" (for "Start Watchdog" only) + */ +#define PSERIES_WDTF_ACTION_HARD_POWEROFF 0x1UL /* poweroff */ +#define PSERIES_WDTF_ACTION_HARD_RESTART 0x2UL /* restart */ +#define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3UL /* dump + restart */ + +/* + * R5: "watchdogNumber": + * PAPR says use -1 (all ones) to stop all watchdogs. + */ +#define PSERIES_WDT_NUM_ALL ((unsigned long)-1) + +/* + * H_WATCHDOG Output + * + * R3: Return code + * + * H_SUCCESS The operation completed. + * + * H_BUSY The hypervisor is too busy; retry the operation. + * + * H_PARAMETER The given "flags" are somehow invalid. Either the + * "operation" or "timeoutAction" is invalid, or a + * reserved bit is set. + * + * H_P2 The given "watchdogNumber" is zero or exceeds the + * supported maximum value. + * + * H_P3 The given "timeoutInMs" is below the supported + * minimum value. + * + * H_NOOP The given "watchdogNumber" is already stopped. + * + * H_HARDWARE The operation failed for ineffable reasons. + * + * H_FUNCTION The H_WATCHDOG hypercall is not supported by this + * hypervisor. + * + * R4: + * + * - For the "Query Watchdog Capabilities" operation, a 64-bit + * structure: + */ +#define PSERIES_WDTQ_MIN_TIMEOUT(cap) (((cap) >> 48) & 0xffff) +#define PSERIES_WDTQ_MAX_NUMBER(cap) (((cap) >> 32) & 0xffff) + +#endif /* _ASM_POWERPC_PAPR_WATCHDOG_H */ diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index f4991d10d89e..2076852e6c2e 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -117,7 +117,8 @@ extern unsigned int boot_cpu_node_count; #define OV1_PPC_2_07 0x01 /* set if we support PowerPC 2.07 */ #define OV1_PPC_3_00 0x80 /* set if we support PowerPC 3.00 */ -#define OV1_PPC_3_1 0x40 /* set if we support PowerPC 3.1 */ +#define OV1_PPC_3_1 0x40 /* set if we support PowerPC 3.1 */ +#define OV1_PPC_3_2 0x20 /* set if we support PowerPC 3.2 */ /* Option vector 2: Open Firmware options supported */ #define OV2_REAL_MODE 0x20 /* set if we want OF in real mode */ diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 3449dd2b577d..541a493e13bf 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -492,11 +492,12 @@ * determine both the compatibility level which we want to emulate and the * compatibility level which the host is capable of emulating. */ +#define PCR_ARCH_31 0x20 /* Architecture 3.1 */ #define PCR_ARCH_300 0x10 /* Architecture 3.00 */ #define PCR_ARCH_207 0x8 /* Architecture 2.07 */ #define PCR_ARCH_206 0x4 /* Architecture 2.06 */ #define PCR_ARCH_205 0x2 /* Architecture 2.05 */ -#define PCR_LOW_BITS (PCR_ARCH_207 | PCR_ARCH_206 | PCR_ARCH_205 | PCR_ARCH_300) +#define PCR_LOW_BITS (PCR_ARCH_207 | PCR_ARCH_206 | PCR_ARCH_205 | PCR_ARCH_300 | PCR_ARCH_31) #define PCR_MASK ~(PCR_HIGH_BITS | PCR_LOW_BITS) /* PCR Reserved Bits */ #define SPRN_HEIR 0x153 /* Hypervisor Emulated Instruction Register */ #define SPRN_TLBINDEXR 0x154 /* P7 TLB control register */ @@ -1344,6 +1345,7 @@ #define PVR_POWER9 0x004E #define PVR_POWER10 0x0080 #define PVR_POWER11 0x0082 +#define PVR_POWER12 0x0083 #define PVR_BE 0x0070 #define PVR_PA6T 0x0090 @@ -1356,6 +1358,19 @@ #define PVR_ARCH_300 0x0f000005 #define PVR_ARCH_31 0x0f000006 #define PVR_ARCH_31_P11 0x0f000007 +#define PVR_ARCH_32 0x0f000008 + +/* + * Kernel-internal sentinel for invalid processor compatibility modes. + * PAPR specifies that the first byte of a valid logical PVR value is + * 0x0f. So 0xffffffff lies permanently outside the PAPR-defined range + * and is safe to repurpose. KVM stores it in vcpu->arch.arch_compat + * when userspace requests an unsupported compatibility mode (e.g., + * Power11 PVR on a Power11 host booted in Power10 compat). + * kvmppc_sanity_check() detects this and prevents the vCPU from + * running with an unsupported arch_compat. + */ +#define PVR_ARCH_INVALID 0xffffffff /* Macros for setting and retrieving special purpose registers */ #ifndef __ASSEMBLER__ diff --git a/arch/powerpc/include/asm/serial.h b/arch/powerpc/include/asm/serial.h index c3eb4a64b3f5..534f0bb97ba9 100644 --- a/arch/powerpc/include/asm/serial.h +++ b/arch/powerpc/include/asm/serial.h @@ -7,8 +7,8 @@ * through the device tree. */ -/* Default baud base if not found in device-tree */ -#define BASE_BAUD ( 1843200 / 16 ) +/* Provides BASE_BAUD, used as fallback if not found in device tree. */ +#include #ifdef CONFIG_PPC_UDBG_16550 extern void find_legacy_serial_ports(void); diff --git a/arch/powerpc/include/asm/synch.h b/arch/powerpc/include/asm/synch.h index 0d3ccb34adfb..d0e9765dee28 100644 --- a/arch/powerpc/include/asm/synch.h +++ b/arch/powerpc/include/asm/synch.h @@ -37,8 +37,12 @@ static inline void ppc_after_tlbiel_barrier(void) * accelerators mapped will use tlbie (which does invalidate the copy) * to invalidate translations. It's not possible to limit POWER10 this * way due to local copy-paste. + * + * POWER12 does not need it. */ - asm volatile(ASM_FTR_IFSET(PPC_CP_ABORT, "", %0) : : "i" (CPU_FTR_ARCH_31) : "memory"); + asm volatile(ASM_FTR_IF(PPC_CP_ABORT, "", %0, %1) : + : "i" (CPU_FTR_ARCH_31|CPU_FTR_ARCH_32), "i" (CPU_FTR_ARCH_31) + : "memory"); } #endif /* __ASSEMBLER__ */ diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h index 834fcc4f7b54..19d1739af0b7 100644 --- a/arch/powerpc/include/asm/syscall.h +++ b/arch/powerpc/include/asm/syscall.h @@ -98,6 +98,12 @@ static inline void syscall_set_return_value(struct task_struct *task, regs->gpr[3] = val; } } + /* + * Mark that a return value has been explicitly set by seccomp or + * ptrace so that system_call_exception() can skip the syscall + * unconditionally, even when the user requested syscall(-1). + */ + set_thread_flag(TIF_SYSCALL_RET); } static inline void syscall_get_arguments(struct task_struct *task, diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h index 0487e94d3416..1e069a2e7ce8 100644 --- a/arch/powerpc/include/asm/thread_info.h +++ b/arch/powerpc/include/asm/thread_info.h @@ -120,6 +120,7 @@ void arch_setup_new_exec(void); #endif #define TIF_POLLING_NRFLAG 19 /* true if poll_idle() is polling TIF_NEED_RESCHED */ #define TIF_32BIT 20 /* 32 bit binary */ +#define TIF_SYSCALL_RET 21 /* syscall error value set */ /* as above, but as bit values */ #define _TIF_SYSCALL_TRACE (1<cpu_features |= CPU_FTR_MMCRA; + cur_cpu_spec->cpu_user_features |= PPC_FEATURE_PSERIES_PERFMON_COMPAT; + + cur_cpu_spec->num_pmcs = 6; + cur_cpu_spec->pmc_type = PPC_PMC_IBM; + + return 1; +} + +static int __init feat_enable_mce_power12(struct dt_cpu_feature *f) +{ + cur_cpu_spec->platform = "power12"; + cur_cpu_spec->machine_check_early = __machine_check_early_realmode_p10; + + return 1; +} + static int __init feat_enable_tm(struct dt_cpu_feature *f) { #ifdef CONFIG_PPC_TRANSACTIONAL_MEM @@ -657,9 +685,11 @@ static struct dt_cpu_feature_match __initdata {"machine-check-power9", feat_enable_mce_power9, 0}, {"machine-check-power10", feat_enable_mce_power10, 0}, {"machine-check-power11", feat_enable_mce_power11, 0}, + {"machine-check-power12", feat_enable_mce_power12, 0}, {"performance-monitor-power9", feat_enable_pmu_power9, 0}, {"performance-monitor-power10", feat_enable_pmu_power10, 0}, {"performance-monitor-power11", feat_enable_pmu_power10, 0}, + {"performance-monitor-power12", feat_enable_pmu_power12, 0}, {"event-based-branch-v3", feat_enable, 0}, {"random-number-generator", feat_enable, 0}, {"system-call-vectored", feat_disable, 0}, @@ -714,6 +744,11 @@ static void __init cpufeatures_setup_start(u32 isa) if (PVR_VER(mfspr(SPRN_PVR)) >= PVR_POWER11) cur_cpu_spec->cpu_features |= CPU_FTR_P11_PVR; } + + if (isa >= ISA_V3_2) { + cur_cpu_spec->cpu_features |= CPU_FTR_ARCH_32; + cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_ARCH_3_2; + } } static bool __init cpufeatures_process_feature(struct dt_cpu_feature *f) @@ -1078,6 +1113,7 @@ static int __init count_cpufeatures_subnodes(unsigned long node, static int __init dt_cpu_ftrs_scan_callback(unsigned long node, const char *uname, int depth, void *data) { + static char dt_cpu_name[64]; const __be32 *prop; int count, i; u32 isa; @@ -1115,8 +1151,8 @@ static int __init dt_cpu_ftrs_scan_callback(unsigned long node, const char } prop = of_get_flat_dt_prop(node, "display-name", NULL); - if (prop && strlen((char *)prop) != 0) { - strscpy(dt_cpu_name, (char *)prop, sizeof(dt_cpu_name)); + if (prop && *(char *)prop != 0) { + strscpy(dt_cpu_name, (char *)prop); cur_cpu_spec->cpu_name = dt_cpu_name; } diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 63432a33ec49..a54f6f979173 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -713,14 +713,18 @@ p_end: .8byte _end - copy_to_here _GLOBAL(copy_and_flush) addi r5,r5,-8 addi r6,r6,-8 -4: li r0,8 /* Use the smallest common */ - /* denominator cache line */ - /* size. This results in */ - /* extra cache line flushes */ - /* but operation is correct. */ - /* Can't get cache line size */ - /* from NACA as it is being */ - /* moved too. */ +4: li r0,8 /* r0 is the number of 8-byte words */ + /* to copy per cache sync iteration. */ + /* 8 words * 8 bytes = 64 bytes. 64B is */ + /* the current default cache line size. */ + /* This is a loop count, not a byte */ + /* count. Increasing it may skip */ + /* dcbst/icbi for lines in between and */ + /* leave stale instructions in icache. */ + /* This results in extra cache line */ + /* flushes but operation is correct. */ + /* Can't get cache line size from NACA */ + /* as it is being moved too. */ mtctr r0 /* put # words/line in ctr */ 3: addi r6,r6,8 /* copy a cache line */ diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index f69de08ad347..15a3c3fd8e70 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -217,8 +217,12 @@ static __always_inline void call_do_softirq(const void *sp) [sp] "b" (sp), [offset] "i" (THREAD_SIZE - STACK_FRAME_MIN_SIZE), [callee] "i" (__do_softirq) : // Clobbers - "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6", - "cr7", "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", + "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6", "cr7", "r0", + /* r2 may be clobbered by the callee when using PCREL mode in the ELFv2 ABI. */ +#ifdef CONFIG_PPC_KERNEL_PCREL + "r2", +#endif + "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" ); } @@ -275,8 +279,12 @@ static __always_inline void call_do_irq(struct pt_regs *regs, void *sp) [sp] "b" (sp), [offset] "i" (THREAD_SIZE - STACK_FRAME_MIN_SIZE), [callee] "i" (__do_irq) : // Clobbers - "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6", - "cr7", "r0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", + "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6", "cr7", "r0", + /* r2 may be clobbered by the callee when using PCREL mode in the ELFv2 ABI. */ +#ifdef CONFIG_PPC_KERNEL_PCREL + "r2", +#endif + "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" ); } diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 53503937de0e..eb9f556b0937 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -86,6 +86,12 @@ */ #define ADDR(x) (u32)(unsigned long)(x) +/* + * Current Power system firmware caps the PVR list array size at 16 entries + * during CAS (Client Architecture Support) negotiation. + */ +#define CAS_MAX_PVR_ENTRIES 16 + #ifdef CONFIG_PPC64 #define OF_WORKAROUNDS 0 #else @@ -947,7 +953,7 @@ struct option_vector7 { } __packed; struct ibm_arch_vec { - struct { __be32 mask, val; } pvrs[16]; + struct { __be32 mask, val; } pvrs[18]; u8 num_vectors; @@ -979,6 +985,10 @@ static const struct ibm_arch_vec ibm_architecture_vec_template __initconst = { .mask = cpu_to_be32(0xfffe0000), /* POWER5/POWER5+ */ .val = cpu_to_be32(0x003a0000), }, + { + .mask = cpu_to_be32(0xffffffff), /* all 2.04-compliant and earlier */ + .val = cpu_to_be32(0x0f000001), + }, { .mask = cpu_to_be32(0xffff0000), /* POWER6 */ .val = cpu_to_be32(0x003e0000), @@ -1011,6 +1021,14 @@ static const struct ibm_arch_vec ibm_architecture_vec_template __initconst = { .mask = cpu_to_be32(0xffff0000), /* POWER11 */ .val = cpu_to_be32(0x00820000), }, + { + .mask = cpu_to_be32(0xffff0000), /* POWER12 */ + .val = cpu_to_be32(0x00830000), + }, + { + .mask = cpu_to_be32(0xffffffff), /* all 3.2-compliant */ + .val = cpu_to_be32(0x0f000008), + }, { .mask = cpu_to_be32(0xffffffff), /* P11 compliant */ .val = cpu_to_be32(0x0f000007), @@ -1032,13 +1050,9 @@ static const struct ibm_arch_vec ibm_architecture_vec_template __initconst = { .val = cpu_to_be32(0x0f000003), }, { - .mask = cpu_to_be32(0xffffffff), /* all 2.05-compliant */ + .mask = cpu_to_be32(0xfffffffd), /* all 2.05-compliant */ .val = cpu_to_be32(0x0f000002), }, - { - .mask = cpu_to_be32(0xfffffffe), /* all 2.04-compliant and earlier */ - .val = cpu_to_be32(0x0f000001), - }, }, .num_vectors = NUM_VECTORS(6), @@ -1048,7 +1062,7 @@ static const struct ibm_arch_vec ibm_architecture_vec_template __initconst = { .byte1 = 0, .arch_versions = OV1_PPC_2_00 | OV1_PPC_2_01 | OV1_PPC_2_02 | OV1_PPC_2_03 | OV1_PPC_2_04 | OV1_PPC_2_05 | OV1_PPC_2_06 | OV1_PPC_2_07, - .arch_versions3 = OV1_PPC_3_00 | OV1_PPC_3_1, + .arch_versions3 = OV1_PPC_3_00 | OV1_PPC_3_1 | OV1_PPC_3_2, }, .vec2_len = VECTOR_LENGTH(sizeof(struct option_vector2)), @@ -1403,6 +1417,22 @@ static void __init prom_send_capabilities(void) ihandle root; prom_arg_t ret; u32 cores; + int start_index = 0; + + /* + * Ensure that when running on Power11 or below hardware, the number + * of PVR entries passed during CAS negotiation does not exceed the + * firmware-imposed limit of 16. + * + * Compute the start_index to skip the oldest leading pvrs[] entries + * when running on Power11 or below hardware, so that the pointer + * passed to ibm,client-architecture-support points to + * ibm_architecture_vec.pvrs[start_index], presenting exactly 16 + * entries to firmware. + */ + if ((ARRAY_SIZE(ibm_architecture_vec_template.pvrs) > CAS_MAX_PVR_ENTRIES) && + (PVR_VER(mfspr(SPRN_PVR)) <= PVR_POWER11)) + start_index = ARRAY_SIZE(ibm_architecture_vec_template.pvrs) - CAS_MAX_PVR_ENTRIES; /* Check ibm,arch-vec-5-platform-support and fixup vec5 if required */ prom_check_platform_support(); @@ -1427,7 +1457,7 @@ static void __init prom_send_capabilities(void) if (call_prom_ret("call-method", 3, 2, &ret, ADDR("ibm,client-architecture-support"), root, - ADDR(&ibm_architecture_vec)) == 0) { + ADDR(&ibm_architecture_vec.pvrs[start_index])) == 0) { /* the call exists... */ if (ret) prom_printf("\nWARNING: ibm,client-architecture" diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index 583dc16e9d3c..722dbfb6fbf8 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c @@ -394,30 +394,23 @@ static ssize_t manage_flash_write(struct file *file, const char __user *buf, size_t count, loff_t *off) { struct rtas_manage_flash_t *const args_buf = &rtas_manage_flash_data; - static const char reject_str[] = "0"; - static const char commit_str[] = "1"; - char stkbuf[10]; - int op; + unsigned int op; + char cmd; guard(mutex)(&rtas_manage_flash_mutex); if ((args_buf->status == MANAGE_AUTH) || (count == 0)) return count; - op = -1; - if (buf) { - if (count > 9) count = 9; - if (copy_from_user (stkbuf, buf, count)) - return -EFAULT; - if (strncmp(stkbuf, reject_str, strlen(reject_str)) == 0) - op = RTAS_REJECT_TMP_IMG; - else if (strncmp(stkbuf, commit_str, strlen(commit_str)) == 0) - op = RTAS_COMMIT_TMP_IMG; - } - - if (op == -1) { /* buf is empty, or contains invalid string */ + if (get_user(cmd, buf)) + return -EFAULT; + + if (cmd == '0') + op = RTAS_REJECT_TMP_IMG; + else if (cmd == '1') + op = RTAS_COMMIT_TMP_IMG; + else return -EINVAL; - } manage_flash(args_buf, op); return count; diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c index 6336ec9aedd0..fd40864bdb70 100644 --- a/arch/powerpc/kernel/rtasd.c +++ b/arch/powerpc/kernel/rtasd.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -160,25 +161,17 @@ static void printk_log_rtas(char *buf, int len) static int log_rtas_len(char * buf) { - int len; + size_t len; struct rtas_error_log *err; - uint32_t extended_log_length; + u32 extended_log_length; - /* rtas fixed header */ - len = 8; err = (struct rtas_error_log *)buf; - extended_log_length = rtas_error_extended_log_length(err); - if (rtas_error_extended(err) && extended_log_length) { - - /* extended header */ - len += extended_log_length; - } + extended_log_length = rtas_error_extended(err) ? rtas_error_extended_log_length(err) : 0; + len = struct_size(err, buffer, extended_log_length); if (rtas_error_log_max == 0) rtas_error_log_max = rtas_get_error_log_max(); - - if (len > rtas_error_log_max) - len = rtas_error_log_max; + len = min(len, rtas_error_log_max); return len; } diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 67c545f61f0d..4afaba19b586 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -304,6 +304,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) break; case 0x004e: /* POWER9 bits 12-15 give chip type */ case 0x0080: /* POWER10 bit 12 gives SMT8/4 */ + case 0x0083: /* POWER12 bit 12 gives SMT8/4 */ maj = (pvr >> 8) & 0x0F; min = pvr & 0xFF; break; diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 3467f86fd78f..6a5a5469aaae 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -289,6 +289,9 @@ void smp_muxed_ipi_set_message(int cpu, int msg) void smp_muxed_ipi_message_pass(int cpu, int msg) { + if (!smp_ops->cause_ipi) + return; + smp_muxed_ipi_set_message(cpu, msg); /* diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c index a9da2af6efa8..9d1b29f44ea0 100644 --- a/arch/powerpc/kernel/syscall.c +++ b/arch/powerpc/kernel/syscall.c @@ -22,6 +22,9 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0) add_random_kstack_offset(); r0 = syscall_enter_from_user_mode(regs, r0); + if (unlikely(test_and_clear_thread_flag(TIF_SYSCALL_RET))) + return syscall_get_error(current, regs); + if (unlikely(r0 >= NR_syscalls)) { if (unlikely(trap_is_unsupported_scv(regs))) { /* Unsupported scv vector */ diff --git a/arch/powerpc/kexec/crash.c b/arch/powerpc/kexec/crash.c index 60a917a6beaa..775895f31037 100644 --- a/arch/powerpc/kexec/crash.c +++ b/arch/powerpc/kexec/crash.c @@ -502,7 +502,7 @@ static void update_crash_elfcorehdr(struct kimage *image, struct memory_notify * ret = get_crash_memory_ranges(&cmem); if (ret) { pr_err("Failed to get crash mem range\n"); - return; + goto out; } /* diff --git a/arch/powerpc/kexec/file_load.c b/arch/powerpc/kexec/file_load.c index 4284f76cbef5..597998235136 100644 --- a/arch/powerpc/kexec/file_load.c +++ b/arch/powerpc/kexec/file_load.c @@ -36,25 +36,19 @@ char *setup_kdump_cmdline(struct kimage *image, char *cmdline, unsigned long cmdline_len) { - int elfcorehdr_strlen; char *cmdline_ptr; - cmdline_ptr = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL); + cmdline_ptr = kmalloc(COMMAND_LINE_SIZE, GFP_KERNEL); if (!cmdline_ptr) return NULL; - elfcorehdr_strlen = sprintf(cmdline_ptr, "elfcorehdr=0x%lx ", - image->elf_load_addr); - - if (elfcorehdr_strlen + cmdline_len > COMMAND_LINE_SIZE) { - pr_err("Appending elfcorehdr= exceeds cmdline size\n"); + if (snprintf(cmdline_ptr, COMMAND_LINE_SIZE, "elfcorehdr=0x%lx %s", + image->elf_load_addr, cmdline_len ? cmdline : "") >= COMMAND_LINE_SIZE) { + pr_err("Prepending elfcorehdr= exceeds cmdline size\n"); kfree(cmdline_ptr); return NULL; } - memcpy(cmdline_ptr + elfcorehdr_strlen, cmdline, cmdline_len); - // Ensure it's nul terminated - cmdline_ptr[COMMAND_LINE_SIZE - 1] = '\0'; return cmdline_ptr; } diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index 8c72e12ea44e..6075b1c88511 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -664,7 +664,7 @@ unsigned int kexec_extra_fdt_size_ppc64(struct kimage *image, struct crash_mem * extra_size += (cpu_nodes - boot_cpu_node_count) * cpu_node_size(); /* Consider extra space for reserved memory ranges if any */ - if (rmem->nr_ranges > 0) + if (rmem && rmem->nr_ranges > 0) extra_size += sizeof(struct fdt_reserve_entry) * rmem->nr_ranges; return extra_size + kdump_extra_fdt_size_ppc64(image, cpu_nodes); diff --git a/arch/powerpc/kexec/ranges.c b/arch/powerpc/kexec/ranges.c index e5fea23b191b..539061d14a77 100644 --- a/arch/powerpc/kexec/ranges.c +++ b/arch/powerpc/kexec/ranges.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -105,19 +106,16 @@ static void __merge_memory_ranges(struct crash_mem *mem_rngs) struct range *ranges; int i, idx; - if (!mem_rngs) + if (!mem_rngs || mem_rngs->nr_ranges <= 1) return; idx = 0; - ranges = &(mem_rngs->ranges[0]); + ranges = mem_rngs->ranges; for (i = 1; i < mem_rngs->nr_ranges; i++) { - if (ranges[i].start <= (ranges[i-1].end + 1)) - ranges[idx].end = ranges[i].end; + if (ranges[i].start <= (ranges[idx].end + 1)) + ranges[idx].end = max(ranges[idx].end, ranges[i].end); else { idx++; - if (i == idx) - continue; - ranges[idx] = ranges[i]; } } diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index 9a0d1c1aca6c..b6bc2fc86dca 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -22,6 +22,7 @@ config KVM select KVM_COMMON select KVM_VFIO select HAVE_KVM_IRQ_BYPASS + select VIRT_XFER_TO_GUEST_WORK config KVM_BOOK3S_HANDLER bool diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 61dbeea317f3..0409ac9e7b31 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -388,7 +388,7 @@ static void kvmppc_set_pvr_hv(struct kvm_vcpu *vcpu, u32 pvr) } /* Dummy value used in computing PCR value below */ -#define PCR_ARCH_31 (PCR_ARCH_300 << 1) +#define PCR_ARCH_32 (PCR_ARCH_31 << 1) static inline unsigned long map_pcr_to_cap(unsigned long pcr) { @@ -417,7 +417,9 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat) struct kvmppc_vcore *vc = vcpu->arch.vcore; /* We can (emulate) our own architecture version and anything older */ - if (cpu_has_feature(CPU_FTR_P11_PVR) || cpu_has_feature(CPU_FTR_ARCH_31)) + if (cpu_has_feature(CPU_FTR_ARCH_32)) + host_pcr_bit = PCR_ARCH_32; + else if (cpu_has_feature(CPU_FTR_P11_PVR) || cpu_has_feature(CPU_FTR_ARCH_31)) host_pcr_bit = PCR_ARCH_31; else if (cpu_has_feature(CPU_FTR_ARCH_300)) host_pcr_bit = PCR_ARCH_300; @@ -446,9 +448,24 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat) guest_pcr_bit = PCR_ARCH_300; break; case PVR_ARCH_31: - case PVR_ARCH_31_P11: guest_pcr_bit = PCR_ARCH_31; break; + case PVR_ARCH_31_P11: + /* + * Need to check this for ISA 3.1, as Power10 and + * Power11 share the same PCR. For any subsequent ISA + * versions, this will be taken care of by the guest vs + * host PCR comparison below. + */ + if (!cpu_has_feature(CPU_FTR_P11_PVR)) { + arch_compat = PVR_ARCH_INVALID; + goto out; + } + guest_pcr_bit = PCR_ARCH_31; + break; + case PVR_ARCH_32: + guest_pcr_bit = PCR_ARCH_32; + break; default: return -EINVAL; } @@ -469,6 +486,7 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat) return -EINVAL; } +out: spin_lock(&vc->lock); vc->arch_compat = arch_compat; kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_LOGICAL_PVR); @@ -479,7 +497,7 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat) vc->pcr = (host_pcr_bit - guest_pcr_bit) | PCR_MASK; spin_unlock(&vc->lock); - return 0; + return kvmppc_sanity_check(vcpu); } static void kvmppc_dump_regs(struct kvm_vcpu *vcpu) @@ -3853,7 +3871,8 @@ static noinline void kvmppc_run_core(struct kvmppc_vcore *vc) */ local_irq_disable(); hard_irq_disable(); - if (lazy_irq_pending() || need_resched() || + xfer_to_guest_mode_prepare(); + if (lazy_irq_pending() || xfer_to_guest_mode_work_pending() || recheck_signals_and_mmu(&core_info)) { local_irq_enable(); vc->vcore_state = VCORE_INACTIVE; @@ -4824,10 +4843,16 @@ static int kvmppc_run_vcpu(struct kvm_vcpu *vcpu) vc->runner = vcpu; if (n_ceded == vc->n_runnable) { kvmppc_vcore_blocked(vc); - } else if (need_resched()) { + } else if (__xfer_to_guest_mode_work_pending()) { kvmppc_vcore_preempt(vc); - /* Let something else run */ - cond_resched_lock(&vc->lock); + /* + * Let something else run. The raw helper is used as + * signal exits are accounted by this path already; + * it may schedule(), so drop the vcore lock. + */ + spin_unlock(&vc->lock); + xfer_to_guest_mode_handle_work(); + spin_lock(&vc->lock); if (vc->vcore_state == VCORE_PREEMPT) kvmppc_vcore_end_preempt(vc); } else { @@ -4895,12 +4920,16 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit, run->exit_reason = KVM_EXIT_FAIL_ENTRY; run->fail_entry.hardware_entry_failure_reason = 0; vcpu->arch.ret = r; - return r; + goto done; } } - if (need_resched()) - cond_resched(); + r = kvm_xfer_to_guest_mode_handle_work(vcpu); + if (r) { + /* -EINTR: signal pending, exit to userspace (KVM_EXIT_INTR) */ + vcpu->arch.ret = r; + goto done; + } kvmppc_update_vpas(vcpu); @@ -4914,9 +4943,13 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit, vcpu->arch.state = KVMPPC_VCPU_RUNNABLE; - if (signal_pending(current)) - goto sigpend; - if (need_resched() || !kvm->arch.mmu_ready) + xfer_to_guest_mode_prepare(); + + /* + * IRQs are disabled here, so on pending work bail to the outer loop, + * which handles it via kvm_xfer_to_guest_mode_handle_work() above. + */ + if (xfer_to_guest_mode_work_pending() || !kvm->arch.mmu_ready) goto out; vcpu->cpu = pcpu; @@ -5068,10 +5101,6 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit, return vcpu->arch.ret; - sigpend: - vcpu->stat.signal_exits++; - run->exit_reason = KVM_EXIT_INTR; - vcpu->arch.ret = -EINTR; out: vcpu->cpu = -1; vcpu->arch.thread_cpu = -1; @@ -6510,6 +6539,61 @@ static bool kvmppc_hash_v3_possible(void) return true; } +static int kvmppc_map_compat_capabilities(u32 cpu_version, + unsigned long *capabilities) +{ + switch (cpu_version) { + case PVR_ARCH_31_P11: + *capabilities |= KVM_PPC_COMPAT_CAP_POWER11; + fallthrough; + case PVR_ARCH_31: + *capabilities |= KVM_PPC_COMPAT_CAP_POWER10; + fallthrough; + case PVR_ARCH_300: + *capabilities |= KVM_PPC_COMPAT_CAP_POWER9; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int kvmppc_get_compat_caps(struct kvm_ppc_compat_caps *host_caps) +{ + struct device_node *np; + unsigned long capabilities = 0; + long rc = -EINVAL; + u32 cpu_version = 0; + + if (kvmhv_on_pseries()) { + if (kvmhv_is_nestedv2()) { + WARN_ON_ONCE(!nested_capabilities); + capabilities = nested_capabilities; + rc = 0; + } else { + for_each_node_by_type(np, "cpu") { + if (!of_property_read_u32(np, "cpu-version", + &cpu_version)) { + of_node_put(np); + break; + } + } + if (!cpu_version) + return -EINVAL; + rc = kvmppc_map_compat_capabilities(cpu_version, + &capabilities); + } + } + + if (rc < 0) + return rc; + + host_caps->compat_capabilities = capabilities & KVM_PPC_COMPAT_BITMASK; + + return rc; +} + static struct kvmppc_ops kvm_ops_hv = { .get_sregs = kvm_arch_vcpu_ioctl_get_sregs_hv, .set_sregs = kvm_arch_vcpu_ioctl_set_sregs_hv, @@ -6552,6 +6636,7 @@ static struct kvmppc_ops kvm_ops_hv = { .hash_v3_possible = kvmppc_hash_v3_possible, .create_vcpu_debugfs = kvmppc_arch_create_vcpu_debugfs_hv, .create_vm_debugfs = kvmppc_arch_create_vm_debugfs_hv, + .get_compat_caps = kvmppc_get_compat_caps, }; static int kvm_init_subcore_bitmap(void) diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index f3ddb24ece74..13ad4cf5fa71 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -598,7 +598,7 @@ static unsigned long watchdog_next_timeout(struct kvm_vcpu *vcpu) if (do_div(nr_jiffies, tb_ticks_per_jiffy)) nr_jiffies++; - return min_t(unsigned long long, nr_jiffies, TIMER_NEXT_MAX_DELTA); + return min(nr_jiffies, TIMER_NEXT_MAX_DELTA); } static void arm_next_watchdog(struct kvm_vcpu *vcpu) @@ -722,6 +722,7 @@ int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) if (vcpu->arch.shared->msr & MSR_WE) { local_irq_enable(); kvm_vcpu_halt(vcpu); + local_irq_disable(); hard_irq_disable(); kvmppc_set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS); diff --git a/arch/powerpc/kvm/mpic.c b/arch/powerpc/kvm/mpic.c index 3070f36d9fb8..fb5f9e65e02e 100644 --- a/arch/powerpc/kvm/mpic.c +++ b/arch/powerpc/kvm/mpic.c @@ -1833,7 +1833,8 @@ int kvm_set_routing_entry(struct kvm *kvm, e->set = mpic_set_irq; e->irqchip.irqchip = ue->u.irqchip.irqchip; e->irqchip.pin = ue->u.irqchip.pin; - if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) + if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS || + e->irqchip.irqchip >= KVM_NR_IRQCHIPS) goto out; break; case KVM_IRQ_ROUTING_MSI: diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 00302399fc37..9194cf492d1c 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -81,21 +81,39 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu) int r; WARN_ON(irqs_disabled()); + /* + * local_irq_disable() first: on 32-bit, hard_irq_disable() alone is a + * raw MSR[EE] clear that bypasses the lockdep/irq-tracing state, and + * the xfer_to_guest_mode helpers assert IRQs are seen as disabled. + */ + local_irq_disable(); hard_irq_disable(); while (true) { - if (need_resched()) { - local_irq_enable(); - cond_resched(); - hard_irq_disable(); - continue; - } + xfer_to_guest_mode_prepare(); - if (signal_pending(current)) { - kvmppc_account_exit(vcpu, SIGNAL_EXITS); - vcpu->run->exit_reason = KVM_EXIT_INTR; - r = -EINTR; - break; + if (xfer_to_guest_mode_work_pending()) { + /* + * The helper must run with IRQs enabled and may + * schedule(). On a pending signal it returns -EINTR + * with run->exit_reason and vcpu->stat.signal_exits + * already set, so just return to userspace. + */ + local_irq_enable(); + r = kvm_xfer_to_guest_mode_handle_work(vcpu); + local_irq_disable(); + hard_irq_disable(); + if (r) { + /* + * The generic helper does not set the exit + * type; record it for the E500 + * CONFIG_KVM_EXIT_TIMING histogram (a no-op + * otherwise). + */ + kvmppc_set_exit_type(vcpu, SIGNAL_EXITS); + break; + } + continue; } vcpu->mode = IN_GUEST_MODE; @@ -116,6 +134,7 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu) local_irq_enable(); trace_kvm_check_requests(vcpu); r = kvmppc_core_check_requests(vcpu); + local_irq_disable(); hard_irq_disable(); if (r > 0) continue; @@ -258,6 +277,12 @@ int kvmppc_sanity_check(struct kvm_vcpu *vcpu) if (!vcpu->arch.pvr) goto out; +#if defined(CONFIG_KVM_BOOK3S_HV_POSSIBLE) + if (vcpu->arch.vcore && + vcpu->arch.vcore->arch_compat == PVR_ARCH_INVALID) + goto out; +#endif + /* PAPR only works with book3s_64 */ if ((vcpu->arch.cpu_type != KVM_CPU_3S_64) && vcpu->arch.papr_enabled) goto out; @@ -635,9 +660,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) * implementations just count online CPUs. */ if (hv_enabled) - r = min_t(unsigned int, num_present_cpus(), KVM_MAX_VCPUS); + r = min(num_present_cpus(), KVM_MAX_VCPUS); else - r = min_t(unsigned int, num_online_cpus(), KVM_MAX_VCPUS); + r = min(num_online_cpus(), KVM_MAX_VCPUS); break; case KVM_CAP_MAX_VCPUS: r = KVM_MAX_VCPUS; @@ -697,6 +722,13 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) } } break; +#if defined(CONFIG_KVM_BOOK3S_HV_POSSIBLE) + case KVM_CAP_PPC_COMPAT_CAPS: + r = 0; + if (hv_enabled && kvmhv_on_pseries()) + r = 1; + break; +#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */ default: r = 0; break; @@ -2463,6 +2495,77 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) r = kvm->arch.kvm_ops->svm_off(kvm); break; } + case KVM_PPC_GET_COMPAT_CAPS: { + struct kvm_ppc_compat_caps host_caps = {}; + u64 usize; + + /* + * Read the size field first to drive copy_struct_from_user. + * size must be the first field of the struct. + */ + r = -EFAULT; + if (get_user(usize, (__u64 __user *)argp)) + goto out; + + r = -E2BIG; + if (unlikely(usize > PAGE_SIZE)) + goto out; + + /* + * Enforce a minimum: reject buffers smaller than the initial + * struct version (VER0). This allows old userspace compiled + * against the original struct to still work on a newer kernel + * that has grown the struct with appended fields. + */ + r = -EINVAL; + if (usize < KVM_PPC_COMPAT_CAPS_SIZE_VER0) + goto out; + + /* + * copy_struct_from_user() handles forward/backward compat: + * usize == ksize: verbatim copy + * usize < ksize: zero-pad trailing (old userspace, new kernel) + * usize > ksize: succeed iff trailing bytes are zero, else -E2BIG + */ + r = copy_struct_from_user(&host_caps, sizeof(host_caps), + argp, usize); + if (r) { + /* + * New userspace with a larger struct called an older + * kernel. Write back ksize in host_caps.size so + * userspace knows which older struct to retry with, + * then fail with -E2BIG. + */ + if (r == -E2BIG) + if (put_user((__u64)sizeof(host_caps), + (__u64 __user *)argp)) + r = -EFAULT; + goto out; + } + + /* Reserved fields must be zero */ + r = -EINVAL; + if (host_caps.flags) + goto out; + + r = -ENOTTY; + if (!kvm->arch.kvm_ops->get_compat_caps) + goto out; + + r = kvm->arch.kvm_ops->get_compat_caps(&host_caps); + if (r) + goto out; + + /* + * Report the number of bytes actually populated by the kernel, + * not usize: if new userspace passed a larger struct with zero + * trailing bytes, we only filled sizeof(host_caps) bytes. + */ + host_caps.size = min_t(u64, usize, sizeof(host_caps)); + r = copy_struct_to_user(argp, usize, &host_caps, + sizeof(host_caps), NULL); + break; + } default: { struct kvm *kvm = filp->private_data; r = kvm->arch.kvm_ops->arch_vm_ioctl(filp, ioctl, arg); diff --git a/arch/powerpc/mm/book3s64/hash_native.c b/arch/powerpc/mm/book3s64/hash_native.c index e9e2dd70c060..ab2a80e59011 100644 --- a/arch/powerpc/mm/book3s64/hash_native.c +++ b/arch/powerpc/mm/book3s64/hash_native.c @@ -184,9 +184,14 @@ static inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize) va |= ssize << 8; sllp = get_sllp_encoding(apsize); va |= sllp << 5; - asm volatile(ASM_FTR_IFSET("tlbiel %0", PPC_TLBIEL_v205(%0, 0), %1) - : : "r" (va), "i" (CPU_FTR_ARCH_206) - : "memory"); + + if (cpu_has_feature(CPU_FTR_ARCH_32)) { + asm volatile(PPC_TLBIEL(%0, %1, 0, 0, 0) : : "r"(va), "r"(0) : "memory"); + } else { + asm volatile(ASM_FTR_IFSET("tlbiel %0", PPC_TLBIEL_v205(%0, 0), %1) + : : "r" (va), "i" (CPU_FTR_ARCH_206) + : "memory"); + } break; default: /* We need 14 to 14 + i bits of va */ @@ -203,9 +208,14 @@ static inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize) */ va |= (vpn & 0xfe); va |= 1; /* L */ - asm volatile(ASM_FTR_IFSET("tlbiel %0", PPC_TLBIEL_v205(%0, 1), %1) - : : "r" (va), "i" (CPU_FTR_ARCH_206) - : "memory"); + + if (cpu_has_feature(CPU_FTR_ARCH_32)) { + asm volatile(PPC_TLBIEL(%0, %1, 0, 0, 0) : : "r"(va), "r"(0) : "memory"); + } else { + asm volatile(ASM_FTR_IFSET("tlbiel %0", PPC_TLBIEL_v205(%0, 1), %1) + : : "r" (va), "i" (CPU_FTR_ARCH_206) + : "memory"); + } break; } trace_tlbie(0, 1, va, 0, 0, 0, 0); diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index 64f0df5bb5cd..0e777d7dc332 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -628,7 +628,9 @@ void __init mmu_early_init_devtree(void) of_scan_flat_dt(dt_scan_mmu_pid_width, NULL); if (hvmode && !mmu_lpid_bits) { - if (early_cpu_has_feature(CPU_FTR_ARCH_207S)) + if (early_cpu_has_feature(CPU_FTR_ARCH_32)) + mmu_lpid_bits = 16; /* POWER12 */ + else if (early_cpu_has_feature(CPU_FTR_ARCH_207S)) mmu_lpid_bits = 12; /* POWER8-10 */ else mmu_lpid_bits = 10; /* POWER7 */ diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index dab106cae22b..fc9db691e820 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -471,8 +471,6 @@ static int bpf_jit_emit_func_call(u32 *image, struct codegen_context *ctx, u64 f 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 __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) { @@ -487,6 +485,8 @@ int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context * } #ifdef CONFIG_PPC_KERNEL_PCREL + long reladdr; + reladdr = func_addr - local_paca->kernelbase; /* @@ -525,7 +525,7 @@ 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)) { - ret = bpf_jit_emit_func_call(image, ctx, func_addr, _R12); + int ret = bpf_jit_emit_func_call(image, ctx, func_addr, _R12); if (ret) return ret; } else { diff --git a/arch/powerpc/net/bpf_timed_may_goto.S b/arch/powerpc/net/bpf_timed_may_goto.S index 6fd8b1c9f4ac..84ecf6fa7f5d 100644 --- a/arch/powerpc/net/bpf_timed_may_goto.S +++ b/arch/powerpc/net/bpf_timed_may_goto.S @@ -36,7 +36,7 @@ SYM_FUNC_START(arch_bpf_timed_may_goto) * BPF_REG_FP is r31; BPF_REG_AX is r12 (stack offset in bytes). */ add r3, r31, r12 - bl bpf_check_timed_may_goto + bl CFUNC(bpf_check_timed_may_goto) /* Put return value back into AX */ mr r12, r3 diff --git a/arch/powerpc/perf/Makefile b/arch/powerpc/perf/Makefile index 78dd7e25219e..bcc804c41338 100644 --- a/arch/powerpc/perf/Makefile +++ b/arch/powerpc/perf/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_PPC_PERF_CTRS) += core-book3s.o obj64-$(CONFIG_PPC_PERF_CTRS) += ppc970-pmu.o power5-pmu.o \ power5+-pmu.o power6-pmu.o power7-pmu.o \ isa207-common.o power8-pmu.o power9-pmu.o \ - generic-compat-pmu.o power10-pmu.o bhrb.o + generic-compat-pmu.o power10-pmu.o bhrb.o power12-pmu.o obj32-$(CONFIG_PPC_PERF_CTRS) += mpc7450-pmu.o obj-$(CONFIG_PPC_POWERNV) += imc-pmu.o diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 720b1a500922..10ded865583c 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -2613,6 +2613,8 @@ static int __init init_ppc64_pmu(void) return 0; else if (!init_power11_pmu()) return 0; + else if (!init_power12_pmu()) + return 0; else if (!init_ppc970_pmu()) return 0; else diff --git a/arch/powerpc/perf/internal.h b/arch/powerpc/perf/internal.h index a70ac471a5a5..84d6ca209c49 100644 --- a/arch/powerpc/perf/internal.h +++ b/arch/powerpc/perf/internal.h @@ -11,4 +11,5 @@ int __init init_power8_pmu(void); int __init init_power9_pmu(void); int __init init_power10_pmu(void); int __init init_power11_pmu(void); +int __init init_power12_pmu(void); int __init init_generic_compat_pmu(void); diff --git a/arch/powerpc/perf/power12-events-list.h b/arch/powerpc/perf/power12-events-list.h new file mode 100644 index 000000000000..cc26796d418b --- /dev/null +++ b/arch/powerpc/perf/power12-events-list.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Performance counter support for POWER12 processors. + * + * Copyright 2026 Athira Rajeev, IBM Corporation. + */ + +/* + * Power12 event codes. + */ +EVENT(PM_CYC, 0x600f4); +EVENT(PM_DISP_STALL_CYC, 0x100f8); +EVENT(PM_EXEC_STALL, 0x30008); +EVENT(PM_INST_CMPL, 0x500fa); +EVENT(PM_BR_CMPL, 0x4d05e); +EVENT(PM_BR_MPRED_CMPL, 0x400f6); +EVENT(PM_BR_FIN, 0x10068); +EVENT(PM_MPRED_BR_FIN, 0x27098); +EVENT(PM_LD_DEMAND_MISS_L1_FIN, 0x400f0); + +/* All L1 D cache load references counted at finish, gated by reject */ +EVENT(PM_LD_REF_L1, 0x100fc); +/* Load Missed L1 */ +EVENT(PM_LD_MISS_L1, 0x3e054); +/* Store Missed L1 */ +EVENT(PM_ST_MISS_L1, 0x300f0); +/* L1 cache data prefetches */ +EVENT(PM_LD_PREFETCH_CACHE_LINE_MISS, 0x1002c); +/* Demand iCache Miss */ +EVENT(PM_L1_ICACHE_MISS, 0x200fc); +/* Instruction fetches from L1 */ +EVENT(PM_INST_FROM_L1, 0x04080); +/* Instruction Demand sectors writtent into IL1 */ +EVENT(PM_INST_FROM_L1MISS, 0x03F00000001C040); +/* Instruction prefetch written into IL1 */ +EVENT(PM_IC_PREF_REQ, 0x040a0); +/* The data cache was reloaded from local core's L3 due to a demand load */ +EVENT(PM_DATA_FROM_L3, 0x10340000003C040); +/* Demand LD - L3 Miss (not L2 hit and not L3 hit) */ +EVENT(PM_DATA_FROM_L3MISS, 0x300fe); +/* All successful D-side store dispatches for this thread */ +EVENT(PM_L2_ST, 0x010000046080); +/* All successful D-side store dispatches for this thread that were L2 Miss */ +EVENT(PM_L2_ST_MISS, 0x26880); +/* Total HW L3 prefetches(Load+store) */ +EVENT(PM_L3_PF_MISS_L3, 0x100000016080); +/* Data PTEG reload */ +EVENT(PM_DTLB_MISS, 0x300fc); +/* ITLB Reloaded */ +EVENT(PM_ITLB_MISS, 0x400fc); + +EVENT(PM_CYC_ALT, 0x0001e); +EVENT(PM_INST_CMPL_ALT, 0x00002); + +/* + * Memory Access Events + * + * Primary PMU event used here is PM_MRK_INST_CMPL (0x401e0) + * To enable capturing of memory profiling, these MMCRA bits + * needs to be programmed and corresponding raw event format + * encoding. + * + * MMCRA bits encoding needed are + * SM (Sampling Mode) + * EM (Eligibility for Random Sampling) + * TECE (Threshold Event Counter Event) + * TS (Threshold Start Event) + * TE (Threshold End Event) + * + * Corresponding Raw Encoding bits: + * sample [EM,SM] + * thresh_sel (TECE) + * thresh start (TS) + * thresh end (TE) + */ + +EVENT(MEM_LOADS, 0x35340401e0); +EVENT(MEM_STORES, 0x353c0401e0); diff --git a/arch/powerpc/perf/power12-pmu.c b/arch/powerpc/perf/power12-pmu.c new file mode 100644 index 000000000000..89cc704b8324 --- /dev/null +++ b/arch/powerpc/perf/power12-pmu.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Performance counter support for Power12 processors. + * + * Copyright 2026 Athira Rajeev, IBM Corporation. + */ + +#define pr_fmt(fmt) "power12-pmu: " fmt + +#include "isa207-common.h" + +/* + * Raw event encoding for Power12: + * + * 60 56 52 48 44 40 36 32 + * | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | + * | | [ ] [ src_match ] [ src_mask ] | [ ] [ l2l3_sel ] [ thresh_ctl ] + * | | | | | | + * | | *- IFM (Linux) | | thresh start/stop -* + * | *- BHRB (Linux) | src_sel + * *- EBB (Linux) *invert_bit + * + * 28 24 20 16 12 8 4 0 + * | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | + * [ ] [ sample ] [ ] [ ] [ pmc ] [unit ] [ ] | m [ pmcxsel ] + * | | | | | | | + * | | | | | | *- mark + * | | | *- L1/L2/L3 cache_sel | |*-radix_scope_qual + * | | sdar_mode | + * | *- sampling mode for marked events *- combine + * | + * *- thresh_sel + * + * Below uses IBM bit numbering. + * + * MMCR1[x:y] = unit (PMCxUNIT) + * MMCR1[24] = pmc1combine[0] + * MMCR1[25] = pmc1combine[1] + * MMCR1[26] = pmc2combine[0] + * MMCR1[27] = pmc2combine[1] + * MMCR1[28] = pmc3combine[0] + * MMCR1[29] = pmc3combine[1] + * MMCR1[30] = pmc4combine[0] + * MMCR1[31] = pmc4combine[1] + * + * if pmc == 3 and unit == 0 and pmcxsel[0:6] == 0b0101011 + * MMCR1[20:27] = thresh_ctl + * else if pmc == 4 and unit == 0xf and pmcxsel[0:6] == 0b0101001 + * MMCR1[20:27] = thresh_ctl + * else + * MMCRA[48:55] = thresh_ctl (THRESH START/END) + * + * if thresh_sel: + * MMCRA[45:47] = thresh_sel + * + * if l2l3_sel: + * MMCR2[56:60] = l2l3_sel[0:4] + * + * MMCR1[16] = cache_sel[0] + * MMCR1[17] = cache_sel[1] + * MMCR1[18] = radix_scope_qual + * + * if mark: + * MMCRA[63] = 1 (SAMPLE_ENABLE) + * MMCRA[57:59] = sample[0:2] (RAND_SAMP_ELIG) + * MMCRA[61:62] = sample[3:4] (RAND_SAMP_MODE) + * + * if EBB and BHRB: + * MMCRA[32:33] = IFM + * + * MMCRA[SDAR_MODE] = sdar_mode[0:1] + */ + +/* + * Some power12 event codes. + */ +#define EVENT(_name, _code) enum{_name = _code} + +#include "power12-events-list.h" + +#undef EVENT + +/* MMCRA IFM bits - POWER12 */ +#define POWER12_MMCRA_IFM1 0x0000000040000000UL +#define POWER12_MMCRA_IFM2 0x0000000080000000UL +#define POWER12_MMCRA_IFM3 0x00000000C0000000UL +#define POWER12_MMCRA_BHRB_MASK 0x00000000C0000000UL + +extern u64 PERF_REG_EXTENDED_MASK; + +/* Table of alternatives, sorted by column 0 */ +static const unsigned int power12_event_alternatives[][MAX_ALT] = { + { PM_INST_CMPL_ALT, PM_INST_CMPL }, + { PM_CYC_ALT, PM_CYC }, +}; + +static int power12_get_alternatives(u64 event, unsigned int flags, u64 alt[]) +{ + int num_alt = 0; + + num_alt = isa207_get_alternatives(event, alt, + ARRAY_SIZE(power12_event_alternatives), flags, + power12_event_alternatives); + + return num_alt; +} + +static int power12_check_attr_config(struct perf_event *ev) +{ + u64 val; + u64 event = ev->attr.config; + + val = (event >> EVENT_SAMPLE_SHIFT) & EVENT_SAMPLE_MASK; + if (val == 0x10 || isa3XX_check_attr_config(ev)) + return -EINVAL; + + return 0; +} + +GENERIC_EVENT_ATTR(cpu-cycles, PM_CYC); +GENERIC_EVENT_ATTR(instructions, PM_INST_CMPL); +GENERIC_EVENT_ATTR(branch-instructions, PM_BR_FIN); +GENERIC_EVENT_ATTR(branch-misses, PM_MPRED_BR_FIN); +GENERIC_EVENT_ATTR(cache-references, PM_LD_REF_L1); +GENERIC_EVENT_ATTR(cache-misses, PM_LD_DEMAND_MISS_L1_FIN); +GENERIC_EVENT_ATTR(mem-loads, MEM_LOADS); +GENERIC_EVENT_ATTR(mem-stores, MEM_STORES); + +CACHE_EVENT_ATTR(L1-dcache-load-misses, PM_LD_MISS_L1); +CACHE_EVENT_ATTR(L1-dcache-loads, PM_LD_REF_L1); +CACHE_EVENT_ATTR(L1-dcache-prefetches, PM_LD_PREFETCH_CACHE_LINE_MISS); +CACHE_EVENT_ATTR(L1-dcache-store-misses, PM_ST_MISS_L1); +CACHE_EVENT_ATTR(L1-icache-load-misses, PM_L1_ICACHE_MISS); +CACHE_EVENT_ATTR(L1-icache-loads, PM_INST_FROM_L1); +CACHE_EVENT_ATTR(L1-icache-prefetches, PM_IC_PREF_REQ); +CACHE_EVENT_ATTR(LLC-load-misses, PM_DATA_FROM_L3MISS); +CACHE_EVENT_ATTR(LLC-loads, PM_DATA_FROM_L3); +CACHE_EVENT_ATTR(LLC-prefetches, PM_L3_PF_MISS_L3); +CACHE_EVENT_ATTR(LLC-store-misses, PM_L2_ST_MISS); +CACHE_EVENT_ATTR(LLC-stores, PM_L2_ST); +CACHE_EVENT_ATTR(branch-load-misses, PM_BR_MPRED_CMPL); +CACHE_EVENT_ATTR(branch-loads, PM_BR_CMPL); +CACHE_EVENT_ATTR(dTLB-load-misses, PM_DTLB_MISS); +CACHE_EVENT_ATTR(iTLB-load-misses, PM_ITLB_MISS); + +static struct attribute *power12_events_attr[] = { + GENERIC_EVENT_PTR(PM_CYC), + GENERIC_EVENT_PTR(PM_INST_CMPL), + GENERIC_EVENT_PTR(PM_BR_FIN), + GENERIC_EVENT_PTR(PM_MPRED_BR_FIN), + GENERIC_EVENT_PTR(PM_LD_REF_L1), + GENERIC_EVENT_PTR(PM_LD_DEMAND_MISS_L1_FIN), + GENERIC_EVENT_PTR(MEM_LOADS), + GENERIC_EVENT_PTR(MEM_STORES), + CACHE_EVENT_PTR(PM_LD_MISS_L1), + CACHE_EVENT_PTR(PM_LD_REF_L1), + CACHE_EVENT_PTR(PM_LD_PREFETCH_CACHE_LINE_MISS), + CACHE_EVENT_PTR(PM_ST_MISS_L1), + CACHE_EVENT_PTR(PM_L1_ICACHE_MISS), + CACHE_EVENT_PTR(PM_INST_FROM_L1), + CACHE_EVENT_PTR(PM_IC_PREF_REQ), + CACHE_EVENT_PTR(PM_DATA_FROM_L3MISS), + CACHE_EVENT_PTR(PM_DATA_FROM_L3), + CACHE_EVENT_PTR(PM_L3_PF_MISS_L3), + CACHE_EVENT_PTR(PM_L2_ST_MISS), + CACHE_EVENT_PTR(PM_L2_ST), + CACHE_EVENT_PTR(PM_BR_MPRED_CMPL), + CACHE_EVENT_PTR(PM_BR_CMPL), + CACHE_EVENT_PTR(PM_DTLB_MISS), + CACHE_EVENT_PTR(PM_ITLB_MISS), + NULL +}; + +static const struct attribute_group power12_pmu_events_group = { + .name = "events", + .attrs = power12_events_attr, +}; + +PMU_FORMAT_ATTR(event, "config:0-59"); +PMU_FORMAT_ATTR(pmcxsel, "config:0-7"); +PMU_FORMAT_ATTR(mark, "config:8"); +PMU_FORMAT_ATTR(combine, "config:10-11"); +PMU_FORMAT_ATTR(unit, "config:12-15"); +PMU_FORMAT_ATTR(pmc, "config:16-19"); +PMU_FORMAT_ATTR(cache_sel, "config:20-21"); +PMU_FORMAT_ATTR(sdar_mode, "config:22-23"); +PMU_FORMAT_ATTR(sample_mode, "config:24-28"); +PMU_FORMAT_ATTR(thresh_sel, "config:29-31"); +PMU_FORMAT_ATTR(thresh_stop, "config:32-35"); +PMU_FORMAT_ATTR(thresh_start, "config:36-39"); +PMU_FORMAT_ATTR(l2l3_sel, "config:40-44"); +PMU_FORMAT_ATTR(src_sel, "config:45-46"); +PMU_FORMAT_ATTR(invert_bit, "config:47"); +PMU_FORMAT_ATTR(src_mask, "config:48-53"); +PMU_FORMAT_ATTR(src_match, "config:54-59"); +PMU_FORMAT_ATTR(radix_scope, "config:9"); +PMU_FORMAT_ATTR(thresh_cmp, "config1:0-17"); + +static struct attribute *power12_pmu_format_attr[] = { + &format_attr_event.attr, + &format_attr_pmcxsel.attr, + &format_attr_mark.attr, + &format_attr_combine.attr, + &format_attr_unit.attr, + &format_attr_pmc.attr, + &format_attr_cache_sel.attr, + &format_attr_sdar_mode.attr, + &format_attr_sample_mode.attr, + &format_attr_thresh_sel.attr, + &format_attr_thresh_stop.attr, + &format_attr_thresh_start.attr, + &format_attr_l2l3_sel.attr, + &format_attr_src_sel.attr, + &format_attr_invert_bit.attr, + &format_attr_src_mask.attr, + &format_attr_src_match.attr, + &format_attr_radix_scope.attr, + &format_attr_thresh_cmp.attr, + NULL, +}; + +static const struct attribute_group power12_pmu_format_group = { + .name = "format", + .attrs = power12_pmu_format_attr, +}; + +static const struct attribute_group *power12_pmu_attr_groups[] = { + &power12_pmu_format_group, + &power12_pmu_events_group, + NULL, +}; + +static int power12_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = PM_CYC, + [PERF_COUNT_HW_INSTRUCTIONS] = PM_INST_CMPL, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = PM_BR_FIN, + [PERF_COUNT_HW_BRANCH_MISSES] = PM_MPRED_BR_FIN, + [PERF_COUNT_HW_CACHE_REFERENCES] = PM_LD_REF_L1, + [PERF_COUNT_HW_CACHE_MISSES] = PM_LD_DEMAND_MISS_L1_FIN, +}; + +static u64 power12_bhrb_filter_map(u64 branch_sample_type) +{ + u64 pmu_bhrb_filter = 0; + + /* BHRB and regular PMU events share the same privilege state + * filter configuration. BHRB is always recorded along with a + * regular PMU event. As the privilege state filter is handled + * in the basic PMC configuration of the accompanying regular + * PMU event, we ignore any separate BHRB specific request. + */ + + /* No branch filter requested */ + if (branch_sample_type & PERF_SAMPLE_BRANCH_ANY) + return pmu_bhrb_filter; + + /* Invalid branch filter options - HW does not support */ + if (branch_sample_type & PERF_SAMPLE_BRANCH_ANY_RETURN) + return -1; + + if (branch_sample_type & PERF_SAMPLE_BRANCH_IND_CALL) { + pmu_bhrb_filter |= POWER12_MMCRA_IFM2; + return pmu_bhrb_filter; + } + + if (branch_sample_type & PERF_SAMPLE_BRANCH_COND) { + pmu_bhrb_filter |= POWER12_MMCRA_IFM3; + return pmu_bhrb_filter; + } + + if (branch_sample_type & PERF_SAMPLE_BRANCH_CALL) + return -1; + + if (branch_sample_type & PERF_SAMPLE_BRANCH_ANY_CALL) { + pmu_bhrb_filter |= POWER12_MMCRA_IFM1; + return pmu_bhrb_filter; + } + + /* Every thing else is unsupported */ + return -1; +} + +static void power12_config_bhrb(u64 pmu_bhrb_filter) +{ + pmu_bhrb_filter &= POWER12_MMCRA_BHRB_MASK; + + /* Enable BHRB filter in PMU */ + mtspr(SPRN_MMCRA, (mfspr(SPRN_MMCRA) | pmu_bhrb_filter)); +} + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + */ +static u64 power12_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + [C(L1D)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = PM_LD_REF_L1, + [C(RESULT_MISS)] = PM_LD_MISS_L1, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = 0, + [C(RESULT_MISS)] = PM_ST_MISS_L1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = PM_LD_PREFETCH_CACHE_LINE_MISS, + [C(RESULT_MISS)] = 0, + }, + }, + [C(L1I)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = PM_INST_FROM_L1, + [C(RESULT_MISS)] = PM_L1_ICACHE_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = PM_INST_FROM_L1MISS, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = PM_IC_PREF_REQ, + [C(RESULT_MISS)] = 0, + }, + }, + [C(LL)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = PM_DATA_FROM_L3, + [C(RESULT_MISS)] = PM_DATA_FROM_L3MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = PM_L2_ST, + [C(RESULT_MISS)] = PM_L2_ST_MISS, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = PM_L3_PF_MISS_L3, + [C(RESULT_MISS)] = 0, + }, + }, + [C(DTLB)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = 0, + [C(RESULT_MISS)] = PM_DTLB_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + }, + [C(ITLB)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = 0, + [C(RESULT_MISS)] = PM_ITLB_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + }, + [C(BPU)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = PM_BR_CMPL, + [C(RESULT_MISS)] = PM_BR_MPRED_CMPL, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + }, + [C(NODE)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + }, +}; + +#undef C + +/* + * Set the MMCR0[CC56RUN] bit to enable counting for + * PMC5 and PMC6 regardless of the state of CTRL[RUN], + * so that we can use counters 5 and 6 as PM_INST_CMPL and + * PM_CYC. + */ +static int power12_compute_mmcr(u64 event[], int n_ev, + unsigned int hwc[], struct mmcr_regs *mmcr, + struct perf_event *pevents[], u32 flags) +{ + int ret; + + ret = isa207_compute_mmcr(event, n_ev, hwc, mmcr, pevents, flags); + if (!ret) + mmcr->mmcr0 |= MMCR0_C56RUN; + return ret; +} + +static struct power_pmu power12_pmu = { + .name = "Power12", + .n_counter = MAX_PMU_COUNTERS, + .add_fields = ISA207_ADD_FIELDS, + .test_adder = ISA207_TEST_ADDER, + .group_constraint_mask = CNST_CACHE_PMC4_MASK, + .group_constraint_val = CNST_CACHE_PMC4_VAL, + .compute_mmcr = power12_compute_mmcr, + .config_bhrb = power12_config_bhrb, + .bhrb_filter_map = power12_bhrb_filter_map, + .get_constraint = isa207_get_constraint, + .get_alternatives = power12_get_alternatives, + .get_mem_data_src = isa207_get_mem_data_src, + .get_mem_weight = isa207_get_mem_weight, + .disable_pmc = isa207_disable_pmc, + .flags = PPMU_HAS_SIER | PPMU_ARCH_207S | + PPMU_ARCH_31 | PPMU_HAS_ATTR_CONFIG1 | + PPMU_P10, + .n_generic = ARRAY_SIZE(power12_generic_events), + .generic_events = power12_generic_events, + .cache_events = &power12_cache_events, + .attr_groups = power12_pmu_attr_groups, + .bhrb_nr = 32, + .capabilities = PERF_PMU_CAP_EXTENDED_REGS, + .check_attr_config = power12_check_attr_config, +}; + +int __init init_power12_pmu(void) +{ + unsigned int pvr; + int rc; + + pvr = mfspr(SPRN_PVR); + if (PVR_VER(pvr) != PVR_POWER12) + return -ENODEV; + + /* Set the PERF_REG_EXTENDED_MASK here */ + PERF_REG_EXTENDED_MASK = PERF_REG_PMU_MASK_31; + + rc = register_power_pmu(&power12_pmu); + if (rc) + return rc; + + /* Tell userspace that EBB is supported */ + cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_EBB; + + return 0; +} diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index fc79f8466933..150813cea945 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -227,13 +227,6 @@ config PPC44x_SIMPLE help This option enables the simple PowerPC 44x platform support. -config PPC4xx_GPIO - bool "PPC4xx GPIO support" - depends on 44x - select GPIOLIB - help - Enable gpiolib support for ppc440 based boards - # 44x specific CPU modules, selected based on the board above. config 440EP bool diff --git a/arch/powerpc/platforms/44x/Makefile b/arch/powerpc/platforms/44x/Makefile index ca7b1bb442d9..4598d8b89bf4 100644 --- a/arch/powerpc/platforms/44x/Makefile +++ b/arch/powerpc/platforms/44x/Makefile @@ -15,4 +15,4 @@ obj-$(CONFIG_FSP2) += fsp2.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_PPC4xx_HSTA_MSI) += hsta_msi.o obj-$(CONFIG_PPC4xx_CPM) += cpm.o -obj-$(CONFIG_PPC4xx_GPIO) += gpio.o + diff --git a/arch/powerpc/platforms/44x/gpio.c b/arch/powerpc/platforms/44x/gpio.c deleted file mode 100644 index aea0d913b59d..000000000000 --- a/arch/powerpc/platforms/44x/gpio.c +++ /dev/null @@ -1,210 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * PPC4xx gpio driver - * - * Copyright (c) 2008 Harris Corporation - * Copyright (c) 2008 Sascha Hauer , Pengutronix - * Copyright (c) MontaVista Software, Inc. 2008. - * - * Author: Steve Falco - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GPIO_MASK(gpio) (0x80000000 >> (gpio)) -#define GPIO_MASK2(gpio) (0xc0000000 >> ((gpio) * 2)) - -/* Physical GPIO register layout */ -struct ppc4xx_gpio { - __be32 or; - __be32 tcr; - __be32 osrl; - __be32 osrh; - __be32 tsrl; - __be32 tsrh; - __be32 odr; - __be32 ir; - __be32 rr1; - __be32 rr2; - __be32 rr3; - __be32 reserved1; - __be32 isr1l; - __be32 isr1h; - __be32 isr2l; - __be32 isr2h; - __be32 isr3l; - __be32 isr3h; -}; - -struct ppc4xx_gpio_chip { - struct gpio_chip gc; - void __iomem *regs; - spinlock_t lock; -}; - -/* - * GPIO LIB API implementation for GPIOs - * - * There are a maximum of 32 gpios in each gpio controller. - */ - -static int ppc4xx_gpio_get(struct gpio_chip *gc, unsigned int gpio) -{ - struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); - struct ppc4xx_gpio __iomem *regs = chip->regs; - - return !!(in_be32(®s->ir) & GPIO_MASK(gpio)); -} - -static inline void -__ppc4xx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); - struct ppc4xx_gpio __iomem *regs = chip->regs; - - if (val) - setbits32(®s->or, GPIO_MASK(gpio)); - else - clrbits32(®s->or, GPIO_MASK(gpio)); -} - -static int ppc4xx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - - __ppc4xx_gpio_set(gc, gpio, val); - - spin_unlock_irqrestore(&chip->lock, flags); - - pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); - - return 0; -} - -static int ppc4xx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) -{ - struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); - struct ppc4xx_gpio __iomem *regs = chip->regs; - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - - /* Disable open-drain function */ - clrbits32(®s->odr, GPIO_MASK(gpio)); - - /* Float the pin */ - clrbits32(®s->tcr, GPIO_MASK(gpio)); - - /* Bits 0-15 use TSRL/OSRL, bits 16-31 use TSRH/OSRH */ - if (gpio < 16) { - clrbits32(®s->osrl, GPIO_MASK2(gpio)); - clrbits32(®s->tsrl, GPIO_MASK2(gpio)); - } else { - clrbits32(®s->osrh, GPIO_MASK2(gpio)); - clrbits32(®s->tsrh, GPIO_MASK2(gpio)); - } - - spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static int -ppc4xx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); - struct ppc4xx_gpio __iomem *regs = chip->regs; - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - - /* First set initial value */ - __ppc4xx_gpio_set(gc, gpio, val); - - /* Disable open-drain function */ - clrbits32(®s->odr, GPIO_MASK(gpio)); - - /* Drive the pin */ - setbits32(®s->tcr, GPIO_MASK(gpio)); - - /* Bits 0-15 use TSRL, bits 16-31 use TSRH */ - if (gpio < 16) { - clrbits32(®s->osrl, GPIO_MASK2(gpio)); - clrbits32(®s->tsrl, GPIO_MASK2(gpio)); - } else { - clrbits32(®s->osrh, GPIO_MASK2(gpio)); - clrbits32(®s->tsrh, GPIO_MASK2(gpio)); - } - - spin_unlock_irqrestore(&chip->lock, flags); - - pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); - - return 0; -} - -static int ppc4xx_gpio_probe(struct platform_device *ofdev) -{ - struct device *dev = &ofdev->dev; - struct device_node *np = dev->of_node; - struct ppc4xx_gpio_chip *chip; - struct gpio_chip *gc; - - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - spin_lock_init(&chip->lock); - - gc = &chip->gc; - - gc->base = -1; - gc->ngpio = 32; - gc->direction_input = ppc4xx_gpio_dir_in; - gc->direction_output = ppc4xx_gpio_dir_out; - gc->get = ppc4xx_gpio_get; - gc->set = ppc4xx_gpio_set; - - gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np); - if (!gc->label) - return -ENOMEM; - - chip->regs = devm_of_iomap(dev, np, 0, NULL); - if (IS_ERR(chip->regs)) - return PTR_ERR(chip->regs); - - return devm_gpiochip_add_data(dev, gc, chip); -} - -static const struct of_device_id ppc4xx_gpio_match[] = { - { - .compatible = "ibm,ppc4xx-gpio", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, ppc4xx_gpio_match); - -static struct platform_driver ppc4xx_gpio_driver = { - .probe = ppc4xx_gpio_probe, - .driver = { - .name = "ppc4xx-gpio", - .of_match_table = ppc4xx_gpio_match, - }, -}; - -static int __init ppc4xx_gpio_init(void) -{ - return platform_driver_register(&ppc4xx_gpio_driver); -} -arch_initcall(ppc4xx_gpio_init); diff --git a/arch/powerpc/platforms/powermac/bootx_init.c b/arch/powerpc/platforms/powermac/bootx_init.c index 72eb99aba40f..abceae91dfde 100644 --- a/arch/powerpc/platforms/powermac/bootx_init.c +++ b/arch/powerpc/platforms/powermac/bootx_init.c @@ -284,9 +284,7 @@ static void __init bootx_scan_dt_build_struct(unsigned long base, dt_push_token(OF_DT_BEGIN_NODE, mem_end); /* get the node's full name */ - namep = np->full_name ? (char *)(base + np->full_name) : NULL; - if (namep == NULL) - namep = ""; + namep = np->full_name ? (char *)(base + np->full_name) : ""; l = strlen(namep); DBG("* struct: %s\n", namep); diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 06ed5e2aa265..2af92e7ba4ee 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -312,7 +312,7 @@ static void __noreturn pnv_restart(char *cmd) pnv_prepare_going_down(); do { - if (!cmd || !strlen(cmd)) + if (!cmd || *cmd == '\0') rc = opal_cec_reboot(); else if (strcmp(cmd, "full") == 0) rc = opal_cec_reboot2(OPAL_REBOOT_FULL_IPL, NULL); diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index 8f41ef364fc6..b1201dbafcaf 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -332,10 +332,12 @@ static void pnv_cause_ipi(int cpu) static void __init pnv_smp_probe(void) { - if (xive_enabled()) - xive_smp_probe(); - else + if (xive_enabled()) { + if (xive_smp_probe() < 0) + return; + } else { xics_smp_probe(); + } if (cpu_has_feature(CPU_FTR_DBELL)) { ic_cause_ipi = smp_ops->cause_ipi; diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index a71c0b0cfb07..33a676fa3d6c 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -361,12 +362,9 @@ static int __init ps3_create_spu(struct spu *spu, void *data) if (result) goto fail_enable; - /* Make sure the spu is in SPE_EX_STATE_EXECUTED. */ - - /* need something better here!!! */ - while (in_be64(&spu_pdata(spu)->shadow->spe_execution_status) - != SPE_EX_STATE_EXECUTED) - (void)0; + while (in_be64(&spu_pdata(spu)->shadow->spe_execution_status) != + SPE_EX_STATE_EXECUTED) + cpu_relax(); return result; diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 3e1f915fe4f6..272e9aab66d4 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -812,18 +812,11 @@ static struct device_node *pci_dma_find(struct device_node *dn, /* parse DMA window property. During normal system boot, only default * DMA window is passed in OF. But, for kdump, a dedicated adapter might - * have both default and DDW in FDT. In this scenario, DDW takes precedence - * over default window. + * have both default and DDW in FDT. In this scenario, default window + * takes precedence over DDW. For a dedicated adapter, default window will + * potentially have more unused TCEs. */ - if (ddw_win) { - struct dynamic_dma_window_prop *p; - - p = (struct dynamic_dma_window_prop *)ddw_prop; - prop->liobn = p->liobn; - prop->dma_base = p->dma_base; - prop->tce_shift = p->tce_shift; - prop->window_shift = p->window_shift; - } else if (default_win) { + if (default_win) { unsigned long offset, size, liobn; of_parse_dma_window(rdn, default_prop, &liobn, &offset, &size); @@ -832,6 +825,14 @@ static struct device_node *pci_dma_find(struct device_node *dn, prop->dma_base = cpu_to_be64(offset); prop->tce_shift = cpu_to_be32(IOMMU_PAGE_SHIFT_4K); prop->window_shift = cpu_to_be32(order_base_2(size)); + } else { + struct dynamic_dma_window_prop *p; + + p = (struct dynamic_dma_window_prop *)ddw_prop; + prop->liobn = p->liobn; + prop->dma_base = p->dma_base; + prop->tce_shift = p->tce_shift; + prop->window_shift = p->window_shift; } return rdn; diff --git a/arch/powerpc/platforms/pseries/papr_platform_attributes.c b/arch/powerpc/platforms/pseries/papr_platform_attributes.c index 9c3758aa54c6..aacdaa1ebf63 100644 --- a/arch/powerpc/platforms/pseries/papr_platform_attributes.c +++ b/arch/powerpc/platforms/pseries/papr_platform_attributes.c @@ -323,12 +323,8 @@ static int __init papr_init(void) } for (idx = 0; idx < num_attrs; idx++) { - bool show_val_desc = true; - /* Do not add the value desc attr if it does not exist */ - if (strnlen(esi_attrs[idx].value_desc, - sizeof(esi_attrs[idx].value_desc)) == 0) - show_val_desc = false; + bool show_val_desc = *esi_attrs[idx].value_desc != '\0'; if (add_attr_group(be64_to_cpu(esi_attrs[idx].id), &papr_groups[idx], diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index adafd593d9d3..7b8713bdd978 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -440,6 +441,8 @@ static __be64 *fwnmi_get_savep(struct pt_regs *regs) static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs) { struct rtas_error_log *h; + u32 extended_log_length; + size_t len; __be64 *savep; savep = fwnmi_get_savep(regs); @@ -449,17 +452,12 @@ static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs) regs->gpr[3] = be64_to_cpu(savep[0]); /* restore original r3 */ h = (struct rtas_error_log *)&savep[1]; + extended_log_length = rtas_error_extended(h) ? rtas_error_extended_log_length(h) : 0; + len = struct_size(h, buffer, extended_log_length); + len = min(len, RTAS_ERROR_LOG_MAX); /* Use the per cpu buffer from paca to store rtas error log */ memset(local_paca->mce_data_buf, 0, RTAS_ERROR_LOG_MAX); - if (!rtas_error_extended(h)) { - memcpy(local_paca->mce_data_buf, h, sizeof(__u64)); - } else { - int len, error_log_length; - - error_log_length = 8 + rtas_error_extended_log_length(h); - len = min_t(int, error_log_length, RTAS_ERROR_LOG_MAX); - memcpy(local_paca->mce_data_buf, h, len); - } + memcpy(local_paca->mce_data_buf, h, len); return (struct rtas_error_log *)local_paca->mce_data_buf; } diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 7faebcffc9df..18e3f1a036e3 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -307,7 +307,7 @@ static int do_remove_property(char *buf, size_t bufsize) if (tmp) *tmp = '\0'; - if (strlen(buf) == 0) + if (*buf == '\0') return -EINVAL; return of_remove_property(np, of_find_property(np, buf, NULL)); @@ -330,7 +330,7 @@ static int do_update_property(char *buf, size_t bufsize) if (!next_prop) return -EINVAL; - if (!strlen(name)) + if (*name == '\0') return -ENODEV; newprop = new_property(name, length, value, NULL); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 1223dc961242..f29e7547c995 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -77,6 +77,7 @@ #include #include #include +#include #include "pseries.h" @@ -185,14 +186,37 @@ static void __init fwnmi_init(void) #endif } +static void pseries_crash_stop_watchdogs(void) +{ + long rc; + + rc = plpar_hcall_norets_notrace(H_WATCHDOG, PSERIES_WDTF_OP_STOP, + PSERIES_WDT_NUM_ALL); + if (rc != H_SUCCESS && rc != H_NOOP) + pr_warn("Could not stop watchdogs before kdump rc=%ld\n", rc); +} + /* * Affix a device for the first timer to the platform bus if * we have firmware support for the H_WATCHDOG hypercall. */ static __init int pseries_wdt_init(void) { - if (firmware_has_feature(FW_FEATURE_WATCHDOG)) - platform_device_register_simple("pseries-wdt", 0, NULL, 0); + struct platform_device *pdev; + + if (!firmware_has_feature(FW_FEATURE_WATCHDOG)) + return 0; + + pdev = platform_device_register_simple("pseries-wdt", 0, NULL, 0); + + if (IS_ERR(pdev)) { + pr_err("Failed to register pseries-wdt platform device\n"); + return PTR_ERR(pdev); + } + + if (crash_shutdown_register(pseries_crash_stop_watchdogs)) + pr_warn("Could not register watchdog crash shutdown handler\n"); + return 0; } machine_subsys_initcall(pseries, pseries_wdt_init); diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index bf3d7ed3be01..9e1bed383e33 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -199,10 +199,12 @@ static int pseries_cause_nmi_ipi(int cpu) static __init void pSeries_smp_probe(void) { - if (xive_enabled()) - xive_smp_probe(); - else + if (xive_enabled()) { + if (xive_smp_probe() < 0) + return; + } else { xics_smp_probe(); + } /* No doorbell facility, must use the interrupt controller for IPIs */ if (!cpu_has_feature(CPU_FTR_DBELL)) diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c index 07ea605ab0e6..b5d200e3ad68 100644 --- a/arch/powerpc/sysdev/cpm_common.c +++ b/arch/powerpc/sysdev/cpm_common.c @@ -181,6 +181,18 @@ static int cpm2_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int cpm2_gpio32_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct cpm2_gpio32_chip *cpm2_gc = gpiochip_get_data(gc); + struct cpm2_ioports __iomem *iop = cpm2_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 cpm2_gpiochip_add32(struct device *dev) { struct device_node *np = dev->of_node; @@ -199,6 +211,7 @@ int cpm2_gpiochip_add32(struct device *dev) gc->ngpio = 32; gc->direction_input = cpm2_gpio32_dir_in; gc->direction_output = cpm2_gpio32_dir_out; + gc->get_direction = cpm2_gpio32_get_direction; gc->get = cpm2_gpio32_get; gc->set = cpm2_gpio32_set; gc->parent = dev; diff --git a/arch/powerpc/sysdev/xive/common.c b/arch/powerpc/sysdev/xive/common.c index dadd1f46ec93..8ae088632337 100644 --- a/arch/powerpc/sysdev/xive/common.c +++ b/arch/powerpc/sysdev/xive/common.c @@ -74,6 +74,8 @@ static struct xive_ipi_desc { */ static unsigned int xive_ipi_cpu_to_irq(unsigned int cpu) { + if (!xive_ipis) + return XIVE_BAD_IRQ; return xive_ipis[early_cpu_to_node(cpu)].irq; } #endif @@ -1132,8 +1134,7 @@ static int __init xive_init_ipis(void) if (!ipi_domain) goto out_free_fwnode; - xive_ipis = kzalloc_objs(*xive_ipis, nr_node_ids, - GFP_KERNEL | __GFP_NOFAIL); + xive_ipis = kzalloc_objs(*xive_ipis, nr_node_ids, GFP_KERNEL); if (!xive_ipis) goto out_free_domain; @@ -1158,6 +1159,7 @@ static int __init xive_init_ipis(void) out_free_xive_ipis: kfree(xive_ipis); + xive_ipis = NULL; out_free_domain: irq_domain_remove(ipi_domain); out_free_fwnode: @@ -1190,6 +1192,9 @@ static int xive_setup_cpu_ipi(unsigned int cpu) pr_debug("Setting up IPI for CPU %d\n", cpu); + if (xive_ipi_irq == XIVE_BAD_IRQ) + return -EIO; + xc = per_cpu(xive_cpu, cpu); /* Check if we are already setup */ @@ -1234,6 +1239,9 @@ noinstr static void xive_cleanup_cpu_ipi(unsigned int cpu, struct xive_cpu *xc) /* Disable the IPI and free the IRQ data */ + if (xive_ipi_irq == XIVE_BAD_IRQ) + return; + /* Already cleaned up ? */ if (xc->hw_ipi == XIVE_BAD_IRQ) return; @@ -1257,15 +1265,23 @@ noinstr static void xive_cleanup_cpu_ipi(unsigned int cpu, struct xive_cpu *xc) xive_ops->put_ipi(cpu, xc); } -void __init xive_smp_probe(void) +int __init xive_smp_probe(void) { - smp_ops->cause_ipi = xive_cause_ipi; + int ret; /* Register the IPI */ - xive_init_ipis(); + ret = xive_init_ipis(); + if (ret < 0) + return ret; /* Allocate and setup IPI for the boot CPU */ - xive_setup_cpu_ipi(smp_processor_id()); + ret = xive_setup_cpu_ipi(smp_processor_id()); + if (ret < 0) + return ret; + + smp_ops->cause_ipi = xive_cause_ipi; + + return 0; } #endif /* CONFIG_SMP */ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 28cf6d2e83c2..7a09e42672b4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -593,6 +593,13 @@ config GPIO_POLARFIRE_SOC help Say yes here to support the GPIO controllers on Microchip FPGAs. +config GPIO_PPC44X + tristate "PPC44x GPIO support" + depends on 44x || COMPILE_TEST + select GPIO_GENERIC + help + Enable gpiolib support for ppc440 based boards. + config GPIO_PXA bool "PXA GPIO support" depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4d0e900402fc..23aedc107cc5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -147,6 +147,7 @@ obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o +obj-$(CONFIG_GPIO_PPC44X) += gpio-ppc44x.o obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o obj-$(CONFIG_GPIO_POLARFIRE_SOC) += gpio-mpfs.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o diff --git a/drivers/gpio/gpio-ppc44x.c b/drivers/gpio/gpio-ppc44x.c new file mode 100644 index 000000000000..189667e0e1ba --- /dev/null +++ b/drivers/gpio/gpio-ppc44x.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PPC44x gpio driver + * + * Copyright (c) 2008 Harris Corporation + * Copyright (c) 2008 Sascha Hauer , Pengutronix + * Copyright (c) MontaVista Software, Inc. 2008. + * + * Author: Steve Falco + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPIO_MASK(gpio) (0x80000000 >> (gpio)) +#define GPIO_MASK2(gpio) (0xc0000000 >> (((gpio) % 16) * 2)) + +/* Physical GPIO register layout */ +struct ppc44x_gpio { + __be32 or; + __be32 tcr; + __be32 osrl; + __be32 osrh; + __be32 tsrl; + __be32 tsrh; + __be32 odr; + __be32 ir; + __be32 rr1; + __be32 rr2; + __be32 rr3; + __be32 reserved1; + __be32 isr1l; + __be32 isr1h; + __be32 isr2l; + __be32 isr2h; + __be32 isr3l; + __be32 isr3h; +}; + +struct ppc44x_gpio_chip { + struct gpio_generic_chip chip; + void __iomem *regs; +}; + +static inline void ppc44x_clrbits32(void __iomem *addr, u32 mask) +{ + u32 val = ioread32be(addr); + + val &= ~mask; + iowrite32be(val, addr); +} + +static inline void ppc44x_setbits32(void __iomem *addr, u32 mask) +{ + u32 val = ioread32be(addr); + + val |= mask; + iowrite32be(val, addr); +} + +/* + * GPIO LIB API implementation for GPIOs + * + * There are a maximum of 32 gpios in each gpio controller. + */ + +static inline void +__ppc44x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct ppc44x_gpio_chip *chip = gpiochip_get_data(gc); + struct gpio_generic_chip *gen_gc = &chip->chip; + + if (val) + gen_gc->sdata |= GPIO_MASK(gpio); + else + gen_gc->sdata &= ~GPIO_MASK(gpio); + + gpio_generic_write_reg(gen_gc, gen_gc->reg_set, gen_gc->sdata); +} + +static int ppc44x_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct ppc44x_gpio_chip *chip = gpiochip_get_data(gc); + struct gpio_generic_chip *gen_gc = &chip->chip; + struct ppc44x_gpio __iomem *regs = chip->regs; + + guard(gpio_generic_lock_irqsave)(gen_gc); + + /* Disable open-drain function */ + ppc44x_clrbits32(®s->odr, GPIO_MASK(gpio)); + + /* Float the pin */ + ppc44x_clrbits32(®s->tcr, GPIO_MASK(gpio)); + gen_gc->sdir &= ~GPIO_MASK(gpio); + + /* Bits 0-15 use TSRL/OSRL, bits 16-31 use TSRH/OSRH */ + if (gpio < 16) { + ppc44x_clrbits32(®s->osrl, GPIO_MASK2(gpio)); + ppc44x_clrbits32(®s->tsrl, GPIO_MASK2(gpio)); + } else { + ppc44x_clrbits32(®s->osrh, GPIO_MASK2(gpio)); + ppc44x_clrbits32(®s->tsrh, GPIO_MASK2(gpio)); + } + + return 0; +} + +static int +ppc44x_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct ppc44x_gpio_chip *chip = gpiochip_get_data(gc); + struct gpio_generic_chip *gen_gc = &chip->chip; + struct ppc44x_gpio __iomem *regs = chip->regs; + + guard(gpio_generic_lock_irqsave)(gen_gc); + + /* First set initial value */ + __ppc44x_gpio_set(gc, gpio, val); + + /* Disable open-drain function */ + ppc44x_clrbits32(®s->odr, GPIO_MASK(gpio)); + + /* Drive the pin */ + ppc44x_setbits32(®s->tcr, GPIO_MASK(gpio)); + gen_gc->sdir |= GPIO_MASK(gpio); + + /* Bits 0-15 use TSRL, bits 16-31 use TSRH */ + if (gpio < 16) { + ppc44x_clrbits32(®s->osrl, GPIO_MASK2(gpio)); + ppc44x_clrbits32(®s->tsrl, GPIO_MASK2(gpio)); + } else { + ppc44x_clrbits32(®s->osrh, GPIO_MASK2(gpio)); + ppc44x_clrbits32(®s->tsrh, GPIO_MASK2(gpio)); + } + + pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + + return 0; +} + +static int ppc44x_gpio_probe(struct platform_device *ofdev) +{ + struct device *dev = &ofdev->dev; + struct ppc44x_gpio __iomem *regs; + struct ppc44x_gpio_chip *chip; + struct gpio_generic_chip_config config; + struct gpio_chip *gc; + int ret; + + regs = devm_platform_ioremap_resource(ofdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->regs = regs; + + config = (struct gpio_generic_chip_config) { + .dev = dev, + .sz = 4, + .dat = ®s->ir, + .set = ®s->or, + .dirout = ®s->tcr, + .flags = GPIO_GENERIC_BIG_ENDIAN | + GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER, + }; + + ret = gpio_generic_chip_init(&chip->chip, &config); + if (ret) + return ret; + + gc = &chip->chip.gc; + gc->label = dev_name(dev); + gc->parent = dev; + gc->direction_input = ppc44x_gpio_dir_in; + gc->direction_output = ppc44x_gpio_dir_out; + + return devm_gpiochip_add_data(dev, gc, chip); +} + +static const struct of_device_id ppc44x_gpio_match[] = { + { + .compatible = "ibm,ppc4xx-gpio", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ppc44x_gpio_match); + +static struct platform_driver ppc44x_gpio_driver = { + .probe = ppc44x_gpio_probe, + .driver = { + .name = "ppc44x-gpio", + .of_match_table = ppc44x_gpio_match, + }, +}; + +MODULE_DESCRIPTION("PPC44x gpio driver"); +MODULE_LICENSE("GPL"); + +module_platform_driver(ppc44x_gpio_driver); diff --git a/drivers/watchdog/pseries-wdt.c b/drivers/watchdog/pseries-wdt.c index 48d67f7c972a..e97b943e1d3c 100644 --- a/drivers/watchdog/pseries-wdt.c +++ b/drivers/watchdog/pseries-wdt.c @@ -12,61 +12,10 @@ #include #include #include +#include #define DRV_NAME "pseries-wdt" -/* - * H_WATCHDOG Input - * - * R4: "flags": - * - * Bits 48-55: "operation" - */ -#define PSERIES_WDTF_OP_START 0x100UL /* start timer */ -#define PSERIES_WDTF_OP_STOP 0x200UL /* stop timer */ -#define PSERIES_WDTF_OP_QUERY 0x300UL /* query timer capabilities */ - -/* - * Bits 56-63: "timeoutAction" (for "Start Watchdog" only) - */ -#define PSERIES_WDTF_ACTION_HARD_POWEROFF 0x1UL /* poweroff */ -#define PSERIES_WDTF_ACTION_HARD_RESTART 0x2UL /* restart */ -#define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3UL /* dump + restart */ - -/* - * H_WATCHDOG Output - * - * R3: Return code - * - * H_SUCCESS The operation completed. - * - * H_BUSY The hypervisor is too busy; retry the operation. - * - * H_PARAMETER The given "flags" are somehow invalid. Either the - * "operation" or "timeoutAction" is invalid, or a - * reserved bit is set. - * - * H_P2 The given "watchdogNumber" is zero or exceeds the - * supported maximum value. - * - * H_P3 The given "timeoutInMs" is below the supported - * minimum value. - * - * H_NOOP The given "watchdogNumber" is already stopped. - * - * H_HARDWARE The operation failed for ineffable reasons. - * - * H_FUNCTION The H_WATCHDOG hypercall is not supported by this - * hypervisor. - * - * R4: - * - * - For the "Query Watchdog Capabilities" operation, a 64-bit - * structure: - */ -#define PSERIES_WDTQ_MIN_TIMEOUT(cap) (((cap) >> 48) & 0xffff) -#define PSERIES_WDTQ_MAX_NUMBER(cap) (((cap) >> 32) & 0xffff) - static const unsigned long pseries_wdt_action[] = { [0] = PSERIES_WDTF_ACTION_HARD_POWEROFF, [1] = PSERIES_WDTF_ACTION_HARD_RESTART, diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 419011097fa8..70e36e6a0ad4 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -997,6 +997,7 @@ struct kvm_enable_cap { #define KVM_CAP_S390_KEYOP 247 #define KVM_CAP_S390_VSIE_ESAMODE 248 #define KVM_CAP_S390_HPAGE_2G 249 +#define KVM_CAP_PPC_COMPAT_CAPS 250 struct kvm_irq_routing_irqchip { __u32 irqchip; @@ -1341,6 +1342,8 @@ struct kvm_s390_keyop { /* Available with KVM_CAP_COUNTER_OFFSET */ #define KVM_ARM_SET_COUNTER_OFFSET _IOW(KVMIO, 0xb5, struct kvm_arm_counter_offset) #define KVM_ARM_GET_REG_WRITABLE_MASKS _IOR(KVMIO, 0xb6, struct reg_mask_range) +/* Available with KVM_CAP_PPC_COMPAT_CAPS */ +#define KVM_PPC_GET_COMPAT_CAPS _IO(KVMIO, 0xb8) /* ioctl for vm fd */ #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device) diff --git a/rust/Makefile b/rust/Makefile index fbe0accc51a3..48adcd9b7851 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -3,6 +3,9 @@ # Where to place rustdoc generated documentation rustdoc_output := $(objtree)/Documentation/output/rust/rustdoc +# Clean generated host directory +clean-files := host/ + obj-$(CONFIG_RUST) += core.o compiler_builtins.o ffi.o always-$(CONFIG_RUST) += exports_core_generated.h @@ -33,7 +36,7 @@ endif obj-$(CONFIG_RUST) += exports.o -always-$(CONFIG_RUST) += libproc_macro2.rlib libquote.rlib libsyn.rlib +always-$(CONFIG_RUST) += host/libproc_macro2.rlib host/libquote.rlib host/libsyn.rlib always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c @@ -171,7 +174,7 @@ quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $< $(rustc_target_envs) \ OBJTREE=$(abspath $(objtree)) \ $(RUSTDOC) $(filter-out $(skip_flags) --remap-path-scope=%,$(if $(rustdoc_host),$(rust_common_flags),$(rust_flags))) \ - $(rustc_target_flags) -L$(objtree)/$(obj) \ + $(rustc_target_flags) -L$(objtree)/$(obj)$(if $(rustdoc_host),/host) \ -Zunstable-options --generate-link-to-definition \ --output $(rustdoc_output) \ --crate-name $(subst rustdoc-,,$@) \ @@ -269,6 +272,7 @@ rustdoc-pin_init_internal: $(src)/pin-init/internal/src/lib.rs \ rustdoc-pin_init: private rustdoc_host = yes rustdoc-pin_init: private rustc_target_flags = $(pin_init-flags) \ + --extern pin_init_internal=$(objtree)/$(obj)/$(libpin_init_internal_name) \ --extern alloc --cfg feature=\"alloc\" rustdoc-pin_init: $(src)/pin-init/src/lib.rs rustdoc-pin_init_internal \ rustdoc-macros FORCE @@ -452,7 +456,8 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \ -fstrict-flex-arrays=% -fmin-function-alignment=% \ -fzero-init-padding-bits=% -mno-fdpic \ -fdiagnostics-show-context -fdiagnostics-show-context=% \ - --param=% --param asan-% -fno-isolate-erroneous-paths-dereference + --param=% --param asan-% -fno-isolate-erroneous-paths-dereference \ + -ffixed-r2 -mmultiple -mno-readonly-in-sdata # Derived from `scripts/Makefile.clang`. BINDGEN_TARGET_x86 := x86_64-linux-gnu @@ -462,6 +467,13 @@ BINDGEN_TARGET_loongarch := loongarch64-linux-gnusf BINDGEN_TARGET_s390 := s390x-linux-gnu # This is only for i386 UM builds, which need the 32-bit target not -m32 BINDGEN_TARGET_i386 := i386-linux-gnu + +ifdef CONFIG_PPC64 +BINDGEN_TARGET_powerpc := powerpc64le-linux-gnu +else +BINDGEN_TARGET_powerpc := powerpc-linux-gnu +endif + BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) @@ -580,23 +592,23 @@ quiet_cmd_rustc_procmacrolibrary = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_Q $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \ $(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \ --emit=dep-info=$(depfile) --emit=link=$@ --crate-type rlib -O \ - --out-dir $(objtree)/$(obj) -L$(objtree)/$(obj) \ + --out-dir $(objtree)/$(obj)/host -L$(objtree)/$(obj)/host \ --crate-name $(patsubst lib%.rlib,%,$(notdir $@)) $< -$(obj)/libproc_macro2.rlib: private skip_clippy = 1 -$(obj)/libproc_macro2.rlib: private rustc_target_flags = $(proc_macro2-flags) -$(obj)/libproc_macro2.rlib: $(src)/proc-macro2/lib.rs FORCE +$(obj)/host/libproc_macro2.rlib: private skip_clippy = 1 +$(obj)/host/libproc_macro2.rlib: private rustc_target_flags = $(proc_macro2-flags) +$(obj)/host/libproc_macro2.rlib: $(src)/proc-macro2/lib.rs FORCE +$(call if_changed_dep,rustc_procmacrolibrary) -$(obj)/libquote.rlib: private skip_clippy = 1 -$(obj)/libquote.rlib: private skip_flags = $(quote-skip_flags) -$(obj)/libquote.rlib: private rustc_target_flags = $(quote-flags) -$(obj)/libquote.rlib: $(src)/quote/lib.rs $(obj)/libproc_macro2.rlib FORCE +$(obj)/host/libquote.rlib: private skip_clippy = 1 +$(obj)/host/libquote.rlib: private skip_flags = $(quote-skip_flags) +$(obj)/host/libquote.rlib: private rustc_target_flags = $(quote-flags) +$(obj)/host/libquote.rlib: $(src)/quote/lib.rs $(obj)/host/libproc_macro2.rlib FORCE +$(call if_changed_dep,rustc_procmacrolibrary) -$(obj)/libsyn.rlib: private skip_clippy = 1 -$(obj)/libsyn.rlib: private rustc_target_flags = $(syn-flags) -$(obj)/libsyn.rlib: $(src)/syn/lib.rs $(obj)/libquote.rlib FORCE +$(obj)/host/libsyn.rlib: private skip_clippy = 1 +$(obj)/host/libsyn.rlib: private rustc_target_flags = $(syn-flags) +$(obj)/host/libsyn.rlib: $(src)/syn/lib.rs $(obj)/host/libquote.rlib FORCE +$(call if_changed_dep,rustc_procmacrolibrary) quiet_cmd_rustc_procmacro = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) P $@ @@ -606,26 +618,26 @@ quiet_cmd_rustc_procmacro = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) -Clinker-flavor=gcc -Clinker=$(HOSTCC) \ -Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \ --emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \ - --crate-type proc-macro -L$(objtree)/$(obj) \ + --crate-type proc-macro -L$(objtree)/$(obj)/host \ --crate-name $(patsubst lib%.$(procmacro-extension),%,$(notdir $@)) \ @$(objtree)/include/generated/rustc_cfg $< # Procedural macros can only be used with the `rustc` that compiled it. $(obj)/$(libzerocopy_derive_name): private skip_clippy = 1 $(obj)/$(libzerocopy_derive_name): private rustc_target_flags = $(zerocopy_derive-flags) -$(obj)/$(libzerocopy_derive_name): $(src)/zerocopy-derive/lib.rs $(obj)/libproc_macro2.rlib \ - $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE +$(obj)/$(libzerocopy_derive_name): $(src)/zerocopy-derive/lib.rs $(obj)/host/libproc_macro2.rlib \ + $(obj)/host/libquote.rlib $(obj)/host/libsyn.rlib FORCE +$(call if_changed_dep,rustc_procmacro) $(obj)/$(libmacros_name): private rustc_target_flags = \ --extern proc_macro2 --extern quote --extern syn -$(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \ - $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE +$(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/host/libproc_macro2.rlib \ + $(obj)/host/libquote.rlib $(obj)/host/libsyn.rlib FORCE +$(call if_changed_dep,rustc_procmacro) $(obj)/$(libpin_init_internal_name): private rustc_target_flags = $(pin_init_internal-flags) $(obj)/$(libpin_init_internal_name): $(src)/pin-init/internal/src/lib.rs \ - $(obj)/libproc_macro2.rlib $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE + $(obj)/host/libproc_macro2.rlib $(obj)/host/libquote.rlib $(obj)/host/libsyn.rlib FORCE +$(call if_changed_dep,rustc_procmacro) # `rustc` requires `-Zunstable-options` to use custom target specifications @@ -683,6 +695,10 @@ ifneq ($(or $(CONFIG_ARM64),$(and $(CONFIG_RISCV),$(CONFIG_64BIT))),) __ashrti3 \ __ashlti3 __lshrti3 endif +ifdef CONFIG_PPC32 + redirect-intrinsics += \ + __udivdi3 __umoddi3 +endif ifdef CONFIG_MODVERSIONS cmd_gendwarfksyms = $(if $(skip_gendwarfksyms),, \ diff --git a/rust/compiler_builtins.rs b/rust/compiler_builtins.rs index dd16c1dc899c..fc6b54636dd5 100644 --- a/rust/compiler_builtins.rs +++ b/rust/compiler_builtins.rs @@ -97,5 +97,11 @@ pub extern "C" fn $ident() { __aeabi_uldivmod, }); +#[cfg(target_arch = "powerpc")] +define_panicking_intrinsics!("`u64` division/modulo should not be used", { + __udivdi3, + __umoddi3, +}); + // NOTE: if you are adding a new intrinsic here, you should also add it to // `redirect-intrinsics` in `rust/Makefile`. diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 998e31052e66..4b90a1390ad5 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -58,7 +58,9 @@ #include "cred.c" #include "device.c" #include "dma.c" +#ifdef CONFIG_DMA_SHARED_BUFFER #include "dma-resv.c" +#endif #include "drm.c" #include "drm_gpuvm.c" #include "err.c" diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh index 031f2192b390..99bfdcaa3396 100755 --- a/scripts/min-tool-version.sh +++ b/scripts/min-tool-version.sh @@ -33,6 +33,8 @@ llvm) rustc) if [ "$SRCARCH" = "s390" ]; then echo 1.96.0 + elif [ "$ARCH" = powerpc ]; then + echo 1.95.0 else echo 1.85.0 fi