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

mailbox.c (6024B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Mailbox interface for Wilco Embedded Controller
      4 *
      5 * Copyright 2018 Google LLC
      6 *
      7 * The Wilco EC is similar to a typical ChromeOS embedded controller.
      8 * It uses the same MEC based low-level communication and a similar
      9 * protocol, but with some important differences.  The EC firmware does
     10 * not support the same mailbox commands so it is not registered as a
     11 * cros_ec device type.
     12 *
     13 * Most messages follow a standard format, but there are some exceptions
     14 * and an interface is provided to do direct/raw transactions that do not
     15 * make assumptions about byte placement.
     16 */
     17
     18#include <linux/delay.h>
     19#include <linux/device.h>
     20#include <linux/io.h>
     21#include <linux/platform_data/wilco-ec.h>
     22#include <linux/platform_device.h>
     23
     24#include "../cros_ec_lpc_mec.h"
     25
     26/* Version of mailbox interface */
     27#define EC_MAILBOX_VERSION		0
     28
     29/* Command to start mailbox transaction */
     30#define EC_MAILBOX_START_COMMAND	0xda
     31
     32/* Version of EC protocol */
     33#define EC_MAILBOX_PROTO_VERSION	3
     34
     35/* Number of header bytes to be counted as data bytes */
     36#define EC_MAILBOX_DATA_EXTRA		2
     37
     38/* Maximum timeout */
     39#define EC_MAILBOX_TIMEOUT		HZ
     40
     41/* EC response flags */
     42#define EC_CMDR_DATA		BIT(0)	/* Data ready for host to read */
     43#define EC_CMDR_PENDING		BIT(1)	/* Write pending to EC */
     44#define EC_CMDR_BUSY		BIT(2)	/* EC is busy processing a command */
     45#define EC_CMDR_CMD		BIT(3)	/* Last host write was a command */
     46
     47/**
     48 * wilco_ec_response_timed_out() - Wait for EC response.
     49 * @ec: EC device.
     50 *
     51 * Return: true if EC timed out, false if EC did not time out.
     52 */
     53static bool wilco_ec_response_timed_out(struct wilco_ec_device *ec)
     54{
     55	unsigned long timeout = jiffies + EC_MAILBOX_TIMEOUT;
     56
     57	do {
     58		if (!(inb(ec->io_command->start) &
     59		      (EC_CMDR_PENDING | EC_CMDR_BUSY)))
     60			return false;
     61		usleep_range(100, 200);
     62	} while (time_before(jiffies, timeout));
     63
     64	return true;
     65}
     66
     67/**
     68 * wilco_ec_checksum() - Compute 8-bit checksum over data range.
     69 * @data: Data to checksum.
     70 * @size: Number of bytes to checksum.
     71 *
     72 * Return: 8-bit checksum of provided data.
     73 */
     74static u8 wilco_ec_checksum(const void *data, size_t size)
     75{
     76	u8 *data_bytes = (u8 *)data;
     77	u8 checksum = 0;
     78	size_t i;
     79
     80	for (i = 0; i < size; i++)
     81		checksum += data_bytes[i];
     82
     83	return checksum;
     84}
     85
     86/**
     87 * wilco_ec_prepare() - Prepare the request structure for the EC.
     88 * @msg: EC message with request information.
     89 * @rq: EC request structure to fill.
     90 */
     91static void wilco_ec_prepare(struct wilco_ec_message *msg,
     92			     struct wilco_ec_request *rq)
     93{
     94	memset(rq, 0, sizeof(*rq));
     95	rq->struct_version = EC_MAILBOX_PROTO_VERSION;
     96	rq->mailbox_id = msg->type;
     97	rq->mailbox_version = EC_MAILBOX_VERSION;
     98	rq->data_size = msg->request_size;
     99
    100	/* Checksum header and data */
    101	rq->checksum = wilco_ec_checksum(rq, sizeof(*rq));
    102	rq->checksum += wilco_ec_checksum(msg->request_data, msg->request_size);
    103	rq->checksum = -rq->checksum;
    104}
    105
    106/**
    107 * wilco_ec_transfer() - Perform actual data transfer.
    108 * @ec: EC device.
    109 * @msg: EC message data for request and response.
    110 * @rq: Filled in request structure
    111 *
    112 * Context: ec->mailbox_lock should be held while using this function.
    113 * Return: number of bytes received or negative error code on failure.
    114 */
    115static int wilco_ec_transfer(struct wilco_ec_device *ec,
    116			     struct wilco_ec_message *msg,
    117			     struct wilco_ec_request *rq)
    118{
    119	struct wilco_ec_response *rs;
    120	u8 checksum;
    121	u8 flag;
    122
    123	/* Write request header, then data */
    124	cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq);
    125	cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size,
    126				 msg->request_data);
    127
    128	/* Start the command */
    129	outb(EC_MAILBOX_START_COMMAND, ec->io_command->start);
    130
    131	/* For some commands (eg shutdown) the EC will not respond, that's OK */
    132	if (msg->flags & WILCO_EC_FLAG_NO_RESPONSE) {
    133		dev_dbg(ec->dev, "EC does not respond to this command\n");
    134		return 0;
    135	}
    136
    137	/* Wait for it to complete */
    138	if (wilco_ec_response_timed_out(ec)) {
    139		dev_dbg(ec->dev, "response timed out\n");
    140		return -ETIMEDOUT;
    141	}
    142
    143	/* Check result */
    144	flag = inb(ec->io_data->start);
    145	if (flag) {
    146		dev_dbg(ec->dev, "bad response: 0x%02x\n", flag);
    147		return -EIO;
    148	}
    149
    150	/* Read back response */
    151	rs = ec->data_buffer;
    152	checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0,
    153					    sizeof(*rs) + EC_MAILBOX_DATA_SIZE,
    154					    (u8 *)rs);
    155	if (checksum) {
    156		dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum);
    157		return -EBADMSG;
    158	}
    159
    160	if (rs->result) {
    161		dev_dbg(ec->dev, "EC reported failure: 0x%02x\n", rs->result);
    162		return -EBADMSG;
    163	}
    164
    165	if (rs->data_size != EC_MAILBOX_DATA_SIZE) {
    166		dev_dbg(ec->dev, "unexpected packet size (%u != %u)\n",
    167			rs->data_size, EC_MAILBOX_DATA_SIZE);
    168		return -EMSGSIZE;
    169	}
    170
    171	if (rs->data_size < msg->response_size) {
    172		dev_dbg(ec->dev, "EC didn't return enough data (%u < %zu)\n",
    173			rs->data_size, msg->response_size);
    174		return -EMSGSIZE;
    175	}
    176
    177	memcpy(msg->response_data, rs->data, msg->response_size);
    178
    179	return rs->data_size;
    180}
    181
    182/**
    183 * wilco_ec_mailbox() - Send EC request and receive EC response.
    184 * @ec: EC device.
    185 * @msg: EC message data for request and response.
    186 *
    187 * On entry msg->type, msg->request_size, and msg->request_data should all be
    188 * filled in. If desired, msg->flags can be set.
    189 *
    190 * If a response is expected, msg->response_size should be set, and
    191 * msg->response_data should point to a buffer with enough space. On exit
    192 * msg->response_data will be filled.
    193 *
    194 * Return: number of bytes received or negative error code on failure.
    195 */
    196int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg)
    197{
    198	struct wilco_ec_request *rq;
    199	int ret;
    200
    201	dev_dbg(ec->dev, "type=%04x flags=%02x rslen=%zu rqlen=%zu\n",
    202		msg->type, msg->flags, msg->response_size, msg->request_size);
    203
    204	mutex_lock(&ec->mailbox_lock);
    205	/* Prepare request packet */
    206	rq = ec->data_buffer;
    207	wilco_ec_prepare(msg, rq);
    208
    209	ret = wilco_ec_transfer(ec, msg, rq);
    210	mutex_unlock(&ec->mailbox_lock);
    211
    212	return ret;
    213
    214}
    215EXPORT_SYMBOL_GPL(wilco_ec_mailbox);