Software APIs
simple_serial.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
6 
12 #include "sw/device/sca/lib/prng.h"
13 #include "sw/device/tests/penetrationtests/firmware/lib/pentest_lib.h"
14 
16 
17 /**
18  * Macro for ignoring return values.
19  *
20  * This is needed because ‘(void)expr;` does not work for gcc.
21  */
22 #define IGNORE_RESULT(expr) \
23  if (expr) { \
24  }
25 
26 enum {
27  /**
28  * Simple serial protocol version 1.1.
29  */
30  kSimpleSerialProtocolVersion = 1,
31  kUartMaxRxPacketSize = 128,
32 };
33 
34 /**
35  * Command handlers.
36  *
37  * Clients can register handlers for commands 'a'-'z' using
38  * `simple_serial_register_handler()` except for 'v' (version), 's' (seed
39  * PRNG), and 't' (select trigger type) which are handled by this library. This
40  * array has an extra element (27) that is initialized in `simple_serial_init()`
41  * to point to `simple_serial_unknown_command()` in order to simplify handling
42  * of invalid commands in `simple_serial_process_packet()`.
43  */
44 static simple_serial_command_handler handlers[27];
45 static const dif_uart_t *uart;
46 
47 static bool simple_serial_is_valid_command(uint8_t cmd) {
48  return cmd >= 'a' && cmd <= 'z';
49 }
50 
51 /**
52  * Converts a hex encoded nibble to binary.
53  *
54  * @param hex A hex encoded nibble.
55  * @param[out] val Value of the nibble.
56  *
57  * @return Result of the operation.
58  */
59 static simple_serial_result_t simple_serial_hex_to_bin(uint8_t hex,
60  uint8_t *val) {
61  if (hex >= '0' && hex <= '9') {
62  *val = hex - '0';
63  } else if (hex >= 'A' && hex <= 'F') {
64  *val = hex - 'A' + 10;
65  } else if (hex >= 'a' && hex <= 'f') {
66  *val = hex - 'a' + 10;
67  } else {
68  return kSimpleSerialError;
69  }
70  return kSimpleSerialOk;
71 }
72 
73 /**
74  * Receives a simple serial packet over UART.
75  *
76  * Simple serial packets are composed of:
77  * - Command: A single byte character,
78  * - Payload: A variable length hex encoded string,
79  * - Terminator: '\n'.
80  *
81  * @param[out] cmd Simple serial command.
82  * @param[out] data Buffer for received packet payload.
83  * @param data_buf_len Length of the packet payload buffer.
84  * @param[out] data_len Received packet payload length.
85  */
86 static void simple_serial_receive_packet(uint8_t *cmd, uint8_t *data,
87  size_t data_buf_len,
88  size_t *data_len) {
89  while (true) {
90  // Read command byte - a single character.
91  IGNORE_RESULT(dif_uart_byte_receive_polled(uart, cmd));
92  if (*cmd == '\n') {
93  continue;
94  }
95  *data_len = 0;
96  // Read payload - a variable length hex encoded string terminated with '\n'.
97  do {
98  uint8_t hex_byte[2];
99  IGNORE_RESULT(dif_uart_byte_receive_polled(uart, &hex_byte[0]));
100  if (hex_byte[0] == '\n') {
101  return;
102  }
103  if (simple_serial_hex_to_bin(hex_byte[0], &hex_byte[0]) !=
104  kSimpleSerialOk) {
105  break;
106  }
107  IGNORE_RESULT(dif_uart_byte_receive_polled(uart, &hex_byte[1]));
108  if (simple_serial_hex_to_bin(hex_byte[1], &hex_byte[1]) !=
109  kSimpleSerialOk) {
110  break;
111  }
112  if (*data_len == data_buf_len) {
113  break;
114  }
115  data[*data_len] = (uint8_t)(hex_byte[0] << 4) | hex_byte[1];
116  ++*data_len;
117  } while (true);
118  }
119 }
120 
121 /**
122  * Returns the index of a command's handler in `handlers`.
123  *
124  * This function returns the index of the last element, which points to
125  * `simple_serial_unknown_command(), if the given command is not valid.
126  *
127  * @param cmd A simple serial command.
128  * @return Command handler's index in `handlers`.
129  */
130 static size_t simple_serial_get_handler_index(uint8_t cmd) {
131  if (simple_serial_is_valid_command(cmd)) {
132  return cmd - 'a';
133  } else {
134  return ARRAYSIZE(handlers) - 1;
135  }
136 }
137 
138 /**
139  * Simple serial 'v' (version) command handler.
140  *
141  * Returns the simple serial version that this file implements. This command is
142  * useful for checking that the host and the device can communicate properly
143  * before starting capturing traces.
144  *
145  * @param data Received packet payload.
146  * @param data_len Payload length.
147  */
148 static void simple_serial_version(const uint8_t *data, size_t data_len) {
149  simple_serial_send_status(kSimpleSerialProtocolVersion);
150 }
151 
152 /**
153  * Simple serial 's' (seed PRNG) command handler.
154  *
155  * This function only supports 4-byte seeds.
156  *
157  * @param seed A buffer holding the seed.
158  * @param seed_len Seed length.
159  */
160 static void simple_serial_seed_prng(const uint8_t *seed, size_t seed_len) {
161  SS_CHECK(seed_len == sizeof(uint32_t));
162  prng_seed(read_32(seed));
163 }
164 
165 /**
166  * Simple serial 't' (select trigger type) command handler.
167  *
168  * This function only supports 1-byte trigger values.
169  *
170  * @param trigger A buffer holding the trigger type.
171  * @param trigger_len Buffer length.
172  */
173 static void simple_serial_select_trigger_type(const uint8_t *trigger,
174  size_t trigger_len) {
175  SS_CHECK(trigger_len == 1);
176  pentest_select_trigger_type((pentest_trigger_type_t)trigger[0]);
177 }
178 
179 /**
180  * Handler for uninmplemented simple serial commands.
181  *
182  * Sends an error packet over UART.
183  *
184  * @param data Received packet payload
185  * @param data_len Payload length.
186  */
187 static void simple_serial_unknown_command(const uint8_t *data,
188  size_t data_len) {
189  simple_serial_send_status(kSimpleSerialError);
190 }
191 
192 void simple_serial_init(const dif_uart_t *uart_) {
193  uart = uart_;
194 
195  for (size_t i = 0; i < ARRAYSIZE(handlers); ++i) {
196  handlers[i] = simple_serial_unknown_command;
197  }
198  handlers[simple_serial_get_handler_index('s')] = simple_serial_seed_prng;
199  handlers[simple_serial_get_handler_index('t')] =
200  simple_serial_select_trigger_type;
201  handlers[simple_serial_get_handler_index('v')] = simple_serial_version;
202 }
203 
205  uint8_t cmd, simple_serial_command_handler handler) {
206  if (!simple_serial_is_valid_command(cmd)) {
207  return kSimpleSerialError;
208  } else if (cmd == 's' || cmd == 't' || cmd == 'v') {
209  // Cannot register handlers for built-in commands.
210  return kSimpleSerialError;
211  } else {
212  handlers[simple_serial_get_handler_index(cmd)] = handler;
213  return kSimpleSerialOk;
214  }
215 }
216 
218  uint8_t cmd;
219  uint8_t data[kUartMaxRxPacketSize];
220  size_t data_len;
221  simple_serial_receive_packet(&cmd, data, ARRAYSIZE(data), &data_len);
222  handlers[simple_serial_get_handler_index(cmd)](data, data_len);
223 }
224 
225 void simple_serial_send_packet(const uint8_t cmd, const uint8_t *data,
226  size_t data_len) {
227  char buf;
228  base_snprintf(&buf, 1, "%c", cmd);
229  IGNORE_RESULT(dif_uart_byte_send_polled(uart, buf));
230  simple_serial_print_hex(data, data_len);
231  base_snprintf(&buf, 1, "\n");
232  IGNORE_RESULT(dif_uart_byte_send_polled(uart, buf));
233 }
234 
235 void simple_serial_send_status(uint8_t res) {
236  simple_serial_send_packet('z', (uint8_t[1]){res}, 1);
237 }
238 
239 void simple_serial_print_hex(const uint8_t *data, size_t data_len) {
240  char buf[2];
241  for (size_t i = 0; i < data_len; ++i) {
242  base_snprintf(&buf[0], 2, "%02x", data[i]);
243  IGNORE_RESULT(dif_uart_byte_send_polled(uart, buf[0]));
244  IGNORE_RESULT(dif_uart_byte_send_polled(uart, buf[1]));
245  }
246 }