Path: blob/master/arch/powerpc/platforms/powernv/opal-elog.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Error log support on PowerNV.3*4* Copyright 2013,2014 IBM Corp.5*/6#include <linux/kernel.h>7#include <linux/init.h>8#include <linux/interrupt.h>9#include <linux/of.h>10#include <linux/slab.h>11#include <linux/sysfs.h>12#include <linux/fs.h>13#include <linux/vmalloc.h>14#include <linux/fcntl.h>15#include <linux/kobject.h>16#include <linux/uaccess.h>17#include <asm/opal.h>1819struct elog_obj {20struct kobject kobj;21struct bin_attribute raw_attr;22uint64_t id;23uint64_t type;24size_t size;25char *buffer;26};27#define to_elog_obj(x) container_of(x, struct elog_obj, kobj)2829struct elog_attribute {30struct attribute attr;31ssize_t (*show)(struct elog_obj *elog, struct elog_attribute *attr,32char *buf);33ssize_t (*store)(struct elog_obj *elog, struct elog_attribute *attr,34const char *buf, size_t count);35};36#define to_elog_attr(x) container_of(x, struct elog_attribute, attr)3738static ssize_t elog_id_show(struct elog_obj *elog_obj,39struct elog_attribute *attr,40char *buf)41{42return sprintf(buf, "0x%llx\n", elog_obj->id);43}4445static const char *elog_type_to_string(uint64_t type)46{47switch (type) {48case 0: return "PEL";49default: return "unknown";50}51}5253static ssize_t elog_type_show(struct elog_obj *elog_obj,54struct elog_attribute *attr,55char *buf)56{57return sprintf(buf, "0x%llx %s\n",58elog_obj->type,59elog_type_to_string(elog_obj->type));60}6162static ssize_t elog_ack_show(struct elog_obj *elog_obj,63struct elog_attribute *attr,64char *buf)65{66return sprintf(buf, "ack - acknowledge log message\n");67}6869static ssize_t elog_ack_store(struct elog_obj *elog_obj,70struct elog_attribute *attr,71const char *buf,72size_t count)73{74/*75* Try to self remove this attribute. If we are successful,76* delete the kobject itself.77*/78if (sysfs_remove_file_self(&elog_obj->kobj, &attr->attr)) {79opal_send_ack_elog(elog_obj->id);80kobject_put(&elog_obj->kobj);81}82return count;83}8485static struct elog_attribute id_attribute =86__ATTR(id, 0444, elog_id_show, NULL);87static struct elog_attribute type_attribute =88__ATTR(type, 0444, elog_type_show, NULL);89static struct elog_attribute ack_attribute =90__ATTR(acknowledge, 0660, elog_ack_show, elog_ack_store);9192static struct kset *elog_kset;9394static ssize_t elog_attr_show(struct kobject *kobj,95struct attribute *attr,96char *buf)97{98struct elog_attribute *attribute;99struct elog_obj *elog;100101attribute = to_elog_attr(attr);102elog = to_elog_obj(kobj);103104if (!attribute->show)105return -EIO;106107return attribute->show(elog, attribute, buf);108}109110static ssize_t elog_attr_store(struct kobject *kobj,111struct attribute *attr,112const char *buf, size_t len)113{114struct elog_attribute *attribute;115struct elog_obj *elog;116117attribute = to_elog_attr(attr);118elog = to_elog_obj(kobj);119120if (!attribute->store)121return -EIO;122123return attribute->store(elog, attribute, buf, len);124}125126static const struct sysfs_ops elog_sysfs_ops = {127.show = elog_attr_show,128.store = elog_attr_store,129};130131static void elog_release(struct kobject *kobj)132{133struct elog_obj *elog;134135elog = to_elog_obj(kobj);136kfree(elog->buffer);137kfree(elog);138}139140static struct attribute *elog_default_attrs[] = {141&id_attribute.attr,142&type_attribute.attr,143&ack_attribute.attr,144NULL,145};146ATTRIBUTE_GROUPS(elog_default);147148static const struct kobj_type elog_ktype = {149.sysfs_ops = &elog_sysfs_ops,150.release = &elog_release,151.default_groups = elog_default_groups,152};153154/* Maximum size of a single log on FSP is 16KB */155#define OPAL_MAX_ERRLOG_SIZE 16384156157static ssize_t raw_attr_read(struct file *filep, struct kobject *kobj,158const struct bin_attribute *bin_attr,159char *buffer, loff_t pos, size_t count)160{161int opal_rc;162163struct elog_obj *elog = to_elog_obj(kobj);164165/* We may have had an error reading before, so let's retry */166if (!elog->buffer) {167elog->buffer = kzalloc(elog->size, GFP_KERNEL);168if (!elog->buffer)169return -EIO;170171opal_rc = opal_read_elog(__pa(elog->buffer),172elog->size, elog->id);173if (opal_rc != OPAL_SUCCESS) {174pr_err_ratelimited("ELOG: log read failed for log-id=%llx\n",175elog->id);176kfree(elog->buffer);177elog->buffer = NULL;178return -EIO;179}180}181182memcpy(buffer, elog->buffer + pos, count);183184return count;185}186187static void create_elog_obj(uint64_t id, size_t size, uint64_t type)188{189struct elog_obj *elog;190int rc;191192elog = kzalloc(sizeof(*elog), GFP_KERNEL);193if (!elog)194return;195196elog->kobj.kset = elog_kset;197198kobject_init(&elog->kobj, &elog_ktype);199200sysfs_bin_attr_init(&elog->raw_attr);201202elog->raw_attr.attr.name = "raw";203elog->raw_attr.attr.mode = 0400;204elog->raw_attr.size = size;205elog->raw_attr.read = raw_attr_read;206207elog->id = id;208elog->size = size;209elog->type = type;210211elog->buffer = kzalloc(elog->size, GFP_KERNEL);212213if (elog->buffer) {214rc = opal_read_elog(__pa(elog->buffer),215elog->size, elog->id);216if (rc != OPAL_SUCCESS) {217pr_err("ELOG: log read failed for log-id=%llx\n",218elog->id);219kfree(elog->buffer);220elog->buffer = NULL;221}222}223224rc = kobject_add(&elog->kobj, NULL, "0x%llx", id);225if (rc) {226kobject_put(&elog->kobj);227return;228}229230/*231* As soon as the sysfs file for this elog is created/activated there is232* a chance the opal_errd daemon (or any userspace) might read and233* acknowledge the elog before kobject_uevent() is called. If that234* happens then there is a potential race between235* elog_ack_store->kobject_put() and kobject_uevent() which leads to a236* use-after-free of a kernfs object resulting in a kernel crash.237*238* To avoid that, we need to take a reference on behalf of the bin file,239* so that our reference remains valid while we call kobject_uevent().240* We then drop our reference before exiting the function, leaving the241* bin file to drop the last reference (if it hasn't already).242*/243244/* Take a reference for the bin file */245kobject_get(&elog->kobj);246rc = sysfs_create_bin_file(&elog->kobj, &elog->raw_attr);247if (rc == 0) {248kobject_uevent(&elog->kobj, KOBJ_ADD);249} else {250/* Drop the reference taken for the bin file */251kobject_put(&elog->kobj);252}253254/* Drop our reference */255kobject_put(&elog->kobj);256257return;258}259260static irqreturn_t elog_event(int irq, void *data)261{262__be64 size;263__be64 id;264__be64 type;265uint64_t elog_size;266uint64_t log_id;267uint64_t elog_type;268int rc;269char name[2+16+1];270struct kobject *kobj;271272rc = opal_get_elog_size(&id, &size, &type);273if (rc != OPAL_SUCCESS) {274pr_err("ELOG: OPAL log info read failed\n");275return IRQ_HANDLED;276}277278elog_size = be64_to_cpu(size);279log_id = be64_to_cpu(id);280elog_type = be64_to_cpu(type);281282WARN_ON(elog_size > OPAL_MAX_ERRLOG_SIZE);283284if (elog_size >= OPAL_MAX_ERRLOG_SIZE)285elog_size = OPAL_MAX_ERRLOG_SIZE;286287sprintf(name, "0x%llx", log_id);288289/* we may get notified twice, let's handle290* that gracefully and not create two conflicting291* entries.292*/293kobj = kset_find_obj(elog_kset, name);294if (kobj) {295/* Drop reference added by kset_find_obj() */296kobject_put(kobj);297return IRQ_HANDLED;298}299300create_elog_obj(log_id, elog_size, elog_type);301302return IRQ_HANDLED;303}304305int __init opal_elog_init(void)306{307int rc = 0, irq;308309/* ELOG not supported by firmware */310if (!opal_check_token(OPAL_ELOG_READ))311return -1;312313elog_kset = kset_create_and_add("elog", NULL, opal_kobj);314if (!elog_kset) {315pr_warn("%s: failed to create elog kset\n", __func__);316return -1;317}318319irq = opal_event_request(ilog2(OPAL_EVENT_ERROR_LOG_AVAIL));320if (!irq) {321pr_err("%s: Can't register OPAL event irq (%d)\n",322__func__, irq);323return irq;324}325326rc = request_threaded_irq(irq, NULL, elog_event,327IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "opal-elog", NULL);328if (rc) {329pr_err("%s: Can't request OPAL event irq (%d)\n",330__func__, rc);331return rc;332}333334/* We are now ready to pull error logs from opal. */335if (opal_check_token(OPAL_ELOG_RESEND))336opal_resend_pending_logs();337338return 0;339}340341342