blob: a5b432eb982101cea875620020053fd2649ba046 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2014 Brian Swetland
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#include "lkboot.h"
25
26#include <app.h>
27
28#include <platform.h>
29#include <stdio.h>
30#include <debug.h>
31#include <string.h>
32#include <pow2.h>
33#include <err.h>
34#include <assert.h>
35#include <trace.h>
36
37#include <lib/sysparam.h>
38
39#include <kernel/thread.h>
40#include <kernel/mutex.h>
41
42#include <kernel/vm.h>
43#include <app/lkboot.h>
44
45#if WITH_LIB_MINIP
46#include <lib/minip.h>
47#endif
48
49#ifndef LKBOOT_WITH_SERVER
50#define LKBOOT_WITH_SERVER 1
51#endif
52#ifndef LKBOOT_AUTOBOOT
53#define LKBOOT_AUTOBOOT 1
54#endif
55#ifndef LKBOOT_AUTOBOOT_TIMEOUT
56#define LKBOOT_AUTOBOOT_TIMEOUT 5000
57#endif
58
59#define LOCAL_TRACE 0
60
61#define STATE_OPEN 0
62#define STATE_DATA 1
63#define STATE_RESP 2
64#define STATE_DONE 3
65#define STATE_ERROR 4
66
67typedef struct LKB {
68 lkb_read_hook *read;
69 lkb_write_hook *write;
70
71 void *cookie;
72
73 int state;
74 size_t avail;
75} lkb_t;
76
77lkb_t *lkboot_create_lkb(void *cookie, lkb_read_hook *read, lkb_write_hook *write) {
78 lkb_t *lkb = malloc(sizeof(lkb_t));
79 if (!lkb)
80 return NULL;
81
82 lkb->cookie = cookie;
83 lkb->state = STATE_OPEN;
84 lkb->avail = 0;
85 lkb->read = read;
86 lkb->write = write;
87
88 return lkb;
89}
90
91static int lkb_send(lkb_t *lkb, u8 opcode, const void *data, size_t len) {
92 msg_hdr_t hdr;
93
94 // once we sent our OKAY or FAIL or errored out, no more writes
95 if (lkb->state >= STATE_DONE) return -1;
96
97 switch (opcode) {
98 case MSG_OKAY:
99 case MSG_FAIL:
100 lkb->state = STATE_DONE;
101 if (len > 0xFFFF) return -1;
102 break;
103 case MSG_LOG:
104 if (len > 0xFFFF) return -1;
105 break;
106 case MSG_SEND_DATA:
107 if (len > 0x10000) return -1;
108 break;
109 case MSG_GO_AHEAD:
110 if (lkb->state == STATE_OPEN) {
111 lkb->state = STATE_DATA;
112 break;
113 }
114 len = 0;
115 default:
116 lkb->state = STATE_ERROR;
117 opcode = MSG_FAIL;
118 data = "internal error";
119 len = 14;
120 break;
121 }
122
123 hdr.opcode = opcode;
124 hdr.extra = 0;
125 hdr.length = (opcode == MSG_SEND_DATA) ? (len - 1) : len;
126 if (lkb->write(lkb->cookie, &hdr, sizeof(hdr)) != sizeof(&hdr)) {
127 printf("xmit hdr fail\n");
128 lkb->state = STATE_ERROR;
129 return -1;
130 }
131 if (len && (lkb->write(lkb->cookie, data, len) != (ssize_t)len)) {
132 printf("xmit data fail\n");
133 lkb->state = STATE_ERROR;
134 return -1;
135 }
136 return 0;
137}
138
139#define lkb_okay(lkb) lkb_send(lkb, MSG_OKAY, NULL, 0)
140#define lkb_fail(lkb, msg) lkb_send(lkb, MSG_FAIL, msg, strlen(msg))
141
142int lkb_write(lkb_t *lkb, const void *_data, size_t len) {
143 const char *data = _data;
144 while (len > 0) {
145 size_t xfer = (len > 65536) ? 65536 : len;
146 if (lkb_send(lkb, MSG_SEND_DATA, data, xfer)) return -1;
147 len -= xfer;
148 data += xfer;
149 }
150 return 0;
151}
152
153int lkb_read(lkb_t *lkb, void *_data, size_t len) {
154 char *data = _data;
155
156 if (lkb->state == STATE_RESP) {
157 return 0;
158 }
159 if (lkb->state == STATE_OPEN) {
160 if (lkb_send(lkb, MSG_GO_AHEAD, NULL, 0)) return -1;
161 }
162 while (len > 0) {
163 if (lkb->avail == 0) {
164 msg_hdr_t hdr;
165 if (lkb->read(lkb->cookie, &hdr, sizeof(hdr))) goto fail;
166 if (hdr.opcode == MSG_END_DATA) {
167 lkb->state = STATE_RESP;
168 return -1;
169 }
170 if (hdr.opcode != MSG_SEND_DATA) goto fail;
171 lkb->avail = ((size_t) hdr.length) + 1;
172 }
173 if (lkb->avail >= len) {
174 if (lkb->read(lkb->cookie, data, len)) goto fail;
175 lkb->avail -= len;
176 return 0;
177 }
178 if (lkb->read(lkb->cookie, data, lkb->avail)) {
179 lkb->state = STATE_ERROR;
180 return -1;
181 }
182 data += lkb->avail;
183 len -= lkb->avail;
184 lkb->avail = 0;
185 }
186 return 0;
187
188fail:
189 lkb->state = STATE_ERROR;
190 return -1;
191}
192
193status_t lkboot_process_command(lkb_t *lkb)
194{
195 msg_hdr_t hdr;
196 char cmd[128];
197 char *arg;
198 int err;
199 const char *result;
200 unsigned len;
201
202 if (lkb->read(lkb->cookie, &hdr, sizeof(hdr))) goto fail;
203 if (hdr.opcode != MSG_CMD) goto fail;
204 if (hdr.length > 127) goto fail;
205 if (lkb->read(lkb->cookie, cmd, hdr.length)) goto fail;
206 cmd[hdr.length] = 0;
207
208 TRACEF("recv '%s'\n", cmd);
209
210 if (!(arg = strchr(cmd, ':'))) goto fail;
211 *arg++ = 0;
212 len = atoul(arg);
213 if (!(arg = strchr(arg, ':'))) goto fail;
214 arg++;
215
216 err = lkb_handle_command(lkb, cmd, arg, len, &result);
217 if (err >= 0) {
218 lkb_okay(lkb);
219 } else {
220 lkb_fail(lkb, result);
221 }
222
223 TRACEF("command handled with success\n");
224 return NO_ERROR;
225
226fail:
227 TRACEF("command failed\n");
228 return ERR_IO;
229}
230
231static status_t lkboot_server(lk_time_t timeout)
232{
233 lkboot_dcc_init();
234
235#if WITH_LIB_MINIP
236 /* open the server's socket */
237 tcp_socket_t *listen_socket = NULL;
238 if (tcp_open_listen(&listen_socket, 1023) < 0) {
239 printf("lkboot: error opening listen socket\n");
240 return ERR_NO_MEMORY;
241 }
242#endif
243
244 /* run the main lkserver loop */
245 printf("lkboot: starting server\n");
246 lk_time_t t = current_time(); /* remember when we started */
247 for (;;) {
248 bool handled_command = false;
249
250 lkb_t *lkb;
251
252#if WITH_LIB_MINIP
253 /* wait for a new connection */
254 lk_time_t sock_timeout = 100;
255 tcp_socket_t *s;
256 if (tcp_accept_timeout(listen_socket, &s, sock_timeout) >= 0) {
257 DEBUG_ASSERT(s);
258
259 /* handle the command and close it */
260 lkb = lkboot_tcp_opened(s);
261 lkboot_process_command(lkb);
262 free(lkb);
263 tcp_close(s);
264 handled_command = true;
265 }
266#endif
267
268 /* check if anything is coming in on dcc */
269 lkb = lkboot_check_dcc_open();
270 if (lkb) {
271 lkboot_process_command(lkb);
272 free(lkb);
273 handled_command = true;
274 }
275
276 /* after the first command, stay in the server loop forever */
277 if (handled_command && timeout != INFINITE_TIME) {
278 timeout = INFINITE_TIME;
279 printf("lkboot: handled command, staying in server loop\n");
280 }
281
282 /* see if we need to drop out and try to direct boot */
283 if (timeout != INFINITE_TIME && (current_time() - t >= timeout)) {
284 break;
285 }
286 }
287
288#if WITH_LIB_MINIP
289 tcp_close(listen_socket);
290#endif
291
292 printf("lkboot: server timed out\n");
293
294 return ERR_TIMED_OUT;
295}
296
297/* platform code can override this to conditionally abort autobooting from flash */
298__WEAK bool platform_abort_autoboot(void)
299{
300 return false;
301}
302
303static void lkboot_task(const struct app_descriptor *app, void *args)
304{
305 /* read a few sysparams to decide if we're going to autoboot */
306 uint8_t autoboot = 1;
307 sysparam_read("lkboot.autoboot", &autoboot, sizeof(autoboot));
308
309 /* let platform code have a shot at disabling the autoboot behavior */
310 if (platform_abort_autoboot())
311 autoboot = 0;
312
313#if !LKBOOT_AUTOBOOT
314 autoboot = 0;
315#endif
316
317 /* if we're going to autoobot, read the timeout value */
318 lk_time_t autoboot_timeout;
319 if (!autoboot) {
320 autoboot_timeout = INFINITE_TIME;
321 } else {
322 autoboot_timeout = LKBOOT_AUTOBOOT_TIMEOUT;
323 sysparam_read("lkboot.autoboot_timeout", &autoboot_timeout, sizeof(autoboot_timeout));
324 }
325
326 TRACEF("autoboot %u autoboot_timeout %u\n", autoboot, (uint)autoboot_timeout);
327
328#if LKBOOT_WITH_SERVER
329 lkboot_server(autoboot_timeout);
330#else
331 if (autoboot_timeout != INFINITE_TIME) {
332 TRACEF("waiting for %u milliseconds before autobooting\n", (uint)autoboot_timeout);
333 thread_sleep(autoboot_timeout);
334 }
335#endif
336
337 if (autoboot_timeout != INFINITE_TIME) {
338 TRACEF("trying to boot from flash...\n");
339 status_t err = do_flash_boot();
340 TRACEF("do_flash_boot returns %d\n", err);
341 }
342
343#if LKBOOT_WITH_SERVER
344 TRACEF("restarting server\n");
345 lkboot_server(INFINITE_TIME);
346#endif
347
348 TRACEF("nothing to do, exiting\n");
349}
350
351APP_START(lkboot)
352 .entry = lkboot_task,
353 .flags = 0,
354APP_END