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

p9_sbe.c (4127B)


      1// SPDX-License-Identifier: GPL-2.0+
      2// Copyright IBM Corp 2019
      3
      4#include <linux/device.h>
      5#include <linux/errno.h>
      6#include <linux/slab.h>
      7#include <linux/fsi-occ.h>
      8#include <linux/mm.h>
      9#include <linux/module.h>
     10#include <linux/mutex.h>
     11#include <linux/platform_device.h>
     12#include <linux/string.h>
     13#include <linux/sysfs.h>
     14
     15#include "common.h"
     16
     17struct p9_sbe_occ {
     18	struct occ occ;
     19	bool sbe_error;
     20	void *ffdc;
     21	size_t ffdc_len;
     22	size_t ffdc_size;
     23	struct mutex sbe_error_lock;	/* lock access to ffdc data */
     24	struct device *sbe;
     25};
     26
     27#define to_p9_sbe_occ(x)	container_of((x), struct p9_sbe_occ, occ)
     28
     29static ssize_t ffdc_read(struct file *filp, struct kobject *kobj,
     30			 struct bin_attribute *battr, char *buf, loff_t pos,
     31			 size_t count)
     32{
     33	ssize_t rc = 0;
     34	struct occ *occ = dev_get_drvdata(kobj_to_dev(kobj));
     35	struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
     36
     37	mutex_lock(&ctx->sbe_error_lock);
     38	if (ctx->sbe_error) {
     39		rc = memory_read_from_buffer(buf, count, &pos, ctx->ffdc,
     40					     ctx->ffdc_len);
     41		if (pos >= ctx->ffdc_len)
     42			ctx->sbe_error = false;
     43	}
     44	mutex_unlock(&ctx->sbe_error_lock);
     45
     46	return rc;
     47}
     48static BIN_ATTR_RO(ffdc, OCC_MAX_RESP_WORDS * 4);
     49
     50static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp,
     51				 size_t resp_len)
     52{
     53	bool notify = false;
     54
     55	mutex_lock(&ctx->sbe_error_lock);
     56	if (!ctx->sbe_error) {
     57		if (resp_len > ctx->ffdc_size) {
     58			if (ctx->ffdc)
     59				kvfree(ctx->ffdc);
     60			ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL);
     61			if (!ctx->ffdc) {
     62				ctx->ffdc_len = 0;
     63				ctx->ffdc_size = 0;
     64				goto done;
     65			}
     66
     67			ctx->ffdc_size = resp_len;
     68		}
     69
     70		notify = true;
     71		ctx->sbe_error = true;
     72		ctx->ffdc_len = resp_len;
     73		memcpy(ctx->ffdc, resp, resp_len);
     74	}
     75
     76done:
     77	mutex_unlock(&ctx->sbe_error_lock);
     78	return notify;
     79}
     80
     81static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len,
     82			       void *resp, size_t resp_len)
     83{
     84	struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
     85	int rc;
     86
     87	rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len);
     88	if (rc < 0) {
     89		if (resp_len) {
     90			if (p9_sbe_occ_save_ffdc(ctx, resp, resp_len))
     91				sysfs_notify(&occ->bus_dev->kobj, NULL,
     92					     bin_attr_ffdc.attr.name);
     93		}
     94
     95		return rc;
     96	}
     97
     98	switch (((struct occ_response *)resp)->return_status) {
     99	case OCC_RESP_CMD_IN_PRG:
    100		rc = -ETIMEDOUT;
    101		break;
    102	case OCC_RESP_SUCCESS:
    103		rc = 0;
    104		break;
    105	case OCC_RESP_CMD_INVAL:
    106	case OCC_RESP_CMD_LEN_INVAL:
    107	case OCC_RESP_DATA_INVAL:
    108	case OCC_RESP_CHKSUM_ERR:
    109		rc = -EINVAL;
    110		break;
    111	case OCC_RESP_INT_ERR:
    112	case OCC_RESP_BAD_STATE:
    113	case OCC_RESP_CRIT_EXCEPT:
    114	case OCC_RESP_CRIT_INIT:
    115	case OCC_RESP_CRIT_WATCHDOG:
    116	case OCC_RESP_CRIT_OCB:
    117	case OCC_RESP_CRIT_HW:
    118		rc = -EREMOTEIO;
    119		break;
    120	default:
    121		rc = -EPROTO;
    122	}
    123
    124	return rc;
    125}
    126
    127static int p9_sbe_occ_probe(struct platform_device *pdev)
    128{
    129	int rc;
    130	struct occ *occ;
    131	struct p9_sbe_occ *ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx),
    132					      GFP_KERNEL);
    133	if (!ctx)
    134		return -ENOMEM;
    135
    136	mutex_init(&ctx->sbe_error_lock);
    137
    138	ctx->sbe = pdev->dev.parent;
    139	occ = &ctx->occ;
    140	occ->bus_dev = &pdev->dev;
    141	platform_set_drvdata(pdev, occ);
    142
    143	occ->powr_sample_time_us = 500;
    144	occ->poll_cmd_data = 0x20;		/* P9 OCC poll data */
    145	occ->send_cmd = p9_sbe_occ_send_cmd;
    146
    147	rc = occ_setup(occ);
    148	if (rc == -ESHUTDOWN)
    149		rc = -ENODEV;	/* Host is shutdown, don't spew errors */
    150
    151	if (!rc) {
    152		rc = device_create_bin_file(occ->bus_dev, &bin_attr_ffdc);
    153		if (rc) {
    154			dev_warn(occ->bus_dev,
    155				 "failed to create SBE error ffdc file\n");
    156			rc = 0;
    157		}
    158	}
    159
    160	return rc;
    161}
    162
    163static int p9_sbe_occ_remove(struct platform_device *pdev)
    164{
    165	struct occ *occ = platform_get_drvdata(pdev);
    166	struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
    167
    168	device_remove_bin_file(occ->bus_dev, &bin_attr_ffdc);
    169
    170	ctx->sbe = NULL;
    171	occ_shutdown(occ);
    172
    173	if (ctx->ffdc)
    174		kvfree(ctx->ffdc);
    175
    176	return 0;
    177}
    178
    179static struct platform_driver p9_sbe_occ_driver = {
    180	.driver = {
    181		.name = "occ-hwmon",
    182	},
    183	.probe	= p9_sbe_occ_probe,
    184	.remove = p9_sbe_occ_remove,
    185};
    186
    187module_platform_driver(p9_sbe_occ_driver);
    188
    189MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
    190MODULE_DESCRIPTION("BMC P9 OCC hwmon driver");
    191MODULE_LICENSE("GPL");