blob: f4c60877d1cf8ae25dd5fd5cce852ca946880d6c [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 Puzdrowski8e96b832017-09-08 16:49:14 +020052
53#include <flash_map/flash_map.h>
54#include <hal/hal_flash.h>
55#include <os/os.h>
56#include <os/os_malloc.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080057
58#include <bootutil/image.h>
59
60#include "boot_serial/boot_serial.h"
61#include "boot_serial_priv.h"
62
63#define BOOT_SERIAL_OUT_MAX 48
64
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020065#ifdef __ZEPHYR__
Carles Cufi0165be82018-03-26 17:43:51 +020066/* base64 lib encodes data to null-terminated string */
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020067#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
68
69#define CRC16_INITIAL_CRC 0 /* what to seed crc16 with */
70#define CRC_CITT_POLYMINAL 0x1021
71
72#define ntohs(x) sys_be16_to_cpu(x)
73#define htons(x) sys_cpu_to_be16(x)
74
75static char in_buf[CONFIG_BOOT_MAX_LINE_INPUT_LEN + 1];
76static char dec_buf[CONFIG_BOOT_MAX_LINE_INPUT_LEN + 1];
77#endif
78
Christopher Collins92ea77f2016-12-12 15:59:26 -080079static uint32_t curr_off;
80static uint32_t img_size;
81static struct nmgr_hdr *bs_hdr;
82
83static char bs_obuf[BOOT_SERIAL_OUT_MAX];
84
85static int bs_cbor_writer(struct cbor_encoder_writer *, const char *data,
86 int len);
87static void boot_serial_output(void);
88
89static struct cbor_encoder_writer bs_writer = {
90 .write = bs_cbor_writer
91};
92static CborEncoder bs_root;
93static CborEncoder bs_rsp;
94
95int
96bs_cbor_writer(struct cbor_encoder_writer *cew, const char *data, int len)
97{
98 memcpy(&bs_obuf[cew->bytes_written], data, len);
99 cew->bytes_written += len;
100
101 return 0;
102}
103
104/*
105 * Looks for 'name' from NULL-terminated json data in buf.
106 * Returns pointer to first character of value for that name.
107 * Returns NULL if 'name' is not found.
108 */
109char *
110bs_find_val(char *buf, char *name)
111{
112 char *ptr;
113
114 ptr = strstr(buf, name);
115 if (!ptr) {
116 return NULL;
117 }
118 ptr += strlen(name);
119
120 while (*ptr != '\0') {
Andrzej Puzdrowski268cdd02018-04-10 12:57:54 +0200121 if (*ptr != ':' && !isspace((int)*ptr)) {
Christopher Collins92ea77f2016-12-12 15:59:26 -0800122 break;
123 }
124 ++ptr;
125 }
126 if (*ptr == '\0') {
127 ptr = NULL;
128 }
129 return ptr;
130}
131
132/*
133 * List images.
134 */
135static void
136bs_list(char *buf, int len)
137{
138 CborEncoder images;
139 CborEncoder image;
140 struct image_header hdr;
141 uint8_t tmpbuf[64];
142 int i, area_id;
143 const struct flash_area *fap;
144
145 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
146 cbor_encode_text_stringz(&bs_rsp, "images");
147 cbor_encoder_create_array(&bs_rsp, &images, CborIndefiniteLength);
148 for (i = 0; i < 2; i++) {
149 area_id = flash_area_id_from_image_slot(i);
150 if (flash_area_open(area_id, &fap)) {
151 continue;
152 }
153
154 flash_area_read(fap, 0, &hdr, sizeof(hdr));
155
156 if (hdr.ih_magic != IMAGE_MAGIC ||
157 bootutil_img_validate(&hdr, fap, tmpbuf, sizeof(tmpbuf),
158 NULL, 0, NULL)) {
159 flash_area_close(fap);
160 continue;
161 }
162 flash_area_close(fap);
163
164 cbor_encoder_create_map(&images, &image, CborIndefiniteLength);
165 cbor_encode_text_stringz(&image, "slot");
166 cbor_encode_int(&image, i);
167 cbor_encode_text_stringz(&image, "version");
168
169 len = snprintf((char *)tmpbuf, sizeof(tmpbuf),
170 "%u.%u.%u.%u", hdr.ih_ver.iv_major, hdr.ih_ver.iv_minor,
171 hdr.ih_ver.iv_revision, (unsigned int)hdr.ih_ver.iv_build_num);
172 cbor_encode_text_stringz(&image, (char *)tmpbuf);
173 cbor_encoder_close_container(&images, &image);
174 }
175 cbor_encoder_close_container(&bs_rsp, &images);
176 cbor_encoder_close_container(&bs_root, &bs_rsp);
177 boot_serial_output();
178}
179
180/*
181 * Image upload request.
182 */
183static void
184bs_upload(char *buf, int len)
185{
186 CborParser parser;
187 struct cbor_buf_reader reader;
188 struct CborValue value;
189 uint8_t img_data[400];
190 long long unsigned int off = UINT_MAX;
191 size_t img_blen = 0;
Fabio Utzig30f6b2a2018-03-29 16:18:53 -0300192 uint8_t rem_bytes;
Christopher Collins92ea77f2016-12-12 15:59:26 -0800193 long long unsigned int data_len = UINT_MAX;
194 const struct cbor_attr_t attr[4] = {
195 [0] = {
196 .attribute = "data",
197 .type = CborAttrByteStringType,
198 .addr.bytestring.data = img_data,
199 .addr.bytestring.len = &img_blen,
200 .len = sizeof(img_data)
201 },
202 [1] = {
203 .attribute = "off",
204 .type = CborAttrUnsignedIntegerType,
205 .addr.uinteger = &off,
206 .nodefault = true
207 },
208 [2] = {
209 .attribute = "len",
210 .type = CborAttrUnsignedIntegerType,
211 .addr.uinteger = &data_len,
212 .nodefault = true
213 }
214 };
215 const struct flash_area *fap = NULL;
216 int rc;
217
218 memset(img_data, 0, sizeof(img_data));
219 cbor_buf_reader_init(&reader, (uint8_t *)buf, len);
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200220#ifdef __ZEPHYR__
221 cbor_parser_cust_reader_init(&reader.r, 0, &parser, &value);
222#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800223 cbor_parser_init(&reader.r, 0, &parser, &value);
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200224#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800225 rc = cbor_read_object(&value, attr);
226 if (rc || off == UINT_MAX) {
227 rc = MGMT_ERR_EINVAL;
228 goto out;
229 }
230
Christopher Collins92ea77f2016-12-12 15:59:26 -0800231 rc = flash_area_open(flash_area_id_from_image_slot(0), &fap);
232 if (rc) {
233 rc = MGMT_ERR_EINVAL;
234 goto out;
235 }
236
237 if (off == 0) {
238 curr_off = 0;
239 if (data_len > fap->fa_size) {
240 rc = MGMT_ERR_EINVAL;
241 goto out;
242 }
243 rc = flash_area_erase(fap, 0, fap->fa_size);
244 if (rc) {
245 rc = MGMT_ERR_EINVAL;
246 goto out;
247 }
248 img_size = data_len;
249 }
250 if (off != curr_off) {
251 rc = 0;
252 goto out;
253 }
Fabio Utzig30f6b2a2018-03-29 16:18:53 -0300254 if (curr_off + img_blen < img_size) {
255 rem_bytes = img_blen % flash_area_align(fap);
256 if (rem_bytes) {
257 img_blen -= rem_bytes;
258 }
259 }
Christopher Collins92ea77f2016-12-12 15:59:26 -0800260 rc = flash_area_write(fap, curr_off, img_data, img_blen);
261 if (rc) {
262 rc = MGMT_ERR_EINVAL;
263 goto out;
264 }
265 curr_off += img_blen;
266
267out:
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200268 BOOT_LOG_INF("RX: 0x%x", rc);
Christopher Collins92ea77f2016-12-12 15:59:26 -0800269 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
270 cbor_encode_text_stringz(&bs_rsp, "rc");
271 cbor_encode_int(&bs_rsp, rc);
272 if (rc == 0) {
273 cbor_encode_text_stringz(&bs_rsp, "off");
274 cbor_encode_uint(&bs_rsp, curr_off);
275 }
276 cbor_encoder_close_container(&bs_root, &bs_rsp);
277
278 boot_serial_output();
279 flash_area_close(fap);
280}
281
282/*
283 * Console echo control. Send empty response, don't do anything.
284 */
285static void
286bs_echo_ctl(char *buf, int len)
287{
288 boot_serial_output();
289}
290
291/*
292 * Reset, and (presumably) boot to newly uploaded image. Flush console
293 * before restarting.
294 */
Andrzej Puzdrowski268cdd02018-04-10 12:57:54 +0200295static void
Christopher Collins92ea77f2016-12-12 15:59:26 -0800296bs_reset(char *buf, int len)
297{
298 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
299 cbor_encode_text_stringz(&bs_rsp, "rc");
300 cbor_encode_int(&bs_rsp, 0);
301 cbor_encoder_close_container(&bs_root, &bs_rsp);
302
303 boot_serial_output();
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200304#ifdef __ZEPHYR__
305 k_sleep(250);
306 sys_reboot(SYS_REBOOT_COLD);
307#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800308 os_cputime_delay_usecs(250000);
309 hal_system_reset();
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200310#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800311}
312
313/*
314 * Parse incoming line of input from console.
315 * Expect newtmgr protocol with serial transport.
316 */
317void
318boot_serial_input(char *buf, int len)
319{
320 struct nmgr_hdr *hdr;
321
322 hdr = (struct nmgr_hdr *)buf;
323 if (len < sizeof(*hdr) ||
324 (hdr->nh_op != NMGR_OP_READ && hdr->nh_op != NMGR_OP_WRITE) ||
325 (ntohs(hdr->nh_len) < len - sizeof(*hdr))) {
326 return;
327 }
328 bs_hdr = hdr;
329 hdr->nh_group = ntohs(hdr->nh_group);
330
331 buf += sizeof(*hdr);
332 len -= sizeof(*hdr);
Christopher Collins92ea77f2016-12-12 15:59:26 -0800333 bs_writer.bytes_written = 0;
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200334#ifdef __ZEPHYR__
335 cbor_encoder_cust_writer_init(&bs_root, &bs_writer, 0);
336#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800337 cbor_encoder_init(&bs_root, &bs_writer, 0);
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200338#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800339
340 /*
341 * Limited support for commands.
342 */
343 if (hdr->nh_group == MGMT_GROUP_ID_IMAGE) {
344 switch (hdr->nh_id) {
345 case IMGMGR_NMGR_OP_STATE:
346 bs_list(buf, len);
347 break;
348 case IMGMGR_NMGR_OP_UPLOAD:
349 bs_upload(buf, len);
350 break;
351 default:
352 break;
353 }
354 } else if (hdr->nh_group == MGMT_GROUP_ID_DEFAULT) {
355 switch (hdr->nh_id) {
356 case NMGR_ID_CONS_ECHO_CTRL:
357 bs_echo_ctl(buf, len);
358 break;
359 case NMGR_ID_RESET:
360 bs_reset(buf, len);
361 break;
362 default:
363 break;
364 }
365 }
366}
367
368static void
369boot_serial_output(void)
370{
371 char *data;
372 int len;
373 uint16_t crc;
374 uint16_t totlen;
375 char pkt_start[2] = { SHELL_NLIP_PKT_START1, SHELL_NLIP_PKT_START2 };
376 char buf[BOOT_SERIAL_OUT_MAX];
377 char encoded_buf[BASE64_ENCODE_SIZE(BOOT_SERIAL_OUT_MAX)];
378
379 data = bs_obuf;
380 len = bs_writer.bytes_written;
381
382 bs_hdr->nh_op++;
383 bs_hdr->nh_flags = NMGR_F_CBOR_RSP_COMPLETE;
384 bs_hdr->nh_len = htons(len);
385 bs_hdr->nh_group = htons(bs_hdr->nh_group);
386
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200387#ifdef __ZEPHYR__
388 crc = crc16((u8_t *)bs_hdr, sizeof(*bs_hdr), CRC_CITT_POLYMINAL, CRC16_INITIAL_CRC,
389 false);
390 crc = crc16(data, len, CRC_CITT_POLYMINAL, crc, true);
391#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800392 crc = crc16_ccitt(CRC16_INITIAL_CRC, bs_hdr, sizeof(*bs_hdr));
393 crc = crc16_ccitt(crc, data, len);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200394#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800395 crc = htons(crc);
396
397 console_write(pkt_start, sizeof(pkt_start));
398
399 totlen = len + sizeof(*bs_hdr) + sizeof(crc);
400 totlen = htons(totlen);
401
402 memcpy(buf, &totlen, sizeof(totlen));
403 totlen = sizeof(totlen);
404 memcpy(&buf[totlen], bs_hdr, sizeof(*bs_hdr));
405 totlen += sizeof(*bs_hdr);
406 memcpy(&buf[totlen], data, len);
407 totlen += len;
408 memcpy(&buf[totlen], &crc, sizeof(crc));
409 totlen += sizeof(crc);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200410#ifdef __ZEPHYR__
411 size_t enc_len;
Carles Cufi0165be82018-03-26 17:43:51 +0200412 base64_encode(encoded_buf, sizeof(encoded_buf), &enc_len, buf, totlen);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200413 totlen = enc_len;
414#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800415 totlen = base64_encode(buf, totlen, encoded_buf, 1);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200416#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800417 console_write(encoded_buf, totlen);
418 console_write("\n", 1);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200419 BOOT_LOG_INF("TX");
Christopher Collins92ea77f2016-12-12 15:59:26 -0800420}
421
422/*
423 * Returns 1 if full packet has been received.
424 */
425static int
426boot_serial_in_dec(char *in, int inlen, char *out, int *out_off, int maxout)
427{
428 int rc;
429 uint16_t crc;
430 uint16_t len;
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200431#ifdef __ZEPHYR__
432 int err;
Carles Cufi0165be82018-03-26 17:43:51 +0200433 err = base64_decode( &out[*out_off], maxout, &rc, in, inlen - 2);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200434 if (err) {
435 return -1;
436 }
437#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800438 if (*out_off + base64_decode_len(in) >= maxout) {
439 return -1;
440 }
441 rc = base64_decode(in, &out[*out_off]);
442 if (rc < 0) {
443 return -1;
444 }
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200445#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800446 *out_off += rc;
447
448 if (*out_off > sizeof(uint16_t)) {
449 len = ntohs(*(uint16_t *)out);
450
451 len = min(len, *out_off - sizeof(uint16_t));
452 out += sizeof(uint16_t);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200453#ifdef __ZEPHYR__
454 crc = crc16(out, len, CRC_CITT_POLYMINAL, CRC16_INITIAL_CRC, true);
455#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800456 crc = crc16_ccitt(CRC16_INITIAL_CRC, out, len);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200457#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800458 if (crc || len <= sizeof(crc)) {
459 return 0;
460 }
461 *out_off -= sizeof(crc);
462 out[*out_off] = '\0';
463
464 return 1;
465 }
466 return 0;
467}
468
469/*
470 * Task which waits reading console, expecting to get image over
471 * serial port.
472 */
473void
474boot_serial_start(int max_input)
475{
476 int rc;
477 int off;
478 char *buf;
479 char *dec;
480 int dec_off;
481 int full_line;
482
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200483#ifdef __ZEPHYR__
484 rc = boot_console_init();
485 buf = in_buf;
486 dec = dec_buf;
487 assert(max_input <= sizeof(in_buf) && max_input <= sizeof(dec_buf));
488#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800489 rc = console_init(NULL);
490 assert(rc == 0);
491 console_echo(0);
Christopher Collins92ea77f2016-12-12 15:59:26 -0800492 buf = os_malloc(max_input);
493 dec = os_malloc(max_input);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200494#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800495 assert(buf && dec);
496
497 off = 0;
498 while (1) {
499 rc = console_read(buf + off, max_input - off, &full_line);
500 if (rc <= 0 && !full_line) {
501 continue;
502 }
503 off += rc;
504 if (!full_line) {
505 continue;
506 }
507 if (buf[0] == SHELL_NLIP_PKT_START1 &&
508 buf[1] == SHELL_NLIP_PKT_START2) {
509 dec_off = 0;
510 rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
511 } else if (buf[0] == SHELL_NLIP_DATA_START1 &&
512 buf[1] == SHELL_NLIP_DATA_START2) {
513 rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
514 }
515 if (rc == 1) {
516 boot_serial_input(&dec[2], dec_off - 2);
517 }
518 off = 0;
519 }
520}