diff options
Diffstat (limited to 'Radio/HW/BladeRF/src/board/bladerf1/image.c')
-rw-r--r-- | Radio/HW/BladeRF/src/board/bladerf1/image.c | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/image.c b/Radio/HW/BladeRF/src/board/bladerf1/image.c new file mode 100644 index 0000000..ddea18b --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf1/image.c @@ -0,0 +1,592 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2013 Daniel Gröber <dxld AT darkboxed DOT org> + * Copyright (C) 2013 Nuand, LLC. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <limits.h> +#include <errno.h> + +#include <libbladeRF.h> + +#include "bladeRF.h" +#include "rel_assert.h" +#include "host_config.h" +#include "sha256.h" +#include "log.h" +#include "minmax.h" + +#include "board/board.h" +#include "driver/spi_flash.h" +#include "helpers/file.h" + +#include "flash.h" + +/* These two are used interchangeably - ensure they're the same! */ +#if SHA256_DIGEST_SIZE != BLADERF_IMAGE_CHECKSUM_LEN +#error "Image checksum size mismatch" +#endif + +#define CALC_IMAGE_SIZE(len) ((size_t) \ + ( \ + BLADERF_IMAGE_MAGIC_LEN + \ + BLADERF_IMAGE_CHECKSUM_LEN + \ + 3 * sizeof(uint16_t) + \ + sizeof(uint64_t) + \ + BLADERF_SERIAL_LENGTH + \ + BLADERF_IMAGE_RESERVED_LEN + \ + 3 * sizeof(uint32_t) + \ + len \ + ) \ +) + +static const char image_magic[] = "bladeRF"; + +#if BLADERF_OS_WINDOWS +#include <time.h> +static uint64_t get_timestamp() +{ + __time64_t now = _time64(NULL); + return (uint64_t)now; +} +#else +#include <sys/time.h> +static inline uint64_t get_timestamp() +{ + uint64_t ret; + struct timeval tv; + + if (gettimeofday(&tv, NULL) == 0) { + ret = tv.tv_sec; + } else { + log_verbose("gettimeofday failed: %s\n", strerror(errno)); + ret = 0; + } + + return ret; +} +#endif + +static void sha256_buffer(const char *buf, size_t len, + char digest[SHA256_DIGEST_SIZE]) +{ + SHA256_CTX ctx; + + SHA256_Init(&ctx); + SHA256_Update(&ctx, buf, len); + SHA256_Final((uint8_t*)digest, &ctx); +} + +static int verify_checksum(uint8_t *buf, size_t buf_len) +{ + char checksum_expected[SHA256_DIGEST_SIZE]; + char checksum_calc[SHA256_DIGEST_SIZE]; + + if (buf_len <= CALC_IMAGE_SIZE(0)) { + log_debug("Provided buffer isn't a full image\n"); + return BLADERF_ERR_INVAL; + } + + /* Backup and clear the expected checksum before we calculate the + * expected checksum */ + memcpy(checksum_expected, &buf[BLADERF_IMAGE_MAGIC_LEN], + sizeof(checksum_expected)); + + memset(&buf[BLADERF_IMAGE_MAGIC_LEN], 0, SHA256_DIGEST_SIZE); + + sha256_buffer((const char *)buf, buf_len, checksum_calc); + + if (memcmp(checksum_expected, checksum_calc, SHA256_DIGEST_SIZE) != 0) { + return BLADERF_ERR_CHECKSUM; + } else { + /* Restore the buffer's checksum so the caller can still use it */ + memcpy(&buf[BLADERF_IMAGE_MAGIC_LEN], checksum_expected, + sizeof(checksum_expected)); + + return 0; + } +} + +static bool image_type_is_valid(bladerf_image_type type) { + switch (type) { + case BLADERF_IMAGE_TYPE_RAW: + case BLADERF_IMAGE_TYPE_FIRMWARE: + case BLADERF_IMAGE_TYPE_FPGA_40KLE: + case BLADERF_IMAGE_TYPE_FPGA_115KLE: + case BLADERF_IMAGE_TYPE_FPGA_A4: + case BLADERF_IMAGE_TYPE_FPGA_A5: + case BLADERF_IMAGE_TYPE_FPGA_A9: + case BLADERF_IMAGE_TYPE_CALIBRATION: + case BLADERF_IMAGE_TYPE_RX_DC_CAL: + case BLADERF_IMAGE_TYPE_TX_DC_CAL: + case BLADERF_IMAGE_TYPE_RX_IQ_CAL: + case BLADERF_IMAGE_TYPE_TX_IQ_CAL: + case BLADERF_IMAGE_TYPE_GAIN_CAL: + return true; + + default: + return false; + } +} + +/* Serialize image contents and fill in checksum */ +static size_t pack_image(struct bladerf_image *img, uint8_t *buf) +{ + size_t i = 0; + uint16_t ver_field; + uint32_t type, len, addr; + uint64_t timestamp; + char checksum[BLADERF_IMAGE_CHECKSUM_LEN]; + + memcpy(&buf[i], img->magic, BLADERF_IMAGE_MAGIC_LEN); + i += BLADERF_IMAGE_MAGIC_LEN; + + memset(&buf[i], 0, BLADERF_IMAGE_CHECKSUM_LEN); + i += BLADERF_IMAGE_CHECKSUM_LEN; + + ver_field = HOST_TO_BE16(img->version.major); + memcpy(&buf[i], &ver_field, sizeof(ver_field)); + i += sizeof(ver_field); + + ver_field = HOST_TO_BE16(img->version.minor); + memcpy(&buf[i], &ver_field, sizeof(ver_field)); + i += sizeof(ver_field); + + ver_field = HOST_TO_BE16(img->version.patch); + memcpy(&buf[i], &ver_field, sizeof(ver_field)); + i += sizeof(ver_field); + + timestamp = HOST_TO_BE64(img->timestamp); + memcpy(&buf[i], ×tamp, sizeof(timestamp)); + i += sizeof(timestamp); + + memcpy(&buf[i], &img->serial, BLADERF_SERIAL_LENGTH); + i += BLADERF_SERIAL_LENGTH; + + memset(&buf[i], 0, BLADERF_IMAGE_RESERVED_LEN); + i += BLADERF_IMAGE_RESERVED_LEN; + + type = HOST_TO_BE32((uint32_t)img->type); + memcpy(&buf[i], &type, sizeof(type)); + i += sizeof(type); + + addr = HOST_TO_BE32(img->address); + memcpy(&buf[i], &addr, sizeof(addr)); + i += sizeof(addr); + + len = HOST_TO_BE32(img->length); + memcpy(&buf[i], &len, sizeof(len)); + i += sizeof(len); + + memcpy(&buf[i], img->data, img->length); + i += img->length; + + sha256_buffer((const char *)buf, i, checksum); + memcpy(&buf[BLADERF_IMAGE_MAGIC_LEN], checksum, BLADERF_IMAGE_CHECKSUM_LEN); + + return i; +} + +/* Unpack flash image from file and validate fields */ +static int unpack_image(struct bladerf_image *img, uint8_t *buf, size_t len) +{ + size_t i = 0; + uint32_t type; + + /* Ensure we have at least a full set of metadata */ + if (len < CALC_IMAGE_SIZE(0)) { + return BLADERF_ERR_INVAL; + } + + memcpy(img->magic, &buf[i], BLADERF_IMAGE_MAGIC_LEN); + img->magic[BLADERF_IMAGE_MAGIC_LEN] = '\0'; + if (strncmp(img->magic, image_magic, BLADERF_IMAGE_MAGIC_LEN)) { + return BLADERF_ERR_INVAL; + } + i += BLADERF_IMAGE_MAGIC_LEN; + + memcpy(img->checksum, &buf[i], BLADERF_IMAGE_CHECKSUM_LEN); + i += BLADERF_IMAGE_CHECKSUM_LEN; + + memcpy(&img->version.major, &buf[i], sizeof(img->version.major)); + i += sizeof(img->version.major); + img->version.major = BE16_TO_HOST(img->version.major); + + memcpy(&img->version.minor, &buf[i], sizeof(img->version.minor)); + i += sizeof(img->version.minor); + img->version.minor = BE16_TO_HOST(img->version.minor); + + memcpy(&img->version.patch, &buf[i], sizeof(img->version.patch)); + i += sizeof(img->version.patch); + img->version.patch = BE16_TO_HOST(img->version.patch); + + memcpy(&img->timestamp, &buf[i], sizeof(img->timestamp)); + i += sizeof(img->timestamp); + img->timestamp = BE64_TO_HOST(img->timestamp); + + memcpy(img->serial, &buf[i], BLADERF_SERIAL_LENGTH); + img->serial[BLADERF_SERIAL_LENGTH] = '\0'; + i += BLADERF_SERIAL_LENGTH; + + memcpy(img->reserved, &buf[i], BLADERF_IMAGE_RESERVED_LEN); + i += BLADERF_IMAGE_RESERVED_LEN; + + memcpy(&type, &buf[i], sizeof(type)); + i += sizeof(type); + type = BE32_TO_HOST(type); + + if (!image_type_is_valid((bladerf_image_type)type)) { + log_debug("Invalid type value in image: %d\n", (int)type); + return BLADERF_ERR_INVAL; + } else { + img->type = (bladerf_image_type)type; + } + + memcpy(&img->address, &buf[i], sizeof(img->address)); + i += sizeof(img->address); + img->address = BE32_TO_HOST(img->address); + + memcpy(&img->length, &buf[i], sizeof(img->length)); + i += sizeof(img->length); + img->length = BE32_TO_HOST(img->length); + + if (len != CALC_IMAGE_SIZE(img->length)) { + log_debug("Image contains more or less data than expected\n"); + return BLADERF_ERR_INVAL; + } + + /* Just slide the data over */ + memmove(&buf[0], &buf[i], img->length); + img->data = buf; + + return 0; +} + +int bladerf_image_print_metadata(const struct bladerf_image *image) { + if (!image) { + return BLADERF_ERR_MEM; + } + + printf("Magic: %s\n", image->magic); + printf("Type: %s\n", bladerf_image_type_to_string(image->type)); + printf("Version: %d.%d.%d\n", + image->version.major, image->version.minor, image->version.patch); + printf("Timestamp: %" PRIx64 "\n", image->timestamp); + printf("Serial: %s\n", image->serial); + printf("Address: %x\n", image->address); + printf("Length: %u\n", image->length); + fflush(stdout); + + return 0; +} + +const char* bladerf_image_type_to_string(bladerf_image_type type) { + switch (type) { + case BLADERF_IMAGE_TYPE_INVALID: + return "Invalid"; + case BLADERF_IMAGE_TYPE_RAW: + return "Raw Data"; + case BLADERF_IMAGE_TYPE_FIRMWARE: + return "Firmware"; + case BLADERF_IMAGE_TYPE_FPGA_40KLE: + return "FPGA 40 KLE Bitstream"; + case BLADERF_IMAGE_TYPE_FPGA_115KLE: + return "FPGA 115 KLE Bitstream"; + case BLADERF_IMAGE_TYPE_FPGA_A4: + return "FPGA A4 Bitstream"; + case BLADERF_IMAGE_TYPE_FPGA_A9: + return "FPGA A9 Bitstream"; + case BLADERF_IMAGE_TYPE_CALIBRATION: + return "Board Calibration"; + case BLADERF_IMAGE_TYPE_RX_DC_CAL: + return "RX DC Offset Calibration Table"; + case BLADERF_IMAGE_TYPE_TX_DC_CAL: + return "TX DC Offset Calibration Table"; + case BLADERF_IMAGE_TYPE_RX_IQ_CAL: + return "RX IQ Balance Calibration Table"; + case BLADERF_IMAGE_TYPE_TX_IQ_CAL: + return "TX IQ Balance Calibration Table"; + case BLADERF_IMAGE_TYPE_FPGA_A5: + return "FPGA A5 Bitstream"; + case BLADERF_IMAGE_TYPE_GAIN_CAL: + return "Gain Calibration"; + default: + return "Unknown Type"; + } +} + +int bladerf_image_write(struct bladerf *dev, + struct bladerf_image *img, const char *file) +{ + int rv; + FILE *f = NULL; + uint8_t *buf = NULL; + size_t buf_len; + + /* Ensure the format identifier is correct */ + if (memcmp(img->magic, image_magic, BLADERF_IMAGE_MAGIC_LEN) != 0) { +#ifdef LOGGING_ENABLED + char badmagic[BLADERF_IMAGE_MAGIC_LEN + 1]; + memset(badmagic, 0, sizeof(badmagic)); + memcpy(&badmagic, &img->magic, BLADERF_IMAGE_MAGIC_LEN); + log_debug("Invalid file format magic value: %s\n", badmagic); +#endif + return BLADERF_ERR_INVAL; + } + + /* Check for a valid image type */ + if (!image_type_is_valid(img->type)) { + log_debug("Invalid image type: %d\n", img->type); + return BLADERF_ERR_INVAL; + } + + /* Just to be tiny bit paranoid... */ + if (!img->data) { + log_debug("Image data pointer is NULL\n"); + return BLADERF_ERR_INVAL; + } + + buf_len = CALC_IMAGE_SIZE(img->length); + buf = (uint8_t *)calloc(1, buf_len); + if (!buf) { + log_verbose("calloc failed: %s\n", strerror(errno)); + return BLADERF_ERR_MEM; + } + + /* If the type is RAW, we should only allow erase-block aligned + * addresses and lengths */ + if (img->type == BLADERF_IMAGE_TYPE_RAW && img->address != 0xffffffff) { + if (img->address % dev->flash_arch->ebsize_bytes != 0) { + log_debug("Image address must be erase block-aligned for RAW.\n"); + rv = BLADERF_ERR_INVAL; + goto error; + } else if (img->length % dev->flash_arch->ebsize_bytes != 0) { + log_debug("Image length must be erase block-aligned for RAW.\n"); + rv = BLADERF_ERR_INVAL; + goto error; + } + } + + pack_image(img, buf); + + f = fopen(file, "wb"); + if (!f) { + if (errno == EACCES) { + rv = BLADERF_ERR_PERMISSION; + } else { + rv = BLADERF_ERR_IO; + } + + log_debug("Failed to open \"%s\": %s\n", file, strerror(errno)); + + goto error; + } + + rv = file_write(f, buf, buf_len); + +error: + if (f) { + fclose(f); + } + free(buf); + return rv; +} + +int bladerf_image_read(struct bladerf_image *img, const char *file) +{ + int rv = -1; + uint8_t *buf = NULL; + size_t buf_len; + + rv = file_read_buffer(file, &buf, &buf_len); + if (rv < 0) { + goto bladerf_image_read_out; + } + + rv = verify_checksum(buf, buf_len); + if (rv < 0) { + goto bladerf_image_read_out; + } + + /* Note: On success, buf->data = buf, with the data memmove'd over. + * Static analysis tools might indicate a false postive leak when + * buf goes out of scope with rv == 0 */ + rv = unpack_image(img, buf, buf_len); + +bladerf_image_read_out: + if (rv != 0) { + free(buf); + } + + return rv; +} + +static inline bool is_page_aligned(struct bladerf *dev, uint32_t val) +{ + return val % dev->flash_arch->psize_bytes == 0; +} + +static inline bool is_valid_addr_len(struct bladerf *dev, + uint32_t addr, uint32_t len) +{ + if (addr >= dev->flash_arch->tsize_bytes) { + return false; + } else if (len > dev->flash_arch->tsize_bytes) { + return false; + } else if ((addr + len) > dev->flash_arch->tsize_bytes) { + return false; + } else { + return true; + } +} + +struct bladerf_image * bladerf_alloc_image(struct bladerf *dev, + bladerf_image_type type, + uint32_t address, + uint32_t length) +{ + struct bladerf_image *image; + + assert(BLADERF_IMAGE_MAGIC_LEN == (sizeof(image_magic) - 1)); + + /* 0xffffffff is a placeholder for images that use the format but don't + * currently have an address in flash to live in */ + if (address != 0xffffffff) { + if (!is_page_aligned(dev, address)) { + log_debug("Address is not page-aligned: 0x%08x\n", address); + return NULL; + } else if (!is_page_aligned(dev, length)) { + log_debug("Length is not page-aligned: 0x%08x\n", length); + return NULL; + } else if (!is_valid_addr_len(dev, address, length)) { + log_debug("Invalid address=0x%08x or length=0x%08x\n", address, length); + return NULL; + } + } + + image = (struct bladerf_image *)calloc(1, sizeof(*image)); + + if (!image) { + return NULL; + } + + if (length) { + image->data = (uint8_t *)calloc(1, length); + if (!image->data) { + free(image); + return NULL; + } + } + + memcpy(image->magic, &image_magic, BLADERF_IMAGE_MAGIC_LEN); + + image->version.major = 0; + image->version.minor = 1; + image->version.patch = 0; + image->timestamp = get_timestamp(); + image->address = address; + image->length = length; + image->type = type; + + return image; +} + +static int make_cal_region(bladerf_fpga_size size, uint16_t vctcxo_trim, + uint8_t *buf, size_t len) +{ + int rv; + static const char fpga_size_40[] = "40"; + static const char fpga_size_115[] = "115"; + static const char fpga_size_A4[] = "A4"; + static const char fpga_size_A5[] = "A5"; + static const char fpga_size_A9[] = "A9"; + const char *fpga_size; + char dac[7] = {0}; + + if (size == BLADERF_FPGA_40KLE) { + fpga_size = fpga_size_40; + } else if (size == BLADERF_FPGA_115KLE) { + fpga_size = fpga_size_115; + } else if (size == BLADERF_FPGA_A4) { + fpga_size = fpga_size_A4; + } else if (size == BLADERF_FPGA_A5) { + fpga_size = fpga_size_A5; + } else if (size == BLADERF_FPGA_A9) { + fpga_size = fpga_size_A9; + } else { + assert(0); /* Bug catcher */ + return BLADERF_ERR_INVAL; + } + + memset(buf, 0xff, len); + + assert(len < INT_MAX); + rv = binkv_add_field((char*)buf, (int)len, "B", fpga_size); + + if (rv < 0) { + return rv; + } + + sprintf(dac, "%u", vctcxo_trim); + + rv = binkv_add_field((char*)buf, (int)len, "DAC", dac); + if (rv < 0) { + return rv; + } + + return 0; +} + +struct bladerf_image * bladerf_alloc_cal_image(struct bladerf *dev, + bladerf_fpga_size fpga_size, + uint16_t vctcxo_trim) +{ + struct bladerf_image *image; + int status; + + image = bladerf_alloc_image(dev, + BLADERF_IMAGE_TYPE_CALIBRATION, + BLADERF_FLASH_ADDR_CAL, + BLADERF_FLASH_BYTE_LEN_CAL); + + if (!image) { + return NULL; + } + + status = make_cal_region(fpga_size, vctcxo_trim, + image->data, image->length); + + if (status != 0) { + bladerf_free_image(image); + image = NULL; + } + + return image; +} + +void bladerf_free_image(struct bladerf_image *image) +{ + if (image) { + free(image->data); + free(image); + } +} |