aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Blanchard <anton@samba.org>2014-04-22 01:01:26 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-04-27 23:11:23 -0400
commit3441f04b4b62758a798f9fbbf2047dfedf0329a5 (patch)
tree57dcaa15ea0d4d4f7bbd6479fd6393b4bbf3f322
parent14ad0c58d5df6e5911a5413abdc2a9be6a8acb51 (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.h14
-rw-r--r--arch/powerpc/platforms/powernv/opal-dump.c81
-rw-r--r--arch/powerpc/platforms/powernv/opal-flash.c106
-rw-r--r--arch/powerpc/platforms/powernv/opal.c63
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 */
43struct opal_sg_entry { 43struct 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 */
49struct opal_sg_list { 49struct 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
930extern void opal_lpc_init(void); 930extern void opal_lpc_init(void);
931 931
932struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
933 unsigned long vmalloc_size);
934void 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
212static 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
223static 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
262nomem:
263 pr_err("%s : Failed to allocate memory\n", __func__);
264 free_dump_sg_list(list);
265 return NULL;
266}
267
268static 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
286static int64_t dump_read_info(uint32_t *id, uint32_t *size, uint32_t *type) 212static 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
346out: 269out:
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 */
86enum { 83enum {
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 */
277static 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 */
310static 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;
351nomem:
352 free_sg_list(list);
353 return NULL;
354}
355
356/*
357 * OPAL update flash 272 * OPAL update flash
358 */ 273 */
359static int opal_flash_update(int op) 274static 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 */
640EXPORT_SYMBOL_GPL(opal_invalid_call); 640EXPORT_SYMBOL_GPL(opal_invalid_call);
641
642/* Convert a region of vmalloc memory to an opal sg list */
643struct 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
685nomem:
686 pr_err("%s : Failed to allocate memory\n", __func__);
687 opal_free_sg_list(first);
688 return NULL;
689}
690
691void 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}