From 50c30b0822cd9b096ccddc72bbd98e6ce5a1dc17 Mon Sep 17 00:00:00 2001 From: Shawn Nock Date: Thu, 2 May 2019 16:54:43 +0000 Subject: [PATCH] WIP shitshow --- src/base64.c | 4 +- src/crc.h | 77 +++++++++++++++++++++++++++ src/crc16_sw.c | 40 ++++++++++++++ src/image.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++-- src/main.c | 4 +- 5 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 src/crc.h create mode 100644 src/crc16_sw.c diff --git a/src/base64.c b/src/base64.c index aa8f464..73c3819 100644 --- a/src/base64.c +++ b/src/base64.c @@ -54,10 +54,12 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, *pos++ = base64_table[in[2] & 0x3f]; in += 3; line_len += 4; - if (line_len >= 72) { + /* + if (line_len >= 72) { *pos++ = '\n'; line_len = 0; } + */ } if (end - in) { diff --git a/src/crc.h b/src/crc.h new file mode 100644 index 0000000..48fc384 --- /dev/null +++ b/src/crc.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 Shawn Nock + * Copyright (c) 2018 Workaround GmbH. + * Copyright (c) 2017 Intel Corporation. + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * Copyright (c) 2018 Google LLC. + * + * SPDX-License-Identifier: Apache-2.0 + */ +/** @file + * @brief CRC computation function + */ + +#ifndef ZEPHYR_INCLUDE_CRC_H_ +#define ZEPHYR_INCLUDE_CRC_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup checksum Checksum + */ + +/** + * @defgroup crc CRC + * @ingroup checksum + * @{ + */ + +/** + * @brief Generic function for computing CRC 16 + * + * Compute CRC 16 by passing in the address of the input, the input length + * and polynomial used in addition to the initial value. + * + * @param src Input bytes for the computation + * @param len Length of the input in bytes + * @param polynomial The polynomial to use omitting the leading x^16 + * coefficient + * @param initial_value Initial value for the CRC computation + * @param pad Adds padding with zeros at the end of input bytes + * + * @return The computed CRC16 value + */ +uint16_t crc16(const uint8_t *src, size_t len, uint16_t polynomial, + uint16_t initial_value, bool pad); + +/** + * @brief Compute ANSI variant of CRC 16 + * + * ANSI variant of CRC 16 is using 0x8005 as its polynomial with the initial + * value set to 0xffff. + * + * @param src Input bytes for the computation + * @param len Length of the input in bytes + * + * @return The computed CRC16 value + */ +static inline uint16_t crc16_ansi(const uint8_t *src, size_t len) +{ + return crc16(src, len, 0x8005, 0xffff, true); +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/crc16_sw.c b/src/crc16_sw.c new file mode 100644 index 0000000..9230c7a --- /dev/null +++ b/src/crc16_sw.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "crc.h" + +uint16_t crc16(const uint8_t *src, size_t len, uint16_t polynomial, + uint16_t initial_value, bool pad) +{ + uint16_t crc = initial_value; + size_t padding = pad ? sizeof(crc) : 0; + size_t i, b; + + /* src length + padding (if required) */ + for (i = 0; i < len + padding; i++) { + + for (b = 0; b < 8; b++) { + uint16_t divide = crc & 0x8000UL; + + crc = (crc << 1U); + + /* choose input bytes or implicit trailing zeros */ + if (i < len) { + crc |= !!(src[i] & (0x80U >> b)); + } + + if (divide != 0U) { + crc = crc ^ polynomial; + } + } + } + + return crc; +} + + diff --git a/src/image.c b/src/image.c index 96efbda..ee2b12e 100644 --- a/src/image.c +++ b/src/image.c @@ -7,16 +7,25 @@ #include #include #include +#include +#include #include "base64.h" +#include "crc.h" #include "image.h" #include "nmgr.h" #include "cbor.h" +#define IMAGE_CHUNK_SIZE 60 + +#define CRC16_INITIAL_CRC 0 /* what to seed crc16 with */ +#define CRC_CITT_POLYMINAL 0x1021 + static int serial_fd = -1; void image_help(void){ - printf("umcumgr image [version|upload]\n"); + fprintf(stderr, "umcumgr image [version|upload]\n"); + exit(1); } static char *hex(char in) { @@ -87,8 +96,17 @@ static void do_version(void) { assert_pkt_start(tmp); size_t out_len; + if (in_len < 2) { + fprintf(stderr, "packet too small, bailing"); + exit(5); + } uint8_t *decoded_buf = base64_decode(tmp+2, in_len-2, &out_len); + if (!decoded_buf) { + fprintf(stderr, "Failed to decode base64, bailing\n"); + exit(5); + } + // Unclear why this would be useful in a line oriented (cannonical mode) program uint8_t *p = decoded_buf; uint16_t pkt_len = ntohs(*(uint16_t *)p); @@ -130,8 +148,119 @@ static void do_version(void) { free(decoded_buf); } -void do_upload(void){ - return; +static void wrap_and_send_pkt(uint8_t *data, size_t len) { + uint8_t b64_buf[BOOT_SERIAL_OUT_MAX]; + struct nmgr_hdr hdr; + hdr.nh_op = NMGR_OP_WRITE; + hdr.nh_group = htons(MGMT_GROUP_ID_IMAGE); + hdr.nh_id = IMGMGR_NMGR_ID_UPLOAD; + + uint16_t crc = crc16((uint8_t*)&hdr, sizeof(struct nmgr_hdr), CRC_CITT_POLYMINAL, CRC16_INITIAL_CRC, false); + crc = crc16(data, len, CRC_CITT_POLYMINAL, crc, true); + crc = htons(crc); + + uint16_t tot_len = len + sizeof(struct nmgr_hdr) + sizeof(crc); + uint16_t net_len = htons(tot_len); + + uint8_t *pos = b64_buf; + memcpy(b64_buf, &net_len, sizeof(tot_len)); + pos += sizeof(tot_len); + memcpy(pos, data, len); + pos += len; + memcpy(pos, &crc, sizeof(crc)); + + size_t out_len; + uint8_t *out_buf = base64_encode(b64_buf, tot_len, &out_len); + + + fprintf(stderr, "Trying to wrap %u bytes\n", (unsigned) len); + + uint8_t start[] = {SHELL_NLIP_PKT_START1, SHELL_NLIP_PKT_START2}; + write(serial_fd, start, sizeof(start)); + write(serial_fd, out_buf, out_len-1); + write(serial_fd, "\n", 1); + fsync(serial_fd); + free(out_buf); +} + +void do_upload(char *filename){ + int fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open \"%s\": %s\n", filename, strerror(errno)); + exit(2); + } + + struct stat st; + fstat(fd, &st); + size_t pos = 0, len = st.st_size; + + while (pos < len){ + CborEncoder root, map; + uint8_t cbor_buf[BOOT_SERIAL_OUT_MAX]; + cbor_encoder_init(&root, cbor_buf, BOOT_SERIAL_OUT_MAX, 0); + cbor_encoder_create_map(&root, &map, pos == 0 ? 3 : 2); + + if (pos == 0) { + cbor_encode_text_stringz(&map, "len"); + cbor_encode_int(&map, len); + } + + cbor_encode_text_stringz(&map, "off"); + cbor_encode_int(&map, pos); + + cbor_encode_text_stringz(&map, "data"); + + uint8_t data[IMAGE_CHUNK_SIZE]; + int actual_size; + if ((actual_size = read(fd, data, IMAGE_CHUNK_SIZE)) < IMAGE_CHUNK_SIZE) { + if (errno != 0) { + fprintf(stderr, "Error reading the firmware file: %s", strerror(errno)); + exit(2); + } + } + cbor_encode_byte_string(&map, data, actual_size); + cbor_encoder_close_container(&root, &map); + wrap_and_send_pkt(data, cbor_encoder_get_buffer_size(&root, cbor_buf)); + + uint8_t resp_buf[BOOT_SERIAL_IN_MAX]; + int in_len = read(serial_fd, &resp_buf, BOOT_SERIAL_IN_MAX); + if (in_len < 0) { + fprintf(stderr, "Failed to read from serial port: %s", strerror(errno)); + exit(4); + } + + if (in_len < 2) { + fprintf(stderr, "packet too small, bailing"); + exit(5); + } + + printlen(resp_buf, in_len); + fflush(stdout); + + size_t out_len; + uint8_t *out = base64_decode(resp_buf+2, in_len-2, &out_len); + + CborParser parser; + CborValue it, rc; + cbor_parser_init(out, out_len, 0, &parser, &it); + cbor_require_type(&it, CborMapType); + if (cbor_value_map_find_value(&it, "rc", &rc)) { + fprintf(stderr, "Failed to find an response code %s:%d\n", __FILE__, __LINE__); + } + int r; + cbor_value_get_int(&rc, &r); + if (!r) { + fprintf(stderr, "Got an error response to a sent chunk, bailing\n"); + exit(3); + } + if (cbor_value_map_find_value(&it, "off", &rc)) { + fprintf(stderr, "Failed to find an offset in the response %s:%d\n", __FILE__, __LINE__); + } else { + cbor_value_get_int(&rc, &r); + printf("Wrote %d\n", r); + } + pos += actual_size; + } } void image_main(int fd, int argc, char **argv){ @@ -143,7 +272,10 @@ void image_main(int fd, int argc, char **argv){ if (!strcmp(argv[0], "version")) { do_version(); } else if (!strcmp(argv[0], "upload")) { - do_upload(); + if (argc < 2) { + fprintf(stderr, "umcumgr image upload \n"); + } + do_upload(argv[1]); } else { image_help(); } diff --git a/src/main.c b/src/main.c index b7841a8..8ed0e4b 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ int main(int argc, char **argv) { } } - int serial_fd = open(serial_port, O_RDWR | O_NOCTTY); + int serial_fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY); if (serial_fd < 0) { fprintf(stderr, "Unable to open %s: %s\n", serial_port, strerror(errno)); return 1; @@ -44,9 +44,11 @@ int main(int argc, char **argv) { return 2; } + t_options.c_cflag[] if (tcsetattr(serial_fd, TCSANOW, &t_options) < 0){ fprintf(stderr, "Failed to set termios attrs.\n"); } + tcflush(serial_fd, TCIOFLUSH); uint8_t rem = (uint8_t)(argc - optind); if (rem) {