/* 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; }
static int64_t dump_read_data(struct dump_obj *dump) { struct opal_sg_list *list; uint64_t addr; int64_t rc; /* Allocate memory */ dump->buffer = vzalloc(PAGE_ALIGN(dump->size)); if (!dump->buffer) { pr_err("%s : Failed to allocate memory\n", __func__); rc = -ENOMEM; goto out; } /* Generate SG list */ list = opal_vmalloc_to_sg_list(dump->buffer, dump->size); if (!list) { rc = -ENOMEM; goto out; } /* First entry address */ addr = __pa(list); /* Fetch data */ rc = OPAL_BUSY_EVENT; while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { rc = opal_dump_read(dump->id, addr); if (rc == OPAL_BUSY_EVENT) { opal_poll_events(NULL); msleep(20); } } if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) pr_warn("%s: Extract dump failed for ID 0x%x\n", __func__, dump->id); /* Free SG list */ opal_free_sg_list(list); out: return rc; }