Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3441f04b authored by Anton Blanchard's avatar Anton Blanchard Committed by Benjamin Herrenschmidt
Browse files

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: default avatarAnton Blanchard <anton@samba.org>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 14ad0c58
Loading
Loading
Loading
Loading
+9 −5
Original line number Diff line number Diff line
@@ -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 */
+2 −79
Original line number Diff line number Diff line
@@ -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;
+2 −104
Original line number Diff line number Diff line
@@ -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,
@@ -271,94 +268,12 @@ static ssize_t manage_store(struct kobject *kobj,
	return count;
}

/*
 * 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;

@@ -368,30 +283,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");
+63 −0
Original line number Diff line number Diff line
@@ -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;
	}
}