1 /*
2 * Copyright 2016 Google Inc. All Rights Reserved.
3 * Author: gkalsi@google.com (Gurjant Kalsi)
4 *
5 * Use of this source code is governed by a MIT-style
6 * license that can be found in the LICENSE file or at
7 * https://opensource.org/licenses/MIT
8 */
9
10 #include <app/moot/usbboot.h>
11
12 #include <app/moot/stubs.h>
13 #include <dev/udc.h>
14 #include <dev/usb.h>
15 #include <dev/usbc.h>
16 #include <lk/err.h>
17 #include <kernel/event.h>
18 #include <kernel/thread.h>
19 #include <lib/bio.h>
20 #include <lib/buildsig.h>
21 #include <lib/version.h>
22 #include <platform.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <lk/trace.h>
27
28 #define LOCAL_TRACE 0
29 #define COMMAND_MAGIC (0x4d4f4f54) // MOOT
30 #define RESP_MAGIC (0x52455350) // RESP
31
32 #define USB_XFER_SIZE (512)
33 #define W(w) (w & 0xff), (w >> 8)
34 #define W3(w) (w & 0xff), ((w >> 8) & 0xff), ((w >> 16) & 0xff)
35
36 // How long should we wait for activity on USB before we continue to boot?
37 #define USB_BOOT_TIMEOUT (3000)
38 #define USB_READ_TIMEOUT (10000)
39
40 static const uint8_t if_descriptor[] = {
41 0x09, /* length */
42 INTERFACE, /* type */
43 0x01, /* interface num */
44 0x00, /* alternates */
45 0x02, /* endpoint count */
46 0xff, /* interface class */
47 0x01, /* interface subclass */
48 0x00, /* interface protocol */
49 0x00, /* string index */
50
51 /* endpoint 1 IN */
52 0x07, /* length */
53 ENDPOINT, /* type */
54 0x1 | 0x80, /* address: 1 IN */
55 0x02, /* type: bulk */
56 W(64), /* max packet size: 64 */
57 00, /* interval */
58
59 /* endpoint 1 OUT */
60 0x07, /* length */
61 ENDPOINT, /* type */
62 0x1, /* address: 1 OUT */
63 0x02, /* type: bulk */
64 W(64), /* max packet size: 64 */
65 00, /* interval */
66 };
67
68 // Everything is okay.
69 #define USB_RESP_NO_ERROR (0x00)
70 #define USB_RESP_XMIT_READY (0x01)
71 #define USB_RESP_RECV_READY (0x02)
72
73 // Malformed reqeust
74 #define USB_RESP_BAD_DATA_LEN (0xAAA0)
75 #define USB_RESP_BAD_MAGIC (0xAAA1)
76 #define USB_RESP_UNKNOWN_COMMAND (0xAAA2)
77
78 // Device Side System Errors
79 #define USB_RESP_SYS_IMAGE_TOO_BIG (0xFFF1)
80 #define USB_RESP_ERR_OPEN_SYS_FLASH (0xFFF2)
81 #define USB_RESP_ERR_ERASE_SYS_FLASH (0xFFF3)
82 #define USB_RESP_ERR_WRITE_SYS_FLASH (0xFFF4)
83 #define USB_RESP_CANT_FIND_BUILDSIG (0xFFF5)
84 #define USB_RESP_USB_READ_ERROR (0xFFF6)
85
86 /* Bootloader commands */
87 #define USB_CMD_FLASH (0x01)
88 #define USB_CMD_BOOT (0x02)
89 #define USB_CMD_DEVINFO (0x03)
90
91 typedef struct cmd_header {
92 uint32_t magic;
93 uint32_t cmd;
94 uint32_t arg;
95 } cmd_header_t;
96
97 typedef struct cmd_response {
98 uint32_t magic;
99 uint32_t code;
100 uint32_t arg;
101 } cmd_response_t;
102
103 // USB Functions
104 static void usb_xmit(void *data, size_t len);
105 static status_t usb_recv(void *data, size_t len, lk_time_t timeout,
106 size_t *actual);
107 static status_t usb_xmit_cplt_cb(ep_t endpoint, usbc_transfer_t *t);
108 static status_t usb_recv_cplt_cb(ep_t endpoint, usbc_transfer_t *t);
109 static status_t usb_register_cb(
110 void *cookie, usb_callback_op_t op, const union usb_callback_args *args);
111
112 static event_t txevt = EVENT_INITIAL_VALUE(txevt, 0, EVENT_FLAG_AUTOUNSIGNAL);
113 static event_t rxevt = EVENT_INITIAL_VALUE(rxevt, 0, EVENT_FLAG_AUTOUNSIGNAL);
114 static volatile bool usb_online = false;
115
116 // Command processor that handles USB boot commands.
handle_usb_cmd(const void * data,const size_t n,cmd_response_t * resp)117 static bool handle_usb_cmd(
118 const void *data, const size_t n, cmd_response_t *resp
119 ) {
120 static uint8_t buffer[4096];
121
122 DEBUG_ASSERT(resp);
123
124 resp->magic = RESP_MAGIC;
125 resp->code = USB_RESP_NO_ERROR;
126 resp->arg = 0;
127
128 // Make sure we have enough data.
129 if (n < sizeof(cmd_header_t)) {
130 resp->code = USB_RESP_BAD_DATA_LEN;
131 return false;
132 }
133
134 cmd_header_t *header = (cmd_header_t *)data;
135 if (header->magic != COMMAND_MAGIC) {
136 resp->code = USB_RESP_BAD_MAGIC;
137 return false;
138 }
139
140 ssize_t image_length;
141 const lk_version_t *version;
142 status_t st;
143 switch (header->cmd) {
144 case USB_CMD_FLASH:
145 image_length = header->arg;
146 if (image_length > (ssize_t)moot_system_info.system_len) {
147 resp->code = USB_RESP_SYS_IMAGE_TOO_BIG;
148 break;
149 }
150
151 // Make space on the flash for the data.
152 bdev_t *dev = bio_open(moot_system_info.system_flash_name);
153 if (!dev) {
154 resp->code = USB_RESP_ERR_OPEN_SYS_FLASH;
155 break;
156 }
157
158 ssize_t n_bytes_erased =
159 bio_erase(dev, moot_system_info.system_offset, image_length);
160 if (n_bytes_erased < image_length) {
161 resp->code = USB_RESP_ERR_ERASE_SYS_FLASH;
162 goto close_and_exit;
163 }
164
165 // Signal to the host to start sending the image over.
166 resp->code = USB_RESP_RECV_READY;
167 resp->arg = 0;
168 usb_xmit((void *)resp, sizeof(*resp));
169
170 off_t addr = moot_system_info.system_offset;
171 while (image_length > 0) {
172 ssize_t xfer = (image_length > (ssize_t)sizeof(buffer)) ?
173 (ssize_t)sizeof(buffer) : image_length;
174
175 size_t bytes_received;
176 st = usb_recv(buffer, xfer, USB_READ_TIMEOUT, &bytes_received);
177 if (st != NO_ERROR) {
178 resp->code = USB_RESP_USB_READ_ERROR;
179 goto close_and_exit;
180 }
181
182 if (xfer != (ssize_t)bytes_received) {
183 resp->code = USB_RESP_BAD_DATA_LEN;
184 goto close_and_exit;
185 }
186
187 ssize_t written = bio_write(dev, buffer, addr, bytes_received);
188 if (written != (ssize_t)bytes_received) {
189 resp->code = USB_RESP_ERR_WRITE_SYS_FLASH;
190 goto close_and_exit;
191 }
192
193 addr += written;
194 image_length -= written;
195 }
196
197 resp->code = USB_RESP_NO_ERROR;
198
199 close_and_exit:
200 bio_close(dev);
201 break;
202 case USB_CMD_BOOT:
203 resp->code = USB_RESP_NO_ERROR;
204 resp->arg = 0;
205 return true;
206 break;
207 case USB_CMD_DEVINFO:
208 st = buildsig_search(
209 (void *)moot_system_info.sys_base_addr,
210 DEFAULT_BUILDSIG_SEARCH_LEN,
211 1024*1024,
212 &version
213 );
214 if (st != NO_ERROR) {
215 resp->code = USB_RESP_CANT_FIND_BUILDSIG;
216 break;
217 }
218
219 size_t buflen = sizeof(buffer);
220 snprintf((char *)buffer, buflen, "%s\n%s\n%s\n%s\n%s",
221 version->arch, version->platform, version->target,
222 version->project, version->buildid);
223
224 resp->code = USB_RESP_XMIT_READY;
225 resp->arg = strnlen((char *)buffer, buflen);
226 usb_xmit((void *)resp, sizeof(*resp));
227 usb_xmit((void *)buffer, resp->arg);
228
229 resp->arg = 0;
230 resp->code = USB_RESP_NO_ERROR;
231 break;
232 default:
233 resp->arg = 0;
234 resp->code = USB_RESP_UNKNOWN_COMMAND;
235 break;
236 }
237
238 finish:
239 return false;
240 }
241
init_usb_boot(void)242 void init_usb_boot(void) {
243 usb_register_callback(&usb_register_cb, NULL);
244 }
245
append_usb_interfaces(void)246 void append_usb_interfaces(void) {
247 usb_append_interface_lowspeed(if_descriptor, sizeof(if_descriptor));
248 usb_append_interface_highspeed(if_descriptor, sizeof(if_descriptor));
249 }
250
attempt_usb_boot(void)251 void attempt_usb_boot(void) {
252 static uint8_t buf[USB_XFER_SIZE];
253
254 lk_time_t start = current_time();
255 lk_time_t timeout = USB_BOOT_TIMEOUT;
256 size_t bytes_received;
257
258 while (current_time() - start < timeout) {
259 if (!usb_online) {
260 thread_yield();
261 continue;
262 }
263
264 status_t r = usb_recv(buf, USB_XFER_SIZE, timeout, &bytes_received);
265 if (r == ERR_TIMED_OUT) {
266 goto finish;
267 } else if (r == NO_ERROR) {
268 // Somebody tried to talk to us over USB, they own the boot now.
269 cmd_response_t response;
270 bool should_boot = handle_usb_cmd(buf, bytes_received, &response);
271 usb_xmit((void *)&response, sizeof(response));
272 timeout = INFINITE_TIME;
273 if (should_boot) {
274 goto finish;
275 }
276 }
277 }
278
279 finish:
280 return;
281 }
282
usb_register_cb(void * cookie,usb_callback_op_t op,const union usb_callback_args * args)283 static status_t usb_register_cb(
284 void *cookie,
285 usb_callback_op_t op,
286 const union usb_callback_args *args
287 ) {
288 if (op == USB_CB_ONLINE) {
289 usbc_setup_endpoint(1, USB_IN, 0x40, USB_BULK);
290 usbc_setup_endpoint(1, USB_OUT, 0x40, USB_BULK);
291 usb_online = true;
292 }
293 return NO_ERROR;
294 }
295
usb_xmit_cplt_cb(ep_t endpoint,usbc_transfer_t * t)296 static status_t usb_xmit_cplt_cb(ep_t endpoint, usbc_transfer_t *t) {
297 event_signal(&txevt, false);
298 return 0;
299 }
300
usb_recv_cplt_cb(ep_t endpoint,usbc_transfer_t * t)301 static status_t usb_recv_cplt_cb(ep_t endpoint, usbc_transfer_t *t) {
302 event_signal(&rxevt, false);
303 return 0;
304 }
305
usb_xmit(void * data,size_t len)306 static void usb_xmit(void *data, size_t len) {
307 usbc_transfer_t transfer = {
308 .callback = &usb_xmit_cplt_cb,
309 .result = 0,
310 .buf = data,
311 .buflen = len,
312 .bufpos = 0,
313 .extra = 0,
314 };
315
316 usbc_queue_tx(1, &transfer);
317 event_wait(&txevt);
318 }
319
usb_recv(void * data,size_t len,lk_time_t timeout,size_t * actual)320 static status_t usb_recv(
321 void *data, size_t len, lk_time_t timeout, size_t *actual
322 ) {
323 usbc_transfer_t transfer = {
324 .callback = &usb_recv_cplt_cb,
325 .result = 0,
326 .buf = data,
327 .buflen = len,
328 .bufpos = 0,
329 .extra = 0,
330 };
331
332 usbc_queue_rx(1, &transfer);
333 status_t res = event_wait_timeout(&rxevt, timeout);
334
335 if (res != NO_ERROR) {
336 return res;
337 }
338
339 *actual = transfer.bufpos;
340 return NO_ERROR;
341 }