]> Git Repo - linux.git/blob - arch/s390/boot/physmem_info.c
Merge patch series "riscv: Extension parsing fixes"
[linux.git] / arch / s390 / boot / physmem_info.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/processor.h>
3 #include <linux/errno.h>
4 #include <linux/init.h>
5 #include <asm/physmem_info.h>
6 #include <asm/stacktrace.h>
7 #include <asm/boot_data.h>
8 #include <asm/sparsemem.h>
9 #include <asm/sections.h>
10 #include <asm/setup.h>
11 #include <asm/sclp.h>
12 #include <asm/uv.h>
13 #include "decompressor.h"
14 #include "boot.h"
15
16 struct physmem_info __bootdata(physmem_info);
17 static unsigned int physmem_alloc_ranges;
18 static unsigned long physmem_alloc_pos;
19
20 /* up to 256 storage elements, 1020 subincrements each */
21 #define ENTRIES_EXTENDED_MAX                                                   \
22         (256 * (1020 / 2) * sizeof(struct physmem_range))
23
24 static struct physmem_range *__get_physmem_range_ptr(u32 n)
25 {
26         if (n < MEM_INLINED_ENTRIES)
27                 return &physmem_info.online[n];
28         if (unlikely(!physmem_info.online_extended)) {
29                 physmem_info.online_extended = (struct physmem_range *)physmem_alloc_range(
30                         RR_MEM_DETECT_EXTENDED, ENTRIES_EXTENDED_MAX, sizeof(long), 0,
31                         physmem_alloc_pos, true);
32         }
33         return &physmem_info.online_extended[n - MEM_INLINED_ENTRIES];
34 }
35
36 /*
37  * sequential calls to add_physmem_online_range with adjacent memory ranges
38  * are merged together into single memory range.
39  */
40 void add_physmem_online_range(u64 start, u64 end)
41 {
42         struct physmem_range *range;
43
44         if (physmem_info.range_count) {
45                 range = __get_physmem_range_ptr(physmem_info.range_count - 1);
46                 if (range->end == start) {
47                         range->end = end;
48                         return;
49                 }
50         }
51
52         range = __get_physmem_range_ptr(physmem_info.range_count);
53         range->start = start;
54         range->end = end;
55         physmem_info.range_count++;
56 }
57
58 static int __diag260(unsigned long rx1, unsigned long rx2)
59 {
60         unsigned long reg1, reg2, ry;
61         union register_pair rx;
62         psw_t old;
63         int rc;
64
65         rx.even = rx1;
66         rx.odd  = rx2;
67         ry = 0x10; /* storage configuration */
68         rc = -1;   /* fail */
69         asm volatile(
70                 "       mvc     0(16,%[psw_old]),0(%[psw_pgm])\n"
71                 "       epsw    %[reg1],%[reg2]\n"
72                 "       st      %[reg1],0(%[psw_pgm])\n"
73                 "       st      %[reg2],4(%[psw_pgm])\n"
74                 "       larl    %[reg1],1f\n"
75                 "       stg     %[reg1],8(%[psw_pgm])\n"
76                 "       diag    %[rx],%[ry],0x260\n"
77                 "       ipm     %[rc]\n"
78                 "       srl     %[rc],28\n"
79                 "1:     mvc     0(16,%[psw_pgm]),0(%[psw_old])\n"
80                 : [reg1] "=&d" (reg1),
81                   [reg2] "=&a" (reg2),
82                   [rc] "+&d" (rc),
83                   [ry] "+&d" (ry),
84                   "+Q" (S390_lowcore.program_new_psw),
85                   "=Q" (old)
86                 : [rx] "d" (rx.pair),
87                   [psw_old] "a" (&old),
88                   [psw_pgm] "a" (&S390_lowcore.program_new_psw)
89                 : "cc", "memory");
90         return rc == 0 ? ry : -1;
91 }
92
93 static int diag260(void)
94 {
95         int rc, i;
96
97         struct {
98                 unsigned long start;
99                 unsigned long end;
100         } storage_extents[8] __aligned(16); /* VM supports up to 8 extends */
101
102         memset(storage_extents, 0, sizeof(storage_extents));
103         rc = __diag260((unsigned long)storage_extents, sizeof(storage_extents));
104         if (rc == -1)
105                 return -1;
106
107         for (i = 0; i < min_t(int, rc, ARRAY_SIZE(storage_extents)); i++)
108                 add_physmem_online_range(storage_extents[i].start, storage_extents[i].end + 1);
109         return 0;
110 }
111
112 static int tprot(unsigned long addr)
113 {
114         unsigned long reg1, reg2;
115         int rc = -EFAULT;
116         psw_t old;
117
118         asm volatile(
119                 "       mvc     0(16,%[psw_old]),0(%[psw_pgm])\n"
120                 "       epsw    %[reg1],%[reg2]\n"
121                 "       st      %[reg1],0(%[psw_pgm])\n"
122                 "       st      %[reg2],4(%[psw_pgm])\n"
123                 "       larl    %[reg1],1f\n"
124                 "       stg     %[reg1],8(%[psw_pgm])\n"
125                 "       tprot   0(%[addr]),0\n"
126                 "       ipm     %[rc]\n"
127                 "       srl     %[rc],28\n"
128                 "1:     mvc     0(16,%[psw_pgm]),0(%[psw_old])\n"
129                 : [reg1] "=&d" (reg1),
130                   [reg2] "=&a" (reg2),
131                   [rc] "+&d" (rc),
132                   "=Q" (S390_lowcore.program_new_psw.addr),
133                   "=Q" (old)
134                 : [psw_old] "a" (&old),
135                   [psw_pgm] "a" (&S390_lowcore.program_new_psw),
136                   [addr] "a" (addr)
137                 : "cc", "memory");
138         return rc;
139 }
140
141 static unsigned long search_mem_end(void)
142 {
143         unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */
144         unsigned long offset = 0;
145         unsigned long pivot;
146
147         while (range > 1) {
148                 range >>= 1;
149                 pivot = offset + range;
150                 if (!tprot(pivot << 20))
151                         offset = pivot;
152         }
153         return (offset + 1) << 20;
154 }
155
156 unsigned long detect_max_physmem_end(void)
157 {
158         unsigned long max_physmem_end = 0;
159
160         if (!sclp_early_get_memsize(&max_physmem_end)) {
161                 physmem_info.info_source = MEM_DETECT_SCLP_READ_INFO;
162         } else {
163                 max_physmem_end = search_mem_end();
164                 physmem_info.info_source = MEM_DETECT_BIN_SEARCH;
165         }
166         return max_physmem_end;
167 }
168
169 void detect_physmem_online_ranges(unsigned long max_physmem_end)
170 {
171         if (!sclp_early_read_storage_info()) {
172                 physmem_info.info_source = MEM_DETECT_SCLP_STOR_INFO;
173         } else if (!diag260()) {
174                 physmem_info.info_source = MEM_DETECT_DIAG260;
175         } else if (max_physmem_end) {
176                 add_physmem_online_range(0, max_physmem_end);
177         }
178 }
179
180 void physmem_set_usable_limit(unsigned long limit)
181 {
182         physmem_info.usable = limit;
183         physmem_alloc_pos = limit;
184 }
185
186 static void die_oom(unsigned long size, unsigned long align, unsigned long min, unsigned long max)
187 {
188         unsigned long start, end, total_mem = 0, total_reserved_mem = 0;
189         struct reserved_range *range;
190         enum reserved_range_type t;
191         int i;
192
193         decompressor_printk("Linux version %s\n", kernel_version);
194         if (!is_prot_virt_guest() && early_command_line[0])
195                 decompressor_printk("Kernel command line: %s\n", early_command_line);
196         decompressor_printk("Out of memory allocating %lx bytes %lx aligned in range %lx:%lx\n",
197                             size, align, min, max);
198         decompressor_printk("Reserved memory ranges:\n");
199         for_each_physmem_reserved_range(t, range, &start, &end) {
200                 decompressor_printk("%016lx %016lx %s\n", start, end, get_rr_type_name(t));
201                 total_reserved_mem += end - start;
202         }
203         decompressor_printk("Usable online memory ranges (info source: %s [%x]):\n",
204                             get_physmem_info_source(), physmem_info.info_source);
205         for_each_physmem_usable_range(i, &start, &end) {
206                 decompressor_printk("%016lx %016lx\n", start, end);
207                 total_mem += end - start;
208         }
209         decompressor_printk("Usable online memory total: %lx Reserved: %lx Free: %lx\n",
210                             total_mem, total_reserved_mem,
211                             total_mem > total_reserved_mem ? total_mem - total_reserved_mem : 0);
212         print_stacktrace(current_frame_address());
213         sclp_early_printk("\n\n -- System halted\n");
214         disabled_wait();
215 }
216
217 void physmem_reserve(enum reserved_range_type type, unsigned long addr, unsigned long size)
218 {
219         physmem_info.reserved[type].start = addr;
220         physmem_info.reserved[type].end = addr + size;
221 }
222
223 void physmem_free(enum reserved_range_type type)
224 {
225         physmem_info.reserved[type].start = 0;
226         physmem_info.reserved[type].end = 0;
227 }
228
229 static bool __physmem_alloc_intersects(unsigned long addr, unsigned long size,
230                                        unsigned long *intersection_start)
231 {
232         unsigned long res_addr, res_size;
233         int t;
234
235         for (t = 0; t < RR_MAX; t++) {
236                 if (!get_physmem_reserved(t, &res_addr, &res_size))
237                         continue;
238                 if (intersects(addr, size, res_addr, res_size)) {
239                         *intersection_start = res_addr;
240                         return true;
241                 }
242         }
243         return ipl_report_certs_intersects(addr, size, intersection_start);
244 }
245
246 static unsigned long __physmem_alloc_range(unsigned long size, unsigned long align,
247                                            unsigned long min, unsigned long max,
248                                            unsigned int from_ranges, unsigned int *ranges_left,
249                                            bool die_on_oom)
250 {
251         unsigned int nranges = from_ranges ?: physmem_info.range_count;
252         unsigned long range_start, range_end;
253         unsigned long intersection_start;
254         unsigned long addr, pos = max;
255
256         align = max(align, 8UL);
257         while (nranges) {
258                 __get_physmem_range(nranges - 1, &range_start, &range_end, false);
259                 pos = min(range_end, pos);
260
261                 if (round_up(min, align) + size > pos)
262                         break;
263                 addr = round_down(pos - size, align);
264                 if (range_start > addr) {
265                         nranges--;
266                         continue;
267                 }
268                 if (__physmem_alloc_intersects(addr, size, &intersection_start)) {
269                         pos = intersection_start;
270                         continue;
271                 }
272
273                 if (ranges_left)
274                         *ranges_left = nranges;
275                 return addr;
276         }
277         if (die_on_oom)
278                 die_oom(size, align, min, max);
279         return 0;
280 }
281
282 unsigned long physmem_alloc_range(enum reserved_range_type type, unsigned long size,
283                                   unsigned long align, unsigned long min, unsigned long max,
284                                   bool die_on_oom)
285 {
286         unsigned long addr;
287
288         max = min(max, physmem_alloc_pos);
289         addr = __physmem_alloc_range(size, align, min, max, 0, NULL, die_on_oom);
290         if (addr)
291                 physmem_reserve(type, addr, size);
292         return addr;
293 }
294
295 unsigned long physmem_alloc_top_down(enum reserved_range_type type, unsigned long size,
296                                      unsigned long align)
297 {
298         struct reserved_range *range = &physmem_info.reserved[type];
299         struct reserved_range *new_range;
300         unsigned int ranges_left;
301         unsigned long addr;
302
303         addr = __physmem_alloc_range(size, align, 0, physmem_alloc_pos, physmem_alloc_ranges,
304                                      &ranges_left, true);
305         /* if not a consecutive allocation of the same type or first allocation */
306         if (range->start != addr + size) {
307                 if (range->end) {
308                         physmem_alloc_pos = __physmem_alloc_range(
309                                 sizeof(struct reserved_range), 0, 0, physmem_alloc_pos,
310                                 physmem_alloc_ranges, &ranges_left, true);
311                         new_range = (struct reserved_range *)physmem_alloc_pos;
312                         *new_range = *range;
313                         range->chain = new_range;
314                         addr = __physmem_alloc_range(size, align, 0, physmem_alloc_pos,
315                                                      ranges_left, &ranges_left, true);
316                 }
317                 range->end = addr + size;
318         }
319         range->start = addr;
320         physmem_alloc_pos = addr;
321         physmem_alloc_ranges = ranges_left;
322         return addr;
323 }
324
325 unsigned long get_physmem_alloc_pos(void)
326 {
327         return physmem_alloc_pos;
328 }
This page took 0.055057 seconds and 4 git commands to generate.