blob: ac935c1b54476891a79f0b788a3e7cae93993174 [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
27#include <bsp/bsp.h>
28
29#include <flash_map/flash_map.h>
30#include <hal/hal_flash.h>
31#include <hal/hal_system.h>
32
33#include <os/endian.h>
34#include <os/os.h>
35#include <os/os_malloc.h>
36#include <os/os_cputime.h>
37
38#include <console/console.h>
39
40#include <tinycbor/cbor.h>
41#include <tinycbor/cbor_buf_reader.h>
42#include <cborattr/cborattr.h>
43#include <base64/base64.h>
44#include <crc/crc16.h>
45
46#include <bootutil/image.h>
47
48#include "boot_serial/boot_serial.h"
49#include "boot_serial_priv.h"
50
51#define BOOT_SERIAL_OUT_MAX 48
52
53static uint32_t curr_off;
54static uint32_t img_size;
55static struct nmgr_hdr *bs_hdr;
56
57static char bs_obuf[BOOT_SERIAL_OUT_MAX];
58
59static int bs_cbor_writer(struct cbor_encoder_writer *, const char *data,
60 int len);
61static void boot_serial_output(void);
62
63static struct cbor_encoder_writer bs_writer = {
64 .write = bs_cbor_writer
65};
66static CborEncoder bs_root;
67static CborEncoder bs_rsp;
68
69int
70bs_cbor_writer(struct cbor_encoder_writer *cew, const char *data, int len)
71{
72 memcpy(&bs_obuf[cew->bytes_written], data, len);
73 cew->bytes_written += len;
74
75 return 0;
76}
77
78/*
79 * Looks for 'name' from NULL-terminated json data in buf.
80 * Returns pointer to first character of value for that name.
81 * Returns NULL if 'name' is not found.
82 */
83char *
84bs_find_val(char *buf, char *name)
85{
86 char *ptr;
87
88 ptr = strstr(buf, name);
89 if (!ptr) {
90 return NULL;
91 }
92 ptr += strlen(name);
93
94 while (*ptr != '\0') {
95 if (*ptr != ':' && !isspace(*ptr)) {
96 break;
97 }
98 ++ptr;
99 }
100 if (*ptr == '\0') {
101 ptr = NULL;
102 }
103 return ptr;
104}
105
106/*
107 * List images.
108 */
109static void
110bs_list(char *buf, int len)
111{
112 CborEncoder images;
113 CborEncoder image;
114 struct image_header hdr;
115 uint8_t tmpbuf[64];
116 int i, area_id;
117 const struct flash_area *fap;
118
119 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
120 cbor_encode_text_stringz(&bs_rsp, "images");
121 cbor_encoder_create_array(&bs_rsp, &images, CborIndefiniteLength);
122 for (i = 0; i < 2; i++) {
123 area_id = flash_area_id_from_image_slot(i);
124 if (flash_area_open(area_id, &fap)) {
125 continue;
126 }
127
128 flash_area_read(fap, 0, &hdr, sizeof(hdr));
129
130 if (hdr.ih_magic != IMAGE_MAGIC ||
131 bootutil_img_validate(&hdr, fap, tmpbuf, sizeof(tmpbuf),
132 NULL, 0, NULL)) {
133 flash_area_close(fap);
134 continue;
135 }
136 flash_area_close(fap);
137
138 cbor_encoder_create_map(&images, &image, CborIndefiniteLength);
139 cbor_encode_text_stringz(&image, "slot");
140 cbor_encode_int(&image, i);
141 cbor_encode_text_stringz(&image, "version");
142
143 len = snprintf((char *)tmpbuf, sizeof(tmpbuf),
144 "%u.%u.%u.%u", hdr.ih_ver.iv_major, hdr.ih_ver.iv_minor,
145 hdr.ih_ver.iv_revision, (unsigned int)hdr.ih_ver.iv_build_num);
146 cbor_encode_text_stringz(&image, (char *)tmpbuf);
147 cbor_encoder_close_container(&images, &image);
148 }
149 cbor_encoder_close_container(&bs_rsp, &images);
150 cbor_encoder_close_container(&bs_root, &bs_rsp);
151 boot_serial_output();
152}
153
154/*
155 * Image upload request.
156 */
157static void
158bs_upload(char *buf, int len)
159{
160 CborParser parser;
161 struct cbor_buf_reader reader;
162 struct CborValue value;
163 uint8_t img_data[400];
164 long long unsigned int off = UINT_MAX;
165 size_t img_blen = 0;
166 long long unsigned int data_len = UINT_MAX;
167 const struct cbor_attr_t attr[4] = {
168 [0] = {
169 .attribute = "data",
170 .type = CborAttrByteStringType,
171 .addr.bytestring.data = img_data,
172 .addr.bytestring.len = &img_blen,
173 .len = sizeof(img_data)
174 },
175 [1] = {
176 .attribute = "off",
177 .type = CborAttrUnsignedIntegerType,
178 .addr.uinteger = &off,
179 .nodefault = true
180 },
181 [2] = {
182 .attribute = "len",
183 .type = CborAttrUnsignedIntegerType,
184 .addr.uinteger = &data_len,
185 .nodefault = true
186 }
187 };
188 const struct flash_area *fap = NULL;
189 int rc;
190
191 memset(img_data, 0, sizeof(img_data));
192 cbor_buf_reader_init(&reader, (uint8_t *)buf, len);
193 cbor_parser_init(&reader.r, 0, &parser, &value);
194 rc = cbor_read_object(&value, attr);
195 if (rc || off == UINT_MAX) {
196 rc = MGMT_ERR_EINVAL;
197 goto out;
198 }
199
200
201 rc = flash_area_open(flash_area_id_from_image_slot(0), &fap);
202 if (rc) {
203 rc = MGMT_ERR_EINVAL;
204 goto out;
205 }
206
207 if (off == 0) {
208 curr_off = 0;
209 if (data_len > fap->fa_size) {
210 rc = MGMT_ERR_EINVAL;
211 goto out;
212 }
213 rc = flash_area_erase(fap, 0, fap->fa_size);
214 if (rc) {
215 rc = MGMT_ERR_EINVAL;
216 goto out;
217 }
218 img_size = data_len;
219 }
220 if (off != curr_off) {
221 rc = 0;
222 goto out;
223 }
224 rc = flash_area_write(fap, curr_off, img_data, img_blen);
225 if (rc) {
226 rc = MGMT_ERR_EINVAL;
227 goto out;
228 }
229 curr_off += img_blen;
230
231out:
232 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
233 cbor_encode_text_stringz(&bs_rsp, "rc");
234 cbor_encode_int(&bs_rsp, rc);
235 if (rc == 0) {
236 cbor_encode_text_stringz(&bs_rsp, "off");
237 cbor_encode_uint(&bs_rsp, curr_off);
238 }
239 cbor_encoder_close_container(&bs_root, &bs_rsp);
240
241 boot_serial_output();
242 flash_area_close(fap);
243}
244
245/*
246 * Console echo control. Send empty response, don't do anything.
247 */
248static void
249bs_echo_ctl(char *buf, int len)
250{
251 boot_serial_output();
252}
253
254/*
255 * Reset, and (presumably) boot to newly uploaded image. Flush console
256 * before restarting.
257 */
258static int
259bs_reset(char *buf, int len)
260{
261 cbor_encoder_create_map(&bs_root, &bs_rsp, CborIndefiniteLength);
262 cbor_encode_text_stringz(&bs_rsp, "rc");
263 cbor_encode_int(&bs_rsp, 0);
264 cbor_encoder_close_container(&bs_root, &bs_rsp);
265
266 boot_serial_output();
267 os_cputime_delay_usecs(250000);
268 hal_system_reset();
269}
270
271/*
272 * Parse incoming line of input from console.
273 * Expect newtmgr protocol with serial transport.
274 */
275void
276boot_serial_input(char *buf, int len)
277{
278 struct nmgr_hdr *hdr;
279
280 hdr = (struct nmgr_hdr *)buf;
281 if (len < sizeof(*hdr) ||
282 (hdr->nh_op != NMGR_OP_READ && hdr->nh_op != NMGR_OP_WRITE) ||
283 (ntohs(hdr->nh_len) < len - sizeof(*hdr))) {
284 return;
285 }
286 bs_hdr = hdr;
287 hdr->nh_group = ntohs(hdr->nh_group);
288
289 buf += sizeof(*hdr);
290 len -= sizeof(*hdr);
291
292 bs_writer.bytes_written = 0;
293 cbor_encoder_init(&bs_root, &bs_writer, 0);
294
295 /*
296 * Limited support for commands.
297 */
298 if (hdr->nh_group == MGMT_GROUP_ID_IMAGE) {
299 switch (hdr->nh_id) {
300 case IMGMGR_NMGR_OP_STATE:
301 bs_list(buf, len);
302 break;
303 case IMGMGR_NMGR_OP_UPLOAD:
304 bs_upload(buf, len);
305 break;
306 default:
307 break;
308 }
309 } else if (hdr->nh_group == MGMT_GROUP_ID_DEFAULT) {
310 switch (hdr->nh_id) {
311 case NMGR_ID_CONS_ECHO_CTRL:
312 bs_echo_ctl(buf, len);
313 break;
314 case NMGR_ID_RESET:
315 bs_reset(buf, len);
316 break;
317 default:
318 break;
319 }
320 }
321}
322
323static void
324boot_serial_output(void)
325{
326 char *data;
327 int len;
328 uint16_t crc;
329 uint16_t totlen;
330 char pkt_start[2] = { SHELL_NLIP_PKT_START1, SHELL_NLIP_PKT_START2 };
331 char buf[BOOT_SERIAL_OUT_MAX];
332 char encoded_buf[BASE64_ENCODE_SIZE(BOOT_SERIAL_OUT_MAX)];
333
334 data = bs_obuf;
335 len = bs_writer.bytes_written;
336
337 bs_hdr->nh_op++;
338 bs_hdr->nh_flags = NMGR_F_CBOR_RSP_COMPLETE;
339 bs_hdr->nh_len = htons(len);
340 bs_hdr->nh_group = htons(bs_hdr->nh_group);
341
342 crc = crc16_ccitt(CRC16_INITIAL_CRC, bs_hdr, sizeof(*bs_hdr));
343 crc = crc16_ccitt(crc, data, len);
344 crc = htons(crc);
345
346 console_write(pkt_start, sizeof(pkt_start));
347
348 totlen = len + sizeof(*bs_hdr) + sizeof(crc);
349 totlen = htons(totlen);
350
351 memcpy(buf, &totlen, sizeof(totlen));
352 totlen = sizeof(totlen);
353 memcpy(&buf[totlen], bs_hdr, sizeof(*bs_hdr));
354 totlen += sizeof(*bs_hdr);
355 memcpy(&buf[totlen], data, len);
356 totlen += len;
357 memcpy(&buf[totlen], &crc, sizeof(crc));
358 totlen += sizeof(crc);
359 totlen = base64_encode(buf, totlen, encoded_buf, 1);
360 console_write(encoded_buf, totlen);
361 console_write("\n", 1);
362}
363
364/*
365 * Returns 1 if full packet has been received.
366 */
367static int
368boot_serial_in_dec(char *in, int inlen, char *out, int *out_off, int maxout)
369{
370 int rc;
371 uint16_t crc;
372 uint16_t len;
373
374 if (*out_off + base64_decode_len(in) >= maxout) {
375 return -1;
376 }
377 rc = base64_decode(in, &out[*out_off]);
378 if (rc < 0) {
379 return -1;
380 }
381 *out_off += rc;
382
383 if (*out_off > sizeof(uint16_t)) {
384 len = ntohs(*(uint16_t *)out);
385
386 len = min(len, *out_off - sizeof(uint16_t));
387 out += sizeof(uint16_t);
388 crc = crc16_ccitt(CRC16_INITIAL_CRC, out, len);
389 if (crc || len <= sizeof(crc)) {
390 return 0;
391 }
392 *out_off -= sizeof(crc);
393 out[*out_off] = '\0';
394
395 return 1;
396 }
397 return 0;
398}
399
400/*
401 * Task which waits reading console, expecting to get image over
402 * serial port.
403 */
404void
405boot_serial_start(int max_input)
406{
407 int rc;
408 int off;
409 char *buf;
410 char *dec;
411 int dec_off;
412 int full_line;
413
414 rc = console_init(NULL);
415 assert(rc == 0);
416 console_echo(0);
417
418 buf = os_malloc(max_input);
419 dec = os_malloc(max_input);
420 assert(buf && dec);
421
422 off = 0;
423 while (1) {
424 rc = console_read(buf + off, max_input - off, &full_line);
425 if (rc <= 0 && !full_line) {
426 continue;
427 }
428 off += rc;
429 if (!full_line) {
430 continue;
431 }
432 if (buf[0] == SHELL_NLIP_PKT_START1 &&
433 buf[1] == SHELL_NLIP_PKT_START2) {
434 dec_off = 0;
435 rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
436 } else if (buf[0] == SHELL_NLIP_DATA_START1 &&
437 buf[1] == SHELL_NLIP_DATA_START2) {
438 rc = boot_serial_in_dec(&buf[2], off - 2, dec, &dec_off, max_input);
439 }
440 if (rc == 1) {
441 boot_serial_input(&dec[2], dec_off - 2);
442 }
443 off = 0;
444 }
445}