Commit 3441f04b4b62758a798f9fbbf2047dfedf0329a5
Committed by
Benjamin Herrenschmidt
1 parent
14ad0c58d5
Exists in
master
and in
13 other branches
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>
Showing 4 changed files with 76 additions and 188 deletions Side-by-side Diff
arch/powerpc/include/asm/opal.h
... | ... | @@ -41,14 +41,14 @@ |
41 | 41 | * size except the last one in the list to be as well. |
42 | 42 | */ |
43 | 43 | struct opal_sg_entry { |
44 | - void *data; | |
45 | - long length; | |
44 | + __be64 data; | |
45 | + __be64 length; | |
46 | 46 | }; |
47 | 47 | |
48 | -/* sg list */ | |
48 | +/* SG list */ | |
49 | 49 | struct opal_sg_list { |
50 | - unsigned long num_entries; | |
51 | - struct opal_sg_list *next; | |
50 | + __be64 length; | |
51 | + __be64 next; | |
52 | 52 | struct opal_sg_entry entry[]; |
53 | 53 | }; |
54 | 54 | |
... | ... | @@ -928,6 +928,10 @@ |
928 | 928 | extern int opal_resync_timebase(void); |
929 | 929 | |
930 | 930 | extern void opal_lpc_init(void); |
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); | |
931 | 935 | |
932 | 936 | #endif /* __ASSEMBLY__ */ |
933 | 937 |
arch/powerpc/platforms/powernv/opal-dump.c
... | ... | @@ -209,80 +209,6 @@ |
209 | 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 | 212 | static int64_t dump_read_info(uint32_t *id, uint32_t *size, uint32_t *type) |
287 | 213 | { |
288 | 214 | int rc; |
289 | 215 | |
... | ... | @@ -314,15 +240,12 @@ |
314 | 240 | } |
315 | 241 | |
316 | 242 | /* Generate SG list */ |
317 | - list = dump_data_to_sglist(dump); | |
243 | + list = opal_vmalloc_to_sg_list(dump->buffer, dump->size); | |
318 | 244 | if (!list) { |
319 | 245 | rc = -ENOMEM; |
320 | 246 | goto out; |
321 | 247 | } |
322 | 248 | |
323 | - /* Translate sg list addr to real address */ | |
324 | - sglist_to_phy_addr(list); | |
325 | - | |
326 | 249 | /* First entry address */ |
327 | 250 | addr = __pa(list); |
328 | 251 | |
... | ... | @@ -341,7 +264,7 @@ |
341 | 264 | __func__, dump->id); |
342 | 265 | |
343 | 266 | /* Free SG list */ |
344 | - free_dump_sg_list(list); | |
267 | + opal_free_sg_list(list); | |
345 | 268 | |
346 | 269 | out: |
347 | 270 | return rc; |
arch/powerpc/platforms/powernv/opal-flash.c
... | ... | @@ -79,9 +79,6 @@ |
79 | 79 | /* XXX: Assume candidate image size is <= 1GB */ |
80 | 80 | #define MAX_IMAGE_SIZE 0x40000000 |
81 | 81 | |
82 | -/* Flash sg list version */ | |
83 | -#define SG_LIST_VERSION (1UL) | |
84 | - | |
85 | 82 | /* Image status */ |
86 | 83 | enum { |
87 | 84 | IMAGE_INVALID, |
88 | 85 | |
... | ... | @@ -272,93 +269,11 @@ |
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 | 272 | * OPAL update flash |
358 | 273 | */ |
359 | 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 | 277 | unsigned long addr; |
363 | 278 | int64_t rc = OPAL_PARAMETER; |
364 | 279 | |
365 | 280 | |
... | ... | @@ -368,29 +283,12 @@ |
368 | 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 | 287 | if (!list) |
373 | 288 | goto invalid_img; |
374 | 289 | |
375 | 290 | /* First entry address */ |
376 | 291 | addr = __pa(list); |
377 | - | |
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 | 292 | |
395 | 293 | pr_alert("FLASH: Image is %u bytes\n", image_data.size); |
396 | 294 | pr_alert("FLASH: Image update requested\n"); |
arch/powerpc/platforms/powernv/opal.c
... | ... | @@ -638,4 +638,67 @@ |
638 | 638 | |
639 | 639 | /* Export this so that test modules can use it */ |
640 | 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 | +} |