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 }