--- zzzz-none-000/linux-5.4.213/fs/pstore/ram.c 2022-09-15 10:04:56.000000000 +0000 +++ miami-7690-761/linux-5.4.213/fs/pstore/ram.c 2024-05-29 11:20:02.000000000 +0000 @@ -38,8 +38,8 @@ module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400); MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); -static ulong ramoops_pmsg_size = MIN_MEM_SIZE; -module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400); +static char *ramoops_pmsg_size_str; +module_param_named(pmsg_size, ramoops_pmsg_size_str, charp, 0400); MODULE_PARM_DESC(pmsg_size, "size of user space message log"); static unsigned long long mem_address; @@ -73,14 +73,14 @@ struct persistent_ram_zone **dprzs; /* Oops dump zones */ struct persistent_ram_zone *cprz; /* Console zone */ struct persistent_ram_zone **fprzs; /* Ftrace zones */ - struct persistent_ram_zone *mprz; /* PMSG zone */ + struct persistent_ram_zone **mprzs; /* PMSG zone */ phys_addr_t phys_addr; unsigned long size; unsigned int memtype; size_t record_size; size_t console_size; size_t ftrace_size; - size_t pmsg_size; + size_t *pmsg_size; int dump_oops; u32 flags; struct persistent_ram_ecc_info ecc_info; @@ -162,6 +162,53 @@ return header_length; } +static ssize_t ramoops_pstore_copy(enum pstore_type_id type, u64 id, void **buf, + struct pstore_info *psinfo) +{ + struct ramoops_context *cxt = psinfo->data; + struct persistent_ram_zone *prz; + size_t size; + + switch (type) { + case PSTORE_TYPE_DMESG: + if (id >= cxt->max_dump_cnt) + return -EINVAL; + prz = cxt->dprzs[id]; + break; + case PSTORE_TYPE_CONSOLE: + prz = cxt->cprz; + break; + case PSTORE_TYPE_FTRACE: + prz = cxt->fprzs[id]; + break; + case PSTORE_TYPE_PMSG: + if (id >= cxt->pstore.num_pmsg) + return -EINVAL; + prz = cxt->mprzs[id]; + break; + default: + return -EINVAL; + } + + if (!prz) + return -ENODEV; + + persistent_ram_save_old(prz); + + size = persistent_ram_old_size(prz); + + if (size == 0) { + *buf = NULL; + return size; + } + + *buf = kmemdup(persistent_ram_old(prz), size, GFP_KERNEL); + if (*buf == NULL) + return -ENOMEM; + + return size; +} + static bool prz_ok(struct persistent_ram_zone *prz) { return !!prz && !!(persistent_ram_old_size(prz) + @@ -257,8 +304,13 @@ if (!prz_ok(prz) && !cxt->console_read_cnt++) prz = ramoops_get_next_prz(&cxt->cprz, 0 /* single */, record); - if (!prz_ok(prz) && !cxt->pmsg_read_cnt++) - prz = ramoops_get_next_prz(&cxt->mprz, 0 /* single */, record); + /* Find the next valid persistent_ram_zone for PMSG */ + while (cxt->pmsg_read_cnt < cxt->pstore.num_pmsg && !prz) { + prz = ramoops_get_next_prz(cxt->mprzs, cxt->pmsg_read_cnt++, + record); + if (!prz_ok(prz)) + continue; + } /* ftrace is last since it may want to dynamically allocate memory. */ if (!prz_ok(prz)) { @@ -439,9 +491,10 @@ if (record->type == PSTORE_TYPE_PMSG) { struct ramoops_context *cxt = record->psi->data; - if (!cxt->mprz) + if (!cxt->mprzs) return -ENOMEM; - return persistent_ram_write_user(cxt->mprz, buf, record->size); + return persistent_ram_write_user(cxt->mprzs[record->part], buf, + record->size); } return -EINVAL; @@ -467,7 +520,9 @@ prz = cxt->fprzs[record->id]; break; case PSTORE_TYPE_PMSG: - prz = cxt->mprz; + if (record->id >= cxt->pstore.num_pmsg) + return -EINVAL; + prz = cxt->mprzs[record->id]; break; default: return -EINVAL; @@ -487,6 +542,7 @@ .read = ramoops_pstore_read, .write = ramoops_pstore_write, .write_user = ramoops_pstore_write_user, + .copy = ramoops_pstore_copy, .erase = ramoops_pstore_erase, }, }; @@ -511,6 +567,49 @@ kfree(cxt->fprzs); cxt->max_ftrace_cnt = 0; } + + /* Free PMSG PRZs */ + if (cxt->mprzs) { + for (i = 0; i < cxt->pstore.num_pmsg; i++) + persistent_ram_free(cxt->mprzs[i]); + kfree(cxt->mprzs); + cxt->pstore.num_pmsg = 0; + } +} + +static int __ramoops_init_prz(const char *name, struct device *dev, + struct ramoops_context *cxt, + struct persistent_ram_zone **prz, + phys_addr_t *paddr, size_t sz, u32 sig, u32 flags, + char *label) +{ + if (!sz) + return 0; + + if (*paddr + sz - cxt->phys_addr > cxt->size) { + dev_err(dev, + "no room for %s mem region (0x%zx@0x%llx) in (0x%lx@0x%llx)\n", + name, sz, (unsigned long long)*paddr, cxt->size, + (unsigned long long)cxt->phys_addr); + return -ENOMEM; + } + + *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info, cxt->memtype, + flags, label); + if (IS_ERR(prz)) { + int err = PTR_ERR(prz); + + dev_err(dev, + "failed to request %s mem region (0x%zx@0x%llx): %d\n", + name, sz, (unsigned long long)*paddr, err); + + return err; + } + + *paddr += sz; + (*prz)->type = pstore_name_to_type(name); + + return 0; } static int ramoops_init_przs(const char *name, @@ -543,14 +642,14 @@ if (record_size == 0) { dev_err(dev, "%s record size == 0 (%zu / %u)\n", name, mem_sz, *cnt); - goto fail; + goto fail_mem; } } else { *cnt = mem_sz / record_size; if (*cnt == 0) { dev_err(dev, "%s record count == 0 (%zu / %zu)\n", name, mem_sz, record_size); - goto fail; + goto fail_mem; } } @@ -559,18 +658,18 @@ name, mem_sz, (unsigned long long)*paddr, cxt->size, (unsigned long long)cxt->phys_addr); - goto fail; + goto fail_mem; } zone_sz = mem_sz / *cnt; if (!zone_sz) { dev_err(dev, "%s zone size == 0\n", name); - goto fail; + goto fail_mem; } prz_ar = kcalloc(*cnt, sizeof(**przs), GFP_KERNEL); if (!prz_ar) - goto fail; + goto fail_mem; for (i = 0; i < *cnt; i++) { char *label; @@ -580,68 +679,81 @@ else label = kasprintf(GFP_KERNEL, "ramoops:%s(%d/%d)", name, i, *cnt - 1); - prz_ar[i] = persistent_ram_new(*paddr, zone_sz, sig, - &cxt->ecc_info, - cxt->memtype, flags, label); + err = __ramoops_init_prz(name, dev, cxt, &prz_ar[i], paddr, + zone_sz, sig, flags, label); kfree(label); - if (IS_ERR(prz_ar[i])) { - err = PTR_ERR(prz_ar[i]); - dev_err(dev, "failed to request %s mem region (0x%zx@0x%llx): %d\n", - name, record_size, - (unsigned long long)*paddr, err); - + if (err) { while (i > 0) { i--; persistent_ram_free(prz_ar[i]); } - kfree(prz_ar); - goto fail; + goto fail_prz; } - *paddr += zone_sz; - prz_ar[i]->type = pstore_name_to_type(name); } *przs = prz_ar; return 0; - -fail: +fail_prz: + kfree(prz_ar); +fail_mem: *cnt = 0; return err; } -static int ramoops_init_prz(const char *name, - struct device *dev, struct ramoops_context *cxt, - struct persistent_ram_zone **prz, - phys_addr_t *paddr, size_t sz, u32 sig) +static int ramoops_init_mprzs(struct device *dev, struct ramoops_context *cxt, + phys_addr_t *paddr, size_t total) { - char *label; + int err = -ENOMEM; + int i; - if (!sz) + if (!total) return 0; - if (*paddr + sz - cxt->phys_addr > cxt->size) { - dev_err(dev, "no room for %s mem region (0x%zx@0x%llx) in (0x%lx@0x%llx)\n", - name, sz, (unsigned long long)*paddr, - cxt->size, (unsigned long long)cxt->phys_addr); + if (*paddr + total - cxt->phys_addr > cxt->size) { + dev_err(dev, "no room for pmsg\n"); return -ENOMEM; } - label = kasprintf(GFP_KERNEL, "ramoops:%s", name); - *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info, - cxt->memtype, PRZ_FLAG_ZAP_OLD, label); - kfree(label); - if (IS_ERR(*prz)) { - int err = PTR_ERR(*prz); + cxt->mprzs = + kcalloc(cxt->pstore.num_pmsg, sizeof(*cxt->mprzs), GFP_KERNEL); + if (!cxt->mprzs) + return -ENOMEM; - dev_err(dev, "failed to request %s mem region (0x%zx@0x%llx): %d\n", - name, sz, (unsigned long long)*paddr, err); - return err; - } + for (i = 0; i < cxt->pstore.num_pmsg; i++) { + char *label; - *paddr += sz; - (*prz)->type = pstore_name_to_type(name); + label = kasprintf(GFP_KERNEL, "ramoops:pmsg(%d/%d)", i, + cxt->pstore.num_pmsg - 1); + err = __ramoops_init_prz("pmsg", dev, cxt, &cxt->mprzs[i], + paddr, cxt->pmsg_size[i], 0, 0, label); + if (err) { + while (i > 0) { + i--; + persistent_ram_free(cxt->mprzs[i]); + } + goto fail_mprzs; + } + } return 0; +fail_mprzs: + kfree(cxt->mprzs); + return err; +} + +static int ramoops_init_prz(const char *name, + struct device *dev, struct ramoops_context *cxt, + struct persistent_ram_zone **prz, + phys_addr_t *paddr, size_t sz, u32 sig) +{ + int err; + char *label; + + label = kasprintf(GFP_KERNEL, "ramoops:%s", name); + err = __ramoops_init_prz(name, dev, cxt, prz, paddr, sz, sig, + PRZ_FLAG_ZAP_OLD, label); + kfree(label); + return err; } static int ramoops_parse_dt_size(struct platform_device *pdev, @@ -673,7 +785,8 @@ struct device_node *parent_node; struct resource *res; u32 value; - int ret; + int ret, i; + unsigned int num_pmsg; dev_dbg(&pdev->dev, "using Device Tree\n"); @@ -699,12 +812,40 @@ parse_size("record-size", pdata->record_size); parse_size("console-size", pdata->console_size); parse_size("ftrace-size", pdata->ftrace_size); - parse_size("pmsg-size", pdata->pmsg_size); parse_size("ecc-size", pdata->ecc_info.ecc_size); parse_size("flags", pdata->flags); #undef parse_size + /* Parse pmesg size */ + if (!of_find_property(of_node, "pmsg-size", &num_pmsg)) + return 0; /* no data */ + + num_pmsg = num_pmsg / sizeof(u32); + + pdata->pmsg_size = devm_kzalloc( + &pdev->dev, sizeof(size_t) * (num_pmsg + 1), GFP_KERNEL); + if (!pdata->pmsg_size) + return -ENOMEM; + + for (i = 0; i < num_pmsg; i++) { + ret = of_property_read_u32_index(of_node, "pmsg-size", i, + &value); + if (ret) { + dev_warn(&pdev->dev, + "%s: failed to read pmsg-size[%d]: %d\n", + __func__, i, ret); + return -EINVAL; + } + + /* CHECK INT_MAX */ + if (value > INT_MAX) { + dev_err(&pdev->dev, "value %x > INT_MAX\n", value); + return -EOVERFLOW; + } + pdata->pmsg_size[i] = value; + } + /* * Some old Chromebooks relied on the kernel setting the * console_size and pmsg_size to the record size since that's @@ -721,13 +862,40 @@ !pdata->console_size && !pdata->ftrace_size && !pdata->pmsg_size && !pdata->ecc_info.ecc_size) { pdata->console_size = pdata->record_size; - pdata->pmsg_size = pdata->record_size; } of_node_put(parent_node); return 0; } +#define ADDR_STRING_SIZE 10 /* "0x00000000" */ +static int update_pmsg_size_mod_param(size_t *pmsg_size, int num) +{ + int i, len; + char str[ADDR_STRING_SIZE + 2]; + + /* string size + commas count + NULL-terminated */ + len = ADDR_STRING_SIZE * num + (num - 1) + 1; + + /* using commandline or already allocation buffer.*/ + if (ramoops_pmsg_size_str) + goto out; + + ramoops_pmsg_size_str = kzalloc(len, GFP_KERNEL); + if (!ramoops_pmsg_size_str) + return -ENOMEM; + + for (i = 0; i < num; i++) { + const char *format = i == (num - 1) ? "0x%x" : "0x%x,"; + + snprintf(str, sizeof(str), format, pmsg_size[i]); + strcat(ramoops_pmsg_size_str, str); + } +out: + return 0; +} +#undef ADDR_STRING_SIZE + static int ramoops_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -737,6 +905,8 @@ size_t dump_mem_sz; phys_addr_t paddr; int err = -EINVAL; + unsigned long pmsg_size_total = 0; + unsigned int num_pmsg = 0; /* * Only a single ramoops area allowed at a time, so fail extra @@ -775,8 +945,18 @@ pdata->console_size = rounddown_pow_of_two(pdata->console_size); if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size)) pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); - if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size)) - pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size); + + if (pdata->pmsg_size) { + unsigned long size; + for (size = pdata->pmsg_size[num_pmsg]; size != 0; + size = pdata->pmsg_size[++num_pmsg]) { + if (!is_power_of_2(size)) + pdata->pmsg_size[num_pmsg] = + rounddown_pow_of_two(size); + + pmsg_size_total += size; + } + } cxt->size = pdata->mem_size; cxt->phys_addr = pdata->mem_address; @@ -784,20 +964,29 @@ cxt->record_size = pdata->record_size; cxt->console_size = pdata->console_size; cxt->ftrace_size = pdata->ftrace_size; - cxt->pmsg_size = pdata->pmsg_size; cxt->dump_oops = pdata->dump_oops; cxt->flags = pdata->flags; cxt->ecc_info = pdata->ecc_info; + cxt->pstore.num_pmsg = num_pmsg; - paddr = cxt->phys_addr; + if (num_pmsg) { + cxt->pmsg_size = kcalloc(num_pmsg, sizeof(size_t), GFP_KERNEL); + if (!cxt->pmsg_size) { + err = -ENOMEM; + goto fail_out; + } + memcpy(cxt->pmsg_size, pdata->pmsg_size, + sizeof(size_t) * num_pmsg); + } - dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size - - cxt->pmsg_size; + paddr = cxt->phys_addr; + dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size - + pmsg_size_total; err = ramoops_init_przs("dmesg", dev, cxt, &cxt->dprzs, &paddr, dump_mem_sz, cxt->record_size, &cxt->max_dump_cnt, 0, 0); if (err) - goto fail_out; + goto fail_init_dprzs; err = ramoops_init_prz("console", dev, cxt, &cxt->cprz, &paddr, cxt->console_size, 0); @@ -815,10 +1004,10 @@ if (err) goto fail_init_fprz; - err = ramoops_init_prz("pmsg", dev, cxt, &cxt->mprz, &paddr, - cxt->pmsg_size, 0); + /* needs its own function because cxt->pmsg_size[] can be different for each slot */ + err = ramoops_init_mprzs(dev, cxt, &paddr, pmsg_size_total); if (err) - goto fail_init_mprz; + goto fail_init_mprzs; cxt->pstore.data = cxt; /* @@ -867,8 +1056,8 @@ record_size = pdata->record_size; dump_oops = pdata->dump_oops; ramoops_console_size = pdata->console_size; - ramoops_pmsg_size = pdata->pmsg_size; ramoops_ftrace_size = pdata->ftrace_size; + update_pmsg_size_mod_param(cxt->pmsg_size, cxt->pstore.num_pmsg); pr_info("using 0x%lx@0x%llx, ecc: %d\n", cxt->size, (unsigned long long)cxt->phys_addr, @@ -880,12 +1069,13 @@ kfree(cxt->pstore.buf); fail_clear: cxt->pstore.bufsize = 0; - persistent_ram_free(cxt->mprz); -fail_init_mprz: +fail_init_mprzs: fail_init_fprz: persistent_ram_free(cxt->cprz); fail_init_cprz: ramoops_free_przs(cxt); +fail_init_dprzs: + kfree(cxt->pmsg_size); fail_out: return err; } @@ -898,8 +1088,8 @@ kfree(cxt->pstore.buf); cxt->pstore.bufsize = 0; + kfree(cxt->pmsg_size); - persistent_ram_free(cxt->mprz); persistent_ram_free(cxt->cprz); ramoops_free_przs(cxt); @@ -922,10 +1112,52 @@ static inline void ramoops_unregister_dummy(void) { + struct ramoops_platform_data *pdata = dummy->dev.platform_data; + kfree(pdata->pmsg_size); platform_device_unregister(dummy); dummy = NULL; } +static unsigned long *parse_size_str(char *size_str) +{ + int i, ret; + unsigned long *size_array, count = 1; + + if (size_str) { + char *s = size_str; + + /* Necessary array size is the number of commas + 1 */ + for (; (s = strchr(s, ',')); s++) + count++; + } + + /* Add NULL-terminated */ + count++; + + size_array = kcalloc(count, sizeof(unsigned long), GFP_KERNEL); + if (!size_array) + goto out; + + if (size_str) { + for (i = 0; i < count; i++) { + int tmp; + + ret = get_option(&size_str, &tmp); + size_array[i] = tmp; + if (ret == 1) + break; + else if (ret != 2) { + size_array[i] = 0; + break; + } + } + } else + size_array[0] = MIN_MEM_SIZE; + +out: + return size_array; +} + static void __init ramoops_register_dummy(void) { struct ramoops_platform_data pdata; @@ -947,7 +1179,9 @@ pdata.record_size = record_size; pdata.console_size = ramoops_console_size; pdata.ftrace_size = ramoops_ftrace_size; - pdata.pmsg_size = ramoops_pmsg_size; + pdata.pmsg_size = parse_size_str(ramoops_pmsg_size_str); + if (!pdata.pmsg_size) + return; pdata.dump_oops = dump_oops; pdata.flags = RAMOOPS_FLAG_FTRACE_PER_CPU; @@ -962,8 +1196,8 @@ if (IS_ERR(dummy)) { pr_info("could not create platform device: %ld\n", PTR_ERR(dummy)); + kfree(pdata.pmsg_size); dummy = NULL; - ramoops_unregister_dummy(); } }