diff mbox

[5/6] powerpc/powernv: Create OPAL sglist helper functions and fix endian issues

Message ID 1398142887-24109-5-git-send-email-anton@samba.org (mailing list archive)
State Accepted
Commit 3441f04b4b62758a798f9fbbf2047dfedf0329a5
Delegated to: Benjamin Herrenschmidt
Headers show

Commit Message

Anton Blanchard April 22, 2014, 5:01 a.m. UTC
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>
---
 arch/powerpc/include/asm/opal.h             |  14 ++--
 arch/powerpc/platforms/powernv/opal-dump.c  |  81 +--------------------
 arch/powerpc/platforms/powernv/opal-flash.c | 106 +---------------------------
 arch/powerpc/platforms/powernv/opal.c       |  63 +++++++++++++++++
 4 files changed, 76 insertions(+), 188 deletions(-)

Comments

Vasant Hegde April 22, 2014, 9:16 a.m. UTC | #1
On 04/22/2014 10:31 AM, Anton Blanchard wrote:
> 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>
> ---
>   arch/powerpc/include/asm/opal.h             |  14 ++--
>   arch/powerpc/platforms/powernv/opal-dump.c  |  81 +--------------------
>   arch/powerpc/platforms/powernv/opal-flash.c | 106 +---------------------------
>   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 1a752ac..afb0fed 100644
> --- a/arch/powerpc/include/asm/opal.h
> +++ b/arch/powerpc/include/asm/opal.h
> @@ -41,14 +41,14 @@ struct opal_takeover_args {
>    * size except the last one in the list to be as well.
>    */
>   struct opal_sg_entry {
> -	void    *data;
> -	long    length;
> +	__be64 data;
> +	__be64 length;
>   };
>
> -/* sg list */
> +/* SG list */
>   struct opal_sg_list {
> -	unsigned long num_entries;
> -	struct opal_sg_list *next;
> +	__be64 length;
> +	__be64 next;
>   	struct opal_sg_entry entry[];
>   };
>
> @@ -929,6 +929,10 @@ extern int opal_resync_timebase(void);
>
>   extern void opal_lpc_init(void);
>
> +struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
> +					     unsigned long vmalloc_size);
> +void opal_free_sg_list(struct opal_sg_list *sg);
> +
>   #endif /* __ASSEMBLY__ */
>
>   #endif /* __OPAL_H */
> diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c
> index b9827b0..f0b4724 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 = {
>   	.default_attrs = dump_default_attrs,
>   };
>
> -static void free_dump_sg_list(struct opal_sg_list *list)
> -{
> -	struct opal_sg_list *sg1;
> -	while (list) {
> -		sg1 = list->next;
> -		kfree(list);
> -		list = sg1;
> -	}
> -	list = NULL;
> -}
> -
> -static struct opal_sg_list *dump_data_to_sglist(struct dump_obj *dump)
> -{
> -	struct opal_sg_list *sg1, *list = NULL;
> -	void *addr;
> -	int64_t size;
> -
> -	addr = dump->buffer;
> -	size = dump->size;
> -
> -	sg1 = kzalloc(PAGE_SIZE, GFP_KERNEL);
> -	if (!sg1)
> -		goto nomem;
> -
> -	list = sg1;
> -	sg1->num_entries = 0;
> -	while (size > 0) {
> -		/* Translate virtual address to physical address */
> -		sg1->entry[sg1->num_entries].data =
> -			(void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT);
> -
> -		if (size > PAGE_SIZE)
> -			sg1->entry[sg1->num_entries].length = PAGE_SIZE;
> -		else
> -			sg1->entry[sg1->num_entries].length = size;
> -
> -		sg1->num_entries++;
> -		if (sg1->num_entries >= SG_ENTRIES_PER_NODE) {
> -			sg1->next = kzalloc(PAGE_SIZE, GFP_KERNEL);
> -			if (!sg1->next)
> -				goto nomem;
> -
> -			sg1 = sg1->next;
> -			sg1->num_entries = 0;
> -		}
> -		addr += PAGE_SIZE;
> -		size -= PAGE_SIZE;
> -	}
> -	return list;
> -
> -nomem:
> -	pr_err("%s : Failed to allocate memory\n", __func__);
> -	free_dump_sg_list(list);
> -	return NULL;
> -}
> -
> -static void sglist_to_phy_addr(struct opal_sg_list *list)
> -{
> -	struct opal_sg_list *sg, *next;
> -
> -	for (sg = list; sg; sg = next) {
> -		next = sg->next;
> -		/* Don't translate NULL pointer for last entry */
> -		if (sg->next)
> -			sg->next = (struct opal_sg_list *)__pa(sg->next);
> -		else
> -			sg->next = NULL;
> -
> -		/* Convert num_entries to length */
> -		sg->num_entries =
> -			sg->num_entries * sizeof(struct opal_sg_entry) + 16;
> -	}
> -}
> -
>   static int64_t dump_read_info(uint32_t *id, uint32_t *size, uint32_t *type)
>   {
>   	int rc;
> @@ -314,15 +240,12 @@ static int64_t dump_read_data(struct dump_obj *dump)
>   	}
>
>   	/* Generate SG list */
> -	list = dump_data_to_sglist(dump);
> +	list = opal_vmalloc_to_sg_list(dump->buffer, dump->size);
>   	if (!list) {
>   		rc = -ENOMEM;
>   		goto out;
>   	}
>
> -	/* Translate sg list addr to real address */
> -	sglist_to_phy_addr(list);
> -
>   	/* First entry address */
>   	addr = __pa(list);
>
> @@ -341,7 +264,7 @@ static int64_t dump_read_data(struct dump_obj *dump)
>   			__func__, dump->id);
>

Shouldn't we convert addr and id before passing to opal_dump_read() here ?


>   	/* Free SG list */
> -	free_dump_sg_list(list);
> +	opal_free_sg_list(list);
>
>   out:
>   	return rc;
> diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c
> index 714ef97..0ae9e5f 100644
> --- a/arch/powerpc/platforms/powernv/opal-flash.c
> +++ b/arch/powerpc/platforms/powernv/opal-flash.c
> @@ -79,9 +79,6 @@
>   /* XXX: Assume candidate image size is <= 1GB */
>   #define MAX_IMAGE_SIZE	0x40000000
>
> -/* Flash sg list version */
> -#define SG_LIST_VERSION (1UL)
> -
>   /* Image status */
>   enum {
>   	IMAGE_INVALID,
> @@ -268,93 +265,11 @@ static ssize_t manage_store(struct kobject *kobj,
>   }
>
>   /*
> - * Free sg list
> - */
> -static void free_sg_list(struct opal_sg_list *list)
> -{
> -	struct opal_sg_list *sg1;
> -	while (list) {
> -		sg1 = list->next;
> -		kfree(list);
> -		list = sg1;
> -	}
> -	list = NULL;
> -}
> -
> -/*
> - * Build candidate image scatter gather list
> - *
> - * list format:
> - *   -----------------------------------
> - *  |  VER (8) | Entry length in bytes  |
> - *   -----------------------------------
> - *  |  Pointer to next entry            |
> - *   -----------------------------------
> - *  |  Address of memory area 1         |
> - *   -----------------------------------
> - *  |  Length of memory area 1          |
> - *   -----------------------------------
> - *  |   .........                       |
> - *   -----------------------------------
> - *  |   .........                       |
> - *   -----------------------------------
> - *  |  Address of memory area N         |
> - *   -----------------------------------
> - *  |  Length of memory area N          |
> - *   -----------------------------------
> - */
> -static struct opal_sg_list *image_data_to_sglist(void)
> -{
> -	struct opal_sg_list *sg1, *list = NULL;
> -	void *addr;
> -	int size;
> -
> -	addr = image_data.data;
> -	size = image_data.size;
> -
> -	sg1 = kzalloc(PAGE_SIZE, GFP_KERNEL);
> -	if (!sg1)
> -		return NULL;
> -
> -	list = sg1;
> -	sg1->num_entries = 0;
> -	while (size > 0) {
> -		/* Translate virtual address to physical address */
> -		sg1->entry[sg1->num_entries].data =
> -			(void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT);
> -
> -		if (size > PAGE_SIZE)
> -			sg1->entry[sg1->num_entries].length = PAGE_SIZE;
> -		else
> -			sg1->entry[sg1->num_entries].length = size;
> -
> -		sg1->num_entries++;
> -		if (sg1->num_entries >= SG_ENTRIES_PER_NODE) {
> -			sg1->next = kzalloc(PAGE_SIZE, GFP_KERNEL);
> -			if (!sg1->next) {
> -				pr_err("%s : Failed to allocate memory\n",
> -				       __func__);
> -				goto nomem;
> -			}
> -
> -			sg1 = sg1->next;
> -			sg1->num_entries = 0;
> -		}
> -		addr += PAGE_SIZE;
> -		size -= PAGE_SIZE;
> -	}
> -	return list;
> -nomem:
> -	free_sg_list(list);
> -	return NULL;
> -}
> -
> -/*
>    * OPAL update flash
>    */
>   static int opal_flash_update(int op)
>   {
> -	struct opal_sg_list *sg, *list, *next;
> +	struct opal_sg_list *list;
>   	unsigned long addr;
>   	int64_t rc = OPAL_PARAMETER;
>
> @@ -364,30 +279,13 @@ static int opal_flash_update(int op)
>   		goto flash;
>   	}
>
> -	list = image_data_to_sglist();
> +	list = opal_vmalloc_to_sg_list(image_data.data, image_data.size);
>   	if (!list)
>   		goto invalid_img;
>
>   	/* First entry address */
>   	addr = __pa(list);
>
> -	/* Translate sg list address to absolute */
> -	for (sg = list; sg; sg = next) {
> -		next = sg->next;
> -		/* Don't translate NULL pointer for last entry */
> -		if (sg->next)
> -			sg->next = (struct opal_sg_list *)__pa(sg->next);
> -		else
> -			sg->next = NULL;
> -
> -		/*
> -		 * Convert num_entries to version/length format
> -		 * to satisfy OPAL.
> -		 */
> -		sg->num_entries = (SG_LIST_VERSION << 56) |
> -			(sg->num_entries * sizeof(struct opal_sg_entry) + 16);
> -	}
> -
>   	pr_alert("FLASH: Image is %u bytes\n", image_data.size);
>   	pr_alert("FLASH: Image update requested\n");
>   	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 17cfc70..360ad80c 100644
> --- a/arch/powerpc/platforms/powernv/opal.c
> +++ b/arch/powerpc/platforms/powernv/opal.c
> @@ -638,3 +638,66 @@ void opal_shutdown(void)
>
>   /* Export this so that test modules can use it */
>   EXPORT_SYMBOL_GPL(opal_invalid_call);
> +
> +/* Convert a region of vmalloc memory to an opal sg list */
> +struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
> +					     unsigned long vmalloc_size)
> +{
> +	struct opal_sg_list *sg, *first = NULL;
> +	unsigned long i = 0;
> +
> +	sg = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +	if (!sg)
> +		goto nomem;
> +
> +	first = sg;
> +
> +	while (vmalloc_size > 0) {
> +		uint64_t data = vmalloc_to_pfn(vmalloc_addr) << PAGE_SHIFT;
> +		uint64_t length = min(vmalloc_size, PAGE_SIZE);
> +
> +		sg->entry[i].data = cpu_to_be64(data);
> +		sg->entry[i].length = cpu_to_be64(length);
> +		i++;
> +
> +		if (i >= SG_ENTRIES_PER_NODE) {
> +			struct opal_sg_list *next;
> +
> +			next = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +			if (!next)
> +				goto nomem;
> +
> +			sg->length = cpu_to_be64(
> +					i * sizeof(struct opal_sg_entry) + 16);
> +			i = 0;
> +			sg->next = cpu_to_be64(__pa(next));
> +			sg = next;
> +		}
> +
> +		vmalloc_addr += length;
> +		vmalloc_size -= length;
> +	}
> +
> +	sg->length = cpu_to_be64(i * sizeof(struct opal_sg_entry) + 16);
> +
> +	return first;
> +
> +nomem:
> +	pr_err("%s : Failed to allocate memory\n", __func__);
> +	opal_free_sg_list(first);
> +	return NULL;
> +}
> +
> +void opal_free_sg_list(struct opal_sg_list *sg)
> +{
> +	while (sg) {
> +		uint64_t next = be64_to_cpu(sg->next);
> +
> +		kfree(sg);
> +
> +		if (next)
> +			sg = __va(next);

This for this fix..

-Vasant

> +		else
> +			sg = NULL;
> +	}
> +}
>
Anton Blanchard April 22, 2014, 9:34 p.m. UTC | #2
Hi,

> Shouldn't we convert addr and id before passing to opal_dump_read()
> here ?

int64_t opal_dump_read(uint32_t dump_id, uint64_t buffer);

All arguments are passed via register, so byteswaping the arguments
would break it.

Anton
diff mbox

Patch

diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 1a752ac..afb0fed 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -41,14 +41,14 @@  struct opal_takeover_args {
  * size except the last one in the list to be as well.
  */
 struct opal_sg_entry {
-	void    *data;
-	long    length;
+	__be64 data;
+	__be64 length;
 };
 
-/* sg list */
+/* SG list */
 struct opal_sg_list {
-	unsigned long num_entries;
-	struct opal_sg_list *next;
+	__be64 length;
+	__be64 next;
 	struct opal_sg_entry entry[];
 };
 
@@ -929,6 +929,10 @@  extern int opal_resync_timebase(void);
 
 extern void opal_lpc_init(void);
 
+struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
+					     unsigned long vmalloc_size);
+void opal_free_sg_list(struct opal_sg_list *sg);
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* __OPAL_H */
diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c
index b9827b0..f0b4724 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 = {
 	.default_attrs = dump_default_attrs,
 };
 
-static void free_dump_sg_list(struct opal_sg_list *list)
-{
-	struct opal_sg_list *sg1;
-	while (list) {
-		sg1 = list->next;
-		kfree(list);
-		list = sg1;
-	}
-	list = NULL;
-}
-
-static struct opal_sg_list *dump_data_to_sglist(struct dump_obj *dump)
-{
-	struct opal_sg_list *sg1, *list = NULL;
-	void *addr;
-	int64_t size;
-
-	addr = dump->buffer;
-	size = dump->size;
-
-	sg1 = kzalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!sg1)
-		goto nomem;
-
-	list = sg1;
-	sg1->num_entries = 0;
-	while (size > 0) {
-		/* Translate virtual address to physical address */
-		sg1->entry[sg1->num_entries].data =
-			(void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT);
-
-		if (size > PAGE_SIZE)
-			sg1->entry[sg1->num_entries].length = PAGE_SIZE;
-		else
-			sg1->entry[sg1->num_entries].length = size;
-
-		sg1->num_entries++;
-		if (sg1->num_entries >= SG_ENTRIES_PER_NODE) {
-			sg1->next = kzalloc(PAGE_SIZE, GFP_KERNEL);
-			if (!sg1->next)
-				goto nomem;
-
-			sg1 = sg1->next;
-			sg1->num_entries = 0;
-		}
-		addr += PAGE_SIZE;
-		size -= PAGE_SIZE;
-	}
-	return list;
-
-nomem:
-	pr_err("%s : Failed to allocate memory\n", __func__);
-	free_dump_sg_list(list);
-	return NULL;
-}
-
-static void sglist_to_phy_addr(struct opal_sg_list *list)
-{
-	struct opal_sg_list *sg, *next;
-
-	for (sg = list; sg; sg = next) {
-		next = sg->next;
-		/* Don't translate NULL pointer for last entry */
-		if (sg->next)
-			sg->next = (struct opal_sg_list *)__pa(sg->next);
-		else
-			sg->next = NULL;
-
-		/* Convert num_entries to length */
-		sg->num_entries =
-			sg->num_entries * sizeof(struct opal_sg_entry) + 16;
-	}
-}
-
 static int64_t dump_read_info(uint32_t *id, uint32_t *size, uint32_t *type)
 {
 	int rc;
@@ -314,15 +240,12 @@  static int64_t dump_read_data(struct dump_obj *dump)
 	}
 
 	/* Generate SG list */
-	list = dump_data_to_sglist(dump);
+	list = opal_vmalloc_to_sg_list(dump->buffer, dump->size);
 	if (!list) {
 		rc = -ENOMEM;
 		goto out;
 	}
 
-	/* Translate sg list addr to real address */
-	sglist_to_phy_addr(list);
-
 	/* First entry address */
 	addr = __pa(list);
 
@@ -341,7 +264,7 @@  static int64_t dump_read_data(struct dump_obj *dump)
 			__func__, dump->id);
 
 	/* Free SG list */
-	free_dump_sg_list(list);
+	opal_free_sg_list(list);
 
 out:
 	return rc;
diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c
index 714ef97..0ae9e5f 100644
--- a/arch/powerpc/platforms/powernv/opal-flash.c
+++ b/arch/powerpc/platforms/powernv/opal-flash.c
@@ -79,9 +79,6 @@ 
 /* XXX: Assume candidate image size is <= 1GB */
 #define MAX_IMAGE_SIZE	0x40000000
 
-/* Flash sg list version */
-#define SG_LIST_VERSION (1UL)
-
 /* Image status */
 enum {
 	IMAGE_INVALID,
@@ -268,93 +265,11 @@  static ssize_t manage_store(struct kobject *kobj,
 }
 
 /*
- * Free sg list
- */
-static void free_sg_list(struct opal_sg_list *list)
-{
-	struct opal_sg_list *sg1;
-	while (list) {
-		sg1 = list->next;
-		kfree(list);
-		list = sg1;
-	}
-	list = NULL;
-}
-
-/*
- * Build candidate image scatter gather list
- *
- * list format:
- *   -----------------------------------
- *  |  VER (8) | Entry length in bytes  |
- *   -----------------------------------
- *  |  Pointer to next entry            |
- *   -----------------------------------
- *  |  Address of memory area 1         |
- *   -----------------------------------
- *  |  Length of memory area 1          |
- *   -----------------------------------
- *  |   .........                       |
- *   -----------------------------------
- *  |   .........                       |
- *   -----------------------------------
- *  |  Address of memory area N         |
- *   -----------------------------------
- *  |  Length of memory area N          |
- *   -----------------------------------
- */
-static struct opal_sg_list *image_data_to_sglist(void)
-{
-	struct opal_sg_list *sg1, *list = NULL;
-	void *addr;
-	int size;
-
-	addr = image_data.data;
-	size = image_data.size;
-
-	sg1 = kzalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!sg1)
-		return NULL;
-
-	list = sg1;
-	sg1->num_entries = 0;
-	while (size > 0) {
-		/* Translate virtual address to physical address */
-		sg1->entry[sg1->num_entries].data =
-			(void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT);
-
-		if (size > PAGE_SIZE)
-			sg1->entry[sg1->num_entries].length = PAGE_SIZE;
-		else
-			sg1->entry[sg1->num_entries].length = size;
-
-		sg1->num_entries++;
-		if (sg1->num_entries >= SG_ENTRIES_PER_NODE) {
-			sg1->next = kzalloc(PAGE_SIZE, GFP_KERNEL);
-			if (!sg1->next) {
-				pr_err("%s : Failed to allocate memory\n",
-				       __func__);
-				goto nomem;
-			}
-
-			sg1 = sg1->next;
-			sg1->num_entries = 0;
-		}
-		addr += PAGE_SIZE;
-		size -= PAGE_SIZE;
-	}
-	return list;
-nomem:
-	free_sg_list(list);
-	return NULL;
-}
-
-/*
  * OPAL update flash
  */
 static int opal_flash_update(int op)
 {
-	struct opal_sg_list *sg, *list, *next;
+	struct opal_sg_list *list;
 	unsigned long addr;
 	int64_t rc = OPAL_PARAMETER;
 
@@ -364,30 +279,13 @@  static int opal_flash_update(int op)
 		goto flash;
 	}
 
-	list = image_data_to_sglist();
+	list = opal_vmalloc_to_sg_list(image_data.data, image_data.size);
 	if (!list)
 		goto invalid_img;
 
 	/* First entry address */
 	addr = __pa(list);
 
-	/* Translate sg list address to absolute */
-	for (sg = list; sg; sg = next) {
-		next = sg->next;
-		/* Don't translate NULL pointer for last entry */
-		if (sg->next)
-			sg->next = (struct opal_sg_list *)__pa(sg->next);
-		else
-			sg->next = NULL;
-
-		/*
-		 * Convert num_entries to version/length format
-		 * to satisfy OPAL.
-		 */
-		sg->num_entries = (SG_LIST_VERSION << 56) |
-			(sg->num_entries * sizeof(struct opal_sg_entry) + 16);
-	}
-
 	pr_alert("FLASH: Image is %u bytes\n", image_data.size);
 	pr_alert("FLASH: Image update requested\n");
 	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 17cfc70..360ad80c 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -638,3 +638,66 @@  void opal_shutdown(void)
 
 /* Export this so that test modules can use it */
 EXPORT_SYMBOL_GPL(opal_invalid_call);
+
+/* Convert a region of vmalloc memory to an opal sg list */
+struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
+					     unsigned long vmalloc_size)
+{
+	struct opal_sg_list *sg, *first = NULL;
+	unsigned long i = 0;
+
+	sg = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!sg)
+		goto nomem;
+
+	first = sg;
+
+	while (vmalloc_size > 0) {
+		uint64_t data = vmalloc_to_pfn(vmalloc_addr) << PAGE_SHIFT;
+		uint64_t length = min(vmalloc_size, PAGE_SIZE);
+
+		sg->entry[i].data = cpu_to_be64(data);
+		sg->entry[i].length = cpu_to_be64(length);
+		i++;
+
+		if (i >= SG_ENTRIES_PER_NODE) {
+			struct opal_sg_list *next;
+
+			next = kzalloc(PAGE_SIZE, GFP_KERNEL);
+			if (!next)
+				goto nomem;
+
+			sg->length = cpu_to_be64(
+					i * sizeof(struct opal_sg_entry) + 16);
+			i = 0;
+			sg->next = cpu_to_be64(__pa(next));
+			sg = next;
+		}
+
+		vmalloc_addr += length;
+		vmalloc_size -= length;
+	}
+
+	sg->length = cpu_to_be64(i * sizeof(struct opal_sg_entry) + 16);
+
+	return first;
+
+nomem:
+	pr_err("%s : Failed to allocate memory\n", __func__);
+	opal_free_sg_list(first);
+	return NULL;
+}
+
+void opal_free_sg_list(struct opal_sg_list *sg)
+{
+	while (sg) {
+		uint64_t next = be64_to_cpu(sg->next);
+
+		kfree(sg);
+
+		if (next)
+			sg = __va(next);
+		else
+			sg = NULL;
+	}
+}