Merge tag 'seccomp-v5.14-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/kees...
[linux-2.6-microblaze.git] / arch / powerpc / mm / drmem.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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  */
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
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
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
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
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);