1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Dynamic reconfiguration memory support
4 *
5 * Copyright 2017 IBM Corporation
6 */
7
8 #define pr_fmt(fmt) "drmem: " fmt
9
10 #include <linux/kernel.h>
11 #include <linux/of.h>
12 #include <linux/of_fdt.h>
13 #include <linux/memblock.h>
14 #include <asm/prom.h>
15 #include <asm/drmem.h>
16
17 static int n_root_addr_cells, n_root_size_cells;
18
19 static struct drmem_lmb_info __drmem_info;
20 struct drmem_lmb_info *drmem_info = &__drmem_info;
21
drmem_lmb_memory_max(void)22 u64 drmem_lmb_memory_max(void)
23 {
24 struct drmem_lmb *last_lmb;
25
26 last_lmb = &drmem_info->lmbs[drmem_info->n_lmbs - 1];
27 return last_lmb->base_addr + drmem_lmb_size();
28 }
29
drmem_lmb_flags(struct drmem_lmb * lmb)30 static u32 drmem_lmb_flags(struct drmem_lmb *lmb)
31 {
32 /*
33 * Return the value of the lmb flags field minus the reserved
34 * bit used internally for hotplug processing.
35 */
36 return lmb->flags & ~DRMEM_LMB_RESERVED;
37 }
38
clone_property(struct property * prop,u32 prop_sz)39 static struct property *clone_property(struct property *prop, u32 prop_sz)
40 {
41 struct property *new_prop;
42
43 new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
44 if (!new_prop)
45 return NULL;
46
47 new_prop->name = kstrdup(prop->name, GFP_KERNEL);
48 new_prop->value = kzalloc(prop_sz, GFP_KERNEL);
49 if (!new_prop->name || !new_prop->value) {
50 kfree(new_prop->name);
51 kfree(new_prop->value);
52 kfree(new_prop);
53 return NULL;
54 }
55
56 new_prop->length = prop_sz;
57 #if defined(CONFIG_OF_DYNAMIC)
58 of_property_set_flag(new_prop, OF_DYNAMIC);
59 #endif
60 return new_prop;
61 }
62
drmem_update_dt_v1(struct device_node * memory,struct property * prop)63 static int drmem_update_dt_v1(struct device_node *memory,
64 struct property *prop)
65 {
66 struct property *new_prop;
67 struct of_drconf_cell_v1 *dr_cell;
68 struct drmem_lmb *lmb;
69 u32 *p;
70
71 new_prop = clone_property(prop, prop->length);
72 if (!new_prop)
73 return -1;
74
75 p = new_prop->value;
76 *p++ = cpu_to_be32(drmem_info->n_lmbs);
77
78 dr_cell = (struct of_drconf_cell_v1 *)p;
79
80 for_each_drmem_lmb(lmb) {
81 dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
82 dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
83 dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
84 dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
85
86 dr_cell++;
87 }
88
89 of_update_property(memory, new_prop);
90 return 0;
91 }
92
init_drconf_v2_cell(struct of_drconf_cell_v2 * dr_cell,struct drmem_lmb * lmb)93 static void init_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
94 struct drmem_lmb *lmb)
95 {
96 dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
97 dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
98 dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
99 dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
100 }
101
drmem_update_dt_v2(struct device_node * memory,struct property * prop)102 static int drmem_update_dt_v2(struct device_node *memory,
103 struct property *prop)
104 {
105 struct property *new_prop;
106 struct of_drconf_cell_v2 *dr_cell;
107 struct drmem_lmb *lmb, *prev_lmb;
108 u32 lmb_sets, prop_sz, seq_lmbs;
109 u32 *p;
110
111 /* First pass, determine how many LMB sets are needed. */
112 lmb_sets = 0;
113 prev_lmb = NULL;
114 for_each_drmem_lmb(lmb) {
115 if (!prev_lmb) {
116 prev_lmb = lmb;
117 lmb_sets++;
118 continue;
119 }
120
121 if (prev_lmb->aa_index != lmb->aa_index ||
122 drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb))
123 lmb_sets++;
124
125 prev_lmb = lmb;
126 }
127
128 prop_sz = lmb_sets * sizeof(*dr_cell) + sizeof(__be32);
129 new_prop = clone_property(prop, prop_sz);
130 if (!new_prop)
131 return -1;
132
133 p = new_prop->value;
134 *p++ = cpu_to_be32(lmb_sets);
135
136 dr_cell = (struct of_drconf_cell_v2 *)p;
137
138 /* Second pass, populate the LMB set data */
139 prev_lmb = NULL;
140 seq_lmbs = 0;
141 for_each_drmem_lmb(lmb) {
142 if (prev_lmb == NULL) {
143 /* Start of first LMB set */
144 prev_lmb = lmb;
145 init_drconf_v2_cell(dr_cell, lmb);
146 seq_lmbs++;
147 continue;
148 }
149
150 if (prev_lmb->aa_index != lmb->aa_index ||
151 drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb)) {
152 /* end of one set, start of another */
153 dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
154 dr_cell++;
155
156 init_drconf_v2_cell(dr_cell, lmb);
157 seq_lmbs = 1;
158 } else {
159 seq_lmbs++;
160 }
161
162 prev_lmb = lmb;
163 }
164
165 /* close out last LMB set */
166 dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
167 of_update_property(memory, new_prop);
168 return 0;
169 }
170
drmem_update_dt(void)171 int drmem_update_dt(void)
172 {
173 struct device_node *memory;
174 struct property *prop;
175 int rc = -1;
176
177 memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
178 if (!memory)
179 return -1;
180
181 prop = of_find_property(memory, "ibm,dynamic-memory", NULL);
182 if (prop) {
183 rc = drmem_update_dt_v1(memory, prop);
184 } else {
185 prop = of_find_property(memory, "ibm,dynamic-memory-v2", NULL);
186 if (prop)
187 rc = drmem_update_dt_v2(memory, prop);
188 }
189
190 of_node_put(memory);
191 return rc;
192 }
193
read_drconf_v1_cell(struct drmem_lmb * lmb,const __be32 ** prop)194 static void read_drconf_v1_cell(struct drmem_lmb *lmb,
195 const __be32 **prop)
196 {
197 const __be32 *p = *prop;
198
199 lmb->base_addr = of_read_number(p, n_root_addr_cells);
200 p += n_root_addr_cells;
201 lmb->drc_index = of_read_number(p++, 1);
202
203 p++; /* skip reserved field */
204
205 lmb->aa_index = of_read_number(p++, 1);
206 lmb->flags = of_read_number(p++, 1);
207
208 *prop = p;
209 }
210
211 static int
__walk_drmem_v1_lmbs(const __be32 * prop,const __be32 * usm,void * data,int (* func)(struct drmem_lmb *,const __be32 **,void *))212 __walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm, void *data,
213 int (*func)(struct drmem_lmb *, const __be32 **, void *))
214 {
215 struct drmem_lmb lmb;
216 u32 i, n_lmbs;
217 int ret = 0;
218
219 n_lmbs = of_read_number(prop++, 1);
220 for (i = 0; i < n_lmbs; i++) {
221 read_drconf_v1_cell(&lmb, &prop);
222 ret = func(&lmb, &usm, data);
223 if (ret)
224 break;
225 }
226
227 return ret;
228 }
229
read_drconf_v2_cell(struct of_drconf_cell_v2 * dr_cell,const __be32 ** prop)230 static void read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
231 const __be32 **prop)
232 {
233 const __be32 *p = *prop;
234
235 dr_cell->seq_lmbs = of_read_number(p++, 1);
236 dr_cell->base_addr = of_read_number(p, n_root_addr_cells);
237 p += n_root_addr_cells;
238 dr_cell->drc_index = of_read_number(p++, 1);
239 dr_cell->aa_index = of_read_number(p++, 1);
240 dr_cell->flags = of_read_number(p++, 1);
241
242 *prop = p;
243 }
244
245 static int
__walk_drmem_v2_lmbs(const __be32 * prop,const __be32 * usm,void * data,int (* func)(struct drmem_lmb *,const __be32 **,void *))246 __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, void *data,
247 int (*func)(struct drmem_lmb *, const __be32 **, void *))
248 {
249 struct of_drconf_cell_v2 dr_cell;
250 struct drmem_lmb lmb;
251 u32 i, j, lmb_sets;
252 int ret = 0;
253
254 lmb_sets = of_read_number(prop++, 1);
255 for (i = 0; i < lmb_sets; i++) {
256 read_drconf_v2_cell(&dr_cell, &prop);
257
258 for (j = 0; j < dr_cell.seq_lmbs; j++) {
259 lmb.base_addr = dr_cell.base_addr;
260 dr_cell.base_addr += drmem_lmb_size();
261
262 lmb.drc_index = dr_cell.drc_index;
263 dr_cell.drc_index++;
264
265 lmb.aa_index = dr_cell.aa_index;
266 lmb.flags = dr_cell.flags;
267
268 ret = func(&lmb, &usm, data);
269 if (ret)
270 break;
271 }
272 }
273
274 return ret;
275 }
276
277 #ifdef CONFIG_PPC_PSERIES
walk_drmem_lmbs_early(unsigned long node,void * data,int (* func)(struct drmem_lmb *,const __be32 **,void *))278 int __init walk_drmem_lmbs_early(unsigned long node, void *data,
279 int (*func)(struct drmem_lmb *, const __be32 **, void *))
280 {
281 const __be32 *prop, *usm;
282 int len, ret = -ENODEV;
283
284 prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len);
285 if (!prop || len < dt_root_size_cells * sizeof(__be32))
286 return ret;
287
288 /* Get the address & size cells */
289 n_root_addr_cells = dt_root_addr_cells;
290 n_root_size_cells = dt_root_size_cells;
291
292 drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
293
294 usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory", &len);
295
296 prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len);
297 if (prop) {
298 ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
299 } else {
300 prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory-v2",
301 &len);
302 if (prop)
303 ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
304 }
305
306 memblock_dump_all();
307 return ret;
308 }
309
310 #endif
311
init_drmem_lmb_size(struct device_node * dn)312 static int init_drmem_lmb_size(struct device_node *dn)
313 {
314 const __be32 *prop;
315 int len;
316
317 if (drmem_info->lmb_size)
318 return 0;
319
320 prop = of_get_property(dn, "ibm,lmb-size", &len);
321 if (!prop || len < n_root_size_cells * sizeof(__be32)) {
322 pr_info("Could not determine LMB size\n");
323 return -1;
324 }
325
326 drmem_info->lmb_size = of_read_number(prop, n_root_size_cells);
327 return 0;
328 }
329
330 /*
331 * Returns the property linux,drconf-usable-memory if
332 * it exists (the property exists only in kexec/kdump kernels,
333 * added by kexec-tools)
334 */
of_get_usable_memory(struct device_node * dn)335 static const __be32 *of_get_usable_memory(struct device_node *dn)
336 {
337 const __be32 *prop;
338 u32 len;
339
340 prop = of_get_property(dn, "linux,drconf-usable-memory", &len);
341 if (!prop || len < sizeof(unsigned int))
342 return NULL;
343
344 return prop;
345 }
346
walk_drmem_lmbs(struct device_node * dn,void * data,int (* func)(struct drmem_lmb *,const __be32 **,void *))347 int walk_drmem_lmbs(struct device_node *dn, void *data,
348 int (*func)(struct drmem_lmb *, const __be32 **, void *))
349 {
350 const __be32 *prop, *usm;
351 int ret = -ENODEV;
352
353 if (!of_root)
354 return ret;
355
356 /* Get the address & size cells */
357 of_node_get(of_root);
358 n_root_addr_cells = of_n_addr_cells(of_root);
359 n_root_size_cells = of_n_size_cells(of_root);
360 of_node_put(of_root);
361
362 if (init_drmem_lmb_size(dn))
363 return ret;
364
365 usm = of_get_usable_memory(dn);
366
367 prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
368 if (prop) {
369 ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
370 } else {
371 prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
372 if (prop)
373 ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
374 }
375
376 return ret;
377 }
378
init_drmem_v1_lmbs(const __be32 * prop)379 static void __init init_drmem_v1_lmbs(const __be32 *prop)
380 {
381 struct drmem_lmb *lmb;
382
383 drmem_info->n_lmbs = of_read_number(prop++, 1);
384 if (drmem_info->n_lmbs == 0)
385 return;
386
387 drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
388 GFP_KERNEL);
389 if (!drmem_info->lmbs)
390 return;
391
392 for_each_drmem_lmb(lmb)
393 read_drconf_v1_cell(lmb, &prop);
394 }
395
init_drmem_v2_lmbs(const __be32 * prop)396 static void __init init_drmem_v2_lmbs(const __be32 *prop)
397 {
398 struct drmem_lmb *lmb;
399 struct of_drconf_cell_v2 dr_cell;
400 const __be32 *p;
401 u32 i, j, lmb_sets;
402 int lmb_index;
403
404 lmb_sets = of_read_number(prop++, 1);
405 if (lmb_sets == 0)
406 return;
407
408 /* first pass, calculate the number of LMBs */
409 p = prop;
410 for (i = 0; i < lmb_sets; i++) {
411 read_drconf_v2_cell(&dr_cell, &p);
412 drmem_info->n_lmbs += dr_cell.seq_lmbs;
413 }
414
415 drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
416 GFP_KERNEL);
417 if (!drmem_info->lmbs)
418 return;
419
420 /* second pass, read in the LMB information */
421 lmb_index = 0;
422 p = prop;
423
424 for (i = 0; i < lmb_sets; i++) {
425 read_drconf_v2_cell(&dr_cell, &p);
426
427 for (j = 0; j < dr_cell.seq_lmbs; j++) {
428 lmb = &drmem_info->lmbs[lmb_index++];
429
430 lmb->base_addr = dr_cell.base_addr;
431 dr_cell.base_addr += drmem_info->lmb_size;
432
433 lmb->drc_index = dr_cell.drc_index;
434 dr_cell.drc_index++;
435
436 lmb->aa_index = dr_cell.aa_index;
437 lmb->flags = dr_cell.flags;
438 }
439 }
440 }
441
drmem_init(void)442 static int __init drmem_init(void)
443 {
444 struct device_node *dn;
445 const __be32 *prop;
446
447 dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
448 if (!dn) {
449 pr_info("No dynamic reconfiguration memory found\n");
450 return 0;
451 }
452
453 if (init_drmem_lmb_size(dn)) {
454 of_node_put(dn);
455 return 0;
456 }
457
458 prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
459 if (prop) {
460 init_drmem_v1_lmbs(prop);
461 } else {
462 prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
463 if (prop)
464 init_drmem_v2_lmbs(prop);
465 }
466
467 of_node_put(dn);
468 return 0;
469 }
470 late_initcall(drmem_init);
471