18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PowerNV OPAL Dump Interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2013,2014 IBM Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kobject.h> 98c2ecf20Sopenharmony_ci#include <linux/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 128c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/opal.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define DUMP_TYPE_FSP 0x01 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct dump_obj { 218c2ecf20Sopenharmony_ci struct kobject kobj; 228c2ecf20Sopenharmony_ci struct bin_attribute dump_attr; 238c2ecf20Sopenharmony_ci uint32_t id; /* becomes object name */ 248c2ecf20Sopenharmony_ci uint32_t type; 258c2ecf20Sopenharmony_ci uint32_t size; 268c2ecf20Sopenharmony_ci char *buffer; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci#define to_dump_obj(x) container_of(x, struct dump_obj, kobj) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct dump_attribute { 318c2ecf20Sopenharmony_ci struct attribute attr; 328c2ecf20Sopenharmony_ci ssize_t (*show)(struct dump_obj *dump, struct dump_attribute *attr, 338c2ecf20Sopenharmony_ci char *buf); 348c2ecf20Sopenharmony_ci ssize_t (*store)(struct dump_obj *dump, struct dump_attribute *attr, 358c2ecf20Sopenharmony_ci const char *buf, size_t count); 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci#define to_dump_attr(x) container_of(x, struct dump_attribute, attr) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic ssize_t dump_id_show(struct dump_obj *dump_obj, 408c2ecf20Sopenharmony_ci struct dump_attribute *attr, 418c2ecf20Sopenharmony_ci char *buf) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x\n", dump_obj->id); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const char* dump_type_to_string(uint32_t type) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci switch (type) { 498c2ecf20Sopenharmony_ci case 0x01: return "SP Dump"; 508c2ecf20Sopenharmony_ci case 0x02: return "System/Platform Dump"; 518c2ecf20Sopenharmony_ci case 0x03: return "SMA Dump"; 528c2ecf20Sopenharmony_ci default: return "unknown"; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic ssize_t dump_type_show(struct dump_obj *dump_obj, 578c2ecf20Sopenharmony_ci struct dump_attribute *attr, 588c2ecf20Sopenharmony_ci char *buf) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x %s\n", dump_obj->type, 628c2ecf20Sopenharmony_ci dump_type_to_string(dump_obj->type)); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic ssize_t dump_ack_show(struct dump_obj *dump_obj, 668c2ecf20Sopenharmony_ci struct dump_attribute *attr, 678c2ecf20Sopenharmony_ci char *buf) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci return sprintf(buf, "ack - acknowledge dump\n"); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Send acknowledgement to OPAL 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic int64_t dump_send_ack(uint32_t dump_id) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int rc; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci rc = opal_dump_ack(dump_id); 808c2ecf20Sopenharmony_ci if (rc) 818c2ecf20Sopenharmony_ci pr_warn("%s: Failed to send ack to Dump ID 0x%x (%d)\n", 828c2ecf20Sopenharmony_ci __func__, dump_id, rc); 838c2ecf20Sopenharmony_ci return rc; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic ssize_t dump_ack_store(struct dump_obj *dump_obj, 878c2ecf20Sopenharmony_ci struct dump_attribute *attr, 888c2ecf20Sopenharmony_ci const char *buf, 898c2ecf20Sopenharmony_ci size_t count) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci /* 928c2ecf20Sopenharmony_ci * Try to self remove this attribute. If we are successful, 938c2ecf20Sopenharmony_ci * delete the kobject itself. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci if (sysfs_remove_file_self(&dump_obj->kobj, &attr->attr)) { 968c2ecf20Sopenharmony_ci dump_send_ack(dump_obj->id); 978c2ecf20Sopenharmony_ci kobject_put(&dump_obj->kobj); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci return count; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* Attributes of a dump 1038c2ecf20Sopenharmony_ci * The binary attribute of the dump itself is dynamic 1048c2ecf20Sopenharmony_ci * due to the dynamic size of the dump 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_cistatic struct dump_attribute id_attribute = 1078c2ecf20Sopenharmony_ci __ATTR(id, 0444, dump_id_show, NULL); 1088c2ecf20Sopenharmony_cistatic struct dump_attribute type_attribute = 1098c2ecf20Sopenharmony_ci __ATTR(type, 0444, dump_type_show, NULL); 1108c2ecf20Sopenharmony_cistatic struct dump_attribute ack_attribute = 1118c2ecf20Sopenharmony_ci __ATTR(acknowledge, 0660, dump_ack_show, dump_ack_store); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic ssize_t init_dump_show(struct dump_obj *dump_obj, 1148c2ecf20Sopenharmony_ci struct dump_attribute *attr, 1158c2ecf20Sopenharmony_ci char *buf) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci return sprintf(buf, "1 - initiate Service Processor(FSP) dump\n"); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int64_t dump_fips_init(uint8_t type) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int rc; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci rc = opal_dump_init(type); 1258c2ecf20Sopenharmony_ci if (rc) 1268c2ecf20Sopenharmony_ci pr_warn("%s: Failed to initiate FSP dump (%d)\n", 1278c2ecf20Sopenharmony_ci __func__, rc); 1288c2ecf20Sopenharmony_ci return rc; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic ssize_t init_dump_store(struct dump_obj *dump_obj, 1328c2ecf20Sopenharmony_ci struct dump_attribute *attr, 1338c2ecf20Sopenharmony_ci const char *buf, 1348c2ecf20Sopenharmony_ci size_t count) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int rc; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci rc = dump_fips_init(DUMP_TYPE_FSP); 1398c2ecf20Sopenharmony_ci if (rc == OPAL_SUCCESS) 1408c2ecf20Sopenharmony_ci pr_info("%s: Initiated FSP dump\n", __func__); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return count; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic struct dump_attribute initiate_attribute = 1468c2ecf20Sopenharmony_ci __ATTR(initiate_dump, 0600, init_dump_show, init_dump_store); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct attribute *initiate_attrs[] = { 1498c2ecf20Sopenharmony_ci &initiate_attribute.attr, 1508c2ecf20Sopenharmony_ci NULL, 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic struct attribute_group initiate_attr_group = { 1548c2ecf20Sopenharmony_ci .attrs = initiate_attrs, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic struct kset *dump_kset; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic ssize_t dump_attr_show(struct kobject *kobj, 1608c2ecf20Sopenharmony_ci struct attribute *attr, 1618c2ecf20Sopenharmony_ci char *buf) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct dump_attribute *attribute; 1648c2ecf20Sopenharmony_ci struct dump_obj *dump; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci attribute = to_dump_attr(attr); 1678c2ecf20Sopenharmony_ci dump = to_dump_obj(kobj); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!attribute->show) 1708c2ecf20Sopenharmony_ci return -EIO; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return attribute->show(dump, attribute, buf); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic ssize_t dump_attr_store(struct kobject *kobj, 1768c2ecf20Sopenharmony_ci struct attribute *attr, 1778c2ecf20Sopenharmony_ci const char *buf, size_t len) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct dump_attribute *attribute; 1808c2ecf20Sopenharmony_ci struct dump_obj *dump; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci attribute = to_dump_attr(attr); 1838c2ecf20Sopenharmony_ci dump = to_dump_obj(kobj); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!attribute->store) 1868c2ecf20Sopenharmony_ci return -EIO; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return attribute->store(dump, attribute, buf, len); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic const struct sysfs_ops dump_sysfs_ops = { 1928c2ecf20Sopenharmony_ci .show = dump_attr_show, 1938c2ecf20Sopenharmony_ci .store = dump_attr_store, 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void dump_release(struct kobject *kobj) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct dump_obj *dump; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci dump = to_dump_obj(kobj); 2018c2ecf20Sopenharmony_ci vfree(dump->buffer); 2028c2ecf20Sopenharmony_ci kfree(dump); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic struct attribute *dump_default_attrs[] = { 2068c2ecf20Sopenharmony_ci &id_attribute.attr, 2078c2ecf20Sopenharmony_ci &type_attribute.attr, 2088c2ecf20Sopenharmony_ci &ack_attribute.attr, 2098c2ecf20Sopenharmony_ci NULL, 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic struct kobj_type dump_ktype = { 2138c2ecf20Sopenharmony_ci .sysfs_ops = &dump_sysfs_ops, 2148c2ecf20Sopenharmony_ci .release = &dump_release, 2158c2ecf20Sopenharmony_ci .default_attrs = dump_default_attrs, 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int64_t dump_read_info(uint32_t *dump_id, uint32_t *dump_size, uint32_t *dump_type) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci __be32 id, size, type; 2218c2ecf20Sopenharmony_ci int rc; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci type = cpu_to_be32(0xffffffff); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci rc = opal_dump_info2(&id, &size, &type); 2268c2ecf20Sopenharmony_ci if (rc == OPAL_PARAMETER) 2278c2ecf20Sopenharmony_ci rc = opal_dump_info(&id, &size); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (rc) { 2308c2ecf20Sopenharmony_ci pr_warn("%s: Failed to get dump info (%d)\n", 2318c2ecf20Sopenharmony_ci __func__, rc); 2328c2ecf20Sopenharmony_ci return rc; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci *dump_id = be32_to_cpu(id); 2368c2ecf20Sopenharmony_ci *dump_size = be32_to_cpu(size); 2378c2ecf20Sopenharmony_ci *dump_type = be32_to_cpu(type); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return rc; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int64_t dump_read_data(struct dump_obj *dump) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct opal_sg_list *list; 2458c2ecf20Sopenharmony_ci uint64_t addr; 2468c2ecf20Sopenharmony_ci int64_t rc; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Allocate memory */ 2498c2ecf20Sopenharmony_ci dump->buffer = vzalloc(PAGE_ALIGN(dump->size)); 2508c2ecf20Sopenharmony_ci if (!dump->buffer) { 2518c2ecf20Sopenharmony_ci pr_err("%s : Failed to allocate memory\n", __func__); 2528c2ecf20Sopenharmony_ci rc = -ENOMEM; 2538c2ecf20Sopenharmony_ci goto out; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Generate SG list */ 2578c2ecf20Sopenharmony_ci list = opal_vmalloc_to_sg_list(dump->buffer, dump->size); 2588c2ecf20Sopenharmony_ci if (!list) { 2598c2ecf20Sopenharmony_ci rc = -ENOMEM; 2608c2ecf20Sopenharmony_ci goto out; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* First entry address */ 2648c2ecf20Sopenharmony_ci addr = __pa(list); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Fetch data */ 2678c2ecf20Sopenharmony_ci rc = OPAL_BUSY_EVENT; 2688c2ecf20Sopenharmony_ci while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 2698c2ecf20Sopenharmony_ci rc = opal_dump_read(dump->id, addr); 2708c2ecf20Sopenharmony_ci if (rc == OPAL_BUSY_EVENT) { 2718c2ecf20Sopenharmony_ci opal_poll_events(NULL); 2728c2ecf20Sopenharmony_ci msleep(20); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) 2778c2ecf20Sopenharmony_ci pr_warn("%s: Extract dump failed for ID 0x%x\n", 2788c2ecf20Sopenharmony_ci __func__, dump->id); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Free SG list */ 2818c2ecf20Sopenharmony_ci opal_free_sg_list(list); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ciout: 2848c2ecf20Sopenharmony_ci return rc; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic ssize_t dump_attr_read(struct file *filep, struct kobject *kobj, 2888c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, 2898c2ecf20Sopenharmony_ci char *buffer, loff_t pos, size_t count) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci ssize_t rc; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci struct dump_obj *dump = to_dump_obj(kobj); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (!dump->buffer) { 2968c2ecf20Sopenharmony_ci rc = dump_read_data(dump); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) { 2998c2ecf20Sopenharmony_ci vfree(dump->buffer); 3008c2ecf20Sopenharmony_ci dump->buffer = NULL; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return -EIO; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci if (rc == OPAL_PARTIAL) { 3058c2ecf20Sopenharmony_ci /* On a partial read, we just return EIO 3068c2ecf20Sopenharmony_ci * and rely on userspace to ask us to try 3078c2ecf20Sopenharmony_ci * again. 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_ci pr_info("%s: Platform dump partially read. ID = 0x%x\n", 3108c2ecf20Sopenharmony_ci __func__, dump->id); 3118c2ecf20Sopenharmony_ci return -EIO; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci memcpy(buffer, dump->buffer + pos, count); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* You may think we could free the dump buffer now and retrieve 3188c2ecf20Sopenharmony_ci * it again later if needed, but due to current firmware limitation, 3198c2ecf20Sopenharmony_ci * that's not the case. So, once read into userspace once, 3208c2ecf20Sopenharmony_ci * we keep the dump around until it's acknowledged by userspace. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return count; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic void create_dump_obj(uint32_t id, size_t size, uint32_t type) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct dump_obj *dump; 3298c2ecf20Sopenharmony_ci int rc; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci dump = kzalloc(sizeof(*dump), GFP_KERNEL); 3328c2ecf20Sopenharmony_ci if (!dump) 3338c2ecf20Sopenharmony_ci return; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci dump->kobj.kset = dump_kset; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci kobject_init(&dump->kobj, &dump_ktype); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci sysfs_bin_attr_init(&dump->dump_attr); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci dump->dump_attr.attr.name = "dump"; 3428c2ecf20Sopenharmony_ci dump->dump_attr.attr.mode = 0400; 3438c2ecf20Sopenharmony_ci dump->dump_attr.size = size; 3448c2ecf20Sopenharmony_ci dump->dump_attr.read = dump_attr_read; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci dump->id = id; 3478c2ecf20Sopenharmony_ci dump->size = size; 3488c2ecf20Sopenharmony_ci dump->type = type; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci rc = kobject_add(&dump->kobj, NULL, "0x%x-0x%x", type, id); 3518c2ecf20Sopenharmony_ci if (rc) { 3528c2ecf20Sopenharmony_ci kobject_put(&dump->kobj); 3538c2ecf20Sopenharmony_ci return; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* 3578c2ecf20Sopenharmony_ci * As soon as the sysfs file for this dump is created/activated there is 3588c2ecf20Sopenharmony_ci * a chance the opal_errd daemon (or any userspace) might read and 3598c2ecf20Sopenharmony_ci * acknowledge the dump before kobject_uevent() is called. If that 3608c2ecf20Sopenharmony_ci * happens then there is a potential race between 3618c2ecf20Sopenharmony_ci * dump_ack_store->kobject_put() and kobject_uevent() which leads to a 3628c2ecf20Sopenharmony_ci * use-after-free of a kernfs object resulting in a kernel crash. 3638c2ecf20Sopenharmony_ci * 3648c2ecf20Sopenharmony_ci * To avoid that, we need to take a reference on behalf of the bin file, 3658c2ecf20Sopenharmony_ci * so that our reference remains valid while we call kobject_uevent(). 3668c2ecf20Sopenharmony_ci * We then drop our reference before exiting the function, leaving the 3678c2ecf20Sopenharmony_ci * bin file to drop the last reference (if it hasn't already). 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Take a reference for the bin file */ 3718c2ecf20Sopenharmony_ci kobject_get(&dump->kobj); 3728c2ecf20Sopenharmony_ci rc = sysfs_create_bin_file(&dump->kobj, &dump->dump_attr); 3738c2ecf20Sopenharmony_ci if (rc == 0) { 3748c2ecf20Sopenharmony_ci kobject_uevent(&dump->kobj, KOBJ_ADD); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci pr_info("%s: New platform dump. ID = 0x%x Size %u\n", 3778c2ecf20Sopenharmony_ci __func__, dump->id, dump->size); 3788c2ecf20Sopenharmony_ci } else { 3798c2ecf20Sopenharmony_ci /* Drop reference count taken for bin file */ 3808c2ecf20Sopenharmony_ci kobject_put(&dump->kobj); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Drop our reference */ 3848c2ecf20Sopenharmony_ci kobject_put(&dump->kobj); 3858c2ecf20Sopenharmony_ci return; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic irqreturn_t process_dump(int irq, void *data) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci int rc; 3918c2ecf20Sopenharmony_ci uint32_t dump_id, dump_size, dump_type; 3928c2ecf20Sopenharmony_ci char name[22]; 3938c2ecf20Sopenharmony_ci struct kobject *kobj; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci rc = dump_read_info(&dump_id, &dump_size, &dump_type); 3968c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS) 3978c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci sprintf(name, "0x%x-0x%x", dump_type, dump_id); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* we may get notified twice, let's handle 4028c2ecf20Sopenharmony_ci * that gracefully and not create two conflicting 4038c2ecf20Sopenharmony_ci * entries. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci kobj = kset_find_obj(dump_kset, name); 4068c2ecf20Sopenharmony_ci if (kobj) { 4078c2ecf20Sopenharmony_ci /* Drop reference added by kset_find_obj() */ 4088c2ecf20Sopenharmony_ci kobject_put(kobj); 4098c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci create_dump_obj(dump_id, dump_size, dump_type); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_civoid __init opal_platform_dump_init(void) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci int rc; 4208c2ecf20Sopenharmony_ci int dump_irq; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* ELOG not supported by firmware */ 4238c2ecf20Sopenharmony_ci if (!opal_check_token(OPAL_DUMP_READ)) 4248c2ecf20Sopenharmony_ci return; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci dump_kset = kset_create_and_add("dump", NULL, opal_kobj); 4278c2ecf20Sopenharmony_ci if (!dump_kset) { 4288c2ecf20Sopenharmony_ci pr_warn("%s: Failed to create dump kset\n", __func__); 4298c2ecf20Sopenharmony_ci return; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci rc = sysfs_create_group(&dump_kset->kobj, &initiate_attr_group); 4338c2ecf20Sopenharmony_ci if (rc) { 4348c2ecf20Sopenharmony_ci pr_warn("%s: Failed to create initiate dump attr group\n", 4358c2ecf20Sopenharmony_ci __func__); 4368c2ecf20Sopenharmony_ci kobject_put(&dump_kset->kobj); 4378c2ecf20Sopenharmony_ci return; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci dump_irq = opal_event_request(ilog2(OPAL_EVENT_DUMP_AVAIL)); 4418c2ecf20Sopenharmony_ci if (!dump_irq) { 4428c2ecf20Sopenharmony_ci pr_err("%s: Can't register OPAL event irq (%d)\n", 4438c2ecf20Sopenharmony_ci __func__, dump_irq); 4448c2ecf20Sopenharmony_ci return; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci rc = request_threaded_irq(dump_irq, NULL, process_dump, 4488c2ecf20Sopenharmony_ci IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 4498c2ecf20Sopenharmony_ci "opal-dump", NULL); 4508c2ecf20Sopenharmony_ci if (rc) { 4518c2ecf20Sopenharmony_ci pr_err("%s: Can't request OPAL event irq (%d)\n", 4528c2ecf20Sopenharmony_ci __func__, rc); 4538c2ecf20Sopenharmony_ci return; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (opal_check_token(OPAL_DUMP_RESEND)) 4578c2ecf20Sopenharmony_ci opal_dump_resend_notification(); 4588c2ecf20Sopenharmony_ci} 459