summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src/board/bladerf1/image.c
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/BladeRF/src/board/bladerf1/image.c')
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/image.c592
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], &timestamp, 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);
+ }
+}