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

debugfs.c (7367B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * debugfs attributes for Wilco EC
      4 *
      5 * Copyright 2019 Google LLC
      6 *
      7 * See Documentation/ABI/testing/debugfs-wilco-ec for usage.
      8 */
      9
     10#include <linux/ctype.h>
     11#include <linux/debugfs.h>
     12#include <linux/fs.h>
     13#include <linux/module.h>
     14#include <linux/platform_data/wilco-ec.h>
     15#include <linux/platform_device.h>
     16
     17#define DRV_NAME "wilco-ec-debugfs"
     18
     19/* The raw bytes will take up more space when represented as a hex string */
     20#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE * 4)
     21
     22struct wilco_ec_debugfs {
     23	struct wilco_ec_device *ec;
     24	struct dentry *dir;
     25	size_t response_size;
     26	u8 raw_data[EC_MAILBOX_DATA_SIZE];
     27	u8 formatted_data[FORMATTED_BUFFER_SIZE];
     28};
     29static struct wilco_ec_debugfs *debug_info;
     30
     31/**
     32 * parse_hex_sentence() - Convert a ascii hex representation into byte array.
     33 * @in: Input buffer of ascii.
     34 * @isize: Length of input buffer.
     35 * @out: Output buffer.
     36 * @osize: Length of output buffer, e.g. max number of bytes to parse.
     37 *
     38 * An valid input is a series of ascii hexadecimal numbers, separated by spaces.
     39 * An example valid input is
     40 * "   00 f2 0    000076 6 0  ff"
     41 *
     42 * If an individual "word" within the hex sentence is longer than MAX_WORD_SIZE,
     43 * then the sentence is illegal, and parsing will fail.
     44 *
     45 * Return: Number of bytes parsed, or negative error code on failure.
     46 */
     47static int parse_hex_sentence(const char *in, int isize, u8 *out, int osize)
     48{
     49	int n_parsed = 0;
     50	int word_start = 0;
     51	int word_end;
     52	int word_len;
     53	/* Temp buffer for holding a "word" of chars that represents one byte */
     54	#define MAX_WORD_SIZE 16
     55	char tmp[MAX_WORD_SIZE + 1];
     56	u8 byte;
     57
     58	while (word_start < isize && n_parsed < osize) {
     59		/* Find the start of the next word */
     60		while (word_start < isize && isspace(in[word_start]))
     61			word_start++;
     62		 /* reached the end of the input before next word? */
     63		if (word_start >= isize)
     64			break;
     65
     66		/* Find the end of this word */
     67		word_end = word_start;
     68		while (word_end < isize && !isspace(in[word_end]))
     69			word_end++;
     70
     71		/* Copy to a tmp NULL terminated string */
     72		word_len = word_end - word_start;
     73		if (word_len > MAX_WORD_SIZE)
     74			return -EINVAL;
     75		memcpy(tmp, in + word_start, word_len);
     76		tmp[word_len] = '\0';
     77
     78		/*
     79		 * Convert from hex string, place in output. If fails to parse,
     80		 * just return -EINVAL because specific error code is only
     81		 * relevant for this one word, returning it would be confusing.
     82		 */
     83		if (kstrtou8(tmp, 16, &byte))
     84			return -EINVAL;
     85		out[n_parsed++] = byte;
     86
     87		word_start = word_end;
     88	}
     89	return n_parsed;
     90}
     91
     92/* The message type takes up two bytes*/
     93#define TYPE_AND_DATA_SIZE ((EC_MAILBOX_DATA_SIZE) + 2)
     94
     95static ssize_t raw_write(struct file *file, const char __user *user_buf,
     96			 size_t count, loff_t *ppos)
     97{
     98	char *buf = debug_info->formatted_data;
     99	struct wilco_ec_message msg;
    100	u8 request_data[TYPE_AND_DATA_SIZE];
    101	ssize_t kcount;
    102	int ret;
    103
    104	if (count > FORMATTED_BUFFER_SIZE)
    105		return -EINVAL;
    106
    107	kcount = simple_write_to_buffer(buf, FORMATTED_BUFFER_SIZE, ppos,
    108					user_buf, count);
    109	if (kcount < 0)
    110		return kcount;
    111
    112	ret = parse_hex_sentence(buf, kcount, request_data, TYPE_AND_DATA_SIZE);
    113	if (ret < 0)
    114		return ret;
    115	/* Need at least two bytes for message type and one byte of data */
    116	if (ret < 3)
    117		return -EINVAL;
    118
    119	msg.type = request_data[0] << 8 | request_data[1];
    120	msg.flags = 0;
    121	msg.request_data = request_data + 2;
    122	msg.request_size = ret - 2;
    123	memset(debug_info->raw_data, 0, sizeof(debug_info->raw_data));
    124	msg.response_data = debug_info->raw_data;
    125	msg.response_size = EC_MAILBOX_DATA_SIZE;
    126
    127	ret = wilco_ec_mailbox(debug_info->ec, &msg);
    128	if (ret < 0)
    129		return ret;
    130	debug_info->response_size = ret;
    131
    132	return count;
    133}
    134
    135static ssize_t raw_read(struct file *file, char __user *user_buf, size_t count,
    136			loff_t *ppos)
    137{
    138	int fmt_len = 0;
    139
    140	if (debug_info->response_size) {
    141		fmt_len = hex_dump_to_buffer(debug_info->raw_data,
    142					     debug_info->response_size,
    143					     16, 1, debug_info->formatted_data,
    144					     sizeof(debug_info->formatted_data),
    145					     true);
    146		/* Only return response the first time it is read */
    147		debug_info->response_size = 0;
    148	}
    149
    150	return simple_read_from_buffer(user_buf, count, ppos,
    151				       debug_info->formatted_data, fmt_len);
    152}
    153
    154static const struct file_operations fops_raw = {
    155	.owner = THIS_MODULE,
    156	.read = raw_read,
    157	.write = raw_write,
    158	.llseek = no_llseek,
    159};
    160
    161#define CMD_KB_CHROME		0x88
    162#define SUB_CMD_H1_GPIO		0x0A
    163#define SUB_CMD_TEST_EVENT	0x0B
    164
    165struct ec_request {
    166	u8 cmd;		/* Always CMD_KB_CHROME */
    167	u8 reserved;
    168	u8 sub_cmd;
    169} __packed;
    170
    171struct ec_response {
    172	u8 status;	/* 0 if allowed */
    173	u8 val;
    174} __packed;
    175
    176static int send_ec_cmd(struct wilco_ec_device *ec, u8 sub_cmd, u8 *out_val)
    177{
    178	struct ec_request rq;
    179	struct ec_response rs;
    180	struct wilco_ec_message msg;
    181	int ret;
    182
    183	memset(&rq, 0, sizeof(rq));
    184	rq.cmd = CMD_KB_CHROME;
    185	rq.sub_cmd = sub_cmd;
    186
    187	memset(&msg, 0, sizeof(msg));
    188	msg.type = WILCO_EC_MSG_LEGACY;
    189	msg.request_data = &rq;
    190	msg.request_size = sizeof(rq);
    191	msg.response_data = &rs;
    192	msg.response_size = sizeof(rs);
    193	ret = wilco_ec_mailbox(ec, &msg);
    194	if (ret < 0)
    195		return ret;
    196	if (rs.status)
    197		return -EIO;
    198
    199	*out_val = rs.val;
    200
    201	return 0;
    202}
    203
    204/**
    205 * h1_gpio_get() - Gets h1 gpio status.
    206 * @arg: The wilco EC device.
    207 * @val: BIT(0)=ENTRY_TO_FACT_MODE, BIT(1)=SPI_CHROME_SEL
    208 */
    209static int h1_gpio_get(void *arg, u64 *val)
    210{
    211	int ret;
    212
    213	ret = send_ec_cmd(arg, SUB_CMD_H1_GPIO, (u8 *)val);
    214	if (ret == 0)
    215		*val &= 0xFF;
    216	return ret;
    217}
    218
    219DEFINE_DEBUGFS_ATTRIBUTE(fops_h1_gpio, h1_gpio_get, NULL, "0x%02llx\n");
    220
    221/**
    222 * test_event_set() - Sends command to EC to cause an EC test event.
    223 * @arg: The wilco EC device.
    224 * @val: unused.
    225 */
    226static int test_event_set(void *arg, u64 val)
    227{
    228	u8 ret;
    229
    230	return send_ec_cmd(arg, SUB_CMD_TEST_EVENT, &ret);
    231}
    232
    233/* Format is unused since it is only required for get method which is NULL */
    234DEFINE_DEBUGFS_ATTRIBUTE(fops_test_event, NULL, test_event_set, "%llu\n");
    235
    236/**
    237 * wilco_ec_debugfs_probe() - Create the debugfs node
    238 * @pdev: The platform device, probably created in core.c
    239 *
    240 * Try to create a debugfs node. If it fails, then we don't want to change
    241 * behavior at all, this is for debugging after all. Just fail silently.
    242 *
    243 * Return: 0 always.
    244 */
    245static int wilco_ec_debugfs_probe(struct platform_device *pdev)
    246{
    247	struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
    248
    249	debug_info = devm_kzalloc(&pdev->dev, sizeof(*debug_info), GFP_KERNEL);
    250	if (!debug_info)
    251		return 0;
    252	debug_info->ec = ec;
    253	debug_info->dir = debugfs_create_dir("wilco_ec", NULL);
    254	if (!debug_info->dir)
    255		return 0;
    256	debugfs_create_file("raw", 0644, debug_info->dir, NULL, &fops_raw);
    257	debugfs_create_file("h1_gpio", 0444, debug_info->dir, ec,
    258			    &fops_h1_gpio);
    259	debugfs_create_file("test_event", 0200, debug_info->dir, ec,
    260			    &fops_test_event);
    261
    262	return 0;
    263}
    264
    265static int wilco_ec_debugfs_remove(struct platform_device *pdev)
    266{
    267	debugfs_remove_recursive(debug_info->dir);
    268
    269	return 0;
    270}
    271
    272static struct platform_driver wilco_ec_debugfs_driver = {
    273	.driver = {
    274		.name = DRV_NAME,
    275	},
    276	.probe = wilco_ec_debugfs_probe,
    277	.remove = wilco_ec_debugfs_remove,
    278};
    279
    280module_platform_driver(wilco_ec_debugfs_driver);
    281
    282MODULE_ALIAS("platform:" DRV_NAME);
    283MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
    284MODULE_LICENSE("GPL v2");
    285MODULE_DESCRIPTION("Wilco EC debugfs driver");