cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

opal-elog.c (8021B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Error log support on PowerNV.
      4 *
      5 * Copyright 2013,2014 IBM Corp.
      6 */
      7#include <linux/kernel.h>
      8#include <linux/init.h>
      9#include <linux/interrupt.h>
     10#include <linux/of.h>
     11#include <linux/slab.h>
     12#include <linux/sysfs.h>
     13#include <linux/fs.h>
     14#include <linux/vmalloc.h>
     15#include <linux/fcntl.h>
     16#include <linux/kobject.h>
     17#include <linux/uaccess.h>
     18#include <asm/opal.h>
     19
     20struct elog_obj {
     21	struct kobject kobj;
     22	struct bin_attribute raw_attr;
     23	uint64_t id;
     24	uint64_t type;
     25	size_t size;
     26	char *buffer;
     27};
     28#define to_elog_obj(x) container_of(x, struct elog_obj, kobj)
     29
     30struct elog_attribute {
     31	struct attribute attr;
     32	ssize_t (*show)(struct elog_obj *elog, struct elog_attribute *attr,
     33			char *buf);
     34	ssize_t (*store)(struct elog_obj *elog, struct elog_attribute *attr,
     35			 const char *buf, size_t count);
     36};
     37#define to_elog_attr(x) container_of(x, struct elog_attribute, attr)
     38
     39static ssize_t elog_id_show(struct elog_obj *elog_obj,
     40			    struct elog_attribute *attr,
     41			    char *buf)
     42{
     43	return sprintf(buf, "0x%llx\n", elog_obj->id);
     44}
     45
     46static const char *elog_type_to_string(uint64_t type)
     47{
     48	switch (type) {
     49	case 0: return "PEL";
     50	default: return "unknown";
     51	}
     52}
     53
     54static ssize_t elog_type_show(struct elog_obj *elog_obj,
     55			      struct elog_attribute *attr,
     56			      char *buf)
     57{
     58	return sprintf(buf, "0x%llx %s\n",
     59		       elog_obj->type,
     60		       elog_type_to_string(elog_obj->type));
     61}
     62
     63static ssize_t elog_ack_show(struct elog_obj *elog_obj,
     64			     struct elog_attribute *attr,
     65			     char *buf)
     66{
     67	return sprintf(buf, "ack - acknowledge log message\n");
     68}
     69
     70static ssize_t elog_ack_store(struct elog_obj *elog_obj,
     71			      struct elog_attribute *attr,
     72			      const char *buf,
     73			      size_t count)
     74{
     75	/*
     76	 * Try to self remove this attribute. If we are successful,
     77	 * delete the kobject itself.
     78	 */
     79	if (sysfs_remove_file_self(&elog_obj->kobj, &attr->attr)) {
     80		opal_send_ack_elog(elog_obj->id);
     81		kobject_put(&elog_obj->kobj);
     82	}
     83	return count;
     84}
     85
     86static struct elog_attribute id_attribute =
     87	__ATTR(id, 0444, elog_id_show, NULL);
     88static struct elog_attribute type_attribute =
     89	__ATTR(type, 0444, elog_type_show, NULL);
     90static struct elog_attribute ack_attribute =
     91	__ATTR(acknowledge, 0660, elog_ack_show, elog_ack_store);
     92
     93static struct kset *elog_kset;
     94
     95static ssize_t elog_attr_show(struct kobject *kobj,
     96			      struct attribute *attr,
     97			      char *buf)
     98{
     99	struct elog_attribute *attribute;
    100	struct elog_obj *elog;
    101
    102	attribute = to_elog_attr(attr);
    103	elog = to_elog_obj(kobj);
    104
    105	if (!attribute->show)
    106		return -EIO;
    107
    108	return attribute->show(elog, attribute, buf);
    109}
    110
    111static ssize_t elog_attr_store(struct kobject *kobj,
    112			       struct attribute *attr,
    113			       const char *buf, size_t len)
    114{
    115	struct elog_attribute *attribute;
    116	struct elog_obj *elog;
    117
    118	attribute = to_elog_attr(attr);
    119	elog = to_elog_obj(kobj);
    120
    121	if (!attribute->store)
    122		return -EIO;
    123
    124	return attribute->store(elog, attribute, buf, len);
    125}
    126
    127static const struct sysfs_ops elog_sysfs_ops = {
    128	.show = elog_attr_show,
    129	.store = elog_attr_store,
    130};
    131
    132static void elog_release(struct kobject *kobj)
    133{
    134	struct elog_obj *elog;
    135
    136	elog = to_elog_obj(kobj);
    137	kfree(elog->buffer);
    138	kfree(elog);
    139}
    140
    141static struct attribute *elog_default_attrs[] = {
    142	&id_attribute.attr,
    143	&type_attribute.attr,
    144	&ack_attribute.attr,
    145	NULL,
    146};
    147ATTRIBUTE_GROUPS(elog_default);
    148
    149static struct kobj_type elog_ktype = {
    150	.sysfs_ops = &elog_sysfs_ops,
    151	.release = &elog_release,
    152	.default_groups = elog_default_groups,
    153};
    154
    155/* Maximum size of a single log on FSP is 16KB */
    156#define OPAL_MAX_ERRLOG_SIZE	16384
    157
    158static ssize_t raw_attr_read(struct file *filep, struct kobject *kobj,
    159			     struct bin_attribute *bin_attr,
    160			     char *buffer, loff_t pos, size_t count)
    161{
    162	int opal_rc;
    163
    164	struct elog_obj *elog = to_elog_obj(kobj);
    165
    166	/* We may have had an error reading before, so let's retry */
    167	if (!elog->buffer) {
    168		elog->buffer = kzalloc(elog->size, GFP_KERNEL);
    169		if (!elog->buffer)
    170			return -EIO;
    171
    172		opal_rc = opal_read_elog(__pa(elog->buffer),
    173					 elog->size, elog->id);
    174		if (opal_rc != OPAL_SUCCESS) {
    175			pr_err_ratelimited("ELOG: log read failed for log-id=%llx\n",
    176					   elog->id);
    177			kfree(elog->buffer);
    178			elog->buffer = NULL;
    179			return -EIO;
    180		}
    181	}
    182
    183	memcpy(buffer, elog->buffer + pos, count);
    184
    185	return count;
    186}
    187
    188static void create_elog_obj(uint64_t id, size_t size, uint64_t type)
    189{
    190	struct elog_obj *elog;
    191	int rc;
    192
    193	elog = kzalloc(sizeof(*elog), GFP_KERNEL);
    194	if (!elog)
    195		return;
    196
    197	elog->kobj.kset = elog_kset;
    198
    199	kobject_init(&elog->kobj, &elog_ktype);
    200
    201	sysfs_bin_attr_init(&elog->raw_attr);
    202
    203	elog->raw_attr.attr.name = "raw";
    204	elog->raw_attr.attr.mode = 0400;
    205	elog->raw_attr.size = size;
    206	elog->raw_attr.read = raw_attr_read;
    207
    208	elog->id = id;
    209	elog->size = size;
    210	elog->type = type;
    211
    212	elog->buffer = kzalloc(elog->size, GFP_KERNEL);
    213
    214	if (elog->buffer) {
    215		rc = opal_read_elog(__pa(elog->buffer),
    216					 elog->size, elog->id);
    217		if (rc != OPAL_SUCCESS) {
    218			pr_err("ELOG: log read failed for log-id=%llx\n",
    219			       elog->id);
    220			kfree(elog->buffer);
    221			elog->buffer = NULL;
    222		}
    223	}
    224
    225	rc = kobject_add(&elog->kobj, NULL, "0x%llx", id);
    226	if (rc) {
    227		kobject_put(&elog->kobj);
    228		return;
    229	}
    230
    231	/*
    232	 * As soon as the sysfs file for this elog is created/activated there is
    233	 * a chance the opal_errd daemon (or any userspace) might read and
    234	 * acknowledge the elog before kobject_uevent() is called. If that
    235	 * happens then there is a potential race between
    236	 * elog_ack_store->kobject_put() and kobject_uevent() which leads to a
    237	 * use-after-free of a kernfs object resulting in a kernel crash.
    238	 *
    239	 * To avoid that, we need to take a reference on behalf of the bin file,
    240	 * so that our reference remains valid while we call kobject_uevent().
    241	 * We then drop our reference before exiting the function, leaving the
    242	 * bin file to drop the last reference (if it hasn't already).
    243	 */
    244
    245	/* Take a reference for the bin file */
    246	kobject_get(&elog->kobj);
    247	rc = sysfs_create_bin_file(&elog->kobj, &elog->raw_attr);
    248	if (rc == 0) {
    249		kobject_uevent(&elog->kobj, KOBJ_ADD);
    250	} else {
    251		/* Drop the reference taken for the bin file */
    252		kobject_put(&elog->kobj);
    253	}
    254
    255	/* Drop our reference */
    256	kobject_put(&elog->kobj);
    257
    258	return;
    259}
    260
    261static irqreturn_t elog_event(int irq, void *data)
    262{
    263	__be64 size;
    264	__be64 id;
    265	__be64 type;
    266	uint64_t elog_size;
    267	uint64_t log_id;
    268	uint64_t elog_type;
    269	int rc;
    270	char name[2+16+1];
    271	struct kobject *kobj;
    272
    273	rc = opal_get_elog_size(&id, &size, &type);
    274	if (rc != OPAL_SUCCESS) {
    275		pr_err("ELOG: OPAL log info read failed\n");
    276		return IRQ_HANDLED;
    277	}
    278
    279	elog_size = be64_to_cpu(size);
    280	log_id = be64_to_cpu(id);
    281	elog_type = be64_to_cpu(type);
    282
    283	WARN_ON(elog_size > OPAL_MAX_ERRLOG_SIZE);
    284
    285	if (elog_size >= OPAL_MAX_ERRLOG_SIZE)
    286		elog_size  =  OPAL_MAX_ERRLOG_SIZE;
    287
    288	sprintf(name, "0x%llx", log_id);
    289
    290	/* we may get notified twice, let's handle
    291	 * that gracefully and not create two conflicting
    292	 * entries.
    293	 */
    294	kobj = kset_find_obj(elog_kset, name);
    295	if (kobj) {
    296		/* Drop reference added by kset_find_obj() */
    297		kobject_put(kobj);
    298		return IRQ_HANDLED;
    299	}
    300
    301	create_elog_obj(log_id, elog_size, elog_type);
    302
    303	return IRQ_HANDLED;
    304}
    305
    306int __init opal_elog_init(void)
    307{
    308	int rc = 0, irq;
    309
    310	/* ELOG not supported by firmware */
    311	if (!opal_check_token(OPAL_ELOG_READ))
    312		return -1;
    313
    314	elog_kset = kset_create_and_add("elog", NULL, opal_kobj);
    315	if (!elog_kset) {
    316		pr_warn("%s: failed to create elog kset\n", __func__);
    317		return -1;
    318	}
    319
    320	irq = opal_event_request(ilog2(OPAL_EVENT_ERROR_LOG_AVAIL));
    321	if (!irq) {
    322		pr_err("%s: Can't register OPAL event irq (%d)\n",
    323		       __func__, irq);
    324		return irq;
    325	}
    326
    327	rc = request_threaded_irq(irq, NULL, elog_event,
    328			IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "opal-elog", NULL);
    329	if (rc) {
    330		pr_err("%s: Can't request OPAL event irq (%d)\n",
    331		       __func__, rc);
    332		return rc;
    333	}
    334
    335	/* We are now ready to pull error logs from opal. */
    336	if (opal_check_token(OPAL_ELOG_RESEND))
    337		opal_resend_pending_logs();
    338
    339	return 0;
    340}