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-dump.c (10520B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * PowerNV OPAL Dump Interface
      4 *
      5 * Copyright 2013,2014 IBM Corp.
      6 */
      7
      8#include <linux/kobject.h>
      9#include <linux/mm.h>
     10#include <linux/slab.h>
     11#include <linux/vmalloc.h>
     12#include <linux/pagemap.h>
     13#include <linux/delay.h>
     14#include <linux/interrupt.h>
     15
     16#include <asm/opal.h>
     17
     18#define DUMP_TYPE_FSP	0x01
     19
     20struct dump_obj {
     21	struct kobject  kobj;
     22	struct bin_attribute dump_attr;
     23	uint32_t	id;  /* becomes object name */
     24	uint32_t	type;
     25	uint32_t	size;
     26	char		*buffer;
     27};
     28#define to_dump_obj(x) container_of(x, struct dump_obj, kobj)
     29
     30struct dump_attribute {
     31	struct attribute attr;
     32	ssize_t (*show)(struct dump_obj *dump, struct dump_attribute *attr,
     33			char *buf);
     34	ssize_t (*store)(struct dump_obj *dump, struct dump_attribute *attr,
     35			 const char *buf, size_t count);
     36};
     37#define to_dump_attr(x) container_of(x, struct dump_attribute, attr)
     38
     39static ssize_t dump_id_show(struct dump_obj *dump_obj,
     40			    struct dump_attribute *attr,
     41			    char *buf)
     42{
     43	return sprintf(buf, "0x%x\n", dump_obj->id);
     44}
     45
     46static const char* dump_type_to_string(uint32_t type)
     47{
     48	switch (type) {
     49	case 0x01: return "SP Dump";
     50	case 0x02: return "System/Platform Dump";
     51	case 0x03: return "SMA Dump";
     52	default: return "unknown";
     53	}
     54}
     55
     56static ssize_t dump_type_show(struct dump_obj *dump_obj,
     57			      struct dump_attribute *attr,
     58			      char *buf)
     59{
     60
     61	return sprintf(buf, "0x%x %s\n", dump_obj->type,
     62		       dump_type_to_string(dump_obj->type));
     63}
     64
     65static ssize_t dump_ack_show(struct dump_obj *dump_obj,
     66			     struct dump_attribute *attr,
     67			     char *buf)
     68{
     69	return sprintf(buf, "ack - acknowledge dump\n");
     70}
     71
     72/*
     73 * Send acknowledgement to OPAL
     74 */
     75static int64_t dump_send_ack(uint32_t dump_id)
     76{
     77	int rc;
     78
     79	rc = opal_dump_ack(dump_id);
     80	if (rc)
     81		pr_warn("%s: Failed to send ack to Dump ID 0x%x (%d)\n",
     82			__func__, dump_id, rc);
     83	return rc;
     84}
     85
     86static ssize_t dump_ack_store(struct dump_obj *dump_obj,
     87			      struct dump_attribute *attr,
     88			      const char *buf,
     89			      size_t count)
     90{
     91	/*
     92	 * Try to self remove this attribute. If we are successful,
     93	 * delete the kobject itself.
     94	 */
     95	if (sysfs_remove_file_self(&dump_obj->kobj, &attr->attr)) {
     96		dump_send_ack(dump_obj->id);
     97		kobject_put(&dump_obj->kobj);
     98	}
     99	return count;
    100}
    101
    102/* Attributes of a dump
    103 * The binary attribute of the dump itself is dynamic
    104 * due to the dynamic size of the dump
    105 */
    106static struct dump_attribute id_attribute =
    107	__ATTR(id, 0444, dump_id_show, NULL);
    108static struct dump_attribute type_attribute =
    109	__ATTR(type, 0444, dump_type_show, NULL);
    110static struct dump_attribute ack_attribute =
    111	__ATTR(acknowledge, 0660, dump_ack_show, dump_ack_store);
    112
    113static ssize_t init_dump_show(struct dump_obj *dump_obj,
    114			      struct dump_attribute *attr,
    115			      char *buf)
    116{
    117	return sprintf(buf, "1 - initiate Service Processor(FSP) dump\n");
    118}
    119
    120static int64_t dump_fips_init(uint8_t type)
    121{
    122	int rc;
    123
    124	rc = opal_dump_init(type);
    125	if (rc)
    126		pr_warn("%s: Failed to initiate FSP dump (%d)\n",
    127			__func__, rc);
    128	return rc;
    129}
    130
    131static ssize_t init_dump_store(struct dump_obj *dump_obj,
    132			       struct dump_attribute *attr,
    133			       const char *buf,
    134			       size_t count)
    135{
    136	int rc;
    137
    138	rc = dump_fips_init(DUMP_TYPE_FSP);
    139	if (rc == OPAL_SUCCESS)
    140		pr_info("%s: Initiated FSP dump\n", __func__);
    141
    142	return count;
    143}
    144
    145static struct dump_attribute initiate_attribute =
    146	__ATTR(initiate_dump, 0600, init_dump_show, init_dump_store);
    147
    148static struct attribute *initiate_attrs[] = {
    149	&initiate_attribute.attr,
    150	NULL,
    151};
    152
    153static const struct attribute_group initiate_attr_group = {
    154	.attrs = initiate_attrs,
    155};
    156
    157static struct kset *dump_kset;
    158
    159static ssize_t dump_attr_show(struct kobject *kobj,
    160			      struct attribute *attr,
    161			      char *buf)
    162{
    163	struct dump_attribute *attribute;
    164	struct dump_obj *dump;
    165
    166	attribute = to_dump_attr(attr);
    167	dump = to_dump_obj(kobj);
    168
    169	if (!attribute->show)
    170		return -EIO;
    171
    172	return attribute->show(dump, attribute, buf);
    173}
    174
    175static ssize_t dump_attr_store(struct kobject *kobj,
    176			       struct attribute *attr,
    177			       const char *buf, size_t len)
    178{
    179	struct dump_attribute *attribute;
    180	struct dump_obj *dump;
    181
    182	attribute = to_dump_attr(attr);
    183	dump = to_dump_obj(kobj);
    184
    185	if (!attribute->store)
    186		return -EIO;
    187
    188	return attribute->store(dump, attribute, buf, len);
    189}
    190
    191static const struct sysfs_ops dump_sysfs_ops = {
    192	.show = dump_attr_show,
    193	.store = dump_attr_store,
    194};
    195
    196static void dump_release(struct kobject *kobj)
    197{
    198	struct dump_obj *dump;
    199
    200	dump = to_dump_obj(kobj);
    201	vfree(dump->buffer);
    202	kfree(dump);
    203}
    204
    205static struct attribute *dump_default_attrs[] = {
    206	&id_attribute.attr,
    207	&type_attribute.attr,
    208	&ack_attribute.attr,
    209	NULL,
    210};
    211ATTRIBUTE_GROUPS(dump_default);
    212
    213static struct kobj_type dump_ktype = {
    214	.sysfs_ops = &dump_sysfs_ops,
    215	.release = &dump_release,
    216	.default_groups = dump_default_groups,
    217};
    218
    219static int64_t dump_read_info(uint32_t *dump_id, uint32_t *dump_size, uint32_t *dump_type)
    220{
    221	__be32 id, size, type;
    222	int rc;
    223
    224	type = cpu_to_be32(0xffffffff);
    225
    226	rc = opal_dump_info2(&id, &size, &type);
    227	if (rc == OPAL_PARAMETER)
    228		rc = opal_dump_info(&id, &size);
    229
    230	if (rc) {
    231		pr_warn("%s: Failed to get dump info (%d)\n",
    232			__func__, rc);
    233		return rc;
    234	}
    235
    236	*dump_id = be32_to_cpu(id);
    237	*dump_size = be32_to_cpu(size);
    238	*dump_type = be32_to_cpu(type);
    239
    240	return rc;
    241}
    242
    243static int64_t dump_read_data(struct dump_obj *dump)
    244{
    245	struct opal_sg_list *list;
    246	uint64_t addr;
    247	int64_t rc;
    248
    249	/* Allocate memory */
    250	dump->buffer = vzalloc(PAGE_ALIGN(dump->size));
    251	if (!dump->buffer) {
    252		pr_err("%s : Failed to allocate memory\n", __func__);
    253		rc = -ENOMEM;
    254		goto out;
    255	}
    256
    257	/* Generate SG list */
    258	list = opal_vmalloc_to_sg_list(dump->buffer, dump->size);
    259	if (!list) {
    260		rc = -ENOMEM;
    261		goto out;
    262	}
    263
    264	/* First entry address */
    265	addr = __pa(list);
    266
    267	/* Fetch data */
    268	rc = OPAL_BUSY_EVENT;
    269	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
    270		rc = opal_dump_read(dump->id, addr);
    271		if (rc == OPAL_BUSY_EVENT) {
    272			opal_poll_events(NULL);
    273			msleep(20);
    274		}
    275	}
    276
    277	if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL)
    278		pr_warn("%s: Extract dump failed for ID 0x%x\n",
    279			__func__, dump->id);
    280
    281	/* Free SG list */
    282	opal_free_sg_list(list);
    283
    284out:
    285	return rc;
    286}
    287
    288static ssize_t dump_attr_read(struct file *filep, struct kobject *kobj,
    289			      struct bin_attribute *bin_attr,
    290			      char *buffer, loff_t pos, size_t count)
    291{
    292	ssize_t rc;
    293
    294	struct dump_obj *dump = to_dump_obj(kobj);
    295
    296	if (!dump->buffer) {
    297		rc = dump_read_data(dump);
    298
    299		if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) {
    300			vfree(dump->buffer);
    301			dump->buffer = NULL;
    302
    303			return -EIO;
    304		}
    305		if (rc == OPAL_PARTIAL) {
    306			/* On a partial read, we just return EIO
    307			 * and rely on userspace to ask us to try
    308			 * again.
    309			 */
    310			pr_info("%s: Platform dump partially read. ID = 0x%x\n",
    311				__func__, dump->id);
    312			return -EIO;
    313		}
    314	}
    315
    316	memcpy(buffer, dump->buffer + pos, count);
    317
    318	/* You may think we could free the dump buffer now and retrieve
    319	 * it again later if needed, but due to current firmware limitation,
    320	 * that's not the case. So, once read into userspace once,
    321	 * we keep the dump around until it's acknowledged by userspace.
    322	 */
    323
    324	return count;
    325}
    326
    327static void create_dump_obj(uint32_t id, size_t size, uint32_t type)
    328{
    329	struct dump_obj *dump;
    330	int rc;
    331
    332	dump = kzalloc(sizeof(*dump), GFP_KERNEL);
    333	if (!dump)
    334		return;
    335
    336	dump->kobj.kset = dump_kset;
    337
    338	kobject_init(&dump->kobj, &dump_ktype);
    339
    340	sysfs_bin_attr_init(&dump->dump_attr);
    341
    342	dump->dump_attr.attr.name = "dump";
    343	dump->dump_attr.attr.mode = 0400;
    344	dump->dump_attr.size = size;
    345	dump->dump_attr.read = dump_attr_read;
    346
    347	dump->id = id;
    348	dump->size = size;
    349	dump->type = type;
    350
    351	rc = kobject_add(&dump->kobj, NULL, "0x%x-0x%x", type, id);
    352	if (rc) {
    353		kobject_put(&dump->kobj);
    354		return;
    355	}
    356
    357	/*
    358	 * As soon as the sysfs file for this dump is created/activated there is
    359	 * a chance the opal_errd daemon (or any userspace) might read and
    360	 * acknowledge the dump before kobject_uevent() is called. If that
    361	 * happens then there is a potential race between
    362	 * dump_ack_store->kobject_put() and kobject_uevent() which leads to a
    363	 * use-after-free of a kernfs object resulting in a kernel crash.
    364	 *
    365	 * To avoid that, we need to take a reference on behalf of the bin file,
    366	 * so that our reference remains valid while we call kobject_uevent().
    367	 * We then drop our reference before exiting the function, leaving the
    368	 * bin file to drop the last reference (if it hasn't already).
    369	 */
    370
    371	/* Take a reference for the bin file */
    372	kobject_get(&dump->kobj);
    373	rc = sysfs_create_bin_file(&dump->kobj, &dump->dump_attr);
    374	if (rc == 0) {
    375		kobject_uevent(&dump->kobj, KOBJ_ADD);
    376
    377		pr_info("%s: New platform dump. ID = 0x%x Size %u\n",
    378			__func__, dump->id, dump->size);
    379	} else {
    380		/* Drop reference count taken for bin file */
    381		kobject_put(&dump->kobj);
    382	}
    383
    384	/* Drop our reference */
    385	kobject_put(&dump->kobj);
    386	return;
    387}
    388
    389static irqreturn_t process_dump(int irq, void *data)
    390{
    391	int rc;
    392	uint32_t dump_id, dump_size, dump_type;
    393	char name[22];
    394	struct kobject *kobj;
    395
    396	rc = dump_read_info(&dump_id, &dump_size, &dump_type);
    397	if (rc != OPAL_SUCCESS)
    398		return IRQ_HANDLED;
    399
    400	sprintf(name, "0x%x-0x%x", dump_type, dump_id);
    401
    402	/* we may get notified twice, let's handle
    403	 * that gracefully and not create two conflicting
    404	 * entries.
    405	 */
    406	kobj = kset_find_obj(dump_kset, name);
    407	if (kobj) {
    408		/* Drop reference added by kset_find_obj() */
    409		kobject_put(kobj);
    410		return IRQ_HANDLED;
    411	}
    412
    413	create_dump_obj(dump_id, dump_size, dump_type);
    414
    415	return IRQ_HANDLED;
    416}
    417
    418void __init opal_platform_dump_init(void)
    419{
    420	int rc;
    421	int dump_irq;
    422
    423	/* Dump not supported by firmware */
    424	if (!opal_check_token(OPAL_DUMP_READ))
    425		return;
    426
    427	dump_kset = kset_create_and_add("dump", NULL, opal_kobj);
    428	if (!dump_kset) {
    429		pr_warn("%s: Failed to create dump kset\n", __func__);
    430		return;
    431	}
    432
    433	rc = sysfs_create_group(&dump_kset->kobj, &initiate_attr_group);
    434	if (rc) {
    435		pr_warn("%s: Failed to create initiate dump attr group\n",
    436			__func__);
    437		kobject_put(&dump_kset->kobj);
    438		return;
    439	}
    440
    441	dump_irq = opal_event_request(ilog2(OPAL_EVENT_DUMP_AVAIL));
    442	if (!dump_irq) {
    443		pr_err("%s: Can't register OPAL event irq (%d)\n",
    444		       __func__, dump_irq);
    445		return;
    446	}
    447
    448	rc = request_threaded_irq(dump_irq, NULL, process_dump,
    449				IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
    450				"opal-dump", NULL);
    451	if (rc) {
    452		pr_err("%s: Can't request OPAL event irq (%d)\n",
    453		       __func__, rc);
    454		return;
    455	}
    456
    457	if (opal_check_token(OPAL_DUMP_RESEND))
    458		opal_dump_resend_notification();
    459}