From 3e93d49175a7b82adedd43072b021401d81f6b76 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 29 Apr 2024 16:34:08 +0200 Subject: [PATCH 01/39] s390/pgtable: Switch read and write softbits for puds There is no reason for the read and write softbits to be swapped in the puds compared to pmds. They are different only because the softbits for puds were introduced at the same time when the softbits for pmds were swapped. The current implementation is not wrong per se, since the macros are defined correctly; only the documentation does not reflect reality. With this patch, the read and write softbits for large pmd and large puds will have the same layout, and will match the existing documentation. Signed-off-by: Claudio Imbrenda Reviewed-by: Heiko Carstens Link: https://lore.kernel.org/r/20240429143409.49892-2-imbrenda@linux.ibm.com Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/pgtable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 6f11d063d545..f64de68b13aa 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -272,8 +272,8 @@ static inline int is_module_addr(void *addr) #define _REGION3_ENTRY_DIRTY 0x2000 /* SW region dirty bit */ #define _REGION3_ENTRY_YOUNG 0x1000 /* SW region young bit */ #define _REGION3_ENTRY_LARGE 0x0400 /* RTTE-format control, large page */ -#define _REGION3_ENTRY_READ 0x0002 /* SW region read bit */ -#define _REGION3_ENTRY_WRITE 0x0001 /* SW region write bit */ +#define _REGION3_ENTRY_WRITE 0x0002 /* SW region write bit */ +#define _REGION3_ENTRY_READ 0x0001 /* SW region read bit */ #ifdef CONFIG_MEM_SOFT_DIRTY #define _REGION3_ENTRY_SOFT_DIRTY 0x4000 /* SW region soft dirty bit */ From 712c5d5f625974877d9302d6b160729288f1feb2 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 29 Apr 2024 16:34:09 +0200 Subject: [PATCH 02/39] s390/pgtable: Add missing hardware bits for puds, pmds Add the table type and ACCF validity bits to _SEGMENT_ENTRY_BITS and _SEGMENT_ENTRY_HARDWARE_BITS{,_LARGE}. For completeness, introduce _REGION3_ENTRY_HARDWARE_BITS_LARGE and _REGION3_ENTRY_HARDWARE_BITS, containing the hardware bits used for large puds and normal puds. Signed-off-by: Claudio Imbrenda Reviewed-by: Heiko Carstens Link: https://lore.kernel.org/r/20240429143409.49892-3-imbrenda@linux.ibm.com Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/pgtable.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index f64de68b13aa..07f0307905ff 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -268,6 +268,8 @@ static inline int is_module_addr(void *addr) #define _REGION3_ENTRY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_LENGTH) #define _REGION3_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_INVALID) +#define _REGION3_ENTRY_HARDWARE_BITS 0xfffffffffffff6ffUL +#define _REGION3_ENTRY_HARDWARE_BITS_LARGE 0xffffffff8001073cUL #define _REGION3_ENTRY_ORIGIN_LARGE ~0x7fffffffUL /* large page address */ #define _REGION3_ENTRY_DIRTY 0x2000 /* SW region dirty bit */ #define _REGION3_ENTRY_YOUNG 0x1000 /* SW region young bit */ @@ -284,9 +286,9 @@ static inline int is_module_addr(void *addr) #define _REGION_ENTRY_BITS 0xfffffffffffff22fUL /* Bits in the segment table entry */ -#define _SEGMENT_ENTRY_BITS 0xfffffffffffffe33UL -#define _SEGMENT_ENTRY_HARDWARE_BITS 0xfffffffffffffe30UL -#define _SEGMENT_ENTRY_HARDWARE_BITS_LARGE 0xfffffffffff00730UL +#define _SEGMENT_ENTRY_BITS 0xfffffffffffffe3fUL +#define _SEGMENT_ENTRY_HARDWARE_BITS 0xfffffffffffffe3cUL +#define _SEGMENT_ENTRY_HARDWARE_BITS_LARGE 0xfffffffffff1073cUL #define _SEGMENT_ENTRY_ORIGIN_LARGE ~0xfffffUL /* large page address */ #define _SEGMENT_ENTRY_ORIGIN ~0x7ffUL/* page table origin */ #define _SEGMENT_ENTRY_PROTECT 0x200 /* segment protection bit */ From 10f70525365146046dddcc3d36bfaea2aee0376a Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Mon, 29 Apr 2024 17:02:52 +0200 Subject: [PATCH 03/39] s390/vdso: Generate unwind information for C modules GDB fails to unwind vDSO functions with error message "PC not saved", for instance when stepping through gettimeofday(). Add -fasynchronous-unwind-tables to CFLAGS to generate .eh_frame DWARF unwind information for the vDSO C modules. Fixes: 4bff8cb54502 ("s390: convert to GENERIC_VDSO") Signed-off-by: Jens Remus Signed-off-by: Alexander Gordeev --- arch/s390/kernel/vdso32/Makefile | 3 ++- arch/s390/kernel/vdso64/Makefile | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/s390/kernel/vdso32/Makefile b/arch/s390/kernel/vdso32/Makefile index b12a274cbb47..9090c0e5de25 100644 --- a/arch/s390/kernel/vdso32/Makefile +++ b/arch/s390/kernel/vdso32/Makefile @@ -20,7 +20,8 @@ KBUILD_AFLAGS_32 += -m31 -s KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS)) KBUILD_CFLAGS_32 := $(filter-out -mno-pic-data-is-text-relative,$(KBUILD_CFLAGS_32)) -KBUILD_CFLAGS_32 += -m31 -fPIC -shared -fno-common -fno-builtin +KBUILD_CFLAGS_32 := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_32)) +KBUILD_CFLAGS_32 += -m31 -fPIC -shared -fno-common -fno-builtin -fasynchronous-unwind-tables LDFLAGS_vdso32.so.dbg += -shared -soname=linux-vdso32.so.1 \ --hash-style=both --build-id=sha1 -melf_s390 -T diff --git a/arch/s390/kernel/vdso64/Makefile b/arch/s390/kernel/vdso64/Makefile index ef9832726097..a44f51de1f1e 100644 --- a/arch/s390/kernel/vdso64/Makefile +++ b/arch/s390/kernel/vdso64/Makefile @@ -26,7 +26,8 @@ KBUILD_AFLAGS_64 += -m64 KBUILD_CFLAGS_64 := $(filter-out -m64,$(KBUILD_CFLAGS)) KBUILD_CFLAGS_64 := $(filter-out -mno-pic-data-is-text-relative,$(KBUILD_CFLAGS_64)) KBUILD_CFLAGS_64 := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_64)) -KBUILD_CFLAGS_64 += -m64 -fPIC -fno-common -fno-builtin +KBUILD_CFLAGS_64 := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_64)) +KBUILD_CFLAGS_64 += -m64 -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables ldflags-y := -shared -soname=linux-vdso64.so.1 \ --hash-style=both --build-id=sha1 -T From fc2f5f10f9bc5e58d38e9fda7dae107ac04a799f Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Mon, 29 Apr 2024 17:02:53 +0200 Subject: [PATCH 04/39] s390/vdso: Create .build-id links for unstripped vdso files Citing Andy Lutomirski from commit dda1e95cee38 ("x86/vdso: Create .build-id links for unstripped vdso files"): "With this change, doing 'make vdso_install' and telling gdb: set debug-file-directory /lib/modules/KVER/vdso will enable vdso debugging with symbols. This is useful for testing, but kernel RPM builds will probably want to manually delete these symlinks or otherwise do something sensible when they strip the vdso/*.so files." Fixes: 4bff8cb54502 ("s390: convert to GENERIC_VDSO") Signed-off-by: Jens Remus Signed-off-by: Alexander Gordeev --- scripts/Makefile.vdsoinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.vdsoinst b/scripts/Makefile.vdsoinst index c477d17b0aa5..a81ca735003e 100644 --- a/scripts/Makefile.vdsoinst +++ b/scripts/Makefile.vdsoinst @@ -21,7 +21,7 @@ $$(dest): $$(src) FORCE $$(call cmd,install) # Some architectures create .build-id symlinks -ifneq ($(filter arm sparc x86, $(SRCARCH)),) +ifneq ($(filter arm s390 sparc x86, $(SRCARCH)),) link := $(install-dir)/.build-id/$$(shell $(READELF) -n $$(src) | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p').debug __default: $$(link) From 185445c7c137822ad856aae91a41e199370cb534 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 29 Apr 2024 14:28:43 +0200 Subject: [PATCH 05/39] s390/vdso: Use standard stack frame layout By default user space is compiled with standard stack frame layout and not with the packed stack layout. The vdso code however inherited the -mpacked-stack compiler option from the kernel. Remove this option to make sure the vdso is compiled with standard stack frame layout. This makes sure that the stack frame backchain location for vdso generated stack frames is the same like for calling code (if compiled with default options). This allows to manually walk stack frames without DWARF information, like the kernel is doing it e.g. with arch_stack_walk_user(). Fixes: 4bff8cb54502 ("s390: convert to GENERIC_VDSO") Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/kernel/vdso32/Makefile | 1 + arch/s390/kernel/vdso64/Makefile | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/s390/kernel/vdso32/Makefile b/arch/s390/kernel/vdso32/Makefile index 9090c0e5de25..4800d80decee 100644 --- a/arch/s390/kernel/vdso32/Makefile +++ b/arch/s390/kernel/vdso32/Makefile @@ -19,6 +19,7 @@ KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS)) KBUILD_AFLAGS_32 += -m31 -s KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS)) +KBUILD_CFLAGS_32 := $(filter-out -mpacked-stack,$(KBUILD_CFLAGS)) KBUILD_CFLAGS_32 := $(filter-out -mno-pic-data-is-text-relative,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 += -m31 -fPIC -shared -fno-common -fno-builtin -fasynchronous-unwind-tables diff --git a/arch/s390/kernel/vdso64/Makefile b/arch/s390/kernel/vdso64/Makefile index a44f51de1f1e..2f2e4e997030 100644 --- a/arch/s390/kernel/vdso64/Makefile +++ b/arch/s390/kernel/vdso64/Makefile @@ -24,6 +24,7 @@ KBUILD_AFLAGS_64 := $(filter-out -m64,$(KBUILD_AFLAGS)) KBUILD_AFLAGS_64 += -m64 KBUILD_CFLAGS_64 := $(filter-out -m64,$(KBUILD_CFLAGS)) +KBUILD_CFLAGS_64 := $(filter-out -mpacked-stack,$(KBUILD_CFLAGS_64)) KBUILD_CFLAGS_64 := $(filter-out -mno-pic-data-is-text-relative,$(KBUILD_CFLAGS_64)) KBUILD_CFLAGS_64 := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_64)) KBUILD_CFLAGS_64 := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_64)) From ebd912ff9919a10609511383d94942362234c077 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 29 Apr 2024 14:28:44 +0200 Subject: [PATCH 06/39] s390/stacktrace: Merge perf_callchain_user() and arch_stack_walk_user() The two functions perf_callchain_user() and arch_stack_walk_user() are nearly identical. Reduce code duplication and add a common helper which can be called by both functions. Fixes: aa44433ac4ee ("s390: add USER_STACKTRACE support") Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/stacktrace.h | 7 ++++++ arch/s390/kernel/perf_event.c | 34 +----------------------------- arch/s390/kernel/stacktrace.c | 33 ++++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/arch/s390/include/asm/stacktrace.h b/arch/s390/include/asm/stacktrace.h index 433fde85b14e..4aefbe32265d 100644 --- a/arch/s390/include/asm/stacktrace.h +++ b/arch/s390/include/asm/stacktrace.h @@ -2,6 +2,7 @@ #ifndef _ASM_S390_STACKTRACE_H #define _ASM_S390_STACKTRACE_H +#include #include #include @@ -12,6 +13,12 @@ struct stack_frame_user { unsigned long empty2[4]; }; +struct perf_callchain_entry_ctx; + +void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *cookie, + struct perf_callchain_entry_ctx *entry, + const struct pt_regs *regs, bool perf); + enum stack_type { STACK_TYPE_UNKNOWN, STACK_TYPE_TASK, diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c index dfa77da2fd2e..5fff629b1a89 100644 --- a/arch/s390/kernel/perf_event.c +++ b/arch/s390/kernel/perf_event.c @@ -218,39 +218,7 @@ void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, void perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) { - struct stack_frame_user __user *sf; - unsigned long ip, sp; - bool first = true; - - if (is_compat_task()) - return; - perf_callchain_store(entry, instruction_pointer(regs)); - sf = (void __user *)user_stack_pointer(regs); - pagefault_disable(); - while (entry->nr < entry->max_stack) { - if (__get_user(sp, &sf->back_chain)) - break; - if (__get_user(ip, &sf->gprs[8])) - break; - if (ip & 0x1) { - /* - * If the instruction address is invalid, and this - * is the first stack frame, assume r14 has not - * been written to the stack yet. Otherwise exit. - */ - if (first && !(regs->gprs[14] & 0x1)) - ip = regs->gprs[14]; - else - break; - } - perf_callchain_store(entry, ip); - /* Sanity check: ABI requires SP to be aligned 8 bytes. */ - if (!sp || sp & 0x7) - break; - sf = (void __user *)sp; - first = false; - } - pagefault_enable(); + arch_stack_walk_user_common(NULL, NULL, entry, regs, true); } /* Perf definitions for PMU event attributes in sysfs */ diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index 7c294da45bf5..e580d4cd2729 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c @@ -5,6 +5,7 @@ * Copyright IBM Corp. 2006 */ +#include #include #include #include @@ -62,8 +63,23 @@ int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry, return 0; } -void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie, - const struct pt_regs *regs) +static inline bool store_ip(stack_trace_consume_fn consume_entry, void *cookie, + struct perf_callchain_entry_ctx *entry, bool perf, + unsigned long ip) +{ +#ifdef CONFIG_PERF_EVENTS + if (perf) { + if (perf_callchain_store(entry, ip)) + return false; + return true; + } +#endif + return consume_entry(cookie, ip); +} + +void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *cookie, + struct perf_callchain_entry_ctx *entry, + const struct pt_regs *regs, bool perf) { struct stack_frame_user __user *sf; unsigned long ip, sp; @@ -71,7 +87,8 @@ void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie, if (is_compat_task()) return; - if (!consume_entry(cookie, instruction_pointer(regs))) + ip = instruction_pointer(regs); + if (!store_ip(consume_entry, cookie, entry, perf, ip)) return; sf = (void __user *)user_stack_pointer(regs); pagefault_disable(); @@ -91,8 +108,8 @@ void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie, else break; } - if (!consume_entry(cookie, ip)) - break; + if (!store_ip(consume_entry, cookie, entry, perf, ip)) + return; /* Sanity check: ABI requires SP to be aligned 8 bytes. */ if (!sp || sp & 0x7) break; @@ -102,6 +119,12 @@ void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie, pagefault_enable(); } +void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie, + const struct pt_regs *regs) +{ + arch_stack_walk_user_common(consume_entry, cookie, NULL, regs, false); +} + unsigned long return_address(unsigned int n) { struct unwind_state state; From 87eceb17a987802aeee718be4decd19b56fc8e33 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 29 Apr 2024 14:28:45 +0200 Subject: [PATCH 07/39] s390/stacktrace: Skip first user stack frame When walking user stack frames the first stack frame (where the stack pointer points to) should be skipped: the return address of the current function is saved in the previous stack frame, not the current stack frame, which is allocated for to be called functions. Fixes: aa44433ac4ee ("s390: add USER_STACKTRACE support") Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/kernel/stacktrace.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index e580d4cd2729..1c9e3b7739a2 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c @@ -95,6 +95,10 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo while (1) { if (__get_user(sp, &sf->back_chain)) break; + /* Sanity check: ABI requires SP to be 8 byte aligned. */ + if (!sp || sp & 0x7) + break; + sf = (void __user *)sp; if (__get_user(ip, &sf->gprs[8])) break; if (ip & 0x1) { @@ -110,10 +114,6 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo } if (!store_ip(consume_entry, cookie, entry, perf, ip)) return; - /* Sanity check: ABI requires SP to be aligned 8 bytes. */ - if (!sp || sp & 0x7) - break; - sf = (void __user *)sp; first = false; } pagefault_enable(); From cd58109283944ea8bdcd0a8211a86cbd2450716a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 29 Apr 2024 14:28:46 +0200 Subject: [PATCH 08/39] s390/stacktrace: Improve detection of invalid instruction pointers Add basic checks to identify invalid instruction pointers when walking stack frames: Instruction pointers must - have even addresses - be larger than mmap_min_addr - lower than the asce_limit of the process Alternatively it would also be possible to walk page tables similar to fast GUP and verify that the mapping of the corresponding page is executable, however that seems to be overkill. Fixes: aa44433ac4ee ("s390: add USER_STACKTRACE support") Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/kernel/stacktrace.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index 1c9e3b7739a2..b4485b0c7f06 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c @@ -77,6 +77,21 @@ static inline bool store_ip(stack_trace_consume_fn consume_entry, void *cookie, return consume_entry(cookie, ip); } +static inline bool ip_invalid(unsigned long ip) +{ + /* + * Perform some basic checks if an instruction address taken + * from unreliable source is invalid. + */ + if (ip & 1) + return true; + if (ip < mmap_min_addr) + return true; + if (ip >= current->mm->context.asce_limit) + return true; + return false; +} + void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *cookie, struct perf_callchain_entry_ctx *entry, const struct pt_regs *regs, bool perf) @@ -87,6 +102,8 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo if (is_compat_task()) return; + if (!current->mm) + return; ip = instruction_pointer(regs); if (!store_ip(consume_entry, cookie, entry, perf, ip)) return; @@ -101,15 +118,16 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo sf = (void __user *)sp; if (__get_user(ip, &sf->gprs[8])) break; - if (ip & 0x1) { + if (ip_invalid(ip)) { /* * If the instruction address is invalid, and this * is the first stack frame, assume r14 has not * been written to the stack yet. Otherwise exit. */ - if (first && !(regs->gprs[14] & 0x1)) - ip = regs->gprs[14]; - else + if (!first) + break; + ip = regs->gprs[14]; + if (ip_invalid(ip)) break; } if (!store_ip(consume_entry, cookie, entry, perf, ip)) From be72ea09c1a5273abf8c6c52ef53e36c701cbf6a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 29 Apr 2024 14:28:47 +0200 Subject: [PATCH 09/39] s390/vdso: Introduce and use struct stack_frame_vdso_wrapper Introduce and use struct stack_frame_vdso_wrapper within vdso user wrapper code. With this structure it is possible to automatically generate an asm-offset define which can be used to save and restore the return address of the calling function. Also use STACK_FRAME_USER_OVERHEAD instead of STACK_FRAME_OVERHEAD to document that the code works with user space stack frames with the standard stack frame layout. Fixes: aa44433ac4ee ("s390: add USER_STACKTRACE support") Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/stacktrace.h | 5 +++++ arch/s390/kernel/asm-offsets.c | 4 ++++ arch/s390/kernel/vdso64/vdso_user_wrapper.S | 18 ++++++++---------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/arch/s390/include/asm/stacktrace.h b/arch/s390/include/asm/stacktrace.h index 4aefbe32265d..85b6738b826a 100644 --- a/arch/s390/include/asm/stacktrace.h +++ b/arch/s390/include/asm/stacktrace.h @@ -13,6 +13,11 @@ struct stack_frame_user { unsigned long empty2[4]; }; +struct stack_frame_vdso_wrapper { + struct stack_frame_user sf; + unsigned long return_address; +}; + struct perf_callchain_entry_ctx; void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *cookie, diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index fa5f6885c74a..28017c418442 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -66,6 +66,10 @@ int main(void) OFFSET(__SF_SIE_CONTROL_PHYS, stack_frame, sie_control_block_phys); DEFINE(STACK_FRAME_OVERHEAD, sizeof(struct stack_frame)); BLANK(); + DEFINE(STACK_FRAME_USER_OVERHEAD, sizeof(struct stack_frame_user)); + OFFSET(__SFVDSO_RETURN_ADDRESS, stack_frame_vdso_wrapper, return_address); + DEFINE(STACK_FRAME_VDSO_OVERHEAD, sizeof(struct stack_frame_vdso_wrapper)); + BLANK(); /* idle data offsets */ OFFSET(__CLOCK_IDLE_ENTER, s390_idle_data, clock_idle_enter); OFFSET(__TIMER_IDLE_ENTER, s390_idle_data, timer_idle_enter); diff --git a/arch/s390/kernel/vdso64/vdso_user_wrapper.S b/arch/s390/kernel/vdso64/vdso_user_wrapper.S index 85247ef5a41b..deee8ca9cdbf 100644 --- a/arch/s390/kernel/vdso64/vdso_user_wrapper.S +++ b/arch/s390/kernel/vdso64/vdso_user_wrapper.S @@ -6,8 +6,6 @@ #include #include -#define WRAPPER_FRAME_SIZE (STACK_FRAME_OVERHEAD+8) - /* * Older glibc version called vdso without allocating a stackframe. This wrapper * is just used to allocate a stackframe. See @@ -20,16 +18,16 @@ __ALIGN __kernel_\func: CFI_STARTPROC - aghi %r15,-WRAPPER_FRAME_SIZE - CFI_DEF_CFA_OFFSET (STACK_FRAME_OVERHEAD + WRAPPER_FRAME_SIZE) - CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD - stg %r14,STACK_FRAME_OVERHEAD(%r15) - CFI_REL_OFFSET 14, STACK_FRAME_OVERHEAD + aghi %r15,-STACK_FRAME_VDSO_OVERHEAD + CFI_DEF_CFA_OFFSET (STACK_FRAME_USER_OVERHEAD + STACK_FRAME_VDSO_OVERHEAD) + CFI_VAL_OFFSET 15,-STACK_FRAME_USER_OVERHEAD + stg %r14,__SFVDSO_RETURN_ADDRESS(%r15) + CFI_REL_OFFSET 14,__SFVDSO_RETURN_ADDRESS brasl %r14,__s390_vdso_\func - lg %r14,STACK_FRAME_OVERHEAD(%r15) + lg %r14,__SFVDSO_RETURN_ADDRESS(%r15) CFI_RESTORE 14 - aghi %r15,WRAPPER_FRAME_SIZE - CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD + aghi %r15,STACK_FRAME_VDSO_OVERHEAD + CFI_DEF_CFA_OFFSET STACK_FRAME_USER_OVERHEAD CFI_RESTORE 15 br %r14 CFI_ENDPROC From 62b672c4ba90e726cc39b5c3d6dffd1ca817e143 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 29 Apr 2024 14:28:48 +0200 Subject: [PATCH 10/39] s390/stackstrace: Detect vdso stack frames Clear the backchain of the extra stack frame added by the vdso user wrapper code. This allows the user stack walker to detect and skip the non-standard stack frame. Without this an incorrect instruction pointer would be added to stack traces, and stack frame walking would be continued with a more or less random back chain. Fixes: aa44433ac4ee ("s390: add USER_STACKTRACE support") Reviewed-by: Jens Remus Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/processor.h | 1 + arch/s390/kernel/asm-offsets.c | 1 + arch/s390/kernel/stacktrace.c | 28 ++++++++++++++++++--- arch/s390/kernel/vdso.c | 13 +++++++--- arch/s390/kernel/vdso64/vdso_user_wrapper.S | 1 + 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index db9982f0e8cd..bbbdc5abe2b2 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -98,6 +98,7 @@ void cpu_detect_mhz_feature(void); extern const struct seq_operations cpuinfo_op; extern void execve_tail(void); +unsigned long vdso_text_size(void); unsigned long vdso_size(void); /* diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 28017c418442..2f65bca2f3f1 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -66,6 +66,7 @@ int main(void) OFFSET(__SF_SIE_CONTROL_PHYS, stack_frame, sie_control_block_phys); DEFINE(STACK_FRAME_OVERHEAD, sizeof(struct stack_frame)); BLANK(); + OFFSET(__SFUSER_BACKCHAIN, stack_frame_user, back_chain); DEFINE(STACK_FRAME_USER_OVERHEAD, sizeof(struct stack_frame_user)); OFFSET(__SFVDSO_RETURN_ADDRESS, stack_frame_vdso_wrapper, return_address); DEFINE(STACK_FRAME_VDSO_OVERHEAD, sizeof(struct stack_frame_vdso_wrapper)); diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index b4485b0c7f06..640363b2a105 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c @@ -92,10 +92,16 @@ static inline bool ip_invalid(unsigned long ip) return false; } +static inline bool ip_within_vdso(unsigned long ip) +{ + return in_range(ip, current->mm->context.vdso_base, vdso_text_size()); +} + void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *cookie, struct perf_callchain_entry_ctx *entry, const struct pt_regs *regs, bool perf) { + struct stack_frame_vdso_wrapper __user *sf_vdso; struct stack_frame_user __user *sf; unsigned long ip, sp; bool first = true; @@ -112,11 +118,25 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo while (1) { if (__get_user(sp, &sf->back_chain)) break; + /* + * VDSO entry code has a non-standard stack frame layout. + * See VDSO user wrapper code for details. + */ + if (!sp && ip_within_vdso(ip)) { + sf_vdso = (void __user *)sf; + if (__get_user(ip, &sf_vdso->return_address)) + break; + sp = (unsigned long)sf + STACK_FRAME_VDSO_OVERHEAD; + sf = (void __user *)sp; + if (__get_user(sp, &sf->back_chain)) + break; + } else { + sf = (void __user *)sp; + if (__get_user(ip, &sf->gprs[8])) + break; + } /* Sanity check: ABI requires SP to be 8 byte aligned. */ - if (!sp || sp & 0x7) - break; - sf = (void __user *)sp; - if (__get_user(ip, &sf->gprs[8])) + if (sp & 0x7) break; if (ip_invalid(ip)) { /* diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c index a45b3a4c91db..2f967ac2b8e3 100644 --- a/arch/s390/kernel/vdso.c +++ b/arch/s390/kernel/vdso.c @@ -210,17 +210,22 @@ static unsigned long vdso_addr(unsigned long start, unsigned long len) return addr; } -unsigned long vdso_size(void) +unsigned long vdso_text_size(void) { - unsigned long size = VVAR_NR_PAGES * PAGE_SIZE; + unsigned long size; if (is_compat_task()) - size += vdso32_end - vdso32_start; + size = vdso32_end - vdso32_start; else - size += vdso64_end - vdso64_start; + size = vdso64_end - vdso64_start; return PAGE_ALIGN(size); } +unsigned long vdso_size(void) +{ + return vdso_text_size() + VVAR_NR_PAGES * PAGE_SIZE; +} + int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) { unsigned long addr = VDSO_BASE; diff --git a/arch/s390/kernel/vdso64/vdso_user_wrapper.S b/arch/s390/kernel/vdso64/vdso_user_wrapper.S index deee8ca9cdbf..e26e68675c08 100644 --- a/arch/s390/kernel/vdso64/vdso_user_wrapper.S +++ b/arch/s390/kernel/vdso64/vdso_user_wrapper.S @@ -23,6 +23,7 @@ __kernel_\func: CFI_VAL_OFFSET 15,-STACK_FRAME_USER_OVERHEAD stg %r14,__SFVDSO_RETURN_ADDRESS(%r15) CFI_REL_OFFSET 14,__SFVDSO_RETURN_ADDRESS + xc __SFUSER_BACKCHAIN(8,%r15),__SFUSER_BACKCHAIN(%r15) brasl %r14,__s390_vdso_\func lg %r14,__SFVDSO_RETURN_ADDRESS(%r15) CFI_RESTORE 14 From fa2ae4a377c0fbbcbcd4252842e702f7e4187170 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 30 Apr 2024 16:29:59 +0200 Subject: [PATCH 11/39] s390/idle: Rewrite psw_idle() in C To ease maintenance and further enhancements, convert the psw_idle() function to C. Reviewed-by: Heiko Carstens Signed-off-by: Sven Schnelle Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/processor.h | 14 ++++++++++++++ arch/s390/kernel/asm-offsets.c | 6 ------ arch/s390/kernel/entry.S | 23 ----------------------- arch/s390/kernel/idle.c | 10 +++++++--- 4 files changed, 21 insertions(+), 32 deletions(-) diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index bbbdc5abe2b2..1e2fc6d6963c 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -40,6 +40,7 @@ #include #include #include +#include typedef long (*sys_call_ptr_t)(struct pt_regs *regs); @@ -92,6 +93,14 @@ static inline void get_cpu_id(struct cpuid *ptr) asm volatile("stidp %0" : "=Q" (*ptr)); } +static __always_inline unsigned long get_cpu_timer(void) +{ + unsigned long timer; + + asm volatile("stpt %[timer]" : [timer] "=Q" (timer)); + return timer; +} + void s390_adjust_jiffies(void); void s390_update_cpu_mhz(void); void cpu_detect_mhz_feature(void); @@ -394,6 +403,11 @@ static __always_inline bool regs_irqs_disabled(struct pt_regs *regs) return arch_irqs_disabled_flags(regs->psw.mask); } +static __always_inline void bpon(void) +{ + asm volatile(ALTERNATIVE("nop", ".insn rrf,0xb2e80000,0,0,13,0", 82)); +} + #endif /* __ASSEMBLY__ */ #endif /* __ASM_S390_PROCESSOR_H */ diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 2f65bca2f3f1..f55979f64d49 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -71,11 +70,6 @@ int main(void) OFFSET(__SFVDSO_RETURN_ADDRESS, stack_frame_vdso_wrapper, return_address); DEFINE(STACK_FRAME_VDSO_OVERHEAD, sizeof(struct stack_frame_vdso_wrapper)); BLANK(); - /* idle data offsets */ - OFFSET(__CLOCK_IDLE_ENTER, s390_idle_data, clock_idle_enter); - OFFSET(__TIMER_IDLE_ENTER, s390_idle_data, timer_idle_enter); - OFFSET(__MT_CYCLES_ENTER, s390_idle_data, mt_cycles_enter); - BLANK(); /* hardware defined lowcore locations 0x000 - 0x1ff */ OFFSET(__LC_EXT_PARAMS, lowcore, ext_params); OFFSET(__LC_EXT_CPU_ADDR, lowcore, ext_cpu_addr); diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 6a1e0fbbaa15..60cf917a7122 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -440,29 +440,6 @@ SYM_CODE_END(\name) INT_HANDLER ext_int_handler,__LC_EXT_OLD_PSW,do_ext_irq INT_HANDLER io_int_handler,__LC_IO_OLD_PSW,do_io_irq -/* - * Load idle PSW. - */ -SYM_FUNC_START(psw_idle) - stg %r14,(__SF_GPRS+8*8)(%r15) - stg %r3,__SF_EMPTY(%r15) - larl %r1,psw_idle_exit - stg %r1,__SF_EMPTY+8(%r15) - larl %r1,smp_cpu_mtid - llgf %r1,0(%r1) - ltgr %r1,%r1 - jz .Lpsw_idle_stcctm - .insn rsy,0xeb0000000017,%r1,5,__MT_CYCLES_ENTER(%r2) -.Lpsw_idle_stcctm: - oi __LC_CPU_FLAGS+7,_CIF_ENABLED_WAIT - BPON - stckf __CLOCK_IDLE_ENTER(%r2) - stpt __TIMER_IDLE_ENTER(%r2) - lpswe __SF_EMPTY(%r15) -SYM_INNER_LABEL(psw_idle_exit, SYM_L_GLOBAL) - BR_EX %r14 -SYM_FUNC_END(psw_idle) - /* * Machine check handler routines */ diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index e7239aaf428b..af9c97c0ad73 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -57,9 +57,13 @@ void noinstr arch_cpu_idle(void) psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; clear_cpu_flag(CIF_NOHZ_DELAY); - - /* psw_idle() returns with interrupts disabled. */ - psw_idle(idle, psw_mask); + set_cpu_flag(CIF_ENABLED_WAIT); + if (smp_cpu_mtid) + stcctm(MT_DIAG, smp_cpu_mtid, (u64 *)&idle->mt_cycles_enter); + idle->clock_idle_enter = get_tod_clock_fast(); + idle->timer_idle_enter = get_cpu_timer(); + bpon(); + __load_psw_mask(psw_mask); } static ssize_t show_idle_count(struct device *dev, From 095c89e99bae9ee23a3cae737974937dfe2bbf25 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 30 Apr 2024 16:30:00 +0200 Subject: [PATCH 12/39] s390/vtime: Use get_cpu_timer() Instead of implementing get_vtimer() use get_cpu_timer() which does the same. Reviewed-by: Heiko Carstens Signed-off-by: Sven Schnelle Signed-off-by: Alexander Gordeev --- arch/s390/kernel/vtime.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 24a18e5ef6e8..ffc1db0cbf9c 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -33,14 +33,6 @@ static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 }; static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 }; static DEFINE_PER_CPU(u64, mt_scaling_jiffies); -static inline u64 get_vtimer(void) -{ - u64 timer; - - asm volatile("stpt %0" : "=Q" (timer)); - return timer; -} - static inline void set_vtimer(u64 expires) { u64 timer; @@ -223,7 +215,7 @@ static u64 vtime_delta(void) { u64 timer = S390_lowcore.last_update_timer; - S390_lowcore.last_update_timer = get_vtimer(); + S390_lowcore.last_update_timer = get_cpu_timer(); return timer - S390_lowcore.last_update_timer; } From 7278a8fb8d032dfdc03d9b5d17e0bc451cdc1492 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 30 Apr 2024 16:30:01 +0200 Subject: [PATCH 13/39] s390: Mark psw in __load_psw_mask() as __unitialized Without __unitialized, the following code is generated when INIT_STACK_ALL_ZERO is enabled: 86: d7 0f f0 a0 f0 a0 xc 160(16,%r15), 160(%r15) 8c: e3 40 f0 a0 00 24 stg %r4, 160(%r15) 92: c0 10 00 00 00 08 larl %r1, 0xa2 98: e3 10 f0 a8 00 24 stg %r1, 168(%r15) 9e: b2 b2 f0 a0 lpswe 160(%r15) The xc is not adding any security because psw is fully initialized with the following instructions. Add __unitialized to the psw definitiation to avoid the superfluous clearing of psw. Reviewed-by: Heiko Carstens Signed-off-by: Sven Schnelle Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/processor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 1e2fc6d6963c..07ad5a1df878 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -314,8 +314,8 @@ static inline void __load_psw(psw_t psw) */ static __always_inline void __load_psw_mask(unsigned long mask) { + psw_t psw __uninitialized; unsigned long addr; - psw_t psw; psw.mask = mask; From 980fffff14f8c788b54b44e5819ff9bc1d1290fb Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 3 May 2024 10:06:48 +0200 Subject: [PATCH 14/39] s390/fpu: Remove comment about TIF_FPU It has been removed in commit 2c6b96762fbd ("s390/fpu: remove TIF_FPU"), so we should not mention TIF_FPU in the comment here anymore. Since the remaining parts of the comment just document the obvious fact that save_user_fpu_regs() saves the FPU state, simply remove the comment now completely. Signed-off-by: Thomas Huth Acked-by: Heiko Carstens Link: https://lore.kernel.org/r/20240503080648.81461-1-thuth@redhat.com Signed-off-by: Alexander Gordeev --- arch/s390/kernel/process.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index dd456b475861..d8740631df4b 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -86,11 +86,6 @@ void arch_release_task_struct(struct task_struct *tsk) int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) { - /* - * Save the floating-point or vector register state of the current - * task and set the TIF_FPU flag to lazy restore the FPU register - * state when returning to user space. - */ save_user_fpu_regs(); *dst = *src; From 22e6824622e8a8889df0f8fc4ed5aea0e702a694 Mon Sep 17 00:00:00 2001 From: Jules Irenge Date: Tue, 7 May 2024 22:13:52 +0100 Subject: [PATCH 15/39] s390/pkey: Use kfree_sensitive() to fix Coccinelle warnings Replace memzero_explicit() and kfree() with kfree_sensitive() to fix warnings reported by Coccinelle: WARNING opportunity for kfree_sensitive/kvfree_sensitive (line 1506) WARNING opportunity for kfree_sensitive/kvfree_sensitive (line 1643) WARNING opportunity for kfree_sensitive/kvfree_sensitive (line 1770) Signed-off-by: Jules Irenge Reviewed-by: Holger Dengler Link: https://lore.kernel.org/r/ZjqZkNi_JUJu73Rg@octinomon.home Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- drivers/s390/crypto/pkey_api.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index dccf664a3d95..933894065623 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -1503,8 +1503,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, rc = pkey_keyblob2pkey(kkey, ktp.keylen, ktp.protkey.protkey, &ktp.protkey.len, &ktp.protkey.type); pr_debug("%s pkey_keyblob2pkey()=%d\n", __func__, rc); - memzero_explicit(kkey, ktp.keylen); - kfree(kkey); + kfree_sensitive(kkey); if (rc) break; if (copy_to_user(utp, &ktp, sizeof(ktp))) @@ -1640,8 +1639,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, &ktp.protkey.type); pr_debug("%s pkey_keyblob2pkey2()=%d\n", __func__, rc); kfree(apqns); - memzero_explicit(kkey, ktp.keylen); - kfree(kkey); + kfree_sensitive(kkey); if (rc) break; if (copy_to_user(utp, &ktp, sizeof(ktp))) @@ -1767,8 +1765,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, protkey, &protkeylen, &ktp.pkeytype); pr_debug("%s pkey_keyblob2pkey3()=%d\n", __func__, rc); kfree(apqns); - memzero_explicit(kkey, ktp.keylen); - kfree(kkey); + kfree_sensitive(kkey); if (rc) { kfree(protkey); break; From 1d8c270de5eb74245d72325d285894a577a945d9 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 7 May 2024 17:03:18 +0200 Subject: [PATCH 16/39] s390/pkey: Wipe sensitive data on failure Wipe sensitive data from stack also if the copy_to_user() fails. Suggested-by: Heiko Carstens Reviewed-by: Harald Freudenberger Reviewed-by: Ingo Franzki Acked-by: Heiko Carstens Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev --- drivers/s390/crypto/pkey_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 933894065623..179287157c2f 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -1377,7 +1377,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, if (rc) break; if (copy_to_user(ucs, &kcs, sizeof(kcs))) - return -EFAULT; + rc = -EFAULT; memzero_explicit(&kcs, sizeof(kcs)); break; } @@ -1412,7 +1412,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, if (rc) break; if (copy_to_user(ucp, &kcp, sizeof(kcp))) - return -EFAULT; + rc = -EFAULT; memzero_explicit(&kcp, sizeof(kcp)); break; } From d65d76a44ffe74c73298ada25b0f578680576073 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 7 May 2024 17:03:19 +0200 Subject: [PATCH 17/39] s390/pkey: Wipe copies of clear-key structures on failure Wipe all sensitive data from stack for all IOCTLs, which convert a clear-key into a protected- or secure-key. Reviewed-by: Harald Freudenberger Reviewed-by: Ingo Franzki Acked-by: Heiko Carstens Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev --- drivers/s390/crypto/pkey_api.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 179287157c2f..1aa78a74fbad 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -1374,9 +1374,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, rc = cca_clr2seckey(kcs.cardnr, kcs.domain, kcs.keytype, kcs.clrkey.clrkey, kcs.seckey.seckey); pr_debug("%s cca_clr2seckey()=%d\n", __func__, rc); - if (rc) - break; - if (copy_to_user(ucs, &kcs, sizeof(kcs))) + if (!rc && copy_to_user(ucs, &kcs, sizeof(kcs))) rc = -EFAULT; memzero_explicit(&kcs, sizeof(kcs)); break; @@ -1409,9 +1407,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, kcp.protkey.protkey, &kcp.protkey.len, &kcp.protkey.type); pr_debug("%s pkey_clr2protkey()=%d\n", __func__, rc); - if (rc) - break; - if (copy_to_user(ucp, &kcp, sizeof(kcp))) + if (!rc && copy_to_user(ucp, &kcp, sizeof(kcp))) rc = -EFAULT; memzero_explicit(&kcp, sizeof(kcp)); break; @@ -1562,11 +1558,14 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, if (copy_from_user(&kcs, ucs, sizeof(kcs))) return -EFAULT; apqns = _copy_apqns_from_user(kcs.apqns, kcs.apqn_entries); - if (IS_ERR(apqns)) + if (IS_ERR(apqns)) { + memzero_explicit(&kcs, sizeof(kcs)); return PTR_ERR(apqns); + } kkey = kzalloc(klen, GFP_KERNEL); if (!kkey) { kfree(apqns); + memzero_explicit(&kcs, sizeof(kcs)); return -ENOMEM; } rc = pkey_clr2seckey2(apqns, kcs.apqn_entries, @@ -1576,15 +1575,18 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, kfree(apqns); if (rc) { kfree(kkey); + memzero_explicit(&kcs, sizeof(kcs)); break; } if (kcs.key) { if (kcs.keylen < klen) { kfree(kkey); + memzero_explicit(&kcs, sizeof(kcs)); return -EINVAL; } if (copy_to_user(kcs.key, kkey, klen)) { kfree(kkey); + memzero_explicit(&kcs, sizeof(kcs)); return -EFAULT; } } From f2ebdadd85af4f4d0cae1e5d009c70eccc78c207 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 7 May 2024 17:03:20 +0200 Subject: [PATCH 18/39] s390/pkey: Wipe copies of protected- and secure-keys Although the clear-key of neither protected- nor secure-keys is accessible, this key material should only be visible to the calling process. So wipe all copies of protected- or secure-keys from stack, even in case of an error. Reviewed-by: Harald Freudenberger Reviewed-by: Ingo Franzki Acked-by: Heiko Carstens Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev --- drivers/s390/crypto/pkey_api.c | 80 ++++++++++++++++------------------ 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 1aa78a74fbad..ffc0b5db55c2 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -1359,10 +1359,9 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, rc = cca_genseckey(kgs.cardnr, kgs.domain, kgs.keytype, kgs.seckey.seckey); pr_debug("%s cca_genseckey()=%d\n", __func__, rc); - if (rc) - break; - if (copy_to_user(ugs, &kgs, sizeof(kgs))) - return -EFAULT; + if (!rc && copy_to_user(ugs, &kgs, sizeof(kgs))) + rc = -EFAULT; + memzero_explicit(&kgs, sizeof(kgs)); break; } case PKEY_CLR2SECK: { @@ -1390,10 +1389,9 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, ksp.seckey.seckey, ksp.protkey.protkey, &ksp.protkey.len, &ksp.protkey.type); pr_debug("%s cca_sec2protkey()=%d\n", __func__, rc); - if (rc) - break; - if (copy_to_user(usp, &ksp, sizeof(ksp))) - return -EFAULT; + if (!rc && copy_to_user(usp, &ksp, sizeof(ksp))) + rc = -EFAULT; + memzero_explicit(&ksp, sizeof(ksp)); break; } case PKEY_CLR2PROTK: { @@ -1437,10 +1435,9 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, rc = pkey_skey2pkey(ksp.seckey.seckey, ksp.protkey.protkey, &ksp.protkey.len, &ksp.protkey.type); pr_debug("%s pkey_skey2pkey()=%d\n", __func__, rc); - if (rc) - break; - if (copy_to_user(usp, &ksp, sizeof(ksp))) - return -EFAULT; + if (!rc && copy_to_user(usp, &ksp, sizeof(ksp))) + rc = -EFAULT; + memzero_explicit(&ksp, sizeof(ksp)); break; } case PKEY_VERIFYKEY: { @@ -1452,10 +1449,9 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, rc = pkey_verifykey(&kvk.seckey, &kvk.cardnr, &kvk.domain, &kvk.keysize, &kvk.attributes); pr_debug("%s pkey_verifykey()=%d\n", __func__, rc); - if (rc) - break; - if (copy_to_user(uvk, &kvk, sizeof(kvk))) - return -EFAULT; + if (!rc && copy_to_user(uvk, &kvk, sizeof(kvk))) + rc = -EFAULT; + memzero_explicit(&kvk, sizeof(kvk)); break; } case PKEY_GENPROTK: { @@ -1468,10 +1464,9 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, rc = pkey_genprotkey(kgp.keytype, kgp.protkey.protkey, &kgp.protkey.len, &kgp.protkey.type); pr_debug("%s pkey_genprotkey()=%d\n", __func__, rc); - if (rc) - break; - if (copy_to_user(ugp, &kgp, sizeof(kgp))) - return -EFAULT; + if (!rc && copy_to_user(ugp, &kgp, sizeof(kgp))) + rc = -EFAULT; + memzero_explicit(&kgp, sizeof(kgp)); break; } case PKEY_VERIFYPROTK: { @@ -1483,6 +1478,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, rc = pkey_verifyprotkey(kvp.protkey.protkey, kvp.protkey.len, kvp.protkey.type); pr_debug("%s pkey_verifyprotkey()=%d\n", __func__, rc); + memzero_explicit(&kvp, sizeof(kvp)); break; } case PKEY_KBLOB2PROTK: { @@ -1500,10 +1496,9 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, &ktp.protkey.len, &ktp.protkey.type); pr_debug("%s pkey_keyblob2pkey()=%d\n", __func__, rc); kfree_sensitive(kkey); - if (rc) - break; - if (copy_to_user(utp, &ktp, sizeof(ktp))) - return -EFAULT; + if (!rc && copy_to_user(utp, &ktp, sizeof(ktp))) + rc = -EFAULT; + memzero_explicit(&ktp, sizeof(ktp)); break; } case PKEY_GENSECK2: { @@ -1529,23 +1524,23 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, pr_debug("%s pkey_genseckey2()=%d\n", __func__, rc); kfree(apqns); if (rc) { - kfree(kkey); + kfree_sensitive(kkey); break; } if (kgs.key) { if (kgs.keylen < klen) { - kfree(kkey); + kfree_sensitive(kkey); return -EINVAL; } if (copy_to_user(kgs.key, kkey, klen)) { - kfree(kkey); + kfree_sensitive(kkey); return -EFAULT; } } kgs.keylen = klen; if (copy_to_user(ugs, &kgs, sizeof(kgs))) rc = -EFAULT; - kfree(kkey); + kfree_sensitive(kkey); break; } case PKEY_CLR2SECK2: { @@ -1574,18 +1569,18 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, pr_debug("%s pkey_clr2seckey2()=%d\n", __func__, rc); kfree(apqns); if (rc) { - kfree(kkey); + kfree_sensitive(kkey); memzero_explicit(&kcs, sizeof(kcs)); break; } if (kcs.key) { if (kcs.keylen < klen) { - kfree(kkey); + kfree_sensitive(kkey); memzero_explicit(&kcs, sizeof(kcs)); return -EINVAL; } if (copy_to_user(kcs.key, kkey, klen)) { - kfree(kkey); + kfree_sensitive(kkey); memzero_explicit(&kcs, sizeof(kcs)); return -EFAULT; } @@ -1594,7 +1589,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, if (copy_to_user(ucs, &kcs, sizeof(kcs))) rc = -EFAULT; memzero_explicit(&kcs, sizeof(kcs)); - kfree(kkey); + kfree_sensitive(kkey); break; } case PKEY_VERIFYKEY2: { @@ -1611,7 +1606,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, &kvk.cardnr, &kvk.domain, &kvk.type, &kvk.size, &kvk.flags); pr_debug("%s pkey_verifykey2()=%d\n", __func__, rc); - kfree(kkey); + kfree_sensitive(kkey); if (rc) break; if (copy_to_user(uvk, &kvk, sizeof(kvk))) @@ -1642,10 +1637,9 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, pr_debug("%s pkey_keyblob2pkey2()=%d\n", __func__, rc); kfree(apqns); kfree_sensitive(kkey); - if (rc) - break; - if (copy_to_user(utp, &ktp, sizeof(ktp))) - return -EFAULT; + if (!rc && copy_to_user(utp, &ktp, sizeof(ktp))) + rc = -EFAULT; + memzero_explicit(&ktp, sizeof(ktp)); break; } case PKEY_APQNS4K: { @@ -1673,7 +1667,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, rc = pkey_apqns4key(kkey, kak.keylen, kak.flags, apqns, &nr_apqns); pr_debug("%s pkey_apqns4key()=%d\n", __func__, rc); - kfree(kkey); + kfree_sensitive(kkey); if (rc && rc != -ENOSPC) { kfree(apqns); break; @@ -1759,7 +1753,7 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, protkey = kmalloc(protkeylen, GFP_KERNEL); if (!protkey) { kfree(apqns); - kfree(kkey); + kfree_sensitive(kkey); return -ENOMEM; } rc = pkey_keyblob2pkey3(apqns, ktp.apqn_entries, @@ -1769,20 +1763,20 @@ static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, kfree(apqns); kfree_sensitive(kkey); if (rc) { - kfree(protkey); + kfree_sensitive(protkey); break; } if (ktp.pkey && ktp.pkeylen) { if (protkeylen > ktp.pkeylen) { - kfree(protkey); + kfree_sensitive(protkey); return -EINVAL; } if (copy_to_user(ktp.pkey, protkey, protkeylen)) { - kfree(protkey); + kfree_sensitive(protkey); return -EFAULT; } } - kfree(protkey); + kfree_sensitive(protkey); ktp.pkeylen = protkeylen; if (copy_to_user(utp, &ktp, sizeof(ktp))) return -EFAULT; From 1084562ec858d96c02d3a47d4afb088922f5a2ca Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Wed, 8 May 2024 11:17:16 +0200 Subject: [PATCH 19/39] s390/irq: Set CIF_NOHZ_DELAY in do_io_irq() Both do_airq_interrupt() and do_io_interrupt() set CIF_NOHZ_DELAY. Move it to do_io_irq() to simplify the code. Reviewed-by: Heiko Carstens Signed-off-by: Sven Schnelle Signed-off-by: Alexander Gordeev --- arch/s390/kernel/irq.c | 1 + drivers/s390/cio/airq.c | 1 - drivers/s390/cio/cio.c | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 6f71b0ce1068..89bcd4205e67 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -150,6 +150,7 @@ void noinstr do_io_irq(struct pt_regs *regs) if (from_idle) account_idle_time_irq(); + set_cpu_flag(CIF_NOHZ_DELAY); do { regs->tpi_info = S390_lowcore.tpi_info; if (S390_lowcore.tpi_info.adapter_IO) diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index a108f2bf5b33..51f1cb31e4aa 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -90,7 +90,6 @@ static irqreturn_t do_airq_interrupt(int irq, void *dummy) struct airq_struct *airq; struct hlist_head *head; - set_cpu_flag(CIF_NOHZ_DELAY); tpi_info = &get_irq_regs()->tpi_info; trace_s390_cio_adapter_int(tpi_info); head = &airq_lists[tpi_info->isc]; diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 7e759c21480e..c32e818f06db 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -535,7 +535,6 @@ static irqreturn_t do_cio_interrupt(int irq, void *dummy) struct subchannel *sch; struct irb *irb; - set_cpu_flag(CIF_NOHZ_DELAY); tpi_info = &get_irq_regs()->tpi_info; trace_s390_cio_interrupt(tpi_info); irb = this_cpu_ptr(&cio_irb); From 4452e8ef8c364113495f414d7e6846d74d7eff81 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 May 2024 21:44:49 +0200 Subject: [PATCH 20/39] s390/iucv: Provide iucv_alloc_device() / iucv_release_device() Provide iucv_alloc_device() and iucv_release_device() helper functions, which can be used to deduplicate more or less identical IUCV device allocation and release code in four different drivers. Suggested-by: Arnd Bergmann Acked-by: Alexandra Winter Link: https://lore.kernel.org/r/20240506194454.1160315-2-hca@linux.ibm.com Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- include/net/iucv/iucv.h | 6 ++++++ net/iucv/iucv.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/include/net/iucv/iucv.h b/include/net/iucv/iucv.h index 5cd7871127c9..b3736e66fe1a 100644 --- a/include/net/iucv/iucv.h +++ b/include/net/iucv/iucv.h @@ -84,6 +84,12 @@ struct iucv_array { extern const struct bus_type iucv_bus; extern struct device *iucv_root; +struct device_driver; + +struct device *iucv_alloc_device(const struct attribute_group **attrs, + struct device_driver *driver, void *priv, + const char *fmt, ...) __printf(4, 5); + /* * struct iucv_path * pathid: 16 bit path identification diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index a4ab615ca3e3..9db7c2c0ae72 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -76,6 +76,41 @@ EXPORT_SYMBOL(iucv_bus); struct device *iucv_root; EXPORT_SYMBOL(iucv_root); +static void iucv_release_device(struct device *device) +{ + kfree(device); +} + +struct device *iucv_alloc_device(const struct attribute_group **attrs, + struct device_driver *driver, + void *priv, const char *fmt, ...) +{ + struct device *dev; + va_list vargs; + int rc; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + goto out_error; + va_start(vargs, fmt); + rc = dev_set_name(dev, fmt, vargs); + va_end(vargs); + if (rc) + goto out_error; + dev->bus = &iucv_bus; + dev->parent = iucv_root; + dev->driver = driver; + dev->groups = attrs; + dev->release = iucv_release_device; + dev_set_drvdata(dev, priv); + return dev; + +out_error: + kfree(dev); + return NULL; +} +EXPORT_SYMBOL(iucv_alloc_device); + static int iucv_available; /* General IUCV interrupt structure */ From 4ee55c956325f5b28a23c5916bfd8372042702b3 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 May 2024 21:44:50 +0200 Subject: [PATCH 21/39] s390/vmlogrdr: Make use of iucv_alloc_device() Make use of iucv_alloc_device() to get rid of quite some code. In addition this also removes a cast to an incompatible function (clang W=1): drivers/s390/char/vmlogrdr.c:746:18: error: cast from 'void (*)(const void *)' to 'void (*)(struct device *)' converts to incompatible function type [-Werror,-Wcast-function-type-strict] 746 | dev->release = (void (*)(struct device *))kfree; | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Reported-by: Nathan Chancellor Closes: https://lore.kernel.org/r/20240417-s390-drivers-fix-cast-function-type-v1-1-fd048c9903b0@kernel.org Acked-by: Alexandra Winter Link: https://lore.kernel.org/r/20240506194454.1160315-3-hca@linux.ibm.com Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- drivers/s390/char/vmlogrdr.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index d7e408c8d0b8..c09e1e09fb66 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -728,23 +728,9 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) struct device *dev; int ret; - dev = kzalloc(sizeof(struct device), GFP_KERNEL); - if (dev) { - dev_set_name(dev, "%s", priv->internal_name); - dev->bus = &iucv_bus; - dev->parent = iucv_root; - dev->driver = &vmlogrdr_driver; - dev->groups = vmlogrdr_attr_groups; - dev_set_drvdata(dev, priv); - /* - * The release function could be called after the - * module has been unloaded. It's _only_ task is to - * free the struct. Therefore, we specify kfree() - * directly here. (Probably a little bit obfuscating - * but legitime ...). - */ - dev->release = (void (*)(struct device *))kfree; - } else + dev = iucv_alloc_device(vmlogrdr_attr_groups, &vmlogrdr_driver, + priv, priv->internal_name); + if (!dev) return -ENOMEM; ret = device_register(dev); if (ret) { From 968bfb566dd193f912ccab47aaa50f3112f6f82d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 May 2024 21:44:51 +0200 Subject: [PATCH 22/39] s390/netiucv: Make use of iucv_alloc_device() Make use of iucv_alloc_device() to get rid of quite some code. In addition this also removes a cast to an incompatible function (clang W=1): drivers/s390/net/netiucv.c:1716:18: error: cast from 'void (*)(const void *)' to 'void (*)(struct device *)' converts to incompatible function type [-Werror,-Wcast-function-type-strict] 1716 | dev->release = (void (*)(struct device *))kfree; | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Reported-by: Nathan Chancellor Closes: https://lore.kernel.org/r/20240417-s390-drivers-fix-cast-function-type-v1-3-fd048c9903b0@kernel.org Acked-by: Alexandra Winter Link: https://lore.kernel.org/r/20240506194454.1160315-4-hca@linux.ibm.com Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- drivers/s390/net/netiucv.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 8852b03f943b..039e18d46f76 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1696,26 +1696,14 @@ static const struct attribute_group *netiucv_attr_groups[] = { static int netiucv_register_device(struct net_device *ndev) { struct netiucv_priv *priv = netdev_priv(ndev); - struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL); + struct device *dev; int ret; IUCV_DBF_TEXT(trace, 3, __func__); - if (dev) { - dev_set_name(dev, "net%s", ndev->name); - dev->bus = &iucv_bus; - dev->parent = iucv_root; - dev->groups = netiucv_attr_groups; - /* - * The release function could be called after the - * module has been unloaded. It's _only_ task is to - * free the struct. Therefore, we specify kfree() - * directly here. (Probably a little bit obfuscating - * but legitime ...). - */ - dev->release = (void (*)(struct device *))kfree; - dev->driver = &netiucv_driver; - } else + dev = iucv_alloc_device(netiucv_attr_groups, &netiucv_driver, NULL, + "net%s", ndev->name); + if (!dev) return -ENOMEM; ret = device_register(dev); From 3bbde49ec560f95b2b09b9cedd0c41ae95ecb92c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 May 2024 21:44:52 +0200 Subject: [PATCH 23/39] s390/smsgiucv_app: Make use of iucv_alloc_device() Make use of iucv_alloc_device() to get rid of quite some code. In addition this also removes a cast to an incompatible function (clang W=1): drivers/s390/net/smsgiucv_app.c:176:26: error: cast from 'void (*)(const void *)' to 'void (*)(struct device *)' converts to incompatible function type [-Werror,-Wcast-function-type-strict] 176 | smsg_app_dev->release = (void (*)(struct device *)) kfree; | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Reported-by: Nathan Chancellor Closes: https://lore.kernel.org/r/20240417-s390-drivers-fix-cast-function-type-v1-2-fd048c9903b0@kernel.org Acked-by: Alexandra Winter Link: https://lore.kernel.org/r/20240506194454.1160315-5-hca@linux.ibm.com Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- drivers/s390/net/smsgiucv_app.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/drivers/s390/net/smsgiucv_app.c b/drivers/s390/net/smsgiucv_app.c index 0a263999f7ae..cc44cbb0028b 100644 --- a/drivers/s390/net/smsgiucv_app.c +++ b/drivers/s390/net/smsgiucv_app.c @@ -156,25 +156,14 @@ static int __init smsgiucv_app_init(void) if (!MACHINE_IS_VM) return -ENODEV; - smsg_app_dev = kzalloc(sizeof(*smsg_app_dev), GFP_KERNEL); + smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus); + if (!smsgiucv_drv) + return -ENODEV; + + smsg_app_dev = iucv_alloc_device(NULL, smsgiucv_drv, NULL, KMSG_COMPONENT); if (!smsg_app_dev) return -ENOMEM; - smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus); - if (!smsgiucv_drv) { - kfree(smsg_app_dev); - return -ENODEV; - } - - rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT); - if (rc) { - kfree(smsg_app_dev); - goto fail; - } - smsg_app_dev->bus = &iucv_bus; - smsg_app_dev->parent = iucv_root; - smsg_app_dev->release = (void (*)(struct device *)) kfree; - smsg_app_dev->driver = smsgiucv_drv; rc = device_register(smsg_app_dev); if (rc) { put_device(smsg_app_dev); From ccec5032291b108e694b55394cd035c9d840052a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 May 2024 21:44:53 +0200 Subject: [PATCH 24/39] tty: hvc-iucv: Make use of iucv_alloc_device() Make use of iucv_alloc_device() to get rid of quite some code. Acked-by: Alexandra Winter Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240506194454.1160315-6-hca@linux.ibm.com Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- drivers/tty/hvc/hvc_iucv.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index b1149bc62ca1..ed4bf40278a7 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -1035,11 +1035,6 @@ static const struct attribute_group *hvc_iucv_dev_attr_groups[] = { NULL, }; -static void hvc_iucv_free(struct device *data) -{ - kfree(data); -} - /** * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance * @id: hvc_iucv_table index @@ -1090,18 +1085,12 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) memcpy(priv->srv_name, name, 8); ASCEBC(priv->srv_name, 8); - /* create and setup device */ - priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL); + priv->dev = iucv_alloc_device(hvc_iucv_dev_attr_groups, NULL, + priv, "hvc_iucv%d", id); if (!priv->dev) { rc = -ENOMEM; goto out_error_dev; } - dev_set_name(priv->dev, "hvc_iucv%d", id); - dev_set_drvdata(priv->dev, priv); - priv->dev->bus = &iucv_bus; - priv->dev->parent = iucv_root; - priv->dev->groups = hvc_iucv_dev_attr_groups; - priv->dev->release = hvc_iucv_free; rc = device_register(priv->dev); if (rc) { put_device(priv->dev); From effb83572685eaa70d05a8dd6307ca574a11fcf3 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 May 2024 21:44:54 +0200 Subject: [PATCH 25/39] s390/iucv: Unexport iucv_root There is no user of iucv_root outside of the core IUCV code left. Therefore remove the EXPORT_SYMBOL. Acked-by: Alexandra Winter Link: https://lore.kernel.org/r/20240506194454.1160315-7-hca@linux.ibm.com Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- include/net/iucv/iucv.h | 1 - net/iucv/iucv.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/net/iucv/iucv.h b/include/net/iucv/iucv.h index b3736e66fe1a..4d114e6d6d23 100644 --- a/include/net/iucv/iucv.h +++ b/include/net/iucv/iucv.h @@ -82,7 +82,6 @@ struct iucv_array { } __attribute__ ((aligned (8))); extern const struct bus_type iucv_bus; -extern struct device *iucv_root; struct device_driver; diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 9db7c2c0ae72..2e61f19621ee 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -73,8 +73,7 @@ const struct bus_type iucv_bus = { }; EXPORT_SYMBOL(iucv_bus); -struct device *iucv_root; -EXPORT_SYMBOL(iucv_root); +static struct device *iucv_root; static void iucv_release_device(struct device *device) { From 207ddb918995369f716e2b1d987ca6880c2bc21d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 13 May 2024 12:16:20 +0200 Subject: [PATCH 26/39] s390/alternatives: Convert runtime sanity check into compile time check __apply_alternatives() contains a runtime check which verifies that the size of the to be patched code area is even. Convert this to a compile time check using a similar ".org" trick, which is already used to verify that old and new code areas have the same size. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/alternative-asm.h | 1 + arch/s390/include/asm/alternative.h | 1 + arch/s390/kernel/alternative.c | 7 ------- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/arch/s390/include/asm/alternative-asm.h b/arch/s390/include/asm/alternative-asm.h index 7db046596b93..608f6287ca9c 100644 --- a/arch/s390/include/asm/alternative-asm.h +++ b/arch/s390/include/asm/alternative-asm.h @@ -15,6 +15,7 @@ .long \alt_start - . .word \feature .byte \orig_end - \orig_start + .org . - ( \orig_end - \orig_start ) & 1 .org . - ( \orig_end - \orig_start ) + ( \alt_end - \alt_start ) .org . - ( \alt_end - \alt_start ) + ( \orig_end - \orig_start ) .endm diff --git a/arch/s390/include/asm/alternative.h b/arch/s390/include/asm/alternative.h index 904dd049f954..dd93b92c3ab6 100644 --- a/arch/s390/include/asm/alternative.h +++ b/arch/s390/include/asm/alternative.h @@ -53,6 +53,7 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end); "\t.long " b_altinstr(num)"b - .\n" /* alt instruction */ \ "\t.word " __stringify(facility) "\n" /* facility bit */ \ "\t.byte " oldinstr_len "\n" /* instruction len */ \ + "\t.org . - (" oldinstr_len ") & 1\n" \ "\t.org . - (" oldinstr_len ") + (" altinstr_len(num) ")\n" \ "\t.org . - (" altinstr_len(num) ") + (" oldinstr_len ")\n" diff --git a/arch/s390/kernel/alternative.c b/arch/s390/kernel/alternative.c index e7bca29f9c34..1ac5f707dd70 100644 --- a/arch/s390/kernel/alternative.c +++ b/arch/s390/kernel/alternative.c @@ -33,13 +33,6 @@ static void __init_or_module __apply_alternatives(struct alt_instr *start, if (!__test_facility(a->facility, alt_stfle_fac_list)) continue; - - if (unlikely(a->instrlen % 2)) { - WARN_ONCE(1, "cpu alternatives instructions length is " - "odd, skipping patching\n"); - continue; - } - s390_kernel_write(instr, replacement, a->instrlen); } } From 72935e3adafdd26fbabf12f3d88165a09516168a Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 10 May 2024 11:39:52 +0200 Subject: [PATCH 27/39] s390/ipl: Fix size of vmcmd buffers for sending z/VM CP diag X'008' cmds z/VM CP diagnose X'008' accepts commands of max 240 characters. Using a smaller value as a buffer size makes kernel send truncated CP commands which are longer than the old buffer size. This can result in invalid CP commands passed to z/VM. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Signed-off-by: Alexander Gordeev --- arch/s390/kernel/ipl.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 7dc54571f18e..eb3fd130ee81 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1958,11 +1958,13 @@ static struct shutdown_action __refdata dump_reipl_action = { * vmcmd shutdown action: Trigger vm command on shutdown. */ -static char vmcmd_on_reboot[128]; -static char vmcmd_on_panic[128]; -static char vmcmd_on_halt[128]; -static char vmcmd_on_poff[128]; -static char vmcmd_on_restart[128]; +#define VMCMD_MAX_SIZE 240 + +static char vmcmd_on_reboot[VMCMD_MAX_SIZE + 1]; +static char vmcmd_on_panic[VMCMD_MAX_SIZE + 1]; +static char vmcmd_on_halt[VMCMD_MAX_SIZE + 1]; +static char vmcmd_on_poff[VMCMD_MAX_SIZE + 1]; +static char vmcmd_on_restart[VMCMD_MAX_SIZE + 1]; DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot); DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic); @@ -2288,8 +2290,8 @@ static int __init vmcmd_on_reboot_setup(char *str) { if (!MACHINE_IS_VM) return 1; - strncpy_skip_quote(vmcmd_on_reboot, str, 127); - vmcmd_on_reboot[127] = 0; + strncpy_skip_quote(vmcmd_on_reboot, str, VMCMD_MAX_SIZE); + vmcmd_on_reboot[VMCMD_MAX_SIZE] = 0; on_reboot_trigger.action = &vmcmd_action; return 1; } @@ -2299,8 +2301,8 @@ static int __init vmcmd_on_panic_setup(char *str) { if (!MACHINE_IS_VM) return 1; - strncpy_skip_quote(vmcmd_on_panic, str, 127); - vmcmd_on_panic[127] = 0; + strncpy_skip_quote(vmcmd_on_panic, str, VMCMD_MAX_SIZE); + vmcmd_on_panic[VMCMD_MAX_SIZE] = 0; on_panic_trigger.action = &vmcmd_action; return 1; } @@ -2310,8 +2312,8 @@ static int __init vmcmd_on_halt_setup(char *str) { if (!MACHINE_IS_VM) return 1; - strncpy_skip_quote(vmcmd_on_halt, str, 127); - vmcmd_on_halt[127] = 0; + strncpy_skip_quote(vmcmd_on_halt, str, VMCMD_MAX_SIZE); + vmcmd_on_halt[VMCMD_MAX_SIZE] = 0; on_halt_trigger.action = &vmcmd_action; return 1; } @@ -2321,8 +2323,8 @@ static int __init vmcmd_on_poff_setup(char *str) { if (!MACHINE_IS_VM) return 1; - strncpy_skip_quote(vmcmd_on_poff, str, 127); - vmcmd_on_poff[127] = 0; + strncpy_skip_quote(vmcmd_on_poff, str, VMCMD_MAX_SIZE); + vmcmd_on_poff[VMCMD_MAX_SIZE] = 0; on_poff_trigger.action = &vmcmd_action; return 1; } From 247576bf624a200b9d4dba6126e760af0d5ebe0e Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 10 May 2024 11:39:53 +0200 Subject: [PATCH 28/39] s390/ipl: Do not accept z/VM CP diag X'008' cmds longer than max length The old implementation of vmcmd sysfs string attributes truncated passed z/VM CP diagnose X'008' commands which were longer than the max allowed number of characters but the reported number of written characters was still equal to the entire length of a given string. This can result in silent failures of some s390-tools (e.g. dumpconf) which can be very hard to detect. Therefore, this commit makes a write attempt to a vmcmd sysfs attribute * fail with E2BIG error if a given string is longer than the maximum allowed one * never destroy the old data in the vmcmd sysfs attribute if the new data doesn't fit into it entirely * return the actual number of written characters if it succeeds Reviewed-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Signed-off-by: Alexander Gordeev --- arch/s390/kernel/ipl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index eb3fd130ee81..f78c40e2b631 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -266,7 +266,11 @@ static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ struct kobj_attribute *attr, \ const char *buf, size_t len) \ { \ - strscpy(_value, buf, sizeof(_value)); \ + if (len >= sizeof(_value)) \ + return -E2BIG; \ + len = strscpy(_value, buf, sizeof(_value)); \ + if (len < 0) \ + return len; \ strim(_value); \ return len; \ } \ From 9c922b73acaf39f867668d9cbe5dc69c23511f84 Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 10 May 2024 12:41:25 +0200 Subject: [PATCH 29/39] s390/ipl: Fix incorrect initialization of len fields in nvme reipl block Use correct symbolic constants IPL_BP_NVME_LEN and IPL_BP0_NVME_LEN to initialize nvme reipl block when 'scp_data' sysfs attribute is being updated. This bug had not been detected before because the corresponding fcp and nvme symbolic constants are equal. Fixes: 23a457b8d57d ("s390: nvme reipl") Reviewed-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Signed-off-by: Alexander Gordeev --- arch/s390/kernel/ipl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index f78c40e2b631..73d09cd4c6b0 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -966,8 +966,8 @@ static ssize_t reipl_nvme_scpdata_write(struct file *filp, struct kobject *kobj, scpdata_len += padding; } - reipl_block_nvme->hdr.len = IPL_BP_FCP_LEN + scpdata_len; - reipl_block_nvme->nvme.len = IPL_BP0_FCP_LEN + scpdata_len; + reipl_block_nvme->hdr.len = IPL_BP_NVME_LEN + scpdata_len; + reipl_block_nvme->nvme.len = IPL_BP0_NVME_LEN + scpdata_len; reipl_block_nvme->nvme.scp_data_len = scpdata_len; return count; From 7faacaeaf6ce12fae78751de5ad869d8f1e1cd7a Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 10 May 2024 12:41:26 +0200 Subject: [PATCH 30/39] s390/ipl: Fix incorrect initialization of nvme dump block Initialize the correct fields of the nvme dump block. This bug had not been detected before because first, the fcp and nvme fields of struct ipl_parameter_block are part of the same union and, therefore, overlap in memory and second, they are identical in structure and size. Fixes: d70e38cb1dee ("s390: nvme dump support") Reviewed-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Signed-off-by: Alexander Gordeev --- arch/s390/kernel/ipl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 73d09cd4c6b0..2f004dfd6131 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1862,9 +1862,9 @@ static int __init dump_nvme_init(void) } dump_block_nvme->hdr.len = IPL_BP_NVME_LEN; dump_block_nvme->hdr.version = IPL_PARM_BLOCK_VERSION; - dump_block_nvme->fcp.len = IPL_BP0_NVME_LEN; - dump_block_nvme->fcp.pbt = IPL_PBT_NVME; - dump_block_nvme->fcp.opt = IPL_PB0_NVME_OPT_DUMP; + dump_block_nvme->nvme.len = IPL_BP0_NVME_LEN; + dump_block_nvme->nvme.pbt = IPL_PBT_NVME; + dump_block_nvme->nvme.opt = IPL_PB0_NVME_OPT_DUMP; dump_capabilities |= DUMP_TYPE_NVME; return 0; } From 005ca0133375a68fbbe4244cc7399b640d78dd91 Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 10 May 2024 12:41:27 +0200 Subject: [PATCH 31/39] s390/ipl: Introduce macros for (re)ipl sysfs attribute 'scp_data' This is a refactoring change to reduce code duplication and improve code reuse. Acked-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Signed-off-by: Alexander Gordeev --- arch/s390/kernel/ipl.c | 246 ++++++++++++++--------------------------- 1 file changed, 83 insertions(+), 163 deletions(-) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 2f004dfd6131..0de11583ec9d 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -279,6 +279,61 @@ static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ sys_##_prefix##_##_name##_show, \ sys_##_prefix##_##_name##_store) +#define IPL_ATTR_SCP_DATA_SHOW_FN(_prefix, _ipl_block) \ +static ssize_t sys_##_prefix##_scp_data_show(struct file *filp, \ + struct kobject *kobj, \ + struct bin_attribute *attr, \ + char *buf, loff_t off, \ + size_t count) \ +{ \ + size_t size = _ipl_block.scp_data_len; \ + void *scp_data = _ipl_block.scp_data; \ + \ + return memory_read_from_buffer(buf, count, &off, \ + scp_data, size); \ +} + +#define IPL_ATTR_SCP_DATA_STORE_FN(_prefix, _ipl_block_hdr, _ipl_block, _ipl_bp_len, _ipl_bp0_len)\ +static ssize_t sys_##_prefix##_scp_data_store(struct file *filp, \ + struct kobject *kobj, \ + struct bin_attribute *attr, \ + char *buf, loff_t off, \ + size_t count) \ +{ \ + size_t scpdata_len = count; \ + size_t padding; \ + \ + if (off) \ + return -EINVAL; \ + \ + memcpy(_ipl_block.scp_data, buf, count); \ + if (scpdata_len % 8) { \ + padding = 8 - (scpdata_len % 8); \ + memset(_ipl_block.scp_data + scpdata_len, \ + 0, padding); \ + scpdata_len += padding; \ + } \ + \ + _ipl_block_hdr.len = _ipl_bp_len + scpdata_len; \ + _ipl_block.len = _ipl_bp0_len + scpdata_len; \ + _ipl_block.scp_data_len = scpdata_len; \ + \ + return count; \ +} + +#define DEFINE_IPL_ATTR_SCP_DATA_RO(_prefix, _ipl_block, _size) \ +IPL_ATTR_SCP_DATA_SHOW_FN(_prefix, _ipl_block) \ +static struct bin_attribute sys_##_prefix##_scp_data_attr = \ + __BIN_ATTR(scp_data, 0444, sys_##_prefix##_scp_data_show, \ + NULL, _size) + +#define DEFINE_IPL_ATTR_SCP_DATA_RW(_prefix, _ipl_block_hdr, _ipl_block, _ipl_bp_len, _ipl_bp0_len, _size)\ +IPL_ATTR_SCP_DATA_SHOW_FN(_prefix, _ipl_block) \ +IPL_ATTR_SCP_DATA_STORE_FN(_prefix, _ipl_block_hdr, _ipl_block, _ipl_bp_len, _ipl_bp0_len)\ +static struct bin_attribute sys_##_prefix##_scp_data_attr = \ + __BIN_ATTR(scp_data, 0644, sys_##_prefix##_scp_data_show, \ + sys_##_prefix##_scp_data_store, _size) + /* * ipl section */ @@ -377,71 +432,38 @@ static ssize_t sys_ipl_device_show(struct kobject *kobj, static struct kobj_attribute sys_ipl_device_attr = __ATTR(device, 0444, sys_ipl_device_show, NULL); -static ssize_t ipl_parameter_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, - loff_t off, size_t count) +static ssize_t sys_ipl_parameter_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { return memory_read_from_buffer(buf, count, &off, &ipl_block, ipl_block.hdr.len); } -static struct bin_attribute ipl_parameter_attr = - __BIN_ATTR(binary_parameter, 0444, ipl_parameter_read, NULL, +static struct bin_attribute sys_ipl_parameter_attr = + __BIN_ATTR(binary_parameter, 0444, sys_ipl_parameter_read, NULL, PAGE_SIZE); -static ssize_t ipl_scp_data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - unsigned int size = ipl_block.fcp.scp_data_len; - void *scp_data = &ipl_block.fcp.scp_data; - - return memory_read_from_buffer(buf, count, &off, scp_data, size); -} - -static ssize_t ipl_nvme_scp_data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - unsigned int size = ipl_block.nvme.scp_data_len; - void *scp_data = &ipl_block.nvme.scp_data; - - return memory_read_from_buffer(buf, count, &off, scp_data, size); -} - -static ssize_t ipl_eckd_scp_data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - unsigned int size = ipl_block.eckd.scp_data_len; - void *scp_data = &ipl_block.eckd.scp_data; - - return memory_read_from_buffer(buf, count, &off, scp_data, size); -} - -static struct bin_attribute ipl_scp_data_attr = - __BIN_ATTR(scp_data, 0444, ipl_scp_data_read, NULL, PAGE_SIZE); - -static struct bin_attribute ipl_nvme_scp_data_attr = - __BIN_ATTR(scp_data, 0444, ipl_nvme_scp_data_read, NULL, PAGE_SIZE); - -static struct bin_attribute ipl_eckd_scp_data_attr = - __BIN_ATTR(scp_data, 0444, ipl_eckd_scp_data_read, NULL, PAGE_SIZE); +DEFINE_IPL_ATTR_SCP_DATA_RO(ipl_fcp, ipl_block.fcp, PAGE_SIZE); static struct bin_attribute *ipl_fcp_bin_attrs[] = { - &ipl_parameter_attr, - &ipl_scp_data_attr, + &sys_ipl_parameter_attr, + &sys_ipl_fcp_scp_data_attr, NULL, }; +DEFINE_IPL_ATTR_SCP_DATA_RO(ipl_nvme, ipl_block.nvme, PAGE_SIZE); + static struct bin_attribute *ipl_nvme_bin_attrs[] = { - &ipl_parameter_attr, - &ipl_nvme_scp_data_attr, + &sys_ipl_parameter_attr, + &sys_ipl_nvme_scp_data_attr, NULL, }; +DEFINE_IPL_ATTR_SCP_DATA_RO(ipl_eckd, ipl_block.eckd, PAGE_SIZE); + static struct bin_attribute *ipl_eckd_bin_attrs[] = { - &ipl_parameter_attr, - &ipl_eckd_scp_data_attr, + &sys_ipl_parameter_attr, + &sys_ipl_eckd_scp_data_attr, NULL, }; @@ -780,44 +802,10 @@ static struct kobj_attribute sys_reipl_ccw_vmparm_attr = /* FCP reipl device attributes */ -static ssize_t reipl_fcp_scpdata_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - size_t size = reipl_block_fcp->fcp.scp_data_len; - void *scp_data = reipl_block_fcp->fcp.scp_data; - - return memory_read_from_buffer(buf, count, &off, scp_data, size); -} - -static ssize_t reipl_fcp_scpdata_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - size_t scpdata_len = count; - size_t padding; - - - if (off) - return -EINVAL; - - memcpy(reipl_block_fcp->fcp.scp_data, buf, count); - if (scpdata_len % 8) { - padding = 8 - (scpdata_len % 8); - memset(reipl_block_fcp->fcp.scp_data + scpdata_len, - 0, padding); - scpdata_len += padding; - } - - reipl_block_fcp->hdr.len = IPL_BP_FCP_LEN + scpdata_len; - reipl_block_fcp->fcp.len = IPL_BP0_FCP_LEN + scpdata_len; - reipl_block_fcp->fcp.scp_data_len = scpdata_len; - - return count; -} -static struct bin_attribute sys_reipl_fcp_scp_data_attr = - __BIN_ATTR(scp_data, 0644, reipl_fcp_scpdata_read, - reipl_fcp_scpdata_write, DIAG308_SCPDATA_SIZE); +DEFINE_IPL_ATTR_SCP_DATA_RW(reipl_fcp, reipl_block_fcp->hdr, + reipl_block_fcp->fcp, + IPL_BP_FCP_LEN, IPL_BP0_FCP_LEN, + DIAG308_SCPDATA_SIZE); static struct bin_attribute *reipl_fcp_bin_attrs[] = { &sys_reipl_fcp_scp_data_attr, @@ -938,44 +926,10 @@ static struct kobj_attribute sys_reipl_fcp_clear_attr = /* NVME reipl device attributes */ -static ssize_t reipl_nvme_scpdata_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - size_t size = reipl_block_nvme->nvme.scp_data_len; - void *scp_data = reipl_block_nvme->nvme.scp_data; - - return memory_read_from_buffer(buf, count, &off, scp_data, size); -} - -static ssize_t reipl_nvme_scpdata_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - size_t scpdata_len = count; - size_t padding; - - if (off) - return -EINVAL; - - memcpy(reipl_block_nvme->nvme.scp_data, buf, count); - if (scpdata_len % 8) { - padding = 8 - (scpdata_len % 8); - memset(reipl_block_nvme->nvme.scp_data + scpdata_len, - 0, padding); - scpdata_len += padding; - } - - reipl_block_nvme->hdr.len = IPL_BP_NVME_LEN + scpdata_len; - reipl_block_nvme->nvme.len = IPL_BP0_NVME_LEN + scpdata_len; - reipl_block_nvme->nvme.scp_data_len = scpdata_len; - - return count; -} - -static struct bin_attribute sys_reipl_nvme_scp_data_attr = - __BIN_ATTR(scp_data, 0644, reipl_nvme_scpdata_read, - reipl_nvme_scpdata_write, DIAG308_SCPDATA_SIZE); +DEFINE_IPL_ATTR_SCP_DATA_RW(reipl_nvme, reipl_block_nvme->hdr, + reipl_block_nvme->nvme, + IPL_BP_NVME_LEN, IPL_BP0_NVME_LEN, + DIAG308_SCPDATA_SIZE); static struct bin_attribute *reipl_nvme_bin_attrs[] = { &sys_reipl_nvme_scp_data_attr, @@ -1071,44 +1025,10 @@ static struct attribute_group reipl_ccw_attr_group_lpar = { /* ECKD reipl device attributes */ -static ssize_t reipl_eckd_scpdata_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - size_t size = reipl_block_eckd->eckd.scp_data_len; - void *scp_data = reipl_block_eckd->eckd.scp_data; - - return memory_read_from_buffer(buf, count, &off, scp_data, size); -} - -static ssize_t reipl_eckd_scpdata_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - size_t scpdata_len = count; - size_t padding; - - if (off) - return -EINVAL; - - memcpy(reipl_block_eckd->eckd.scp_data, buf, count); - if (scpdata_len % 8) { - padding = 8 - (scpdata_len % 8); - memset(reipl_block_eckd->eckd.scp_data + scpdata_len, - 0, padding); - scpdata_len += padding; - } - - reipl_block_eckd->hdr.len = IPL_BP_ECKD_LEN + scpdata_len; - reipl_block_eckd->eckd.len = IPL_BP0_ECKD_LEN + scpdata_len; - reipl_block_eckd->eckd.scp_data_len = scpdata_len; - - return count; -} - -static struct bin_attribute sys_reipl_eckd_scp_data_attr = - __BIN_ATTR(scp_data, 0644, reipl_eckd_scpdata_read, - reipl_eckd_scpdata_write, DIAG308_SCPDATA_SIZE); +DEFINE_IPL_ATTR_SCP_DATA_RW(reipl_eckd, reipl_block_eckd->hdr, + reipl_block_eckd->eckd, + IPL_BP_ECKD_LEN, IPL_BP0_ECKD_LEN, + DIAG308_SCPDATA_SIZE); static struct bin_attribute *reipl_eckd_bin_attrs[] = { &sys_reipl_eckd_scp_data_attr, From 24bf80f78e5ad45bc3e0886cef86def712bdf3ed Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 10 May 2024 12:41:28 +0200 Subject: [PATCH 32/39] s390/ipl: Introduce sysfs attribute 'scp_data' for dump ipl This is analogous to the reipl's sysfs attribute named equally and enables tools such as s390-tools' dumpconf to pass additional kernel cmdline parameters to a stand-alone dumper such as zfcpdump (e.g. to enable debug output with 'dump_debug' parameter) or ngdump. Acked-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Signed-off-by: Alexander Gordeev --- arch/s390/kernel/ipl.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 0de11583ec9d..ff6bd177f69f 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1572,6 +1572,11 @@ DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n", DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", dump_block_fcp->fcp.devno); +DEFINE_IPL_ATTR_SCP_DATA_RW(dump_fcp, dump_block_fcp->hdr, + dump_block_fcp->fcp, + IPL_BP_FCP_LEN, IPL_BP0_FCP_LEN, + DIAG308_SCPDATA_SIZE); + static struct attribute *dump_fcp_attrs[] = { &sys_dump_fcp_device_attr.attr, &sys_dump_fcp_wwpn_attr.attr, @@ -1581,9 +1586,15 @@ static struct attribute *dump_fcp_attrs[] = { NULL, }; +static struct bin_attribute *dump_fcp_bin_attrs[] = { + &sys_dump_fcp_scp_data_attr, + NULL, +}; + static struct attribute_group dump_fcp_attr_group = { .name = IPL_FCP_STR, .attrs = dump_fcp_attrs, + .bin_attrs = dump_fcp_bin_attrs, }; /* NVME dump device attributes */ @@ -1596,6 +1607,11 @@ DEFINE_IPL_ATTR_RW(dump_nvme, bootprog, "%lld\n", "%llx\n", DEFINE_IPL_ATTR_RW(dump_nvme, br_lba, "%lld\n", "%llx\n", dump_block_nvme->nvme.br_lba); +DEFINE_IPL_ATTR_SCP_DATA_RW(dump_nvme, dump_block_nvme->hdr, + dump_block_nvme->nvme, + IPL_BP_NVME_LEN, IPL_BP0_NVME_LEN, + DIAG308_SCPDATA_SIZE); + static struct attribute *dump_nvme_attrs[] = { &sys_dump_nvme_fid_attr.attr, &sys_dump_nvme_nsid_attr.attr, @@ -1604,9 +1620,15 @@ static struct attribute *dump_nvme_attrs[] = { NULL, }; +static struct bin_attribute *dump_nvme_bin_attrs[] = { + &sys_dump_nvme_scp_data_attr, + NULL, +}; + static struct attribute_group dump_nvme_attr_group = { .name = IPL_NVME_STR, .attrs = dump_nvme_attrs, + .bin_attrs = dump_nvme_bin_attrs, }; /* ECKD dump device attributes */ @@ -1620,6 +1642,11 @@ IPL_ATTR_BR_CHR_STORE_FN(dump, dump_block_eckd->eckd); static struct kobj_attribute sys_dump_eckd_br_chr_attr = __ATTR(br_chr, 0644, eckd_dump_br_chr_show, eckd_dump_br_chr_store); +DEFINE_IPL_ATTR_SCP_DATA_RW(dump_eckd, dump_block_eckd->hdr, + dump_block_eckd->eckd, + IPL_BP_ECKD_LEN, IPL_BP0_ECKD_LEN, + DIAG308_SCPDATA_SIZE); + static struct attribute *dump_eckd_attrs[] = { &sys_dump_eckd_device_attr.attr, &sys_dump_eckd_bootprog_attr.attr, @@ -1627,9 +1654,15 @@ static struct attribute *dump_eckd_attrs[] = { NULL, }; +static struct bin_attribute *dump_eckd_bin_attrs[] = { + &sys_dump_eckd_scp_data_attr, + NULL, +}; + static struct attribute_group dump_eckd_attr_group = { .name = IPL_ECKD_STR, .attrs = dump_eckd_attrs, + .bin_attrs = dump_eckd_bin_attrs, }; /* CCW dump device attributes */ From 830999bd7e72f4128b9dfa37090d9fa8120ce323 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Fri, 3 May 2024 11:31:42 +0200 Subject: [PATCH 33/39] s390/cpacf: Split and rework cpacf query functions Rework the cpacf query functions to use the correct RRE or RRF instruction formats and set register fields within instructions correctly. Fixes: 1afd43e0fbba ("s390/crypto: allow to query all known cpacf functions") Reported-by: Nina Schoetterl-Glausch Suggested-by: Heiko Carstens Suggested-by: Juergen Christ Suggested-by: Holger Dengler Signed-off-by: Harald Freudenberger Reviewed-by: Holger Dengler Reviewed-by: Juergen Christ Cc: Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cpacf.h | 101 +++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 20 deletions(-) diff --git a/arch/s390/include/asm/cpacf.h b/arch/s390/include/asm/cpacf.h index b378e2b57ad8..153dc4fcc40a 100644 --- a/arch/s390/include/asm/cpacf.h +++ b/arch/s390/include/asm/cpacf.h @@ -166,28 +166,79 @@ typedef struct { unsigned char bytes[16]; } cpacf_mask_t; -/** - * cpacf_query() - check if a specific CPACF function is available - * @opcode: the opcode of the crypto instruction - * @func: the function code to test for - * - * Executes the query function for the given crypto instruction @opcode - * and checks if @func is available - * - * Returns 1 if @func is available for @opcode, 0 otherwise - */ -static __always_inline void __cpacf_query(unsigned int opcode, cpacf_mask_t *mask) +static __always_inline void __cpacf_query_rre(u32 opc, u8 r1, u8 r2, + cpacf_mask_t *mask) { asm volatile( - " lghi 0,0\n" /* query function */ - " lgr 1,%[mask]\n" - " spm 0\n" /* pckmo doesn't change the cc */ - /* Parameter regs are ignored, but must be nonzero and unique */ - "0: .insn rrf,%[opc] << 16,2,4,6,0\n" - " brc 1,0b\n" /* handle partial completion */ - : "=m" (*mask) - : [mask] "d" ((unsigned long)mask), [opc] "i" (opcode) - : "cc", "0", "1"); + " la %%r1,%[mask]\n" + " xgr %%r0,%%r0\n" + " .insn rre,%[opc] << 16,%[r1],%[r2]\n" + : [mask] "=R" (*mask) + : [opc] "i" (opc), + [r1] "i" (r1), [r2] "i" (r2) + : "cc", "r0", "r1"); +} + +static __always_inline void __cpacf_query_rrf(u32 opc, + u8 r1, u8 r2, u8 r3, u8 m4, + cpacf_mask_t *mask) +{ + asm volatile( + " la %%r1,%[mask]\n" + " xgr %%r0,%%r0\n" + " .insn rrf,%[opc] << 16,%[r1],%[r2],%[r3],%[m4]\n" + : [mask] "=R" (*mask) + : [opc] "i" (opc), [r1] "i" (r1), [r2] "i" (r2), + [r3] "i" (r3), [m4] "i" (m4) + : "cc", "r0", "r1"); +} + +static __always_inline void __cpacf_query(unsigned int opcode, + cpacf_mask_t *mask) +{ + switch (opcode) { + case CPACF_KDSA: + __cpacf_query_rre(CPACF_KDSA, 0, 2, mask); + break; + case CPACF_KIMD: + __cpacf_query_rre(CPACF_KIMD, 0, 2, mask); + break; + case CPACF_KLMD: + __cpacf_query_rre(CPACF_KLMD, 0, 2, mask); + break; + case CPACF_KM: + __cpacf_query_rre(CPACF_KM, 2, 4, mask); + break; + case CPACF_KMA: + __cpacf_query_rrf(CPACF_KMA, 2, 4, 6, 0, mask); + break; + case CPACF_KMAC: + __cpacf_query_rre(CPACF_KMAC, 0, 2, mask); + break; + case CPACF_KMC: + __cpacf_query_rre(CPACF_KMC, 2, 4, mask); + break; + case CPACF_KMCTR: + __cpacf_query_rrf(CPACF_KMCTR, 2, 4, 6, 0, mask); + break; + case CPACF_KMF: + __cpacf_query_rre(CPACF_KMF, 2, 4, mask); + break; + case CPACF_KMO: + __cpacf_query_rre(CPACF_KMO, 2, 4, mask); + break; + case CPACF_PCC: + __cpacf_query_rre(CPACF_PCC, 0, 0, mask); + break; + case CPACF_PCKMO: + __cpacf_query_rre(CPACF_PCKMO, 0, 0, mask); + break; + case CPACF_PRNO: + __cpacf_query_rre(CPACF_PRNO, 2, 4, mask); + break; + default: + BUG(); + } } static __always_inline int __cpacf_check_opcode(unsigned int opcode) @@ -215,6 +266,16 @@ static __always_inline int __cpacf_check_opcode(unsigned int opcode) } } +/** + * cpacf_query() - check if a specific CPACF function is available + * @opcode: the opcode of the crypto instruction + * @func: the function code to test for + * + * Executes the query function for the given crypto instruction @opcode + * and checks if @func is available + * + * Returns 1 if @func is available for @opcode, 0 otherwise + */ static __always_inline int cpacf_query(unsigned int opcode, cpacf_mask_t *mask) { if (__cpacf_check_opcode(opcode)) { From 32e8bd6423fc127d2b37bdcf804fd76af3bbec79 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Tue, 14 May 2024 10:09:32 +0200 Subject: [PATCH 34/39] s390/cpacf: Make use of invalid opcode produce a link error Instead of calling BUG() at runtime introduce and use a prototype for a non-existing function to produce a link error during compile when a not supported opcode is used with the __cpacf_query() or __cpacf_check_opcode() inline functions. Suggested-by: Heiko Carstens Signed-off-by: Harald Freudenberger Reviewed-by: Holger Dengler Reviewed-by: Juergen Christ Cc: stable@vger.kernel.org Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cpacf.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/cpacf.h b/arch/s390/include/asm/cpacf.h index 153dc4fcc40a..c786538e397c 100644 --- a/arch/s390/include/asm/cpacf.h +++ b/arch/s390/include/asm/cpacf.h @@ -166,6 +166,13 @@ typedef struct { unsigned char bytes[16]; } cpacf_mask_t; +/* + * Prototype for a not existing function to produce a link + * error if __cpacf_query() or __cpacf_check_opcode() is used + * with an invalid compile time const opcode. + */ +void __cpacf_bad_opcode(void); + static __always_inline void __cpacf_query_rre(u32 opc, u8 r1, u8 r2, cpacf_mask_t *mask) { @@ -237,7 +244,7 @@ static __always_inline void __cpacf_query(unsigned int opcode, __cpacf_query_rre(CPACF_PRNO, 2, 4, mask); break; default: - BUG(); + __cpacf_bad_opcode(); } } @@ -262,7 +269,8 @@ static __always_inline int __cpacf_check_opcode(unsigned int opcode) case CPACF_KMA: return test_facility(146); /* check for MSA8 */ default: - BUG(); + __cpacf_bad_opcode(); + return 0; } } From d4f9d5a99a3fd1b1c691b7a1a6f8f3f25f4116c9 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 13 May 2024 14:49:13 +0200 Subject: [PATCH 35/39] s390/ap: Fix crash in AP internal function modify_bitmap() A system crash like this Failing address: 200000cb7df6f000 TEID: 200000cb7df6f403 Fault in home space mode while using kernel ASCE. AS:00000002d71bc007 R3:00000003fe5b8007 S:000000011a446000 P:000000015660c13d Oops: 0038 ilc:3 [#1] PREEMPT SMP Modules linked in: mlx5_ib ... CPU: 8 PID: 7556 Comm: bash Not tainted 6.9.0-rc7 #8 Hardware name: IBM 3931 A01 704 (LPAR) Krnl PSW : 0704e00180000000 0000014b75e7b606 (ap_parse_bitmap_str+0x10e/0x1f8) R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:2 PM:0 RI:0 EA:3 Krnl GPRS: 0000000000000001 ffffffffffffffc0 0000000000000001 00000048f96b75d3 000000cb00000100 ffffffffffffffff ffffffffffffffff 000000cb7df6fce0 000000cb7df6fce0 00000000ffffffff 000000000000002b 00000048ffffffff 000003ff9b2dbc80 200000cb7df6fcd8 0000014bffffffc0 000000cb7df6fbc8 Krnl Code: 0000014b75e7b5fc: a7840047 brc 8,0000014b75e7b68a 0000014b75e7b600: 18b2 lr %r11,%r2 #0000014b75e7b602: a7f4000a brc 15,0000014b75e7b616 >0000014b75e7b606: eb22d00000e6 laog %r2,%r2,0(%r13) 0000014b75e7b60c: a7680001 lhi %r6,1 0000014b75e7b610: 187b lr %r7,%r11 0000014b75e7b612: 84960021 brxh %r9,%r6,0000014b75e7b654 0000014b75e7b616: 18e9 lr %r14,%r9 Call Trace: [<0000014b75e7b606>] ap_parse_bitmap_str+0x10e/0x1f8 ([<0000014b75e7b5dc>] ap_parse_bitmap_str+0xe4/0x1f8) [<0000014b75e7b758>] apmask_store+0x68/0x140 [<0000014b75679196>] kernfs_fop_write_iter+0x14e/0x1e8 [<0000014b75598524>] vfs_write+0x1b4/0x448 [<0000014b7559894c>] ksys_write+0x74/0x100 [<0000014b7618a440>] __do_syscall+0x268/0x328 [<0000014b761a3558>] system_call+0x70/0x98 INFO: lockdep is turned off. Last Breaking-Event-Address: [<0000014b75e7b636>] ap_parse_bitmap_str+0x13e/0x1f8 Kernel panic - not syncing: Fatal exception: panic_on_oops occured when /sys/bus/ap/a[pq]mask was updated with a relative mask value (like +0x10-0x12,+60,-90) with one of the numeric values exceeding INT_MAX. The fix is simple: use unsigned long values for the internal variables. The correct checks are already in place in the function but a simple int for the internal variables was used with the possibility to overflow. Reported-by: Marc Hartmayer Signed-off-by: Harald Freudenberger Tested-by: Marc Hartmayer Reviewed-by: Holger Dengler Cc: Signed-off-by: Heiko Carstens --- drivers/s390/crypto/ap_bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index c20b45092079..8b7703f7da76 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1091,7 +1091,7 @@ EXPORT_SYMBOL(ap_hex2bitmap); */ static int modify_bitmap(const char *str, unsigned long *bitmap, int bits) { - int a, i, z; + unsigned long a, i, z; char *np, sign; /* bits needs to be a multiple of 8 */ From 306d6bda8f97432f9cb69b5cbd86afd3a8ca182f Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 13 May 2024 11:21:48 +0200 Subject: [PATCH 36/39] s390/ap: Fix bind complete udev event sent after each AP bus scan With the mentioned commit (see the fixes tag) on every AP bus scan an uevent "AP bus change bindings complete" is emitted. Furthermore if an AP device switched from one driver to another, for example by manipulating the apmask, there was never a "bindings complete" uevent generated. The "bindings complete" event should be sent once when all AP devices have been bound to device drivers and again if unbind/bind actions take place and finally all AP devices are bound again. Therefore implement this. Fixes: 778412ab915d ("s390/ap: rearm APQNs bindings complete completion") Reported-by: Marc Hartmayer Signed-off-by: Harald Freudenberger Reviewed-by: Holger Dengler Signed-off-by: Heiko Carstens --- drivers/s390/crypto/ap_bus.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 8b7703f7da76..898865be0dad 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -732,9 +732,9 @@ static void ap_check_bindings_complete(void) if (bound == apqns) { if (!completion_done(&ap_apqn_bindings_complete)) { complete_all(&ap_apqn_bindings_complete); + ap_send_bindings_complete_uevent(); pr_debug("%s all apqn bindings complete\n", __func__); } - ap_send_bindings_complete_uevent(); } } } @@ -894,6 +894,12 @@ static int ap_device_probe(struct device *dev) goto out; } + /* + * Rearm the bindings complete completion to trigger + * bindings complete when all devices are bound again + */ + reinit_completion(&ap_apqn_bindings_complete); + /* Add queue/card to list of active queues/cards */ spin_lock_bh(&ap_queues_lock); if (is_queue_dev(dev)) From e7dec0b7926f3cd493c697c4c389df77e8e8a34c Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Wed, 15 May 2024 09:20:27 +0200 Subject: [PATCH 37/39] s390/boot: Remove alt_stfle_fac_list from decompressor It is nowhere used in the decompressor, therefore remove it. Fixes: 17e89e1340a3 ("s390/facilities: move stfl information from lowcore to global data") Reviewed-by: Heiko Carstens Signed-off-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/boot/startup.c | 1 - arch/s390/kernel/setup.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index 467283b112cd..182aac6a0f77 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -32,7 +32,6 @@ unsigned long __bootdata_preserved(MODULES_END); unsigned long __bootdata_preserved(max_mappable); u64 __bootdata_preserved(stfle_fac_list[16]); -u64 __bootdata_preserved(alt_stfle_fac_list[16]); struct oldmem_data __bootdata_preserved(oldmem_data); struct machine_info machine; diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index cbd5290939df..90c2c786bb35 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -155,7 +155,7 @@ unsigned int __bootdata_preserved(zlib_dfltcc_support); EXPORT_SYMBOL(zlib_dfltcc_support); u64 __bootdata_preserved(stfle_fac_list[16]); EXPORT_SYMBOL(stfle_fac_list); -u64 __bootdata_preserved(alt_stfle_fac_list[16]); +u64 alt_stfle_fac_list[16]; struct oldmem_data __bootdata_preserved(oldmem_data); unsigned long VMALLOC_START; From d890e6af50e44cf49766b3d25a45586d8485ef08 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 15 May 2024 14:52:21 +0200 Subject: [PATCH 38/39] s390/kprobes: Remove custom insn slot allocator Since commit c98d2ecae08f ("s390/mm: Uncouple physical vs virtual address spaces") the kernel image and module area are within the same 4GB area. This eliminates the need of a custom insn slot allocator for kprobes within the kernel image, since standard module_alloc() allocated pages are sufficient for PC relative instructions with a signed 32 bit offset. Signed-off-by: Heiko Carstens --- arch/s390/kernel/Makefile | 1 - arch/s390/kernel/kprobes.c | 67 +++++----------------------- arch/s390/kernel/kprobes.h | 9 ---- arch/s390/kernel/kprobes_insn_page.S | 22 --------- 4 files changed, 10 insertions(+), 89 deletions(-) delete mode 100644 arch/s390/kernel/kprobes.h delete mode 100644 arch/s390/kernel/kprobes_insn_page.S diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index db2d9ba5a86d..7241fa194709 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -59,7 +59,6 @@ obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o obj-$(CONFIG_COMPAT) += $(compat-obj-y) obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_KPROBES) += kprobes.o -obj-$(CONFIG_KPROBES) += kprobes_insn_page.o obj-$(CONFIG_KPROBES) += mcount.o obj-$(CONFIG_RETHOOK) += rethook.o obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index f0cf20d4b3c5..d8b8e341cfc2 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -24,7 +24,6 @@ #include #include #include -#include "kprobes.h" #include "entry.h" DEFINE_PER_CPU(struct kprobe *, current_kprobe); @@ -32,8 +31,6 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); struct kretprobe_blackpoint kretprobe_blacklist[] = { }; -static int insn_page_in_use; - void *alloc_insn_page(void) { void *page; @@ -45,26 +42,6 @@ void *alloc_insn_page(void) return page; } -static void *alloc_s390_insn_page(void) -{ - if (xchg(&insn_page_in_use, 1) == 1) - return NULL; - return &kprobes_insn_page; -} - -static void free_s390_insn_page(void *page) -{ - xchg(&insn_page_in_use, 0); -} - -struct kprobe_insn_cache kprobe_s390_insn_slots = { - .mutex = __MUTEX_INITIALIZER(kprobe_s390_insn_slots.mutex), - .alloc = alloc_s390_insn_page, - .free = free_s390_insn_page, - .pages = LIST_HEAD_INIT(kprobe_s390_insn_slots.pages), - .insn_size = MAX_INSN_SIZE, -}; - static void copy_instruction(struct kprobe *p) { kprobe_opcode_t insn[MAX_INSN_SIZE]; @@ -78,10 +55,10 @@ static void copy_instruction(struct kprobe *p) if (probe_is_insn_relative_long(&insn[0])) { /* * For pc-relative instructions in RIL-b or RIL-c format patch - * the RI2 displacement field. We have already made sure that - * the insn slot for the patched instruction is within the same - * 2GB area as the original instruction (either kernel image or - * module area). Therefore the new displacement will always fit. + * the RI2 displacement field. The insn slot for the to be + * patched instruction is within the same 4GB area like the + * original instruction. Therefore the new displacement will + * always fit. */ disp = *(s32 *)&insn[1]; addr = (u64)(unsigned long)p->addr; @@ -93,34 +70,6 @@ static void copy_instruction(struct kprobe *p) } NOKPROBE_SYMBOL(copy_instruction); -static int s390_get_insn_slot(struct kprobe *p) -{ - /* - * Get an insn slot that is within the same 2GB area like the original - * instruction. That way instructions with a 32bit signed displacement - * field can be patched and executed within the insn slot. - */ - p->ainsn.insn = NULL; - if (is_kernel((unsigned long)p->addr)) - p->ainsn.insn = get_s390_insn_slot(); - else if (is_module_addr(p->addr)) - p->ainsn.insn = get_insn_slot(); - return p->ainsn.insn ? 0 : -ENOMEM; -} -NOKPROBE_SYMBOL(s390_get_insn_slot); - -static void s390_free_insn_slot(struct kprobe *p) -{ - if (!p->ainsn.insn) - return; - if (is_kernel((unsigned long)p->addr)) - free_s390_insn_slot(p->ainsn.insn, 0); - else - free_insn_slot(p->ainsn.insn, 0); - p->ainsn.insn = NULL; -} -NOKPROBE_SYMBOL(s390_free_insn_slot); - /* Check if paddr is at an instruction boundary */ static bool can_probe(unsigned long paddr) { @@ -174,7 +123,8 @@ int arch_prepare_kprobe(struct kprobe *p) /* Make sure the probe isn't going on a difficult instruction */ if (probe_is_prohibited_opcode(p->addr)) return -EINVAL; - if (s390_get_insn_slot(p)) + p->ainsn.insn = get_insn_slot(); + if (!p->ainsn.insn) return -ENOMEM; copy_instruction(p); return 0; @@ -216,7 +166,10 @@ NOKPROBE_SYMBOL(arch_disarm_kprobe); void arch_remove_kprobe(struct kprobe *p) { - s390_free_insn_slot(p); + if (!p->ainsn.insn) + return; + free_insn_slot(p->ainsn.insn, 0); + p->ainsn.insn = NULL; } NOKPROBE_SYMBOL(arch_remove_kprobe); diff --git a/arch/s390/kernel/kprobes.h b/arch/s390/kernel/kprobes.h deleted file mode 100644 index dc3ed5098ee7..000000000000 --- a/arch/s390/kernel/kprobes.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -#ifndef _ARCH_S390_KPROBES_H -#define _ARCH_S390_KPROBES_H - -#include - -DEFINE_INSN_CACHE_OPS(s390_insn); - -#endif diff --git a/arch/s390/kernel/kprobes_insn_page.S b/arch/s390/kernel/kprobes_insn_page.S deleted file mode 100644 index 0fe4d725e98b..000000000000 --- a/arch/s390/kernel/kprobes_insn_page.S +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#include - -/* - * insn_page is a special 4k aligned dummy function for kprobes. - * It will contain all kprobed instructions that are out-of-line executed. - * The page must be within the kernel image to guarantee that the - * out-of-line instructions are within 2GB distance of their original - * location. Using a dummy function ensures that the insn_page is within - * the text section of the kernel and mapped read-only/executable from - * the beginning on, thus avoiding to split large mappings if the page - * would be in the data section instead. - */ - .section .kprobes.text, "ax" - .balign 4096 -SYM_CODE_START(kprobes_insn_page) - .rept 2048 - .word 0x07fe - .endr -SYM_CODE_END(kprobes_insn_page) - .previous From c1248638f8c35b74400efa7e02e78ecda23373f9 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 16 May 2024 13:56:23 +0200 Subject: [PATCH 39/39] s390/zcrypt: Use kvcalloc() instead of kvmalloc_array() sparse warns about a large memset() call within zcrypt_device_status_mask_ext(): drivers/s390/crypto/zcrypt_api.c:1303:15: warning: memset with byte count of 262144 Get rid of this warning by making sure that all callers of this function allocate memory with __GFP_ZERO, which zeroes memory already at allocation time, which again allows to remove the memset() call. Reviewed-by: Harald Freudenberger Signed-off-by: Heiko Carstens --- drivers/s390/crypto/zcrypt_api.c | 9 +++------ drivers/s390/crypto/zcrypt_ccamisc.c | 12 ++++++------ drivers/s390/crypto/zcrypt_ep11misc.c | 6 +++--- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index eba07f8ef308..74036886ca87 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -1300,9 +1300,6 @@ void zcrypt_device_status_mask_ext(struct zcrypt_device_status_ext *devstatus) struct zcrypt_device_status_ext *stat; int card, queue; - memset(devstatus, 0, MAX_ZDEV_ENTRIES_EXT - * sizeof(struct zcrypt_device_status_ext)); - spin_lock(&zcrypt_list_lock); for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { @@ -1607,9 +1604,9 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, size_t total_size = MAX_ZDEV_ENTRIES_EXT * sizeof(struct zcrypt_device_status_ext); - device_status = kvmalloc_array(MAX_ZDEV_ENTRIES_EXT, - sizeof(struct zcrypt_device_status_ext), - GFP_KERNEL); + device_status = kvcalloc(MAX_ZDEV_ENTRIES_EXT, + sizeof(struct zcrypt_device_status_ext), + GFP_KERNEL); if (!device_status) return -ENOMEM; zcrypt_device_status_mask_ext(device_status); diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index 6087547328ce..7bef2cc4e461 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1762,9 +1762,9 @@ static int findcard(u64 mkvp, u16 *pcardnr, u16 *pdomain, return -EINVAL; /* fetch status of all crypto cards */ - device_status = kvmalloc_array(MAX_ZDEV_ENTRIES_EXT, - sizeof(struct zcrypt_device_status_ext), - GFP_KERNEL); + device_status = kvcalloc(MAX_ZDEV_ENTRIES_EXT, + sizeof(struct zcrypt_device_status_ext), + GFP_KERNEL); if (!device_status) return -ENOMEM; zcrypt_device_status_mask_ext(device_status); @@ -1878,9 +1878,9 @@ int cca_findcard2(u32 **apqns, u32 *nr_apqns, u16 cardnr, u16 domain, struct cca_info ci; /* fetch status of all crypto cards */ - device_status = kvmalloc_array(MAX_ZDEV_ENTRIES_EXT, - sizeof(struct zcrypt_device_status_ext), - GFP_KERNEL); + device_status = kvcalloc(MAX_ZDEV_ENTRIES_EXT, + sizeof(struct zcrypt_device_status_ext), + GFP_KERNEL); if (!device_status) return -ENOMEM; zcrypt_device_status_mask_ext(device_status); diff --git a/drivers/s390/crypto/zcrypt_ep11misc.c b/drivers/s390/crypto/zcrypt_ep11misc.c index 9bcf8fc69ebe..b43db17a4e0e 100644 --- a/drivers/s390/crypto/zcrypt_ep11misc.c +++ b/drivers/s390/crypto/zcrypt_ep11misc.c @@ -1588,9 +1588,9 @@ int ep11_findcard2(u32 **apqns, u32 *nr_apqns, u16 cardnr, u16 domain, struct ep11_card_info eci; /* fetch status of all crypto cards */ - device_status = kvmalloc_array(MAX_ZDEV_ENTRIES_EXT, - sizeof(struct zcrypt_device_status_ext), - GFP_KERNEL); + device_status = kvcalloc(MAX_ZDEV_ENTRIES_EXT, + sizeof(struct zcrypt_device_status_ext), + GFP_KERNEL); if (!device_status) return -ENOMEM; zcrypt_device_status_mask_ext(device_status);