/* * This file is part of the bladeRF project: * http://www.github.com/nuand/bladeRF * * Copyright (C) 2013 Daniel Gröber * 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 #include #include #include #include #include #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 static uint64_t get_timestamp() { __time64_t now = _time64(NULL); return (uint64_t)now; } #else #include 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); } }