coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
crosec_proto.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <console/console.h>
4 #include <stdint.h>
5 #include <string.h>
6 
7 #include "ec.h"
8 #include "ec_commands.h"
9 #include "ec_message.h"
10 
11 /* Common utilities */
12 void *__weak crosec_get_buffer(size_t size, int req)
13 {
14  printk(BIOS_DEBUG, "crosec_get_buffer() implementation required.\n");
15  return NULL;
16 }
17 
18 /* Dumps EC command / response data into debug output.
19  *
20  * @param name Message prefix name.
21  * @param cmd Command code, or -1 to ignore cmd message.
22  * @param data Data buffer to print.
23  * @param len Length of data.
24  */
25 static void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data,
26  int len)
27 {
28  int i;
29 
30  printk(BIOS_DEBUG, "%s: ", name);
31  if (cmd != -1)
32  printk(BIOS_DEBUG, "cmd=%#x: ", cmd);
33  for (i = 0; i < len; i++)
34  printk(BIOS_DEBUG, "%02x ", data[i]);
35  printk(BIOS_DEBUG, "\n");
36 }
37 
38 /* Calculate a simple 8-bit checksum of a data block
39  *
40  * @param data Data block to checksum
41  * @param size Size of data block in bytes
42  * @return checksum value (0 to 255)
43  */
44 static int cros_ec_calc_checksum(const uint8_t *data, int size)
45 {
46  int csum, i;
47 
48  for (i = csum = 0; i < size; i++)
49  csum += data[i];
50  return csum & 0xff;
51 }
52 
53 /* Standard Chrome EC protocol, version 3 */
54 
55 struct ec_command_v3 {
56  struct ec_host_request header;
58 };
59 
61  struct ec_host_response header;
63 };
64 
65 /**
66  * Create a request packet for protocol version 3.
67  *
68  * @param cec_command Command description.
69  * @param cmd Packed command bit stream.
70  * @return packet size in bytes, or <0 if error.
71  */
73  struct ec_command_v3 *cmd)
74 {
75  struct ec_host_request *rq = &cmd->header;
76  int out_bytes = cec_command->cmd_size_in + sizeof(*rq);
77 
78  /* Fail if output size is too big */
79  if (out_bytes > sizeof(*cmd)) {
80  printk(BIOS_ERR, "%s: Cannot send %d bytes\n", __func__,
81  cec_command->cmd_size_in);
83  }
84 
85  /* Fill in request packet */
87  rq->checksum = 0;
88  rq->command = cec_command->cmd_code;
89  rq->command_version = cec_command->cmd_version;
90  rq->reserved = 0;
91  rq->data_len = cec_command->cmd_size_in;
92 
93  /* Copy data after header */
94  memcpy(cmd->data, cec_command->cmd_data_in, cec_command->cmd_size_in);
95 
96  /* Write checksum field so the entire packet sums to 0 */
98  (const uint8_t*)cmd, out_bytes));
99 
100  cros_ec_dump_data("out", rq->command, (const uint8_t *)cmd, out_bytes);
101 
102  /* Return size of request packet */
103  return out_bytes;
104 }
105 
106 /**
107  * Prepare the device to receive a protocol version 3 response.
108  *
109  * @param cec_command Command description.
110  * @param resp Response buffer.
111  * @return maximum expected number of bytes in response, or <0 if error.
112  */
114  const struct chromeec_command *cec_command,
115  struct ec_response_v3 *resp)
116 {
117  int in_bytes = cec_command->cmd_size_out + sizeof(resp->header);
118 
119  /* Fail if input size is too big */
120  if (in_bytes > sizeof(*resp)) {
121  printk(BIOS_ERR, "%s: Cannot receive %d bytes\n", __func__,
122  cec_command->cmd_size_out);
123  return -EC_RES_RESPONSE_TOO_BIG;
124  }
125 
126  /* Return expected size of response packet */
127  return in_bytes;
128 }
129 
130 /**
131  * Handle a protocol version 3 response packet.
132  *
133  * The packet must already be stored in the response buffer.
134  *
135  * @param resp Response buffer.
136  * @param cec_command Command structure to receive valid response.
137  * @return number of bytes of response data, or <0 if error
138  */
139 static int handle_proto3_response(struct ec_response_v3 *resp,
141 {
142  struct ec_host_response *rs = &resp->header;
143  int in_bytes;
144  int csum;
145 
146  cros_ec_dump_data("in-header", -1, (const uint8_t*)rs, sizeof(*rs));
147 
148  /* Check input data */
150  printk(BIOS_ERR, "%s: EC response version mismatch\n", __func__);
151  return -EC_RES_INVALID_RESPONSE;
152  }
153 
154  if (rs->reserved) {
155  printk(BIOS_ERR, "%s: EC response reserved != 0\n", __func__);
156  return -EC_RES_INVALID_RESPONSE;
157  }
158 
159  if (rs->data_len > sizeof(resp->data) ||
160  rs->data_len > cec_command->cmd_size_out) {
161  printk(BIOS_ERR, "%s: EC returned too much data\n", __func__);
162  return -EC_RES_RESPONSE_TOO_BIG;
163  }
164 
165  cros_ec_dump_data("in-data", -1, resp->data, rs->data_len);
166 
167  /* Update in_bytes to actual data size */
168  in_bytes = sizeof(*rs) + rs->data_len;
169 
170  /* Verify checksum */
171  csum = cros_ec_calc_checksum((const uint8_t *)resp, in_bytes);
172  if (csum) {
173  printk(BIOS_ERR, "%s: EC response checksum invalid: 0x%02x\n",
174  __func__, csum);
175  return -EC_RES_INVALID_CHECKSUM;
176  }
177 
178  /* Return raw response. */
179  cec_command->cmd_code = rs->result;
180  cec_command->cmd_size_out = rs->data_len;
181  memcpy(cec_command->cmd_data_out, resp->data, rs->data_len);
182 
183  /* Return error result, if any */
184  if (rs->result) {
185  printk(BIOS_ERR, "%s: EC response with error code: %d\n",
186  __func__, rs->result);
187  return -(int)rs->result;
188  }
189 
190  return rs->data_len;
191 }
192 
194  crosec_io_t crosec_io, void *context)
195 {
196  int out_bytes, in_bytes;
197  int rv;
198  struct ec_command_v3 *cmd;
199  struct ec_response_v3 *resp;
200 
201  if ((cmd = crosec_get_buffer(sizeof(*cmd), 1)) == NULL)
202  return -EC_RES_ERROR;
203  if ((resp = crosec_get_buffer(sizeof(*resp), 0)) == NULL)
204  return -EC_RES_ERROR;
205 
206  /* Create request packet */
207  out_bytes = create_proto3_request(cec_command, cmd);
208  if (out_bytes < 0) {
209  return out_bytes;
210  }
211 
212  /* Prepare response buffer */
214  if (in_bytes < 0) {
215  return in_bytes;
216  }
217 
218  rv = crosec_io(out_bytes, in_bytes, context);
219  if (rv != 0) {
220  printk(BIOS_ERR, "%s: failed to complete I/O: Err = %#x.\n",
221  __func__, rv >= 0 ? rv : -rv);
222  return -EC_RES_ERROR;
223  }
224 
225  /* Process the response */
226  return handle_proto3_response(resp, cec_command);
227 }
228 
230  crosec_io_t crosec_io, void *context)
231 {
232  int rv = send_command_proto3(cec_command, crosec_io, context);
233  if (rv < 0) {
234  cec_command->cmd_code = rv;
235  return 1;
236  }
237  return 0;
238 }
239 
241  crosec_io_t crosec_io, void *context)
242 {
243  // TODO(hungte) Detect and fallback to v2 if we need.
244  return crosec_command_proto_v3(cec_command, crosec_io, context);
245 }
const char * name
Definition: mmu.c:92
void * memcpy(void *dest, const void *src, size_t n)
Definition: memcpy.c:7
#define printk(level,...)
Definition: stdlib.h:16
static int crosec_command_proto_v3(struct chromeec_command *cec_command, crosec_io_t crosec_io, void *context)
Definition: crosec_proto.c:229
static int handle_proto3_response(struct ec_response_v3 *resp, struct chromeec_command *cec_command)
Handle a protocol version 3 response packet.
Definition: crosec_proto.c:139
static void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data, int len)
Definition: crosec_proto.c:25
static int create_proto3_request(const struct chromeec_command *cec_command, struct ec_command_v3 *cmd)
Create a request packet for protocol version 3.
Definition: crosec_proto.c:72
static int prepare_proto3_response_buffer(const struct chromeec_command *cec_command, struct ec_response_v3 *resp)
Prepare the device to receive a protocol version 3 response.
Definition: crosec_proto.c:113
int crosec_command_proto(struct chromeec_command *cec_command, crosec_io_t crosec_io, void *context)
Definition: crosec_proto.c:240
static int send_command_proto3(struct chromeec_command *cec_command, crosec_io_t crosec_io, void *context)
Definition: crosec_proto.c:193
static int cros_ec_calc_checksum(const uint8_t *data, int size)
Definition: crosec_proto.c:44
void *__weak crosec_get_buffer(size_t size, int req)
Definition: crosec_proto.c:12
int(* crosec_io_t)(size_t req_size, size_t resp_size, void *context)
Definition: ec.h:175
cec_command
Definition: ec_commands.h:5071
#define EC_HOST_RESPONSE_VERSION
Definition: ec_commands.h:926
@ EC_RES_INVALID_CHECKSUM
Definition: ec_commands.h:628
@ EC_RES_REQUEST_TRUNCATED
Definition: ec_commands.h:634
@ EC_RES_ERROR
Definition: ec_commands.h:623
@ EC_RES_INVALID_RESPONSE
Definition: ec_commands.h:626
@ EC_RES_RESPONSE_TOO_BIG
Definition: ec_commands.h:635
#define EC_HOST_REQUEST_VERSION
Definition: ec_commands.h:903
@ MSG_BYTES
Definition: ec_message.h:23
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
const struct smm_save_state_ops *legacy_ops __weak
Definition: save_state.c:8
#define NULL
Definition: stddef.h:19
unsigned char uint8_t
Definition: stdint.h:8
uint8_t data[MSG_BYTES]
Definition: crosec_proto.c:57
struct ec_host_request header
Definition: crosec_proto.c:56
struct ec_host_request - Version 3 request from host.
Definition: ec_commands.h:917
uint8_t reserved
Definition: ec_commands.h:922
uint16_t data_len
Definition: ec_commands.h:923
uint8_t struct_version
Definition: ec_commands.h:918
uint8_t command_version
Definition: ec_commands.h:921
uint8_t checksum
Definition: ec_commands.h:919
uint16_t command
Definition: ec_commands.h:920
struct ec_host_response - Version 3 response from EC.
Definition: ec_commands.h:937
uint16_t data_len
Definition: ec_commands.h:941
uint16_t reserved
Definition: ec_commands.h:942
uint8_t struct_version
Definition: ec_commands.h:938
uint8_t data[MSG_BYTES]
Definition: crosec_proto.c:62
struct ec_host_response header
Definition: crosec_proto.c:61