blob: 623225c8041c764341e267d641b258ae9e25ae33 [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#include "bootutil/bootutil_log.h"
28
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020029#ifdef __ZEPHYR__
30#include <misc/reboot.h>
31#include <misc/byteorder.h>
32#include <misc/__assert.h>
33#include <flash.h>
34#include <crc16.h>
35#include <serial_adapter/serial_adapter.h>
Carles Cufi0165be82018-03-26 17:43:51 +020036#include <base64.h>
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +020037#include <cbor.h>
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020038#else
Christopher Collins92ea77f2016-12-12 15:59:26 -080039#include <bsp/bsp.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080040#include <hal/hal_system.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080041#include <os/endian.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080042#include <os/os_cputime.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080043#include <console/console.h>
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020044#include <crc/crc16.h>
45#include <base64/base64.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080046#include <tinycbor/cbor.h>
47#include <tinycbor/cbor_buf_reader.h>
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +020048#endif /* __ZEPHYR__ */
49
Christopher Collins92ea77f2016-12-12 15:59:26 -080050#include <cborattr/cborattr.h>
Andrzej Puzdrowskib788c712018-04-12 12:42:49 +020051#include <flash_map_backend/flash_map_backend.h>
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020052#include <hal/hal_flash.h>
53#include <os/os.h>
54#include <os/os_malloc.h>
Christopher Collins92ea77f2016-12-12 15:59:26 -080055
56#include <bootutil/image.h>
57
58#include "boot_serial/boot_serial.h"
59#include "boot_serial_priv.h"
60
61#define BOOT_SERIAL_OUT_MAX 48
62
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020063#ifdef __ZEPHYR__
Carles Cufi0165be82018-03-26 17:43:51 +020064/* base64 lib encodes data to null-terminated string */
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +020065#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
66
67#define CRC16_INITIAL_CRC 0 /* what to seed crc16 with */
68#define CRC_CITT_POLYMINAL 0x1021
69
70#define ntohs(x) sys_be16_to_cpu(x)
71#define htons(x) sys_cpu_to_be16(x)
72
73static char in_buf[CONFIG_BOOT_MAX_LINE_INPUT_LEN + 1];
74static char dec_buf[CONFIG_BOOT_MAX_LINE_INPUT_LEN + 1];
75#endif
76
Christopher Collins92ea77f2016-12-12 15:59:26 -080077static uint32_t curr_off;
78static uint32_t img_size;
79static struct nmgr_hdr *bs_hdr;
80
81static char bs_obuf[BOOT_SERIAL_OUT_MAX];
82
83static int bs_cbor_writer(struct cbor_encoder_writer *, const char *data,
84 int len);
85static void boot_serial_output(void);
86
87static struct cbor_encoder_writer bs_writer = {
88 .write = bs_cbor_writer
89};
90static CborEncoder bs_root;
91static CborEncoder bs_rsp;
92
93int
94bs_cbor_writer(struct cbor_encoder_writer *cew, const char *data, int len)
95{
96 memcpy(&bs_obuf[cew->bytes_written], data, len);
97 cew->bytes_written += len;
98
99 return 0;
100}
101
102/*
103 * Looks for 'name' from NULL-terminated json data in buf.
104 * Returns pointer to first character of value for that name.
105 * Returns NULL if 'name' is not found.
106 */
107char *
108bs_find_val(char *buf, char *name)
109{
110 char *ptr;
111
112 ptr = strstr(buf, name);
113 if (!ptr) {
114 return NULL;
115 }
116 ptr += strlen(name);
117
118 while (*ptr != '\0') {
Andrzej Puzdrowski268cdd02018-04-10 12:57:54 +0200119 if (*ptr != ':' && !isspace((int)*ptr)) {
Christopher Collins92ea77f2016-12-12 15:59:26 -0800120 break;
121 }
122 ++ptr;
123 }
124 if (*ptr == '\0') {
125 ptr = NULL;
126 }
127 return ptr;
128}
129
130/*
131 * List images.
132 */
133static void
134bs_list(char *buf, int len)
135{
136 CborEncoder images;
137 CborEncoder image;
138 struct image_header hdr;
139 uint8_t tmpbuf[64];
140 int i, area_id;
141 const struct flash_area *fap;
142
143 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
144 cbor_encode_text_stringz(&bs_rsp, "images");
145 cbor_encoder_create_array(&bs_rsp, &images, CborIndefiniteLength);
146 for (i = 0; i < 2; i++) {
147 area_id = flash_area_id_from_image_slot(i);
148 if (flash_area_open(area_id, &fap)) {
149 continue;
150 }
151
152 flash_area_read(fap, 0, &hdr, sizeof(hdr));
153
154 if (hdr.ih_magic != IMAGE_MAGIC ||
155 bootutil_img_validate(&hdr, fap, tmpbuf, sizeof(tmpbuf),
156 NULL, 0, NULL)) {
157 flash_area_close(fap);
158 continue;
159 }
160 flash_area_close(fap);
161
162 cbor_encoder_create_map(&images, &image, CborIndefiniteLength);
163 cbor_encode_text_stringz(&image, "slot");
164 cbor_encode_int(&image, i);
165 cbor_encode_text_stringz(&image, "version");
166
167 len = snprintf((char *)tmpbuf, sizeof(tmpbuf),
168 "%u.%u.%u.%u", hdr.ih_ver.iv_major, hdr.ih_ver.iv_minor,
169 hdr.ih_ver.iv_revision, (unsigned int)hdr.ih_ver.iv_build_num);
170 cbor_encode_text_stringz(&image, (char *)tmpbuf);
171 cbor_encoder_close_container(&images, &image);
172 }
173 cbor_encoder_close_container(&bs_rsp, &images);
174 cbor_encoder_close_container(&bs_root, &bs_rsp);
175 boot_serial_output();
176}
177
178/*
179 * Image upload request.
180 */
181static void
182bs_upload(char *buf, int len)
183{
184 CborParser parser;
185 struct cbor_buf_reader reader;
186 struct CborValue value;
187 uint8_t img_data[400];
188 long long unsigned int off = UINT_MAX;
189 size_t img_blen = 0;
Fabio Utzig30f6b2a2018-03-29 16:18:53 -0300190 uint8_t rem_bytes;
Christopher Collins92ea77f2016-12-12 15:59:26 -0800191 long long unsigned int data_len = UINT_MAX;
192 const struct cbor_attr_t attr[4] = {
193 [0] = {
194 .attribute = "data",
195 .type = CborAttrByteStringType,
196 .addr.bytestring.data = img_data,
197 .addr.bytestring.len = &img_blen,
198 .len = sizeof(img_data)
199 },
200 [1] = {
201 .attribute = "off",
202 .type = CborAttrUnsignedIntegerType,
203 .addr.uinteger = &off,
204 .nodefault = true
205 },
206 [2] = {
207 .attribute = "len",
208 .type = CborAttrUnsignedIntegerType,
209 .addr.uinteger = &data_len,
210 .nodefault = true
211 }
212 };
213 const struct flash_area *fap = NULL;
214 int rc;
215
216 memset(img_data, 0, sizeof(img_data));
217 cbor_buf_reader_init(&reader, (uint8_t *)buf, len);
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200218#ifdef __ZEPHYR__
219 cbor_parser_cust_reader_init(&reader.r, 0, &parser, &value);
220#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800221 cbor_parser_init(&reader.r, 0, &parser, &value);
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200222#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800223 rc = cbor_read_object(&value, attr);
224 if (rc || off == UINT_MAX) {
225 rc = MGMT_ERR_EINVAL;
226 goto out;
227 }
228
Christopher Collins92ea77f2016-12-12 15:59:26 -0800229 rc = flash_area_open(flash_area_id_from_image_slot(0), &fap);
230 if (rc) {
231 rc = MGMT_ERR_EINVAL;
232 goto out;
233 }
234
235 if (off == 0) {
236 curr_off = 0;
237 if (data_len > fap->fa_size) {
238 rc = MGMT_ERR_EINVAL;
239 goto out;
240 }
241 rc = flash_area_erase(fap, 0, fap->fa_size);
242 if (rc) {
243 rc = MGMT_ERR_EINVAL;
244 goto out;
245 }
246 img_size = data_len;
247 }
248 if (off != curr_off) {
249 rc = 0;
250 goto out;
251 }
Fabio Utzig30f6b2a2018-03-29 16:18:53 -0300252 if (curr_off + img_blen < img_size) {
253 rem_bytes = img_blen % flash_area_align(fap);
254 if (rem_bytes) {
255 img_blen -= rem_bytes;
256 }
257 }
Christopher Collins92ea77f2016-12-12 15:59:26 -0800258 rc = flash_area_write(fap, curr_off, img_data, img_blen);
259 if (rc) {
260 rc = MGMT_ERR_EINVAL;
261 goto out;
262 }
263 curr_off += img_blen;
264
265out:
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200266 BOOT_LOG_INF("RX: 0x%x", rc);
Christopher Collins92ea77f2016-12-12 15:59:26 -0800267 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
268 cbor_encode_text_stringz(&bs_rsp, "rc");
269 cbor_encode_int(&bs_rsp, rc);
270 if (rc == 0) {
271 cbor_encode_text_stringz(&bs_rsp, "off");
272 cbor_encode_uint(&bs_rsp, curr_off);
273 }
274 cbor_encoder_close_container(&bs_root, &bs_rsp);
275
276 boot_serial_output();
277 flash_area_close(fap);
278}
279
280/*
281 * Console echo control. Send empty response, don't do anything.
282 */
283static void
284bs_echo_ctl(char *buf, int len)
285{
286 boot_serial_output();
287}
288
289/*
290 * Reset, and (presumably) boot to newly uploaded image. Flush console
291 * before restarting.
292 */
Andrzej Puzdrowski268cdd02018-04-10 12:57:54 +0200293static void
Christopher Collins92ea77f2016-12-12 15:59:26 -0800294bs_reset(char *buf, int len)
295{
296 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
297 cbor_encode_text_stringz(&bs_rsp, "rc");
298 cbor_encode_int(&bs_rsp, 0);
299 cbor_encoder_close_container(&bs_root, &bs_rsp);
300
301 boot_serial_output();
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200302#ifdef __ZEPHYR__
303 k_sleep(250);
304 sys_reboot(SYS_REBOOT_COLD);
305#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800306 os_cputime_delay_usecs(250000);
307 hal_system_reset();
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200308#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800309}
310
311/*
312 * Parse incoming line of input from console.
313 * Expect newtmgr protocol with serial transport.
314 */
315void
316boot_serial_input(char *buf, int len)
317{
318 struct nmgr_hdr *hdr;
319
320 hdr = (struct nmgr_hdr *)buf;
321 if (len < sizeof(*hdr) ||
322 (hdr->nh_op != NMGR_OP_READ && hdr->nh_op != NMGR_OP_WRITE) ||
323 (ntohs(hdr->nh_len) < len - sizeof(*hdr))) {
324 return;
325 }
326 bs_hdr = hdr;
327 hdr->nh_group = ntohs(hdr->nh_group);
328
329 buf += sizeof(*hdr);
330 len -= sizeof(*hdr);
Christopher Collins92ea77f2016-12-12 15:59:26 -0800331 bs_writer.bytes_written = 0;
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200332#ifdef __ZEPHYR__
333 cbor_encoder_cust_writer_init(&bs_root, &bs_writer, 0);
334#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800335 cbor_encoder_init(&bs_root, &bs_writer, 0);
Andrzej Puzdrowski386b5922018-04-06 19:26:24 +0200336#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800337
338 /*
339 * Limited support for commands.
340 */
341 if (hdr->nh_group == MGMT_GROUP_ID_IMAGE) {
342 switch (hdr->nh_id) {
343 case IMGMGR_NMGR_OP_STATE:
344 bs_list(buf, len);
345 break;
346 case IMGMGR_NMGR_OP_UPLOAD:
347 bs_upload(buf, len);
348 break;
349 default:
350 break;
351 }
352 } else if (hdr->nh_group == MGMT_GROUP_ID_DEFAULT) {
353 switch (hdr->nh_id) {
354 case NMGR_ID_CONS_ECHO_CTRL:
355 bs_echo_ctl(buf, len);
356 break;
357 case NMGR_ID_RESET:
358 bs_reset(buf, len);
359 break;
360 default:
361 break;
362 }
363 }
364}
365
366static void
367boot_serial_output(void)
368{
369 char *data;
370 int len;
371 uint16_t crc;
372 uint16_t totlen;
373 char pkt_start[2] = { SHELL_NLIP_PKT_START1, SHELL_NLIP_PKT_START2 };
374 char buf[BOOT_SERIAL_OUT_MAX];
375 char encoded_buf[BASE64_ENCODE_SIZE(BOOT_SERIAL_OUT_MAX)];
376
377 data = bs_obuf;
378 len = bs_writer.bytes_written;
379
380 bs_hdr->nh_op++;
381 bs_hdr->nh_flags = NMGR_F_CBOR_RSP_COMPLETE;
382 bs_hdr->nh_len = htons(len);
383 bs_hdr->nh_group = htons(bs_hdr->nh_group);
384
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200385#ifdef __ZEPHYR__
386 crc = crc16((u8_t *)bs_hdr, sizeof(*bs_hdr), CRC_CITT_POLYMINAL, CRC16_INITIAL_CRC,
387 false);
388 crc = crc16(data, len, CRC_CITT_POLYMINAL, crc, true);
389#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800390 crc = crc16_ccitt(CRC16_INITIAL_CRC, bs_hdr, sizeof(*bs_hdr));
391 crc = crc16_ccitt(crc, data, len);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200392#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800393 crc = htons(crc);
394
395 console_write(pkt_start, sizeof(pkt_start));
396
397 totlen = len + sizeof(*bs_hdr) + sizeof(crc);
398 totlen = htons(totlen);
399
400 memcpy(buf, &totlen, sizeof(totlen));
401 totlen = sizeof(totlen);
402 memcpy(&buf[totlen], bs_hdr, sizeof(*bs_hdr));
403 totlen += sizeof(*bs_hdr);
404 memcpy(&buf[totlen], data, len);
405 totlen += len;
406 memcpy(&buf[totlen], &crc, sizeof(crc));
407 totlen += sizeof(crc);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200408#ifdef __ZEPHYR__
409 size_t enc_len;
Carles Cufi0165be82018-03-26 17:43:51 +0200410 base64_encode(encoded_buf, sizeof(encoded_buf), &enc_len, buf, totlen);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200411 totlen = enc_len;
412#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800413 totlen = base64_encode(buf, totlen, encoded_buf, 1);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200414#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800415 console_write(encoded_buf, totlen);
416 console_write("\n", 1);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200417 BOOT_LOG_INF("TX");
Christopher Collins92ea77f2016-12-12 15:59:26 -0800418}
419
420/*
421 * Returns 1 if full packet has been received.
422 */
423static int
424boot_serial_in_dec(char *in, int inlen, char *out, int *out_off, int maxout)
425{
426 int rc;
427 uint16_t crc;
428 uint16_t len;
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200429#ifdef __ZEPHYR__
430 int err;
Carles Cufi0165be82018-03-26 17:43:51 +0200431 err = base64_decode( &out[*out_off], maxout, &rc, in, inlen - 2);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200432 if (err) {
433 return -1;
434 }
435#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800436 if (*out_off + base64_decode_len(in) >= maxout) {
437 return -1;
438 }
439 rc = base64_decode(in, &out[*out_off]);
440 if (rc < 0) {
441 return -1;
442 }
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200443#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800444 *out_off += rc;
445
446 if (*out_off > sizeof(uint16_t)) {
447 len = ntohs(*(uint16_t *)out);
448
449 len = min(len, *out_off - sizeof(uint16_t));
450 out += sizeof(uint16_t);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200451#ifdef __ZEPHYR__
452 crc = crc16(out, len, CRC_CITT_POLYMINAL, CRC16_INITIAL_CRC, true);
453#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800454 crc = crc16_ccitt(CRC16_INITIAL_CRC, out, len);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200455#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800456 if (crc || len <= sizeof(crc)) {
457 return 0;
458 }
459 *out_off -= sizeof(crc);
460 out[*out_off] = '\0';
461
462 return 1;
463 }
464 return 0;
465}
466
467/*
468 * Task which waits reading console, expecting to get image over
469 * serial port.
470 */
471void
472boot_serial_start(int max_input)
473{
474 int rc;
475 int off;
476 char *buf;
477 char *dec;
478 int dec_off;
479 int full_line;
480
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200481#ifdef __ZEPHYR__
482 rc = boot_console_init();
483 buf = in_buf;
484 dec = dec_buf;
485 assert(max_input <= sizeof(in_buf) && max_input <= sizeof(dec_buf));
486#else
Christopher Collins92ea77f2016-12-12 15:59:26 -0800487 rc = console_init(NULL);
488 assert(rc == 0);
489 console_echo(0);
Christopher Collins92ea77f2016-12-12 15:59:26 -0800490 buf = os_malloc(max_input);
491 dec = os_malloc(max_input);
Andrzej Puzdrowski8e96b832017-09-08 16:49:14 +0200492#endif
Christopher Collins92ea77f2016-12-12 15:59:26 -0800493 assert(buf && dec);
494
495 off = 0;
496 while (1) {
497 rc = console_read(buf + off, max_input - off, &full_line);
498 if (rc <= 0 && !full_line) {
499 continue;
500 }
501 off += rc;
502 if (!full_line) {
503 continue;
504 }
505 if (buf[0] == SHELL_NLIP_PKT_START1 &&
506 buf[1] == SHELL_NLIP_PKT_START2) {
507 dec_off = 0;
508 rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
509 } else if (buf[0] == SHELL_NLIP_DATA_START1 &&
510 buf[1] == SHELL_NLIP_DATA_START2) {
511 rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
512 }
513 if (rc == 1) {
514 boot_serial_input(&dec[2], dec_off - 2);
515 }
516 off = 0;
517 }
518}