Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Dynamic reconfiguration memory support
0004  *
0005  * Copyright 2017 IBM Corporation
0006  */
0007 
0008 #define pr_fmt(fmt) "drmem: " fmt
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/of.h>
0012 #include <linux/of_fdt.h>
0013 #include <linux/memblock.h>
0014 #include <linux/slab.h>
0015 #include <asm/drmem.h>
0016 
0017 static int n_root_addr_cells, n_root_size_cells;
0018 
0019 static struct drmem_lmb_info __drmem_info;
0020 struct drmem_lmb_info *drmem_info = &__drmem_info;
0021 static bool in_drmem_update;
0022 
0023 u64 drmem_lmb_memory_max(void)
0024 {
0025     struct drmem_lmb *last_lmb;
0026 
0027     last_lmb = &drmem_info->lmbs[drmem_info->n_lmbs - 1];
0028     return last_lmb->base_addr + drmem_lmb_size();
0029 }
0030 
0031 static u32 drmem_lmb_flags(struct drmem_lmb *lmb)
0032 {
0033     /*
0034      * Return the value of the lmb flags field minus the reserved
0035      * bit used internally for hotplug processing.
0036      */
0037     return lmb->flags & ~DRMEM_LMB_RESERVED;
0038 }
0039 
0040 static struct property *clone_property(struct property *prop, u32 prop_sz)
0041 {
0042     struct property *new_prop;
0043 
0044     new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
0045     if (!new_prop)
0046         return NULL;
0047 
0048     new_prop->name = kstrdup(prop->name, GFP_KERNEL);
0049     new_prop->value = kzalloc(prop_sz, GFP_KERNEL);
0050     if (!new_prop->name || !new_prop->value) {
0051         kfree(new_prop->name);
0052         kfree(new_prop->value);
0053         kfree(new_prop);
0054         return NULL;
0055     }
0056 
0057     new_prop->length = prop_sz;
0058 #if defined(CONFIG_OF_DYNAMIC)
0059     of_property_set_flag(new_prop, OF_DYNAMIC);
0060 #endif
0061     return new_prop;
0062 }
0063 
0064 static int drmem_update_dt_v1(struct device_node *memory,
0065                   struct property *prop)
0066 {
0067     struct property *new_prop;
0068     struct of_drconf_cell_v1 *dr_cell;
0069     struct drmem_lmb *lmb;
0070     u32 *p;
0071 
0072     new_prop = clone_property(prop, prop->length);
0073     if (!new_prop)
0074         return -1;
0075 
0076     p = new_prop->value;
0077     *p++ = cpu_to_be32(drmem_info->n_lmbs);
0078 
0079     dr_cell = (struct of_drconf_cell_v1 *)p;
0080 
0081     for_each_drmem_lmb(lmb) {
0082         dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
0083         dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
0084         dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
0085         dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
0086 
0087         dr_cell++;
0088     }
0089 
0090     of_update_property(memory, new_prop);
0091     return 0;
0092 }
0093 
0094 static void init_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
0095                 struct drmem_lmb *lmb)
0096 {
0097     dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
0098     dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
0099     dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
0100     dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
0101 }
0102 
0103 static int drmem_update_dt_v2(struct device_node *memory,
0104                   struct property *prop)
0105 {
0106     struct property *new_prop;
0107     struct of_drconf_cell_v2 *dr_cell;
0108     struct drmem_lmb *lmb, *prev_lmb;
0109     u32 lmb_sets, prop_sz, seq_lmbs;
0110     u32 *p;
0111 
0112     /* First pass, determine how many LMB sets are needed. */
0113     lmb_sets = 0;
0114     prev_lmb = NULL;
0115     for_each_drmem_lmb(lmb) {
0116         if (!prev_lmb) {
0117             prev_lmb = lmb;
0118             lmb_sets++;
0119             continue;
0120         }
0121 
0122         if (prev_lmb->aa_index != lmb->aa_index ||
0123             drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb))
0124             lmb_sets++;
0125 
0126         prev_lmb = lmb;
0127     }
0128 
0129     prop_sz = lmb_sets * sizeof(*dr_cell) + sizeof(__be32);
0130     new_prop = clone_property(prop, prop_sz);
0131     if (!new_prop)
0132         return -1;
0133 
0134     p = new_prop->value;
0135     *p++ = cpu_to_be32(lmb_sets);
0136 
0137     dr_cell = (struct of_drconf_cell_v2 *)p;
0138 
0139     /* Second pass, populate the LMB set data */
0140     prev_lmb = NULL;
0141     seq_lmbs = 0;
0142     for_each_drmem_lmb(lmb) {
0143         if (prev_lmb == NULL) {
0144             /* Start of first LMB set */
0145             prev_lmb = lmb;
0146             init_drconf_v2_cell(dr_cell, lmb);
0147             seq_lmbs++;
0148             continue;
0149         }
0150 
0151         if (prev_lmb->aa_index != lmb->aa_index ||
0152             drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb)) {
0153             /* end of one set, start of another */
0154             dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
0155             dr_cell++;
0156 
0157             init_drconf_v2_cell(dr_cell, lmb);
0158             seq_lmbs = 1;
0159         } else {
0160             seq_lmbs++;
0161         }
0162 
0163         prev_lmb = lmb;
0164     }
0165 
0166     /* close out last LMB set */
0167     dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
0168     of_update_property(memory, new_prop);
0169     return 0;
0170 }
0171 
0172 int drmem_update_dt(void)
0173 {
0174     struct device_node *memory;
0175     struct property *prop;
0176     int rc = -1;
0177 
0178     memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
0179     if (!memory)
0180         return -1;
0181 
0182     /*
0183      * Set in_drmem_update to prevent the notifier callback to process the
0184      * DT property back since the change is coming from the LMB tree.
0185      */
0186     in_drmem_update = true;
0187     prop = of_find_property(memory, "ibm,dynamic-memory", NULL);
0188     if (prop) {
0189         rc = drmem_update_dt_v1(memory, prop);
0190     } else {
0191         prop = of_find_property(memory, "ibm,dynamic-memory-v2", NULL);
0192         if (prop)
0193             rc = drmem_update_dt_v2(memory, prop);
0194     }
0195     in_drmem_update = false;
0196 
0197     of_node_put(memory);
0198     return rc;
0199 }
0200 
0201 static void read_drconf_v1_cell(struct drmem_lmb *lmb,
0202                        const __be32 **prop)
0203 {
0204     const __be32 *p = *prop;
0205 
0206     lmb->base_addr = of_read_number(p, n_root_addr_cells);
0207     p += n_root_addr_cells;
0208     lmb->drc_index = of_read_number(p++, 1);
0209 
0210     p++; /* skip reserved field */
0211 
0212     lmb->aa_index = of_read_number(p++, 1);
0213     lmb->flags = of_read_number(p++, 1);
0214 
0215     *prop = p;
0216 }
0217 
0218 static int
0219 __walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm, void *data,
0220              int (*func)(struct drmem_lmb *, const __be32 **, void *))
0221 {
0222     struct drmem_lmb lmb;
0223     u32 i, n_lmbs;
0224     int ret = 0;
0225 
0226     n_lmbs = of_read_number(prop++, 1);
0227     for (i = 0; i < n_lmbs; i++) {
0228         read_drconf_v1_cell(&lmb, &prop);
0229         ret = func(&lmb, &usm, data);
0230         if (ret)
0231             break;
0232     }
0233 
0234     return ret;
0235 }
0236 
0237 static void read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
0238                        const __be32 **prop)
0239 {
0240     const __be32 *p = *prop;
0241 
0242     dr_cell->seq_lmbs = of_read_number(p++, 1);
0243     dr_cell->base_addr = of_read_number(p, n_root_addr_cells);
0244     p += n_root_addr_cells;
0245     dr_cell->drc_index = of_read_number(p++, 1);
0246     dr_cell->aa_index = of_read_number(p++, 1);
0247     dr_cell->flags = of_read_number(p++, 1);
0248 
0249     *prop = p;
0250 }
0251 
0252 static int
0253 __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, void *data,
0254              int (*func)(struct drmem_lmb *, const __be32 **, void *))
0255 {
0256     struct of_drconf_cell_v2 dr_cell;
0257     struct drmem_lmb lmb;
0258     u32 i, j, lmb_sets;
0259     int ret = 0;
0260 
0261     lmb_sets = of_read_number(prop++, 1);
0262     for (i = 0; i < lmb_sets; i++) {
0263         read_drconf_v2_cell(&dr_cell, &prop);
0264 
0265         for (j = 0; j < dr_cell.seq_lmbs; j++) {
0266             lmb.base_addr = dr_cell.base_addr;
0267             dr_cell.base_addr += drmem_lmb_size();
0268 
0269             lmb.drc_index = dr_cell.drc_index;
0270             dr_cell.drc_index++;
0271 
0272             lmb.aa_index = dr_cell.aa_index;
0273             lmb.flags = dr_cell.flags;
0274 
0275             ret = func(&lmb, &usm, data);
0276             if (ret)
0277                 break;
0278         }
0279     }
0280 
0281     return ret;
0282 }
0283 
0284 #ifdef CONFIG_PPC_PSERIES
0285 int __init walk_drmem_lmbs_early(unsigned long node, void *data,
0286         int (*func)(struct drmem_lmb *, const __be32 **, void *))
0287 {
0288     const __be32 *prop, *usm;
0289     int len, ret = -ENODEV;
0290 
0291     prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len);
0292     if (!prop || len < dt_root_size_cells * sizeof(__be32))
0293         return ret;
0294 
0295     /* Get the address & size cells */
0296     n_root_addr_cells = dt_root_addr_cells;
0297     n_root_size_cells = dt_root_size_cells;
0298 
0299     drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
0300 
0301     usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory", &len);
0302 
0303     prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len);
0304     if (prop) {
0305         ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
0306     } else {
0307         prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory-v2",
0308                        &len);
0309         if (prop)
0310             ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
0311     }
0312 
0313     memblock_dump_all();
0314     return ret;
0315 }
0316 
0317 /*
0318  * Update the LMB associativity index.
0319  */
0320 static int update_lmb(struct drmem_lmb *updated_lmb,
0321               __maybe_unused const __be32 **usm,
0322               __maybe_unused void *data)
0323 {
0324     struct drmem_lmb *lmb;
0325 
0326     for_each_drmem_lmb(lmb) {
0327         if (lmb->drc_index != updated_lmb->drc_index)
0328             continue;
0329 
0330         lmb->aa_index = updated_lmb->aa_index;
0331         break;
0332     }
0333     return 0;
0334 }
0335 
0336 /*
0337  * Update the LMB associativity index.
0338  *
0339  * This needs to be called when the hypervisor is updating the
0340  * dynamic-reconfiguration-memory node property.
0341  */
0342 void drmem_update_lmbs(struct property *prop)
0343 {
0344     /*
0345      * Don't update the LMBs if triggered by the update done in
0346      * drmem_update_dt(), the LMB values have been used to the update the DT
0347      * property in that case.
0348      */
0349     if (in_drmem_update)
0350         return;
0351     if (!strcmp(prop->name, "ibm,dynamic-memory"))
0352         __walk_drmem_v1_lmbs(prop->value, NULL, NULL, update_lmb);
0353     else if (!strcmp(prop->name, "ibm,dynamic-memory-v2"))
0354         __walk_drmem_v2_lmbs(prop->value, NULL, NULL, update_lmb);
0355 }
0356 #endif
0357 
0358 static int init_drmem_lmb_size(struct device_node *dn)
0359 {
0360     const __be32 *prop;
0361     int len;
0362 
0363     if (drmem_info->lmb_size)
0364         return 0;
0365 
0366     prop = of_get_property(dn, "ibm,lmb-size", &len);
0367     if (!prop || len < n_root_size_cells * sizeof(__be32)) {
0368         pr_info("Could not determine LMB size\n");
0369         return -1;
0370     }
0371 
0372     drmem_info->lmb_size = of_read_number(prop, n_root_size_cells);
0373     return 0;
0374 }
0375 
0376 /*
0377  * Returns the property linux,drconf-usable-memory if
0378  * it exists (the property exists only in kexec/kdump kernels,
0379  * added by kexec-tools)
0380  */
0381 static const __be32 *of_get_usable_memory(struct device_node *dn)
0382 {
0383     const __be32 *prop;
0384     u32 len;
0385 
0386     prop = of_get_property(dn, "linux,drconf-usable-memory", &len);
0387     if (!prop || len < sizeof(unsigned int))
0388         return NULL;
0389 
0390     return prop;
0391 }
0392 
0393 int walk_drmem_lmbs(struct device_node *dn, void *data,
0394             int (*func)(struct drmem_lmb *, const __be32 **, void *))
0395 {
0396     const __be32 *prop, *usm;
0397     int ret = -ENODEV;
0398 
0399     if (!of_root)
0400         return ret;
0401 
0402     /* Get the address & size cells */
0403     of_node_get(of_root);
0404     n_root_addr_cells = of_n_addr_cells(of_root);
0405     n_root_size_cells = of_n_size_cells(of_root);
0406     of_node_put(of_root);
0407 
0408     if (init_drmem_lmb_size(dn))
0409         return ret;
0410 
0411     usm = of_get_usable_memory(dn);
0412 
0413     prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
0414     if (prop) {
0415         ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
0416     } else {
0417         prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
0418         if (prop)
0419             ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
0420     }
0421 
0422     return ret;
0423 }
0424 
0425 static void __init init_drmem_v1_lmbs(const __be32 *prop)
0426 {
0427     struct drmem_lmb *lmb;
0428 
0429     drmem_info->n_lmbs = of_read_number(prop++, 1);
0430     if (drmem_info->n_lmbs == 0)
0431         return;
0432 
0433     drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
0434                    GFP_KERNEL);
0435     if (!drmem_info->lmbs)
0436         return;
0437 
0438     for_each_drmem_lmb(lmb)
0439         read_drconf_v1_cell(lmb, &prop);
0440 }
0441 
0442 static void __init init_drmem_v2_lmbs(const __be32 *prop)
0443 {
0444     struct drmem_lmb *lmb;
0445     struct of_drconf_cell_v2 dr_cell;
0446     const __be32 *p;
0447     u32 i, j, lmb_sets;
0448     int lmb_index;
0449 
0450     lmb_sets = of_read_number(prop++, 1);
0451     if (lmb_sets == 0)
0452         return;
0453 
0454     /* first pass, calculate the number of LMBs */
0455     p = prop;
0456     for (i = 0; i < lmb_sets; i++) {
0457         read_drconf_v2_cell(&dr_cell, &p);
0458         drmem_info->n_lmbs += dr_cell.seq_lmbs;
0459     }
0460 
0461     drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
0462                    GFP_KERNEL);
0463     if (!drmem_info->lmbs)
0464         return;
0465 
0466     /* second pass, read in the LMB information */
0467     lmb_index = 0;
0468     p = prop;
0469 
0470     for (i = 0; i < lmb_sets; i++) {
0471         read_drconf_v2_cell(&dr_cell, &p);
0472 
0473         for (j = 0; j < dr_cell.seq_lmbs; j++) {
0474             lmb = &drmem_info->lmbs[lmb_index++];
0475 
0476             lmb->base_addr = dr_cell.base_addr;
0477             dr_cell.base_addr += drmem_info->lmb_size;
0478 
0479             lmb->drc_index = dr_cell.drc_index;
0480             dr_cell.drc_index++;
0481 
0482             lmb->aa_index = dr_cell.aa_index;
0483             lmb->flags = dr_cell.flags;
0484         }
0485     }
0486 }
0487 
0488 static int __init drmem_init(void)
0489 {
0490     struct device_node *dn;
0491     const __be32 *prop;
0492 
0493     dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
0494     if (!dn) {
0495         pr_info("No dynamic reconfiguration memory found\n");
0496         return 0;
0497     }
0498 
0499     if (init_drmem_lmb_size(dn)) {
0500         of_node_put(dn);
0501         return 0;
0502     }
0503 
0504     prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
0505     if (prop) {
0506         init_drmem_v1_lmbs(prop);
0507     } else {
0508         prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
0509         if (prop)
0510             init_drmem_v2_lmbs(prop);
0511     }
0512 
0513     of_node_put(dn);
0514     return 0;
0515 }
0516 late_initcall(drmem_init);