Skip to content

Commit

Permalink
powerpc/powernv: Create OPAL sglist helper functions and fix endian i…
Browse files Browse the repository at this point in the history
…ssues

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>
  • Loading branch information
Anton Blanchard authored and Benjamin Herrenschmidt committed Apr 28, 2014
1 parent 14ad0c5 commit 3441f04
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 188 deletions.
14 changes: 9 additions & 5 deletions arch/powerpc/include/asm/opal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
};

Expand Down Expand Up @@ -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 */
81 changes: 2 additions & 79 deletions arch/powerpc/platforms/powernv/opal-dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand Down
106 changes: 2 additions & 104 deletions arch/powerpc/platforms/powernv/opal-flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand All @@ -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");
Expand Down
63 changes: 63 additions & 0 deletions arch/powerpc/platforms/powernv/opal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

0 comments on commit 3441f04

Please sign in to comment.