Skip to content

Commit 82caf7a

Browse files
Alexander GordeevVasily Gorbik
authored andcommitted
s390/kdump: rework invocation of DAT-off code
Calling kdump kernel is a two-step process that involves invocation of the purgatory code: first time - to verify the new kernel checksum and second time - to call the new kernel itself. The purgatory code operates on real addresses and does not expect any memory protection. Therefore, before the purgatory code is entered the DAT mode is always turned off. However, it is only restored upon return from the new kernel checksum verification. In case the purgatory was called to start the new kernel and failed the control is returned to the old kernel, but the DAT mode continues staying off. The new kernel start failure is unlikely and leads to the disabled wait state anyway. Still that poses a risk, since the kernel code in general is not DAT-off safe and even calling the disabled_wait() function might crash. Introduce call_nodat() macro that allows entering DAT-off mode, calling an arbitrary function and restoring DAT mode back on. Switch all invocations of DAT-off code to that macro and avoid the above described scenario altogether. Name the call_nodat() macro in small letters after the already existing call_on_stack() and put it to the same header file. Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com> Reviewed-by: Heiko Carstens <hca@linux.ibm.com> [hca@linux.ibm.com: some small modifications to call_nodat() macro] Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
1 parent 39218bc commit 82caf7a

2 files changed

Lines changed: 61 additions & 14 deletions

File tree

arch/s390/include/asm/stacktrace.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,51 @@ static __always_inline unsigned long get_stack_pointer(struct task_struct *task,
189189
(rettype)r2; \
190190
})
191191

192+
/*
193+
* Use call_nodat() to call a function with DAT disabled.
194+
* Proper sign and zero extension of function arguments is done.
195+
* Usage:
196+
*
197+
* rc = call_nodat(nr, rettype, fn, t1, a1, t2, a2, ...)
198+
*
199+
* - nr specifies the number of function arguments of fn.
200+
* - fn is the function to be called, where fn is a physical address.
201+
* - rettype is the return type of fn.
202+
* - t1, a1, ... are pairs, where t1 must match the type of the first
203+
* argument of fn, t2 the second, etc. a1 is the corresponding
204+
* first function argument (not name), etc.
205+
*
206+
* fn() is called with standard C function call ABI, with the exception
207+
* that no useful stackframe or stackpointer is passed via register 15.
208+
* Therefore the called function must not use r15 to access the stack.
209+
*/
210+
#define call_nodat(nr, rettype, fn, ...) \
211+
({ \
212+
rettype (*__fn)(CALL_PARM_##nr(__VA_ARGS__)) = (fn); \
213+
psw_t psw_enter, psw_leave; \
214+
CALL_LARGS_##nr(__VA_ARGS__); \
215+
CALL_REGS_##nr; \
216+
\
217+
CALL_TYPECHECK_##nr(__VA_ARGS__); \
218+
psw_enter.mask = PSW_KERNEL_BITS & ~PSW_MASK_DAT; \
219+
psw_enter.addr = (unsigned long)__fn; \
220+
asm volatile( \
221+
" epsw 0,1\n" \
222+
" risbg 1,0,0,31,32\n" \
223+
" larl 7,1f\n" \
224+
" stg 1,%[psw_leave]\n" \
225+
" stg 7,8+%[psw_leave]\n" \
226+
" la 7,%[psw_leave]\n" \
227+
" lra 7,0(7)\n" \
228+
" larl 1,0f\n" \
229+
" lra 14,0(1)\n" \
230+
" lpswe %[psw_enter]\n" \
231+
"0: lpswe 0(7)\n" \
232+
"1:\n" \
233+
: CALL_FMT_##nr, [psw_leave] "=Q" (psw_leave) \
234+
: [psw_enter] "Q" (psw_enter) \
235+
: "7", CALL_CLOBBER_##nr); \
236+
(rettype)r2; \
237+
})
238+
192239
#endif /* _ASM_S390_STACKTRACE_H */

arch/s390/kernel/machine_kexec.c

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <asm/sclp.h>
3131

3232
typedef void (*relocate_kernel_t)(unsigned long, unsigned long, unsigned long);
33+
typedef int (*purgatory_t)(int);
3334

3435
extern const unsigned char relocate_kernel[];
3536
extern const unsigned long long relocate_kernel_len;
@@ -40,11 +41,14 @@ extern const unsigned long long relocate_kernel_len;
4041
* Reset the system, copy boot CPU registers to absolute zero,
4142
* and jump to the kdump image
4243
*/
43-
static void __do_machine_kdump(void *image)
44+
static void __do_machine_kdump(void *data)
4445
{
45-
int (*start_kdump)(int);
46+
struct kimage *image = data;
47+
purgatory_t purgatory;
4648
unsigned long prefix;
4749

50+
purgatory = (purgatory_t)image->start;
51+
4852
/* store_status() saved the prefix register to lowcore */
4953
prefix = (unsigned long) S390_lowcore.prefixreg_save_area;
5054

@@ -59,11 +63,9 @@ static void __do_machine_kdump(void *image)
5963
memcpy(absolute_pointer(__LC_FPREGS_SAVE_AREA),
6064
phys_to_virt(prefix + __LC_FPREGS_SAVE_AREA), 512);
6165

62-
__load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA);
63-
start_kdump = (void *)((struct kimage *) image)->start;
64-
start_kdump(1);
66+
call_nodat(1, int, purgatory, int, 1);
6567

66-
/* Die if start_kdump returns */
68+
/* Die if kdump returns */
6769
disabled_wait();
6870
}
6971

@@ -112,13 +114,9 @@ static noinline void __machine_kdump(void *image)
112114

113115
static int do_start_kdump(struct kimage *image)
114116
{
115-
int (*start_kdump)(int) = (void *)image->start;
116-
int rc;
117+
purgatory_t purgatory = (purgatory_t)image->start;
117118

118-
__arch_local_irq_stnsm(0xfb); /* disable DAT */
119-
rc = start_kdump(0);
120-
__arch_local_irq_stosm(0x04); /* enable DAT */
121-
return rc;
119+
return call_nodat(1, int, purgatory, int, 0);
122120
}
123121

124122
#endif /* CONFIG_CRASH_DUMP */
@@ -258,8 +256,10 @@ static void __do_machine_kexec(void *data)
258256
diag308_subcode |= DIAG308_FLAG_EI;
259257
s390_reset_system();
260258

261-
__arch_local_irq_stnsm(0xfb); /* disable DAT - avoid no-execute */
262-
(*(relocate_kernel_t)data_mover)(entry, image->start, diag308_subcode);
259+
call_nodat(3, void, (relocate_kernel_t)data_mover,
260+
unsigned long, entry,
261+
unsigned long, image->start,
262+
unsigned long, diag308_subcode);
263263

264264
/* Die if kexec returns */
265265
disabled_wait();

0 commit comments

Comments
 (0)