xref: /OK3568_Linux_fs/kernel/arch/s390/boot/mem_detect.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include <linux/errno.h>
3*4882a593Smuzhiyun #include <linux/init.h>
4*4882a593Smuzhiyun #include <asm/sclp.h>
5*4882a593Smuzhiyun #include <asm/sections.h>
6*4882a593Smuzhiyun #include <asm/mem_detect.h>
7*4882a593Smuzhiyun #include <asm/sparsemem.h>
8*4882a593Smuzhiyun #include "compressed/decompressor.h"
9*4882a593Smuzhiyun #include "boot.h"
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun unsigned long __bootdata(max_physmem_end);
12*4882a593Smuzhiyun struct mem_detect_info __bootdata(mem_detect);
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun /* up to 256 storage elements, 1020 subincrements each */
15*4882a593Smuzhiyun #define ENTRIES_EXTENDED_MAX						       \
16*4882a593Smuzhiyun 	(256 * (1020 / 2) * sizeof(struct mem_detect_block))
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun /*
19*4882a593Smuzhiyun  * To avoid corrupting old kernel memory during dump, find lowest memory
20*4882a593Smuzhiyun  * chunk possible either right after the kernel end (decompressed kernel) or
21*4882a593Smuzhiyun  * after initrd (if it is present and there is no hole between the kernel end
22*4882a593Smuzhiyun  * and initrd)
23*4882a593Smuzhiyun  */
mem_detect_alloc_extended(void)24*4882a593Smuzhiyun static void *mem_detect_alloc_extended(void)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	unsigned long offset = ALIGN(mem_safe_offset(), sizeof(u64));
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE &&
29*4882a593Smuzhiyun 	    INITRD_START < offset + ENTRIES_EXTENDED_MAX)
30*4882a593Smuzhiyun 		offset = ALIGN(INITRD_START + INITRD_SIZE, sizeof(u64));
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	return (void *)offset;
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun 
__get_mem_detect_block_ptr(u32 n)35*4882a593Smuzhiyun static struct mem_detect_block *__get_mem_detect_block_ptr(u32 n)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun 	if (n < MEM_INLINED_ENTRIES)
38*4882a593Smuzhiyun 		return &mem_detect.entries[n];
39*4882a593Smuzhiyun 	if (unlikely(!mem_detect.entries_extended))
40*4882a593Smuzhiyun 		mem_detect.entries_extended = mem_detect_alloc_extended();
41*4882a593Smuzhiyun 	return &mem_detect.entries_extended[n - MEM_INLINED_ENTRIES];
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /*
45*4882a593Smuzhiyun  * sequential calls to add_mem_detect_block with adjacent memory areas
46*4882a593Smuzhiyun  * are merged together into single memory block.
47*4882a593Smuzhiyun  */
add_mem_detect_block(u64 start,u64 end)48*4882a593Smuzhiyun void add_mem_detect_block(u64 start, u64 end)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	struct mem_detect_block *block;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	if (mem_detect.count) {
53*4882a593Smuzhiyun 		block = __get_mem_detect_block_ptr(mem_detect.count - 1);
54*4882a593Smuzhiyun 		if (block->end == start) {
55*4882a593Smuzhiyun 			block->end = end;
56*4882a593Smuzhiyun 			return;
57*4882a593Smuzhiyun 		}
58*4882a593Smuzhiyun 	}
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	block = __get_mem_detect_block_ptr(mem_detect.count);
61*4882a593Smuzhiyun 	block->start = start;
62*4882a593Smuzhiyun 	block->end = end;
63*4882a593Smuzhiyun 	mem_detect.count++;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun 
__diag260(unsigned long rx1,unsigned long rx2)66*4882a593Smuzhiyun static int __diag260(unsigned long rx1, unsigned long rx2)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun 	register unsigned long _rx1 asm("2") = rx1;
69*4882a593Smuzhiyun 	register unsigned long _rx2 asm("3") = rx2;
70*4882a593Smuzhiyun 	register unsigned long _ry asm("4") = 0x10; /* storage configuration */
71*4882a593Smuzhiyun 	int rc = -1;				    /* fail */
72*4882a593Smuzhiyun 	unsigned long reg1, reg2;
73*4882a593Smuzhiyun 	psw_t old;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	asm volatile(
76*4882a593Smuzhiyun 		"	mvc	0(16,%[psw_old]),0(%[psw_pgm])\n"
77*4882a593Smuzhiyun 		"	epsw	%0,%1\n"
78*4882a593Smuzhiyun 		"	st	%0,0(%[psw_pgm])\n"
79*4882a593Smuzhiyun 		"	st	%1,4(%[psw_pgm])\n"
80*4882a593Smuzhiyun 		"	larl	%0,1f\n"
81*4882a593Smuzhiyun 		"	stg	%0,8(%[psw_pgm])\n"
82*4882a593Smuzhiyun 		"	diag	%[rx],%[ry],0x260\n"
83*4882a593Smuzhiyun 		"	ipm	%[rc]\n"
84*4882a593Smuzhiyun 		"	srl	%[rc],28\n"
85*4882a593Smuzhiyun 		"1:	mvc	0(16,%[psw_pgm]),0(%[psw_old])\n"
86*4882a593Smuzhiyun 		: "=&d" (reg1), "=&a" (reg2),
87*4882a593Smuzhiyun 		  "+Q" (S390_lowcore.program_new_psw),
88*4882a593Smuzhiyun 		  "=Q" (old),
89*4882a593Smuzhiyun 		  [rc] "+&d" (rc), [ry] "+d" (_ry)
90*4882a593Smuzhiyun 		: [rx] "d" (_rx1), "d" (_rx2),
91*4882a593Smuzhiyun 		  [psw_old] "a" (&old),
92*4882a593Smuzhiyun 		  [psw_pgm] "a" (&S390_lowcore.program_new_psw)
93*4882a593Smuzhiyun 		: "cc", "memory");
94*4882a593Smuzhiyun 	return rc == 0 ? _ry : -1;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
diag260(void)97*4882a593Smuzhiyun static int diag260(void)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	int rc, i;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	struct {
102*4882a593Smuzhiyun 		unsigned long start;
103*4882a593Smuzhiyun 		unsigned long end;
104*4882a593Smuzhiyun 	} storage_extents[8] __aligned(16); /* VM supports up to 8 extends */
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	memset(storage_extents, 0, sizeof(storage_extents));
107*4882a593Smuzhiyun 	rc = __diag260((unsigned long)storage_extents, sizeof(storage_extents));
108*4882a593Smuzhiyun 	if (rc == -1)
109*4882a593Smuzhiyun 		return -1;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	for (i = 0; i < min_t(int, rc, ARRAY_SIZE(storage_extents)); i++)
112*4882a593Smuzhiyun 		add_mem_detect_block(storage_extents[i].start, storage_extents[i].end + 1);
113*4882a593Smuzhiyun 	return 0;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun 
tprot(unsigned long addr)116*4882a593Smuzhiyun static int tprot(unsigned long addr)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	unsigned long reg1, reg2;
119*4882a593Smuzhiyun 	int rc = -EFAULT;
120*4882a593Smuzhiyun 	psw_t old;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	asm volatile(
123*4882a593Smuzhiyun 		"	mvc	0(16,%[psw_old]),0(%[psw_pgm])\n"
124*4882a593Smuzhiyun 		"	epsw	%[reg1],%[reg2]\n"
125*4882a593Smuzhiyun 		"	st	%[reg1],0(%[psw_pgm])\n"
126*4882a593Smuzhiyun 		"	st	%[reg2],4(%[psw_pgm])\n"
127*4882a593Smuzhiyun 		"	larl	%[reg1],1f\n"
128*4882a593Smuzhiyun 		"	stg	%[reg1],8(%[psw_pgm])\n"
129*4882a593Smuzhiyun 		"	tprot	0(%[addr]),0\n"
130*4882a593Smuzhiyun 		"	ipm	%[rc]\n"
131*4882a593Smuzhiyun 		"	srl	%[rc],28\n"
132*4882a593Smuzhiyun 		"1:	mvc	0(16,%[psw_pgm]),0(%[psw_old])\n"
133*4882a593Smuzhiyun 		: [reg1] "=&d" (reg1),
134*4882a593Smuzhiyun 		  [reg2] "=&a" (reg2),
135*4882a593Smuzhiyun 		  [rc] "+&d" (rc),
136*4882a593Smuzhiyun 		  "=Q" (S390_lowcore.program_new_psw.addr),
137*4882a593Smuzhiyun 		  "=Q" (old)
138*4882a593Smuzhiyun 		: [psw_old] "a" (&old),
139*4882a593Smuzhiyun 		  [psw_pgm] "a" (&S390_lowcore.program_new_psw),
140*4882a593Smuzhiyun 		  [addr] "a" (addr)
141*4882a593Smuzhiyun 		: "cc", "memory");
142*4882a593Smuzhiyun 	return rc;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun 
search_mem_end(void)145*4882a593Smuzhiyun static void search_mem_end(void)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */
148*4882a593Smuzhiyun 	unsigned long offset = 0;
149*4882a593Smuzhiyun 	unsigned long pivot;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	while (range > 1) {
152*4882a593Smuzhiyun 		range >>= 1;
153*4882a593Smuzhiyun 		pivot = offset + range;
154*4882a593Smuzhiyun 		if (!tprot(pivot << 20))
155*4882a593Smuzhiyun 			offset = pivot;
156*4882a593Smuzhiyun 	}
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	add_mem_detect_block(0, (offset + 1) << 20);
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun 
detect_memory(void)161*4882a593Smuzhiyun void detect_memory(void)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	sclp_early_get_memsize(&max_physmem_end);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	if (!sclp_early_read_storage_info()) {
166*4882a593Smuzhiyun 		mem_detect.info_source = MEM_DETECT_SCLP_STOR_INFO;
167*4882a593Smuzhiyun 		return;
168*4882a593Smuzhiyun 	}
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	if (!diag260()) {
171*4882a593Smuzhiyun 		mem_detect.info_source = MEM_DETECT_DIAG260;
172*4882a593Smuzhiyun 		return;
173*4882a593Smuzhiyun 	}
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	if (max_physmem_end) {
176*4882a593Smuzhiyun 		add_mem_detect_block(0, max_physmem_end);
177*4882a593Smuzhiyun 		mem_detect.info_source = MEM_DETECT_SCLP_READ_INFO;
178*4882a593Smuzhiyun 		return;
179*4882a593Smuzhiyun 	}
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	search_mem_end();
182*4882a593Smuzhiyun 	mem_detect.info_source = MEM_DETECT_BIN_SEARCH;
183*4882a593Smuzhiyun 	max_physmem_end = get_mem_detect_end();
184*4882a593Smuzhiyun }
185