Skip to content

Commit 5ade5be

Browse files
svens-s390Vasily Gorbik
authored andcommitted
s390: Add infrastructure to patch lowcore accesses
The s390 architecture defines two special per-CPU data pages called the "prefix area". In s390-linux terminology this is usually called "lowcore". This memory area contains system configuration data like old/new PSW's for system call/interrupt/machine check handlers and lots of other data. It is normally mapped to logical address 0. This area can only be accessed when in supervisor mode. This means that kernel code can dereference NULL pointers, because accesses to address 0 are allowed. Parts of lowcore can be write protected, but read accesses and write accesses outside of the write protected areas are not caught. To remove this limitation for debugging and testing, remap lowcore to another address and define a function get_lowcore() which simply returns the address where lowcore is mapped at. This would normally introduce a pointer dereference (=memory read). As lowcore is used for several very often used variables, add code to patch this function during runtime, so we avoid the memory reads. For C code get_lowcore() has to be used, for assembly code it is the GET_LC macro. When using this macro/function a reference is added to alternative patching. All these locations will be patched to the actual lowcore location when the kernel is booted or a module is loaded. To make debugging/bisecting problems easier, this patch adds all the infrastructure but the lowcore address is still hardwired to 0. This way the code can be converted on a per function basis, and the functionality is enabled in a patch after all the functions have been converted. Note that this requires at least z16 because the old lpsw instruction only allowed a 12 bit displacement. z16 introduced lpswey which allows 20 bits (signed), so the lowcore can effectively be mapped from address 0 - 0x7e000. To use 0x7e000 as address, a 6 byte lgfi instruction would have to be used in the alternative. To save two bytes, llilh can be used, but this only allows to set bits 16-31 of the address. In order to use the llilh instruction, use 0x70000 as alternative lowcore address. This is still large enough to catch NULL pointer dereferences into large arrays. Reviewed-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Sven Schnelle <svens@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
1 parent 13be21f commit 5ade5be

12 files changed

Lines changed: 57 additions & 2 deletions

File tree

arch/s390/boot/boot.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,10 @@ extern char _end[], _decompressor_end[];
9191
extern unsigned char _compressed_start[];
9292
extern unsigned char _compressed_end[];
9393
extern struct vmlinux_info _vmlinux_info;
94+
9495
#define vmlinux _vmlinux_info
9596

97+
#define __lowcore_pa(x) ((unsigned long)(x) % sizeof(struct lowcore))
9698
#define __abs_lowcore_pa(x) (((unsigned long)(x) - __abs_lowcore) % sizeof(struct lowcore))
9799
#define __kernel_va(x) ((void *)((unsigned long)(x) - __kaslr_offset_phys + __kaslr_offset))
98100
#define __kernel_pa(x) ((unsigned long)(x) - __kaslr_offset + __kaslr_offset_phys)

arch/s390/boot/ipl_parm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <linux/init.h>
44
#include <linux/ctype.h>
55
#include <linux/pgtable.h>
6+
#include <asm/abs_lowcore.h>
67
#include <asm/page-states.h>
78
#include <asm/ebcdic.h>
89
#include <asm/sclp.h>

arch/s390/boot/startup.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ unsigned long __bootdata_preserved(vmemmap_size);
3030
unsigned long __bootdata_preserved(MODULES_VADDR);
3131
unsigned long __bootdata_preserved(MODULES_END);
3232
unsigned long __bootdata_preserved(max_mappable);
33+
int __bootdata_preserved(relocate_lowcore);
3334

3435
u64 __bootdata_preserved(stfle_fac_list[16]);
3536
struct oldmem_data __bootdata_preserved(oldmem_data);

arch/s390/boot/vmem.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ atomic_long_t __bootdata_preserved(direct_pages_count[PG_DIRECT_MAP_MAX]);
2626
enum populate_mode {
2727
POPULATE_NONE,
2828
POPULATE_DIRECT,
29+
POPULATE_LOWCORE,
2930
POPULATE_ABS_LOWCORE,
3031
POPULATE_IDENTITY,
3132
POPULATE_KERNEL,
@@ -242,6 +243,8 @@ static unsigned long _pa(unsigned long addr, unsigned long size, enum populate_m
242243
return -1;
243244
case POPULATE_DIRECT:
244245
return addr;
246+
case POPULATE_LOWCORE:
247+
return __lowcore_pa(addr);
245248
case POPULATE_ABS_LOWCORE:
246249
return __abs_lowcore_pa(addr);
247250
case POPULATE_KERNEL:
@@ -418,6 +421,7 @@ static void pgtable_populate(unsigned long addr, unsigned long end, enum populat
418421

419422
void setup_vmem(unsigned long kernel_start, unsigned long kernel_end, unsigned long asce_limit)
420423
{
424+
unsigned long lowcore_address = 0;
421425
unsigned long start, end;
422426
unsigned long asce_type;
423427
unsigned long asce_bits;
@@ -455,12 +459,17 @@ void setup_vmem(unsigned long kernel_start, unsigned long kernel_end, unsigned l
455459
__arch_set_page_dat((void *)swapper_pg_dir, 1UL << CRST_ALLOC_ORDER);
456460
__arch_set_page_dat((void *)invalid_pg_dir, 1UL << CRST_ALLOC_ORDER);
457461

462+
if (relocate_lowcore)
463+
lowcore_address = LOWCORE_ALT_ADDRESS;
464+
458465
/*
459466
* To allow prefixing the lowcore must be mapped with 4KB pages.
460467
* To prevent creation of a large page at address 0 first map
461468
* the lowcore and create the identity mapping only afterwards.
462469
*/
463-
pgtable_populate(0, sizeof(struct lowcore), POPULATE_DIRECT);
470+
pgtable_populate(lowcore_address,
471+
lowcore_address + sizeof(struct lowcore),
472+
POPULATE_LOWCORE);
464473
for_each_physmem_usable_range(i, &start, &end) {
465474
pgtable_populate((unsigned long)__identity_va(start),
466475
(unsigned long)__identity_va(end),

arch/s390/include/asm/abs_lowcore.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#ifndef _ASM_S390_ABS_LOWCORE_H
33
#define _ASM_S390_ABS_LOWCORE_H
44

5+
#include <asm/sections.h>
56
#include <asm/lowcore.h>
67

78
#define ABS_LOWCORE_MAP_SIZE (NR_CPUS * sizeof(struct lowcore))
@@ -24,4 +25,11 @@ static inline void put_abs_lowcore(struct lowcore *lc)
2425
put_cpu();
2526
}
2627

28+
extern int __bootdata_preserved(relocate_lowcore);
29+
30+
static inline int have_relocated_lowcore(void)
31+
{
32+
return relocate_lowcore;
33+
}
34+
2735
#endif /* _ASM_S390_ABS_LOWCORE_H */

arch/s390/include/asm/alternative.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
#define ALT_TYPE_FACILITY 0
3535
#define ALT_TYPE_SPEC 1
36+
#define ALT_TYPE_LOWCORE 2
3637

3738
#define ALT_DATA_SHIFT 0
3839
#define ALT_TYPE_SHIFT 20
@@ -50,6 +51,9 @@
5051
ALT_TYPE_SPEC << ALT_TYPE_SHIFT | \
5152
(facility) << ALT_DATA_SHIFT)
5253

54+
#define ALT_LOWCORE (ALT_CTX_EARLY << ALT_CTX_SHIFT | \
55+
ALT_TYPE_LOWCORE << ALT_TYPE_SHIFT)
56+
5357
#ifndef __ASSEMBLY__
5458

5559
#include <linux/types.h>

arch/s390/include/asm/lowcore.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@
1414
#include <asm/ctlreg.h>
1515
#include <asm/cpu.h>
1616
#include <asm/types.h>
17+
#include <asm/alternative.h>
1718

1819
#define LC_ORDER 1
1920
#define LC_PAGES 2
2021

22+
#define LOWCORE_ALT_ADDRESS _AC(0x70000, UL)
23+
24+
#ifndef __ASSEMBLY__
25+
2126
struct pgm_tdb {
2227
u64 data[32];
2328
};
@@ -214,7 +219,14 @@ struct lowcore {
214219

215220
static __always_inline struct lowcore *get_lowcore(void)
216221
{
217-
return NULL;
222+
struct lowcore *lc;
223+
224+
if (__is_defined(__DECOMPRESSOR))
225+
return NULL;
226+
asm(ALTERNATIVE("llilh %[lc],0", "llilh %[lc],%[alt]", ALT_LOWCORE)
227+
: [lc] "=d" (lc)
228+
: [alt] "i" (LOWCORE_ALT_ADDRESS >> 16));
229+
return lc;
218230
}
219231

220232
extern struct lowcore *lowcore_ptr[];
@@ -224,4 +236,13 @@ static inline void set_prefix(__u32 address)
224236
asm volatile("spx %0" : : "Q" (address) : "memory");
225237
}
226238

239+
#else /* __ASSEMBLY__ */
240+
241+
.macro GET_LC reg
242+
ALTERNATIVE "llilh \reg,0", \
243+
__stringify(llilh \reg, LOWCORE_ALT_ADDRESS >> 16), \
244+
ALT_LOWCORE
245+
.endm
246+
247+
#endif /* __ASSEMBLY__ */
227248
#endif /* _ASM_S390_LOWCORE_H */

arch/s390/kernel/abs_lowcore.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <asm/abs_lowcore.h>
55

66
unsigned long __bootdata_preserved(__abs_lowcore);
7+
int __bootdata_preserved(relocate_lowcore);
78

89
int abs_lowcore_map(int cpu, struct lowcore *lc, bool alloc)
910
{

arch/s390/kernel/alternative.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <linux/uaccess.h>
44
#include <asm/nospec-branch.h>
5+
#include <asm/abs_lowcore.h>
56
#include <asm/alternative.h>
67
#include <asm/facility.h>
78

@@ -25,6 +26,9 @@ void __apply_alternatives(struct alt_instr *start, struct alt_instr *end, unsign
2526
case ALT_TYPE_SPEC:
2627
replace = nobp_enabled();
2728
break;
29+
case ALT_TYPE_LOWCORE:
30+
replace = have_relocated_lowcore();
31+
break;
2832
default:
2933
replace = false;
3034
}

arch/s390/kernel/alternative.h

Whitespace-only changes.

0 commit comments

Comments
 (0)