blob: 5e6fff103f677ae75e8673a31b5431db8ad991b0 [file] [log] [blame]
Christopher Collins92ea77f2016-12-12 15:59:26 -08001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19#include <assert.h>
20#include <stddef.h>
21#include <inttypes.h>
22#include <ctype.h>
23#include <stdio.h>
24
25#include "sysflash/sysflash.h"
26
Fabio Utzig1a2e41a2017-11-17 12:13:09 -020027#define BOOT_LOG_LEVEL BOOT_LOG_LEVEL_INFO
28#include "bootutil/bootutil_log.h"
29
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020030#ifdef __ZEPHYR__
31#include <misc/reboot.h>
32#include <misc/byteorder.h>
33#include <misc/__assert.h>
34#include <flash.h>
35#include <crc16.h>
36#include <serial_adapter/serial_adapter.h>
Carles Cufi0165be82018-03-26 17:43:51 +020037#include <base64.h>
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +020038#include <cbor.h>
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020039#else
Christopher Collins92ea77f2016-12-12 15:59:26 -080040#include <bsp/bsp.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080041#include <hal/hal_system.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080042#include <os/endian.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080043#include <os/os_cputime.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080044#include <console/console.h>
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020045#include <crc/crc16.h>
46#include <base64/base64.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080047#include <tinycbor/cbor.h>
48#include <tinycbor/cbor_buf_reader.h>
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +020049#endif /* __ZEPHYR__ */
50
Christopher Collins92ea77f2016-12-12 15:59:26 -080051#include <cborattr/cborattr.h>
Andrzej Puzdrowskib788c712018-04-12 12:42:49 +020052#include <flash_map_backend/flash_map_backend.h>
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020053#include <hal/hal_flash.h>
54#include <os/os.h>
55#include <os/os_malloc.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080056
57#include <bootutil/image.h>
58
59#include "boot_serial/boot_serial.h"
60#include "boot_serial_priv.h"
61
62#define BOOT_SERIAL_OUT_MAX 48
63
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020064#ifdef __ZEPHYR__
Carles Cufi0165be82018-03-26 17:43:51 +020065/* base64 lib encodes data to null-terminated string */
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020066#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
67
68#define CRC16_INITIAL_CRC 0 /* what to seed crc16 with */
69#define CRC_CITT_POLYMINAL 0x1021
70
71#define ntohs(x) sys_be16_to_cpu(x)
72#define htons(x) sys_cpu_to_be16(x)
73
74static char in_buf[CONFIG_BOOT_MAX_LINE_INPUT_LEN + 1];
75static char dec_buf[CONFIG_BOOT_MAX_LINE_INPUT_LEN + 1];
76#endif
77
Christopher Collins92ea77f2016-12-12 15:59:26 -080078static uint32_t curr_off;
79static uint32_t img_size;
80static struct nmgr_hdr *bs_hdr;
81
82static char bs_obuf[BOOT_SERIAL_OUT_MAX];
83
84static int bs_cbor_writer(struct cbor_encoder_writer *, const char *data,
85 int len);
86static void boot_serial_output(void);
87
88static struct cbor_encoder_writer bs_writer = {
89 .write = bs_cbor_writer
90};
91static CborEncoder bs_root;
92static CborEncoder bs_rsp;
93
94int
95bs_cbor_writer(struct cbor_encoder_writer *cew, const char *data, int len)
96{
97 memcpy(&bs_obuf[cew->bytes_written], data, len);
98 cew->bytes_written += len;
99
100 return 0;
101}
102
103/*
104 * Looks for 'name' from NULL-terminated json data in buf.
105 * Returns pointer to first character of value for that name.
106 * Returns NULL if 'name' is not found.
107 */
108char *
109bs_find_val(char *buf, char *name)
110{
111 char *ptr;
112
113 ptr = strstr(buf, name);
114 if (!ptr) {
115 return NULL;
116 }
117 ptr += strlen(name);
118
119 while (*ptr != '\0') {
Andrzej Puzdrowski268cdd02018-04-10 12:57:54 +0200120 if (*ptr != ':' && !isspace((int)*ptr)) {
Christopher Collins92ea77f2016-12-12 15:59:26 -0800121 break;
122 }
123 ++ptr;
124 }
125 if (*ptr == '\0') {
126 ptr = NULL;
127 }
128 return ptr;
129}
130
131/*
132 * List images.
133 */
134static void
135bs_list(char *buf, int len)
136{
137 CborEncoder images;
138 CborEncoder image;
139 struct image_header hdr;
140 uint8_t tmpbuf[64];
141 int i, area_id;
142 const struct flash_area *fap;
143
144 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
145 cbor_encode_text_stringz(&bs_rsp, "images");
146 cbor_encoder_create_array(&bs_rsp, &images, CborIndefiniteLength);
147 for (i = 0; i < 2; i++) {
148 area_id = flash_area_id_from_image_slot(i);
149 if (flash_area_open(area_id, &fap)) {
150 continue;
151 }
152
153 flash_area_read(fap, 0, &hdr, sizeof(hdr));
154
155 if (hdr.ih_magic != IMAGE_MAGIC ||
156 bootutil_img_validate(&hdr, fap, tmpbuf, sizeof(tmpbuf),
157 NULL, 0, NULL)) {
158 flash_area_close(fap);
159 continue;
160 }
161 flash_area_close(fap);
162
163 cbor_encoder_create_map(&images, &image, CborIndefiniteLength);
164 cbor_encode_text_stringz(&image, "slot");
165 cbor_encode_int(&image, i);
166 cbor_encode_text_stringz(&image, "version");
167
168 len = snprintf((char *)tmpbuf, sizeof(tmpbuf),
169 "%u.%u.%u.%u", hdr.ih_ver.iv_major, hdr.ih_ver.iv_minor,
170 hdr.ih_ver.iv_revision, (unsigned int)hdr.ih_ver.iv_build_num);
171 cbor_encode_text_stringz(&image, (char *)tmpbuf);
172 cbor_encoder_close_container(&images, &image);
173 }
174 cbor_encoder_close_container(&bs_rsp, &images);
175 cbor_encoder_close_container(&bs_root, &bs_rsp);
176 boot_serial_output();
177}
178
179/*
180 * Image upload request.
181 */
182static void
183bs_upload(char *buf, int len)
184{
185 CborParser parser;
186 struct cbor_buf_reader reader;
187 struct CborValue value;
188 uint8_t img_data[400];
189 long long unsigned int off = UINT_MAX;
190 size_t img_blen = 0;
Fabio Utzig30f6b2a2018-03-29 16:18:53 -0300191 uint8_t rem_bytes;
Christopher Collins92ea77f2016-12-12 15:59:26 -0800192 long long unsigned int data_len = UINT_MAX;
193 const struct cbor_attr_t attr[4] = {
194 [0] = {
195 .attribute = "data",
196 .type = CborAttrByteStringType,
197 .addr.bytestring.data = img_data,
198 .addr.bytestring.len = &img_blen,
199 .len = sizeof(img_data)
200 },
201 [1] = {
202 .attribute = "off",
203 .type = CborAttrUnsignedIntegerType,
204 .addr.uinteger = &off,
205 .nodefault = true
206 },
207 [2] = {
208 .attribute = "len",
209 .type = CborAttrUnsignedIntegerType,
210 .addr.uinteger = &data_len,
211 .nodefault = true
212 }
213 };
214 const struct flash_area *fap = NULL;
215 int rc;
216
217 memset(img_data, 0, sizeof(img_data));
218 cbor_buf_reader_init(&reader, (uint8_t *)buf, len);
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200219#ifdef __ZEPHYR__
220 cbor_parser_cust_reader_init(&reader.r, 0, &parser, &value);
221#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800222 cbor_parser_init(&reader.r, 0, &parser, &value);
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200223#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800224 rc = cbor_read_object(&value, attr);
225 if (rc || off == UINT_MAX) {
226 rc = MGMT_ERR_EINVAL;
227 goto out;
228 }
229
Christopher Collins92ea77f2016-12-12 15:59:26 -0800230 rc = flash_area_open(flash_area_id_from_image_slot(0), &fap);
231 if (rc) {
232 rc = MGMT_ERR_EINVAL;
233 goto out;
234 }
235
236 if (off == 0) {
237 curr_off = 0;
238 if (data_len > fap->fa_size) {
239 rc = MGMT_ERR_EINVAL;
240 goto out;
241 }
242 rc = flash_area_erase(fap, 0, fap->fa_size);
243 if (rc) {
244 rc = MGMT_ERR_EINVAL;
245 goto out;
246 }
247 img_size = data_len;
248 }
249 if (off != curr_off) {
250 rc = 0;
251 goto out;
252 }
Fabio Utzig30f6b2a2018-03-29 16:18:53 -0300253 if (curr_off + img_blen < img_size) {
254 rem_bytes = img_blen % flash_area_align(fap);
255 if (rem_bytes) {
256 img_blen -= rem_bytes;
257 }
258 }
Christopher Collins92ea77f2016-12-12 15:59:26 -0800259 rc = flash_area_write(fap, curr_off, img_data, img_blen);
260 if (rc) {
261 rc = MGMT_ERR_EINVAL;
262 goto out;
263 }
264 curr_off += img_blen;
265
266out:
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200267 BOOT_LOG_INF("RX: 0x%x", rc);
Christopher Collins92ea77f2016-12-12 15:59:26 -0800268 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
269 cbor_encode_text_stringz(&bs_rsp, "rc");
270 cbor_encode_int(&bs_rsp, rc);
271 if (rc == 0) {
272 cbor_encode_text_stringz(&bs_rsp, "off");
273 cbor_encode_uint(&bs_rsp, curr_off);
274 }
275 cbor_encoder_close_container(&bs_root, &bs_rsp);
276
277 boot_serial_output();
278 flash_area_close(fap);
279}
280
281/*
282 * Console echo control. Send empty response, don't do anything.
283 */
284static void
285bs_echo_ctl(char *buf, int len)
286{
287 boot_serial_output();
288}
289
290/*
291 * Reset, and (presumably) boot to newly uploaded image. Flush console
292 * before restarting.
293 */
Andrzej Puzdrowski268cdd02018-04-10 12:57:54 +0200294static void
Christopher Collins92ea77f2016-12-12 15:59:26 -0800295bs_reset(char *buf, int len)
296{
297 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
298 cbor_encode_text_stringz(&bs_rsp, "rc");
299 cbor_encode_int(&bs_rsp, 0);
300 cbor_encoder_close_container(&bs_root, &bs_rsp);
301
302 boot_serial_output();
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200303#ifdef __ZEPHYR__
304 k_sleep(250);
305 sys_reboot(SYS_REBOOT_COLD);
306#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800307 os_cputime_delay_usecs(250000);
308 hal_system_reset();
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200309#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800310}
311
312/*
313 * Parse incoming line of input from console.
314 * Expect newtmgr protocol with serial transport.
315 */
316void
317boot_serial_input(char *buf, int len)
318{
319 struct nmgr_hdr *hdr;
320
321 hdr = (struct nmgr_hdr *)buf;
322 if (len < sizeof(*hdr) ||
323 (hdr->nh_op != NMGR_OP_READ && hdr->nh_op != NMGR_OP_WRITE) ||
324 (ntohs(hdr->nh_len) < len - sizeof(*hdr))) {
325 return;
326 }
327 bs_hdr = hdr;
328 hdr->nh_group = ntohs(hdr->nh_group);
329
330 buf += sizeof(*hdr);
331 len -= sizeof(*hdr);
Christopher Collins92ea77f2016-12-12 15:59:26 -0800332 bs_writer.bytes_written = 0;
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200333#ifdef __ZEPHYR__
334 cbor_encoder_cust_writer_init(&bs_root, &bs_writer, 0);
335#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800336 cbor_encoder_init(&bs_root, &bs_writer, 0);
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200337#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800338
339 /*
340 * Limited support for commands.
341 */
342 if (hdr->nh_group == MGMT_GROUP_ID_IMAGE) {
343 switch (hdr->nh_id) {
344 case IMGMGR_NMGR_OP_STATE:
345 bs_list(buf, len);
346 break;
347 case IMGMGR_NMGR_OP_UPLOAD:
348 bs_upload(buf, len);
349 break;
350 default:
351 break;
352 }
353 } else if (hdr->nh_group == MGMT_GROUP_ID_DEFAULT) {
354 switch (hdr->nh_id) {
355 case NMGR_ID_CONS_ECHO_CTRL:
356 bs_echo_ctl(buf, len);
357 break;
358 case NMGR_ID_RESET:
359 bs_reset(buf, len);
360 break;
361 default:
362 break;
363 }
364 }
365}
366
367static void
368boot_serial_output(void)
369{
370 char *data;
371 int len;
372 uint16_t crc;
373 uint16_t totlen;
374 char pkt_start[2] = { SHELL_NLIP_PKT_START1, SHELL_NLIP_PKT_START2 };
375 char buf[BOOT_SERIAL_OUT_MAX];
376 char encoded_buf[BASE64_ENCODE_SIZE(BOOT_SERIAL_OUT_MAX)];
377
378 data = bs_obuf;
379 len = bs_writer.bytes_written;
380
381 bs_hdr->nh_op++;
382 bs_hdr->nh_flags = NMGR_F_CBOR_RSP_COMPLETE;
383 bs_hdr->nh_len = htons(len);
384 bs_hdr->nh_group = htons(bs_hdr->nh_group);
385
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200386#ifdef __ZEPHYR__
387 crc = crc16((u8_t *)bs_hdr, sizeof(*bs_hdr), CRC_CITT_POLYMINAL, CRC16_INITIAL_CRC,
388 false);
389 crc = crc16(data, len, CRC_CITT_POLYMINAL, crc, true);
390#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800391 crc = crc16_ccitt(CRC16_INITIAL_CRC, bs_hdr, sizeof(*bs_hdr));
392 crc = crc16_ccitt(crc, data, len);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200393#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800394 crc = htons(crc);
395
396 console_write(pkt_start, sizeof(pkt_start));
397
398 totlen = len + sizeof(*bs_hdr) + sizeof(crc);
399 totlen = htons(totlen);
400
401 memcpy(buf, &totlen, sizeof(totlen));
402 totlen = sizeof(totlen);
403 memcpy(&buf[totlen], bs_hdr, sizeof(*bs_hdr));
404 totlen += sizeof(*bs_hdr);
405 memcpy(&buf[totlen], data, len);
406 totlen += len;
407 memcpy(&buf[totlen], &crc, sizeof(crc));
408 totlen += sizeof(crc);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200409#ifdef __ZEPHYR__
410 size_t enc_len;
Carles Cufi0165be82018-03-26 17:43:51 +0200411 base64_encode(encoded_buf, sizeof(encoded_buf), &enc_len, buf, totlen);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200412 totlen = enc_len;
413#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800414 totlen = base64_encode(buf, totlen, encoded_buf, 1);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200415#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800416 console_write(encoded_buf, totlen);
417 console_write("\n", 1);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200418 BOOT_LOG_INF("TX");
Christopher Collins92ea77f2016-12-12 15:59:26 -0800419}
420
421/*
422 * Returns 1 if full packet has been received.
423 */
424static int
425boot_serial_in_dec(char *in, int inlen, char *out, int *out_off, int maxout)
426{
427 int rc;
428 uint16_t crc;
429 uint16_t len;
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200430#ifdef __ZEPHYR__
431 int err;
Carles Cufi0165be82018-03-26 17:43:51 +0200432 err = base64_decode( &out[*out_off], maxout, &rc, in, inlen - 2);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200433 if (err) {
434 return -1;
435 }
436#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800437 if (*out_off + base64_decode_len(in) >= maxout) {
438 return -1;
439 }
440 rc = base64_decode(in, &out[*out_off]);
441 if (rc < 0) {
442 return -1;
443 }
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200444#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800445 *out_off += rc;
446
447 if (*out_off > sizeof(uint16_t)) {
448 len = ntohs(*(uint16_t *)out);
449
450 len = min(len, *out_off - sizeof(uint16_t));
451 out += sizeof(uint16_t);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200452#ifdef __ZEPHYR__
453 crc = crc16(out, len, CRC_CITT_POLYMINAL, CRC16_INITIAL_CRC, true);
454#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800455 crc = crc16_ccitt(CRC16_INITIAL_CRC, out, len);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200456#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800457 if (crc || len <= sizeof(crc)) {
458 return 0;
459 }
460 *out_off -= sizeof(crc);
461 out[*out_off] = '\0';
462
463 return 1;
464 }
465 return 0;
466}
467
468/*
469 * Task which waits reading console, expecting to get image over
470 * serial port.
471 */
472void
473boot_serial_start(int max_input)
474{
475 int rc;
476 int off;
477 char *buf;
478 char *dec;
479 int dec_off;
480 int full_line;
481
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200482#ifdef __ZEPHYR__
483 rc = boot_console_init();
484 buf = in_buf;
485 dec = dec_buf;
486 assert(max_input <= sizeof(in_buf) && max_input <= sizeof(dec_buf));
487#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800488 rc = console_init(NULL);
489 assert(rc == 0);
490 console_echo(0);
Christopher Collins92ea77f2016-12-12 15:59:26 -0800491 buf = os_malloc(max_input);
492 dec = os_malloc(max_input);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200493#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800494 assert(buf && dec);
495
496 off = 0;
497 while (1) {
498 rc = console_read(buf + off, max_input - off, &full_line);
499 if (rc <= 0 && !full_line) {
500 continue;
501 }
502 off += rc;
503 if (!full_line) {
504 continue;
505 }
506 if (buf[0] == SHELL_NLIP_PKT_START1 &&
507 buf[1] == SHELL_NLIP_PKT_START2) {
508 dec_off = 0;
509 rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
510 } else if (buf[0] == SHELL_NLIP_DATA_START1 &&
511 buf[1] == SHELL_NLIP_DATA_START2) {
512 rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
513 }
514 if (rc == 1) {
515 boot_serial_input(&dec[2], dec_off - 2);
516 }
517 off = 0;
518 }
519}