xref: /openbmc/linux/arch/s390/boot/physmem_info.c (revision 8c37cb7d)
1*8c37cb7dSVasily Gorbik // SPDX-License-Identifier: GPL-2.0
2*8c37cb7dSVasily Gorbik #include <linux/errno.h>
3*8c37cb7dSVasily Gorbik #include <linux/init.h>
4*8c37cb7dSVasily Gorbik #include <asm/setup.h>
5*8c37cb7dSVasily Gorbik #include <asm/processor.h>
6*8c37cb7dSVasily Gorbik #include <asm/sclp.h>
7*8c37cb7dSVasily Gorbik #include <asm/sections.h>
8*8c37cb7dSVasily Gorbik #include <asm/physmem_info.h>
9*8c37cb7dSVasily Gorbik #include <asm/sparsemem.h>
10*8c37cb7dSVasily Gorbik #include "decompressor.h"
11*8c37cb7dSVasily Gorbik #include "boot.h"
12*8c37cb7dSVasily Gorbik 
13*8c37cb7dSVasily Gorbik struct physmem_info __bootdata(physmem_info);
14*8c37cb7dSVasily Gorbik 
15*8c37cb7dSVasily Gorbik /* up to 256 storage elements, 1020 subincrements each */
16*8c37cb7dSVasily Gorbik #define ENTRIES_EXTENDED_MAX						       \
17*8c37cb7dSVasily Gorbik 	(256 * (1020 / 2) * sizeof(struct physmem_range))
18*8c37cb7dSVasily Gorbik 
19*8c37cb7dSVasily Gorbik static struct physmem_range *__get_physmem_range_ptr(u32 n)
20*8c37cb7dSVasily Gorbik {
21*8c37cb7dSVasily Gorbik 	if (n < MEM_INLINED_ENTRIES)
22*8c37cb7dSVasily Gorbik 		return &physmem_info.online[n];
23*8c37cb7dSVasily Gorbik 	return &physmem_info.online_extended[n - MEM_INLINED_ENTRIES];
24*8c37cb7dSVasily Gorbik }
25*8c37cb7dSVasily Gorbik 
26*8c37cb7dSVasily Gorbik /*
27*8c37cb7dSVasily Gorbik  * sequential calls to add_physmem_online_range with adjacent memory ranges
28*8c37cb7dSVasily Gorbik  * are merged together into single memory range.
29*8c37cb7dSVasily Gorbik  */
30*8c37cb7dSVasily Gorbik void add_physmem_online_range(u64 start, u64 end)
31*8c37cb7dSVasily Gorbik {
32*8c37cb7dSVasily Gorbik 	struct physmem_range *range;
33*8c37cb7dSVasily Gorbik 
34*8c37cb7dSVasily Gorbik 	if (physmem_info.range_count) {
35*8c37cb7dSVasily Gorbik 		range = __get_physmem_range_ptr(physmem_info.range_count - 1);
36*8c37cb7dSVasily Gorbik 		if (range->end == start) {
37*8c37cb7dSVasily Gorbik 			range->end = end;
38*8c37cb7dSVasily Gorbik 			return;
39*8c37cb7dSVasily Gorbik 		}
40*8c37cb7dSVasily Gorbik 	}
41*8c37cb7dSVasily Gorbik 
42*8c37cb7dSVasily Gorbik 	range = __get_physmem_range_ptr(physmem_info.range_count);
43*8c37cb7dSVasily Gorbik 	range->start = start;
44*8c37cb7dSVasily Gorbik 	range->end = end;
45*8c37cb7dSVasily Gorbik 	physmem_info.range_count++;
46*8c37cb7dSVasily Gorbik }
47*8c37cb7dSVasily Gorbik 
48*8c37cb7dSVasily Gorbik static int __diag260(unsigned long rx1, unsigned long rx2)
49*8c37cb7dSVasily Gorbik {
50*8c37cb7dSVasily Gorbik 	unsigned long reg1, reg2, ry;
51*8c37cb7dSVasily Gorbik 	union register_pair rx;
52*8c37cb7dSVasily Gorbik 	psw_t old;
53*8c37cb7dSVasily Gorbik 	int rc;
54*8c37cb7dSVasily Gorbik 
55*8c37cb7dSVasily Gorbik 	rx.even = rx1;
56*8c37cb7dSVasily Gorbik 	rx.odd	= rx2;
57*8c37cb7dSVasily Gorbik 	ry = 0x10; /* storage configuration */
58*8c37cb7dSVasily Gorbik 	rc = -1;   /* fail */
59*8c37cb7dSVasily Gorbik 	asm volatile(
60*8c37cb7dSVasily Gorbik 		"	mvc	0(16,%[psw_old]),0(%[psw_pgm])\n"
61*8c37cb7dSVasily Gorbik 		"	epsw	%[reg1],%[reg2]\n"
62*8c37cb7dSVasily Gorbik 		"	st	%[reg1],0(%[psw_pgm])\n"
63*8c37cb7dSVasily Gorbik 		"	st	%[reg2],4(%[psw_pgm])\n"
64*8c37cb7dSVasily Gorbik 		"	larl	%[reg1],1f\n"
65*8c37cb7dSVasily Gorbik 		"	stg	%[reg1],8(%[psw_pgm])\n"
66*8c37cb7dSVasily Gorbik 		"	diag	%[rx],%[ry],0x260\n"
67*8c37cb7dSVasily Gorbik 		"	ipm	%[rc]\n"
68*8c37cb7dSVasily Gorbik 		"	srl	%[rc],28\n"
69*8c37cb7dSVasily Gorbik 		"1:	mvc	0(16,%[psw_pgm]),0(%[psw_old])\n"
70*8c37cb7dSVasily Gorbik 		: [reg1] "=&d" (reg1),
71*8c37cb7dSVasily Gorbik 		  [reg2] "=&a" (reg2),
72*8c37cb7dSVasily Gorbik 		  [rc] "+&d" (rc),
73*8c37cb7dSVasily Gorbik 		  [ry] "+&d" (ry),
74*8c37cb7dSVasily Gorbik 		  "+Q" (S390_lowcore.program_new_psw),
75*8c37cb7dSVasily Gorbik 		  "=Q" (old)
76*8c37cb7dSVasily Gorbik 		: [rx] "d" (rx.pair),
77*8c37cb7dSVasily Gorbik 		  [psw_old] "a" (&old),
78*8c37cb7dSVasily Gorbik 		  [psw_pgm] "a" (&S390_lowcore.program_new_psw)
79*8c37cb7dSVasily Gorbik 		: "cc", "memory");
80*8c37cb7dSVasily Gorbik 	return rc == 0 ? ry : -1;
81*8c37cb7dSVasily Gorbik }
82*8c37cb7dSVasily Gorbik 
83*8c37cb7dSVasily Gorbik static int diag260(void)
84*8c37cb7dSVasily Gorbik {
85*8c37cb7dSVasily Gorbik 	int rc, i;
86*8c37cb7dSVasily Gorbik 
87*8c37cb7dSVasily Gorbik 	struct {
88*8c37cb7dSVasily Gorbik 		unsigned long start;
89*8c37cb7dSVasily Gorbik 		unsigned long end;
90*8c37cb7dSVasily Gorbik 	} storage_extents[8] __aligned(16); /* VM supports up to 8 extends */
91*8c37cb7dSVasily Gorbik 
92*8c37cb7dSVasily Gorbik 	memset(storage_extents, 0, sizeof(storage_extents));
93*8c37cb7dSVasily Gorbik 	rc = __diag260((unsigned long)storage_extents, sizeof(storage_extents));
94*8c37cb7dSVasily Gorbik 	if (rc == -1)
95*8c37cb7dSVasily Gorbik 		return -1;
96*8c37cb7dSVasily Gorbik 
97*8c37cb7dSVasily Gorbik 	for (i = 0; i < min_t(int, rc, ARRAY_SIZE(storage_extents)); i++)
98*8c37cb7dSVasily Gorbik 		add_physmem_online_range(storage_extents[i].start, storage_extents[i].end + 1);
99*8c37cb7dSVasily Gorbik 	return 0;
100*8c37cb7dSVasily Gorbik }
101*8c37cb7dSVasily Gorbik 
102*8c37cb7dSVasily Gorbik static int tprot(unsigned long addr)
103*8c37cb7dSVasily Gorbik {
104*8c37cb7dSVasily Gorbik 	unsigned long reg1, reg2;
105*8c37cb7dSVasily Gorbik 	int rc = -EFAULT;
106*8c37cb7dSVasily Gorbik 	psw_t old;
107*8c37cb7dSVasily Gorbik 
108*8c37cb7dSVasily Gorbik 	asm volatile(
109*8c37cb7dSVasily Gorbik 		"	mvc	0(16,%[psw_old]),0(%[psw_pgm])\n"
110*8c37cb7dSVasily Gorbik 		"	epsw	%[reg1],%[reg2]\n"
111*8c37cb7dSVasily Gorbik 		"	st	%[reg1],0(%[psw_pgm])\n"
112*8c37cb7dSVasily Gorbik 		"	st	%[reg2],4(%[psw_pgm])\n"
113*8c37cb7dSVasily Gorbik 		"	larl	%[reg1],1f\n"
114*8c37cb7dSVasily Gorbik 		"	stg	%[reg1],8(%[psw_pgm])\n"
115*8c37cb7dSVasily Gorbik 		"	tprot	0(%[addr]),0\n"
116*8c37cb7dSVasily Gorbik 		"	ipm	%[rc]\n"
117*8c37cb7dSVasily Gorbik 		"	srl	%[rc],28\n"
118*8c37cb7dSVasily Gorbik 		"1:	mvc	0(16,%[psw_pgm]),0(%[psw_old])\n"
119*8c37cb7dSVasily Gorbik 		: [reg1] "=&d" (reg1),
120*8c37cb7dSVasily Gorbik 		  [reg2] "=&a" (reg2),
121*8c37cb7dSVasily Gorbik 		  [rc] "+&d" (rc),
122*8c37cb7dSVasily Gorbik 		  "=Q" (S390_lowcore.program_new_psw.addr),
123*8c37cb7dSVasily Gorbik 		  "=Q" (old)
124*8c37cb7dSVasily Gorbik 		: [psw_old] "a" (&old),
125*8c37cb7dSVasily Gorbik 		  [psw_pgm] "a" (&S390_lowcore.program_new_psw),
126*8c37cb7dSVasily Gorbik 		  [addr] "a" (addr)
127*8c37cb7dSVasily Gorbik 		: "cc", "memory");
128*8c37cb7dSVasily Gorbik 	return rc;
129*8c37cb7dSVasily Gorbik }
130*8c37cb7dSVasily Gorbik 
131*8c37cb7dSVasily Gorbik static unsigned long search_mem_end(void)
132*8c37cb7dSVasily Gorbik {
133*8c37cb7dSVasily Gorbik 	unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */
134*8c37cb7dSVasily Gorbik 	unsigned long offset = 0;
135*8c37cb7dSVasily Gorbik 	unsigned long pivot;
136*8c37cb7dSVasily Gorbik 
137*8c37cb7dSVasily Gorbik 	while (range > 1) {
138*8c37cb7dSVasily Gorbik 		range >>= 1;
139*8c37cb7dSVasily Gorbik 		pivot = offset + range;
140*8c37cb7dSVasily Gorbik 		if (!tprot(pivot << 20))
141*8c37cb7dSVasily Gorbik 			offset = pivot;
142*8c37cb7dSVasily Gorbik 	}
143*8c37cb7dSVasily Gorbik 	return (offset + 1) << 20;
144*8c37cb7dSVasily Gorbik }
145*8c37cb7dSVasily Gorbik 
146*8c37cb7dSVasily Gorbik unsigned long detect_memory(unsigned long *safe_addr)
147*8c37cb7dSVasily Gorbik {
148*8c37cb7dSVasily Gorbik 	unsigned long max_physmem_end = 0;
149*8c37cb7dSVasily Gorbik 
150*8c37cb7dSVasily Gorbik 	sclp_early_get_memsize(&max_physmem_end);
151*8c37cb7dSVasily Gorbik 	physmem_info.online_extended = (struct physmem_range *)ALIGN(*safe_addr, sizeof(u64));
152*8c37cb7dSVasily Gorbik 
153*8c37cb7dSVasily Gorbik 	if (!sclp_early_read_storage_info()) {
154*8c37cb7dSVasily Gorbik 		physmem_info.info_source = MEM_DETECT_SCLP_STOR_INFO;
155*8c37cb7dSVasily Gorbik 	} else if (!diag260()) {
156*8c37cb7dSVasily Gorbik 		physmem_info.info_source = MEM_DETECT_DIAG260;
157*8c37cb7dSVasily Gorbik 		max_physmem_end = max_physmem_end ?: get_physmem_usable_end();
158*8c37cb7dSVasily Gorbik 	} else if (max_physmem_end) {
159*8c37cb7dSVasily Gorbik 		add_physmem_online_range(0, max_physmem_end);
160*8c37cb7dSVasily Gorbik 		physmem_info.info_source = MEM_DETECT_SCLP_READ_INFO;
161*8c37cb7dSVasily Gorbik 	} else {
162*8c37cb7dSVasily Gorbik 		max_physmem_end = search_mem_end();
163*8c37cb7dSVasily Gorbik 		add_physmem_online_range(0, max_physmem_end);
164*8c37cb7dSVasily Gorbik 		physmem_info.info_source = MEM_DETECT_BIN_SEARCH;
165*8c37cb7dSVasily Gorbik 	}
166*8c37cb7dSVasily Gorbik 
167*8c37cb7dSVasily Gorbik 	if (physmem_info.range_count > MEM_INLINED_ENTRIES) {
168*8c37cb7dSVasily Gorbik 		*safe_addr += (physmem_info.range_count - MEM_INLINED_ENTRIES) *
169*8c37cb7dSVasily Gorbik 			      sizeof(struct physmem_range);
170*8c37cb7dSVasily Gorbik 	}
171*8c37cb7dSVasily Gorbik 
172*8c37cb7dSVasily Gorbik 	return max_physmem_end;
173*8c37cb7dSVasily Gorbik }
174*8c37cb7dSVasily Gorbik 
175*8c37cb7dSVasily Gorbik void physmem_set_usable_limit(unsigned long limit)
176*8c37cb7dSVasily Gorbik {
177*8c37cb7dSVasily Gorbik 	struct physmem_range *range;
178*8c37cb7dSVasily Gorbik 	int i;
179*8c37cb7dSVasily Gorbik 
180*8c37cb7dSVasily Gorbik 	/* make sure mem_detect.usable ends up within online memory block */
181*8c37cb7dSVasily Gorbik 	for (i = 0; i < physmem_info.range_count; i++) {
182*8c37cb7dSVasily Gorbik 		range = __get_physmem_range_ptr(i);
183*8c37cb7dSVasily Gorbik 		if (range->start >= limit)
184*8c37cb7dSVasily Gorbik 			break;
185*8c37cb7dSVasily Gorbik 		if (range->end >= limit) {
186*8c37cb7dSVasily Gorbik 			physmem_info.usable = limit;
187*8c37cb7dSVasily Gorbik 			break;
188*8c37cb7dSVasily Gorbik 		}
189*8c37cb7dSVasily Gorbik 		physmem_info.usable = range->end;
190*8c37cb7dSVasily Gorbik 	}
191*8c37cb7dSVasily Gorbik }
192