diff options
| -rw-r--r-- | arch/powerpc/include/asm/opal.h | 14 | ||||
| -rw-r--r-- | arch/powerpc/platforms/powernv/opal-dump.c | 81 | ||||
| -rw-r--r-- | arch/powerpc/platforms/powernv/opal-flash.c | 106 | ||||
| -rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 63 |
4 files changed, 76 insertions, 188 deletions
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 1a752ac8c0ba..afb0fedd1214 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h | |||
| @@ -41,14 +41,14 @@ struct opal_takeover_args { | |||
| 41 | * size except the last one in the list to be as well. | 41 | * size except the last one in the list to be as well. |
| 42 | */ | 42 | */ |
| 43 | struct opal_sg_entry { | 43 | struct opal_sg_entry { |
| 44 | void *data; | 44 | __be64 data; |
| 45 | long length; | 45 | __be64 length; |
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | /* sg list */ | 48 | /* SG list */ |
| 49 | struct opal_sg_list { | 49 | struct opal_sg_list { |
| 50 | unsigned long num_entries; | 50 | __be64 length; |
| 51 | struct opal_sg_list *next; | 51 | __be64 next; |
| 52 | struct opal_sg_entry entry[]; | 52 | struct opal_sg_entry entry[]; |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| @@ -929,6 +929,10 @@ extern int opal_resync_timebase(void); | |||
| 929 | 929 | ||
| 930 | extern void opal_lpc_init(void); | 930 | extern void opal_lpc_init(void); |
| 931 | 931 | ||
| 932 | struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr, | ||
| 933 | unsigned long vmalloc_size); | ||
| 934 | void opal_free_sg_list(struct opal_sg_list *sg); | ||
| 935 | |||
| 932 | #endif /* __ASSEMBLY__ */ | 936 | #endif /* __ASSEMBLY__ */ |
| 933 | 937 | ||
| 934 | #endif /* __OPAL_H */ | 938 | #endif /* __OPAL_H */ |
diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c index b9827b0d87e4..f0b472427c57 100644 --- a/arch/powerpc/platforms/powernv/opal-dump.c +++ b/arch/powerpc/platforms/powernv/opal-dump.c | |||
| @@ -209,80 +209,6 @@ static struct kobj_type dump_ktype = { | |||
| 209 | .default_attrs = dump_default_attrs, | 209 | .default_attrs = dump_default_attrs, |
| 210 | }; | 210 | }; |
| 211 | 211 | ||
| 212 | static void free_dump_sg_list(struct opal_sg_list *list) | ||
| 213 | { | ||
| 214 | struct opal_sg_list *sg1; | ||
| 215 | while (list) { | ||
| 216 | sg1 = list->next; | ||
| 217 | kfree(list); | ||
| 218 | list = sg1; | ||
| 219 | } | ||
| 220 | list = NULL; | ||
| 221 | } | ||
| 222 | |||
| 223 | static struct opal_sg_list *dump_data_to_sglist(struct dump_obj *dump) | ||
| 224 | { | ||
| 225 | struct opal_sg_list *sg1, *list = NULL; | ||
| 226 | void *addr; | ||
| 227 | int64_t size; | ||
| 228 | |||
| 229 | addr = dump->buffer; | ||
| 230 | size = dump->size; | ||
| 231 | |||
| 232 | sg1 = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 233 | if (!sg1) | ||
| 234 | goto nomem; | ||
| 235 | |||
| 236 | list = sg1; | ||
| 237 | sg1->num_entries = 0; | ||
| 238 | while (size > 0) { | ||
| 239 | /* Translate virtual address to physical address */ | ||
| 240 | sg1->entry[sg1->num_entries].data = | ||
| 241 | (void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT); | ||
| 242 | |||
| 243 | if (size > PAGE_SIZE) | ||
| 244 | sg1->entry[sg1->num_entries].length = PAGE_SIZE; | ||
| 245 | else | ||
| 246 | sg1->entry[sg1->num_entries].length = size; | ||
| 247 | |||
| 248 | sg1->num_entries++; | ||
| 249 | if (sg1->num_entries >= SG_ENTRIES_PER_NODE) { | ||
| 250 | sg1->next = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 251 | if (!sg1->next) | ||
| 252 | goto nomem; | ||
| 253 | |||
| 254 | sg1 = sg1->next; | ||
| 255 | sg1->num_entries = 0; | ||
| 256 | } | ||
| 257 | addr += PAGE_SIZE; | ||
| 258 | size -= PAGE_SIZE; | ||
| 259 | } | ||
| 260 | return list; | ||
| 261 | |||
| 262 | nomem: | ||
| 263 | pr_err("%s : Failed to allocate memory\n", __func__); | ||
| 264 | free_dump_sg_list(list); | ||
| 265 | return NULL; | ||
| 266 | } | ||
| 267 | |||
| 268 | static void sglist_to_phy_addr(struct opal_sg_list *list) | ||
| 269 | { | ||
| 270 | struct opal_sg_list *sg, *next; | ||
| 271 | |||
| 272 | for (sg = list; sg; sg = next) { | ||
| 273 | next = sg->next; | ||
| 274 | /* Don't translate NULL pointer for last entry */ | ||
| 275 | if (sg->next) | ||
| 276 | sg->next = (struct opal_sg_list *)__pa(sg->next); | ||
| 277 | else | ||
| 278 | sg->next = NULL; | ||
| 279 | |||
| 280 | /* Convert num_entries to length */ | ||
| 281 | sg->num_entries = | ||
| 282 | sg->num_entries * sizeof(struct opal_sg_entry) + 16; | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | static int64_t dump_read_info(uint32_t *id, uint32_t *size, uint32_t *type) | 212 | static int64_t dump_read_info(uint32_t *id, uint32_t *size, uint32_t *type) |
| 287 | { | 213 | { |
| 288 | int rc; | 214 | int rc; |
| @@ -314,15 +240,12 @@ static int64_t dump_read_data(struct dump_obj *dump) | |||
| 314 | } | 240 | } |
| 315 | 241 | ||
| 316 | /* Generate SG list */ | 242 | /* Generate SG list */ |
| 317 | list = dump_data_to_sglist(dump); | 243 | list = opal_vmalloc_to_sg_list(dump->buffer, dump->size); |
| 318 | if (!list) { | 244 | if (!list) { |
| 319 | rc = -ENOMEM; | 245 | rc = -ENOMEM; |
| 320 | goto out; | 246 | goto out; |
| 321 | } | 247 | } |
| 322 | 248 | ||
| 323 | /* Translate sg list addr to real address */ | ||
| 324 | sglist_to_phy_addr(list); | ||
| 325 | |||
| 326 | /* First entry address */ | 249 | /* First entry address */ |
| 327 | addr = __pa(list); | 250 | addr = __pa(list); |
| 328 | 251 | ||
| @@ -341,7 +264,7 @@ static int64_t dump_read_data(struct dump_obj *dump) | |||
| 341 | __func__, dump->id); | 264 | __func__, dump->id); |
| 342 | 265 | ||
| 343 | /* Free SG list */ | 266 | /* Free SG list */ |
| 344 | free_dump_sg_list(list); | 267 | opal_free_sg_list(list); |
| 345 | 268 | ||
| 346 | out: | 269 | out: |
| 347 | return rc; | 270 | return rc; |
diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c index a968fd1888b3..dc487ff04704 100644 --- a/arch/powerpc/platforms/powernv/opal-flash.c +++ b/arch/powerpc/platforms/powernv/opal-flash.c | |||
| @@ -79,9 +79,6 @@ | |||
| 79 | /* XXX: Assume candidate image size is <= 1GB */ | 79 | /* XXX: Assume candidate image size is <= 1GB */ |
| 80 | #define MAX_IMAGE_SIZE 0x40000000 | 80 | #define MAX_IMAGE_SIZE 0x40000000 |
| 81 | 81 | ||
| 82 | /* Flash sg list version */ | ||
| 83 | #define SG_LIST_VERSION (1UL) | ||
| 84 | |||
| 85 | /* Image status */ | 82 | /* Image status */ |
| 86 | enum { | 83 | enum { |
| 87 | IMAGE_INVALID, | 84 | IMAGE_INVALID, |
| @@ -272,93 +269,11 @@ static ssize_t manage_store(struct kobject *kobj, | |||
| 272 | } | 269 | } |
| 273 | 270 | ||
| 274 | /* | 271 | /* |
| 275 | * Free sg list | ||
| 276 | */ | ||
| 277 | static void free_sg_list(struct opal_sg_list *list) | ||
| 278 | { | ||
| 279 | struct opal_sg_list *sg1; | ||
| 280 | while (list) { | ||
| 281 | sg1 = list->next; | ||
| 282 | kfree(list); | ||
| 283 | list = sg1; | ||
| 284 | } | ||
| 285 | list = NULL; | ||
| 286 | } | ||
| 287 | |||
| 288 | /* | ||
| 289 | * Build candidate image scatter gather list | ||
| 290 | * | ||
| 291 | * list format: | ||
| 292 | * ----------------------------------- | ||
| 293 | * | VER (8) | Entry length in bytes | | ||
| 294 | * ----------------------------------- | ||
| 295 | * | Pointer to next entry | | ||
| 296 | * ----------------------------------- | ||
| 297 | * | Address of memory area 1 | | ||
| 298 | * ----------------------------------- | ||
| 299 | * | Length of memory area 1 | | ||
| 300 | * ----------------------------------- | ||
| 301 | * | ......... | | ||
| 302 | * ----------------------------------- | ||
| 303 | * | ......... | | ||
| 304 | * ----------------------------------- | ||
| 305 | * | Address of memory area N | | ||
| 306 | * ----------------------------------- | ||
| 307 | * | Length of memory area N | | ||
| 308 | * ----------------------------------- | ||
| 309 | */ | ||
| 310 | static struct opal_sg_list *image_data_to_sglist(void) | ||
| 311 | { | ||
| 312 | struct opal_sg_list *sg1, *list = NULL; | ||
| 313 | void *addr; | ||
| 314 | int size; | ||
| 315 | |||
| 316 | addr = image_data.data; | ||
| 317 | size = image_data.size; | ||
| 318 | |||
| 319 | sg1 = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 320 | if (!sg1) | ||
| 321 | return NULL; | ||
| 322 | |||
| 323 | list = sg1; | ||
| 324 | sg1->num_entries = 0; | ||
| 325 | while (size > 0) { | ||
| 326 | /* Translate virtual address to physical address */ | ||
| 327 | sg1->entry[sg1->num_entries].data = | ||
| 328 | (void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT); | ||
| 329 | |||
| 330 | if (size > PAGE_SIZE) | ||
| 331 | sg1->entry[sg1->num_entries].length = PAGE_SIZE; | ||
| 332 | else | ||
| 333 | sg1->entry[sg1->num_entries].length = size; | ||
| 334 | |||
| 335 | sg1->num_entries++; | ||
| 336 | if (sg1->num_entries >= SG_ENTRIES_PER_NODE) { | ||
| 337 | sg1->next = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 338 | if (!sg1->next) { | ||
| 339 | pr_err("%s : Failed to allocate memory\n", | ||
| 340 | __func__); | ||
| 341 | goto nomem; | ||
| 342 | } | ||
| 343 | |||
| 344 | sg1 = sg1->next; | ||
| 345 | sg1->num_entries = 0; | ||
| 346 | } | ||
| 347 | addr += PAGE_SIZE; | ||
| 348 | size -= PAGE_SIZE; | ||
| 349 | } | ||
| 350 | return list; | ||
| 351 | nomem: | ||
| 352 | free_sg_list(list); | ||
| 353 | return NULL; | ||
| 354 | } | ||
| 355 | |||
| 356 | /* | ||
| 357 | * OPAL update flash | 272 | * OPAL update flash |
| 358 | */ | 273 | */ |
| 359 | static int opal_flash_update(int op) | 274 | static int opal_flash_update(int op) |
| 360 | { | 275 | { |
| 361 | struct opal_sg_list *sg, *list, *next; | 276 | struct opal_sg_list *list; |
| 362 | unsigned long addr; | 277 | unsigned long addr; |
| 363 | int64_t rc = OPAL_PARAMETER; | 278 | int64_t rc = OPAL_PARAMETER; |
| 364 | 279 | ||
| @@ -368,30 +283,13 @@ static int opal_flash_update(int op) | |||
| 368 | goto flash; | 283 | goto flash; |
| 369 | } | 284 | } |
| 370 | 285 | ||
| 371 | list = image_data_to_sglist(); | 286 | list = opal_vmalloc_to_sg_list(image_data.data, image_data.size); |
| 372 | if (!list) | 287 | if (!list) |
| 373 | goto invalid_img; | 288 | goto invalid_img; |
| 374 | 289 | ||
| 375 | /* First entry address */ | 290 | /* First entry address */ |
| 376 | addr = __pa(list); | 291 | addr = __pa(list); |
| 377 | 292 | ||
| 378 | /* Translate sg list address to absolute */ | ||
| 379 | for (sg = list; sg; sg = next) { | ||
| 380 | next = sg->next; | ||
| 381 | /* Don't translate NULL pointer for last entry */ | ||
| 382 | if (sg->next) | ||
| 383 | sg->next = (struct opal_sg_list *)__pa(sg->next); | ||
| 384 | else | ||
| 385 | sg->next = NULL; | ||
| 386 | |||
| 387 | /* | ||
| 388 | * Convert num_entries to version/length format | ||
| 389 | * to satisfy OPAL. | ||
| 390 | */ | ||
| 391 | sg->num_entries = (SG_LIST_VERSION << 56) | | ||
| 392 | (sg->num_entries * sizeof(struct opal_sg_entry) + 16); | ||
| 393 | } | ||
| 394 | |||
| 395 | pr_alert("FLASH: Image is %u bytes\n", image_data.size); | 293 | pr_alert("FLASH: Image is %u bytes\n", image_data.size); |
| 396 | pr_alert("FLASH: Image update requested\n"); | 294 | pr_alert("FLASH: Image update requested\n"); |
| 397 | pr_alert("FLASH: Image will be updated during system reboot\n"); | 295 | pr_alert("FLASH: Image will be updated during system reboot\n"); |
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 17cfc70082aa..360ad80c754c 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c | |||
| @@ -638,3 +638,66 @@ void opal_shutdown(void) | |||
| 638 | 638 | ||
| 639 | /* Export this so that test modules can use it */ | 639 | /* Export this so that test modules can use it */ |
| 640 | EXPORT_SYMBOL_GPL(opal_invalid_call); | 640 | EXPORT_SYMBOL_GPL(opal_invalid_call); |
| 641 | |||
| 642 | /* Convert a region of vmalloc memory to an opal sg list */ | ||
| 643 | struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr, | ||
| 644 | unsigned long vmalloc_size) | ||
| 645 | { | ||
| 646 | struct opal_sg_list *sg, *first = NULL; | ||
| 647 | unsigned long i = 0; | ||
| 648 | |||
| 649 | sg = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 650 | if (!sg) | ||
| 651 | goto nomem; | ||
| 652 | |||
| 653 | first = sg; | ||
| 654 | |||
| 655 | while (vmalloc_size > 0) { | ||
| 656 | uint64_t data = vmalloc_to_pfn(vmalloc_addr) << PAGE_SHIFT; | ||
| 657 | uint64_t length = min(vmalloc_size, PAGE_SIZE); | ||
| 658 | |||
| 659 | sg->entry[i].data = cpu_to_be64(data); | ||
| 660 | sg->entry[i].length = cpu_to_be64(length); | ||
| 661 | i++; | ||
| 662 | |||
| 663 | if (i >= SG_ENTRIES_PER_NODE) { | ||
| 664 | struct opal_sg_list *next; | ||
| 665 | |||
| 666 | next = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 667 | if (!next) | ||
| 668 | goto nomem; | ||
| 669 | |||
| 670 | sg->length = cpu_to_be64( | ||
| 671 | i * sizeof(struct opal_sg_entry) + 16); | ||
| 672 | i = 0; | ||
| 673 | sg->next = cpu_to_be64(__pa(next)); | ||
| 674 | sg = next; | ||
| 675 | } | ||
| 676 | |||
| 677 | vmalloc_addr += length; | ||
| 678 | vmalloc_size -= length; | ||
| 679 | } | ||
| 680 | |||
| 681 | sg->length = cpu_to_be64(i * sizeof(struct opal_sg_entry) + 16); | ||
| 682 | |||
| 683 | return first; | ||
| 684 | |||
| 685 | nomem: | ||
| 686 | pr_err("%s : Failed to allocate memory\n", __func__); | ||
| 687 | opal_free_sg_list(first); | ||
| 688 | return NULL; | ||
| 689 | } | ||
| 690 | |||
| 691 | void opal_free_sg_list(struct opal_sg_list *sg) | ||
| 692 | { | ||
| 693 | while (sg) { | ||
| 694 | uint64_t next = be64_to_cpu(sg->next); | ||
| 695 | |||
| 696 | kfree(sg); | ||
| 697 | |||
| 698 | if (next) | ||
| 699 | sg = __va(next); | ||
| 700 | else | ||
| 701 | sg = NULL; | ||
| 702 | } | ||
| 703 | } | ||
