1 // SPDX-License-Identifier: GPL-2.0-or-later 1 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 <linux/slab.h> 15 #include <asm/drmem.h> 16 17 static int n_root_addr_cells, n_root_size_cell 18 19 static struct drmem_lmb_info __drmem_info; 20 struct drmem_lmb_info *drmem_info = &__drmem_i 21 static bool in_drmem_update; 22 23 u64 drmem_lmb_memory_max(void) 24 { 25 struct drmem_lmb *last_lmb; 26 27 last_lmb = &drmem_info->lmbs[drmem_inf 28 return last_lmb->base_addr + drmem_lmb 29 } 30 31 static u32 drmem_lmb_flags(struct drmem_lmb *l 32 { 33 /* 34 * Return the value of the lmb flags f 35 * bit used internally for hotplug pro 36 */ 37 return lmb->flags & ~DRMEM_LMB_RESERVE 38 } 39 40 static struct property *clone_property(struct 41 { 42 struct property *new_prop; 43 44 new_prop = kzalloc(sizeof(*new_prop), 45 if (!new_prop) 46 return NULL; 47 48 new_prop->name = kstrdup(prop->name, G 49 new_prop->value = kzalloc(prop_sz, GFP 50 if (!new_prop->name || !new_prop->valu 51 kfree(new_prop->name); 52 kfree(new_prop->value); 53 kfree(new_prop); 54 return NULL; 55 } 56 57 new_prop->length = prop_sz; 58 #if defined(CONFIG_OF_DYNAMIC) 59 of_property_set_flag(new_prop, OF_DYNA 60 #endif 61 return new_prop; 62 } 63 64 static int drmem_update_dt_v1(struct device_no 65 struct property 66 { 67 struct property *new_prop; 68 struct of_drconf_cell_v1 *dr_cell; 69 struct drmem_lmb *lmb; 70 __be32 *p; 71 72 new_prop = clone_property(prop, prop-> 73 if (!new_prop) 74 return -1; 75 76 p = new_prop->value; 77 *p++ = cpu_to_be32(drmem_info->n_lmbs) 78 79 dr_cell = (struct of_drconf_cell_v1 *) 80 81 for_each_drmem_lmb(lmb) { 82 dr_cell->base_addr = cpu_to_be 83 dr_cell->drc_index = cpu_to_be 84 dr_cell->aa_index = cpu_to_be3 85 dr_cell->flags = cpu_to_be32(d 86 87 dr_cell++; 88 } 89 90 of_update_property(memory, new_prop); 91 return 0; 92 } 93 94 static void init_drconf_v2_cell(struct of_drco 95 struct drmem_l 96 { 97 dr_cell->base_addr = cpu_to_be64(lmb-> 98 dr_cell->drc_index = cpu_to_be32(lmb-> 99 dr_cell->aa_index = cpu_to_be32(lmb->a 100 dr_cell->flags = cpu_to_be32(drmem_lmb 101 } 102 103 static int drmem_update_dt_v2(struct device_no 104 struct property 105 { 106 struct property *new_prop; 107 struct of_drconf_cell_v2 *dr_cell; 108 struct drmem_lmb *lmb, *prev_lmb; 109 u32 lmb_sets, prop_sz, seq_lmbs; 110 u32 *p; 111 112 /* First pass, determine how many LMB 113 lmb_sets = 0; 114 prev_lmb = NULL; 115 for_each_drmem_lmb(lmb) { 116 if (!prev_lmb) { 117 prev_lmb = lmb; 118 lmb_sets++; 119 continue; 120 } 121 122 if (prev_lmb->aa_index != lmb- 123 drmem_lmb_flags(prev_lmb) 124 lmb_sets++; 125 126 prev_lmb = lmb; 127 } 128 129 prop_sz = lmb_sets * sizeof(*dr_cell) 130 new_prop = clone_property(prop, prop_s 131 if (!new_prop) 132 return -1; 133 134 p = new_prop->value; 135 *p++ = cpu_to_be32(lmb_sets); 136 137 dr_cell = (struct of_drconf_cell_v2 *) 138 139 /* Second pass, populate the LMB set d 140 prev_lmb = NULL; 141 seq_lmbs = 0; 142 for_each_drmem_lmb(lmb) { 143 if (prev_lmb == NULL) { 144 /* Start of first LMB 145 prev_lmb = lmb; 146 init_drconf_v2_cell(dr 147 seq_lmbs++; 148 continue; 149 } 150 151 if (prev_lmb->aa_index != lmb- 152 drmem_lmb_flags(prev_lmb) 153 /* end of one set, sta 154 dr_cell->seq_lmbs = cp 155 dr_cell++; 156 157 init_drconf_v2_cell(dr 158 seq_lmbs = 1; 159 } else { 160 seq_lmbs++; 161 } 162 163 prev_lmb = lmb; 164 } 165 166 /* close out last LMB set */ 167 dr_cell->seq_lmbs = cpu_to_be32(seq_lm 168 of_update_property(memory, new_prop); 169 return 0; 170 } 171 172 int drmem_update_dt(void) 173 { 174 struct device_node *memory; 175 struct property *prop; 176 int rc = -1; 177 178 memory = of_find_node_by_path("/ibm,dy 179 if (!memory) 180 return -1; 181 182 /* 183 * Set in_drmem_update to prevent the 184 * DT property back since the change i 185 */ 186 in_drmem_update = true; 187 prop = of_find_property(memory, "ibm,d 188 if (prop) { 189 rc = drmem_update_dt_v1(memory 190 } else { 191 prop = of_find_property(memory 192 if (prop) 193 rc = drmem_update_dt_v 194 } 195 in_drmem_update = false; 196 197 of_node_put(memory); 198 return rc; 199 } 200 201 static void read_drconf_v1_cell(struct drmem_l 202 const _ 203 { 204 const __be32 *p = *prop; 205 206 lmb->base_addr = of_read_number(p, n_r 207 p += n_root_addr_cells; 208 lmb->drc_index = of_read_number(p++, 1 209 210 p++; /* skip reserved field */ 211 212 lmb->aa_index = of_read_number(p++, 1) 213 lmb->flags = of_read_number(p++, 1); 214 215 *prop = p; 216 } 217 218 static int 219 __walk_drmem_v1_lmbs(const __be32 *prop, const 220 int (*func)(struct drmem_ 221 { 222 struct drmem_lmb lmb; 223 u32 i, n_lmbs; 224 int ret = 0; 225 226 n_lmbs = of_read_number(prop++, 1); 227 for (i = 0; i < n_lmbs; i++) { 228 read_drconf_v1_cell(&lmb, &pro 229 ret = func(&lmb, &usm, data); 230 if (ret) 231 break; 232 } 233 234 return ret; 235 } 236 237 static void read_drconf_v2_cell(struct of_drco 238 const _ 239 { 240 const __be32 *p = *prop; 241 242 dr_cell->seq_lmbs = of_read_number(p++ 243 dr_cell->base_addr = of_read_number(p, 244 p += n_root_addr_cells; 245 dr_cell->drc_index = of_read_number(p+ 246 dr_cell->aa_index = of_read_number(p++ 247 dr_cell->flags = of_read_number(p++, 1 248 249 *prop = p; 250 } 251 252 static int 253 __walk_drmem_v2_lmbs(const __be32 *prop, const 254 int (*func)(struct drmem_ 255 { 256 struct of_drconf_cell_v2 dr_cell; 257 struct drmem_lmb lmb; 258 u32 i, j, lmb_sets; 259 int ret = 0; 260 261 lmb_sets = of_read_number(prop++, 1); 262 for (i = 0; i < lmb_sets; i++) { 263 read_drconf_v2_cell(&dr_cell, 264 265 for (j = 0; j < dr_cell.seq_lm 266 lmb.base_addr = dr_cel 267 dr_cell.base_addr += d 268 269 lmb.drc_index = dr_cel 270 dr_cell.drc_index++; 271 272 lmb.aa_index = dr_cell 273 lmb.flags = dr_cell.fl 274 275 ret = func(&lmb, &usm, 276 if (ret) 277 break; 278 } 279 } 280 281 return ret; 282 } 283 284 #ifdef CONFIG_PPC_PSERIES 285 int __init walk_drmem_lmbs_early(unsigned long 286 int (*func)(struct drmem_lmb * 287 { 288 const __be32 *prop, *usm; 289 int len, ret = -ENODEV; 290 291 prop = of_get_flat_dt_prop(node, "ibm, 292 if (!prop || len < dt_root_size_cells 293 return ret; 294 295 /* Get the address & size cells */ 296 n_root_addr_cells = dt_root_addr_cells 297 n_root_size_cells = dt_root_size_cells 298 299 drmem_info->lmb_size = dt_mem_next_cel 300 301 usm = of_get_flat_dt_prop(node, "linux 302 303 prop = of_get_flat_dt_prop(node, "ibm, 304 if (prop) { 305 ret = __walk_drmem_v1_lmbs(pro 306 } else { 307 prop = of_get_flat_dt_prop(nod 308 &le 309 if (prop) 310 ret = __walk_drmem_v2_ 311 } 312 313 memblock_dump_all(); 314 return ret; 315 } 316 317 /* 318 * Update the LMB associativity index. 319 */ 320 static int update_lmb(struct drmem_lmb *update 321 __maybe_unused const __b 322 __maybe_unused void *dat 323 { 324 struct drmem_lmb *lmb; 325 326 for_each_drmem_lmb(lmb) { 327 if (lmb->drc_index != updated_ 328 continue; 329 330 lmb->aa_index = updated_lmb->a 331 break; 332 } 333 return 0; 334 } 335 336 /* 337 * Update the LMB associativity index. 338 * 339 * This needs to be called when the hypervisor 340 * dynamic-reconfiguration-memory node propert 341 */ 342 void drmem_update_lmbs(struct property *prop) 343 { 344 /* 345 * Don't update the LMBs if triggered 346 * drmem_update_dt(), the LMB values h 347 * property in that case. 348 */ 349 if (in_drmem_update) 350 return; 351 if (!strcmp(prop->name, "ibm,dynamic-m 352 __walk_drmem_v1_lmbs(prop->val 353 else if (!strcmp(prop->name, "ibm,dyna 354 __walk_drmem_v2_lmbs(prop->val 355 } 356 #endif 357 358 static int init_drmem_lmb_size(struct device_n 359 { 360 const __be32 *prop; 361 int len; 362 363 if (drmem_info->lmb_size) 364 return 0; 365 366 prop = of_get_property(dn, "ibm,lmb-si 367 if (!prop || len < n_root_size_cells * 368 pr_info("Could not determine L 369 return -1; 370 } 371 372 drmem_info->lmb_size = of_read_number( 373 return 0; 374 } 375 376 /* 377 * Returns the property linux,drconf-usable-me 378 * it exists (the property exists only in kexe 379 * added by kexec-tools) 380 */ 381 static const __be32 *of_get_usable_memory(stru 382 { 383 const __be32 *prop; 384 u32 len; 385 386 prop = of_get_property(dn, "linux,drco 387 if (!prop || len < sizeof(unsigned int 388 return NULL; 389 390 return prop; 391 } 392 393 int walk_drmem_lmbs(struct device_node *dn, vo 394 int (*func)(struct drmem_l 395 { 396 struct device_node *root = of_find_nod 397 const __be32 *prop, *usm; 398 int ret = -ENODEV; 399 400 if (!root) 401 return ret; 402 403 /* Get the address & size cells */ 404 n_root_addr_cells = of_n_addr_cells(ro 405 n_root_size_cells = of_n_size_cells(ro 406 of_node_put(root); 407 408 if (init_drmem_lmb_size(dn)) 409 return ret; 410 411 usm = of_get_usable_memory(dn); 412 413 prop = of_get_property(dn, "ibm,dynami 414 if (prop) { 415 ret = __walk_drmem_v1_lmbs(pro 416 } else { 417 prop = of_get_property(dn, "ib 418 if (prop) 419 ret = __walk_drmem_v2_ 420 } 421 422 return ret; 423 } 424 425 static void __init init_drmem_v1_lmbs(const __ 426 { 427 struct drmem_lmb *lmb; 428 429 drmem_info->n_lmbs = of_read_number(pr 430 if (drmem_info->n_lmbs == 0) 431 return; 432 433 drmem_info->lmbs = kcalloc(drmem_info- 434 GFP_KERNEL) 435 if (!drmem_info->lmbs) 436 return; 437 438 for_each_drmem_lmb(lmb) 439 read_drconf_v1_cell(lmb, &prop 440 } 441 442 static void __init init_drmem_v2_lmbs(const __ 443 { 444 struct drmem_lmb *lmb; 445 struct of_drconf_cell_v2 dr_cell; 446 const __be32 *p; 447 u32 i, j, lmb_sets; 448 int lmb_index; 449 450 lmb_sets = of_read_number(prop++, 1); 451 if (lmb_sets == 0) 452 return; 453 454 /* first pass, calculate the number of 455 p = prop; 456 for (i = 0; i < lmb_sets; i++) { 457 read_drconf_v2_cell(&dr_cell, 458 drmem_info->n_lmbs += dr_cell. 459 } 460 461 drmem_info->lmbs = kcalloc(drmem_info- 462 GFP_KERNEL) 463 if (!drmem_info->lmbs) 464 return; 465 466 /* second pass, read in the LMB inform 467 lmb_index = 0; 468 p = prop; 469 470 for (i = 0; i < lmb_sets; i++) { 471 read_drconf_v2_cell(&dr_cell, 472 473 for (j = 0; j < dr_cell.seq_lm 474 lmb = &drmem_info->lmb 475 476 lmb->base_addr = dr_ce 477 dr_cell.base_addr += d 478 479 lmb->drc_index = dr_ce 480 dr_cell.drc_index++; 481 482 lmb->aa_index = dr_cel 483 lmb->flags = dr_cell.f 484 } 485 } 486 } 487 488 static int __init drmem_init(void) 489 { 490 struct device_node *dn; 491 const __be32 *prop; 492 493 dn = of_find_node_by_path("/ibm,dynami 494 if (!dn) 495 return 0; 496 497 if (init_drmem_lmb_size(dn)) { 498 of_node_put(dn); 499 return 0; 500 } 501 502 prop = of_get_property(dn, "ibm,dynami 503 if (prop) { 504 init_drmem_v1_lmbs(prop); 505 } else { 506 prop = of_get_property(dn, "ib 507 if (prop) 508 init_drmem_v2_lmbs(pro 509 } 510 511 of_node_put(dn); 512 return 0; 513 } 514 late_initcall(drmem_init); 515
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.