diff options
author | Anton Blanchard <anton@samba.org> | 2014-04-22 01:01:26 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-04-27 23:11:23 -0400 |
commit | 3441f04b4b62758a798f9fbbf2047dfedf0329a5 (patch) | |
tree | 57dcaa15ea0d4d4f7bbd6479fd6393b4bbf3f322 | |
parent | 14ad0c58d5df6e5911a5413abdc2a9be6a8acb51 (diff) |
powerpc/powernv: Create OPAL sglist helper functions and fix endian issues
We have two copies of code that creates an OPAL sg list. Consolidate
these into a common set of helpers and fix the endian issues.
The flash interface embedded a version number in the num_entries
field, whereas the dump interface did did not. Since versioning
wasn't added to the flash interface and it is impossible to add
this in a backwards compatible way, just remove it.
Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-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 | } | ||