diff options
Diffstat (limited to 'firmware/microblaze/lib/loader_parser.c')
-rw-r--r-- | firmware/microblaze/lib/loader_parser.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/firmware/microblaze/lib/loader_parser.c b/firmware/microblaze/lib/loader_parser.c new file mode 100644 index 000000000..96457a164 --- /dev/null +++ b/firmware/microblaze/lib/loader_parser.c @@ -0,0 +1,324 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "loader_parser.h" +#include <quadradio/loader_bits.h> +#include <quadradio/flashdir.h> +#include <quadradio/simple_binary_format.h> +#include <spi_flash.h> +#include <nonstdio.h> +//#include <assert.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include "ethernet.h" +#include "qr_settings.h" + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +static spi_flash_async_state_t async_state; + + +static caldiv_eeprom_setter_t _caldiv_set_rev = NULL; +static caldiv_eeprom_setter_t _caldiv_set_ser = NULL; +static caldiv_eeprom_setter_t _caldiv_set_mod = NULL; + +void +register_caldiv_eeprom_setters(caldiv_eeprom_setter_t set_rev, + caldiv_eeprom_setter_t set_ser, + caldiv_eeprom_setter_t set_mod) +{ + _caldiv_set_rev = set_rev; + _caldiv_set_ser = set_ser; + _caldiv_set_mod = set_mod; +} + + +// big-endian +static uint32_t +get32(const unsigned char *s) +{ + return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; +} + +// big-endian +static unsigned char * +put32(unsigned char *s, uint32_t v) +{ + s[0] = (v >> 24) & 0xff; + s[1] = (v >> 16) & 0xff; + s[2] = (v >> 8) & 0xff; + s[3] = v & 0xff; + return s + 4; +} + +static bool +erased_p(uint32_t flash_addr, size_t nbytes) +{ + unsigned char buf[64]; + + size_t n; + for (size_t i = 0; i < nbytes; i += n, flash_addr += n){ + n = min(nbytes - i, sizeof(buf)); + spi_flash_read(flash_addr, n, buf); + for (size_t j = 0; j < n; j++) + if (buf[j] != 0xff) + return false; + } + return true; +} + +static bool +erase_flash(uint32_t addr, uint32_t len) +{ + if (addr % spi_flash_sector_size() != 0) + return false; + + if (len % spi_flash_sector_size() != 0) + return false; + + spi_flash_async_erase_start(&async_state, addr, len); + // FIXME? check to see if erase was successful + return true; +} + +static bool +map_slot(uint32_t slot, uint32_t *slot_start, uint32_t *slot_len, uint32_t *status) +{ + // This case doesn't require a valid flashdir, and in fact can be used as + // part of writing the intial flashdir. + if (QLD_SLOT_DOM(slot) == QLD_DOM_UNMAPPED){ + int flash_size = get_flash_size(); + if (flash_size == 0){ + *status = QLDS_FAILED; // Can't find the flash. most likely a h/w problem. + return false; + } + *slot_start = 0; + *slot_len = flash_size; + return true; + } + + const struct flashdir *fd = get_flashdir(); + if (fd == 0) + return false; + + uint32_t slot_num = QLD_SLOT_NUM(slot); + + switch(QLD_SLOT_DOM(slot)){ + case QLD_DOM_FPGA: + if (slot_num >= fd->fpga_nslots){ + *status = QLDS_INVALID_ARG; + return false; + } + *slot_start = fd->slot[slot_num + fd->fpga_slot0].start << spi_flash_log2_sector_size(); + *slot_len = fd->slot[slot_num + fd->fpga_slot0].len << spi_flash_log2_sector_size(); + return true; + + case QLD_DOM_FW: + if (slot_num >= fd->fw_nslots){ + *status = QLDS_INVALID_ARG; + return false; + } + *slot_start = fd->slot[slot_num + fd->fw_slot0].start << spi_flash_log2_sector_size(); + *slot_len = fd->slot[slot_num + fd->fw_slot0].len << spi_flash_log2_sector_size(); + return true; + + default: + *status = QLDS_INVALID_ARG; + return false; + } +} + + +static bool +check_flashdir(void) +{ + return get_flashdir() != 0; +} + +void +loader_parser(const unsigned char *input, size_t ilen, + unsigned char *output, size_t max_olen, size_t *actual_olen) +{ + //assert (max_olen >= 8); + if (!(max_olen >= 8)) + abort(); + + *actual_olen = 0; + uint32_t status = QLDS_BAD_PKT; + + uint32_t cmd = get32(input); + uint32_t nonce = get32(input+4); + uint32_t slot = 0; + uint32_t addr = 0; + uint32_t len = 0; + + if (ilen < 8){ + nonce = -1; + goto done; + } + + uint32_t slot_start; // offset in flash + uint32_t slot_len; // length in bytes + + if (ilen >= 5 * sizeof(uint32_t)){ + slot = get32(input+8); + addr = get32(input+12); + len = get32(input+16); + } + + switch (cmd){ + case QLD_FLASH_ERASE_START: + // <QLD_FLASH_ERASE_START> <nonce> <slot> <addr> <len> + if (ilen != 5 * sizeof(uint32_t)) + goto done; + + if (!check_flashdir()){ + status = QLDS_BAD_FLASHDIR; + goto done; + } + if (!map_slot(slot, &slot_start, &slot_len, &status)) + goto done; + + if (QLD_SLOT_DOM(slot) != QLD_DOM_UNMAPPED){ + addr = slot_start; + len = slot_len; + } + //printf("flash_erase: addr = 0x%x, len=0x%x\n", addr, len); + + if (0 && erased_p(addr, len)){ // already erased? + async_state.first = async_state.last = async_state.current = 0; + goto ok; + } + + if (erase_flash(addr, len)) + goto ok; + + status = QLDS_FAILED; + goto done; + + + case QLD_FLASH_ERASE_POLL: + // <QLD_FLASH_ERASE_POLL> <nonce> + if (ilen != 2 * sizeof(uint32_t)) + goto done; + + if (spi_flash_async_erase_poll(&async_state)) + goto ok; + + status = QLDS_BUSY; + goto done; + + + case QLD_FLASH_WRITE: + // <QLD_FLASH_WRITE> <nonce> <slot> <addr> <len> <data ...> + if (ilen < 5 * sizeof(uint32_t)) + goto done; + + if (ilen != 5 * sizeof(uint32_t) + len) + goto done; + + if (!check_flashdir()){ + status = QLDS_BAD_FLASHDIR; + goto done; + } + if (!map_slot(slot, &slot_start, &slot_len, &status)) + goto done; + + addr += slot_start; + len = min(len, slot_len); + + if (spi_flash_program(addr, len, &input[5*sizeof(uint32_t)])) + goto ok; + + status = QLDS_FAILED; + goto done; + + + case QLD_FLASH_READ: + case QLD_MEM_READ: + case QLD_MEM_WRITE: + case QLD_GOTO: + status = QLDS_NOTIMPLEMENTED; + goto done; + + case QLD_PING: + // <QLD_PING> <nonce> + if (ilen != 2 * sizeof(uint32_t)) + goto done; + goto ok; + +#if 0 + case QLD_EEPROM_SET_XXX: + // <QLD_EEPROM_SET_XXX> <nonce> <arg> <idlen> <idstr> <data ...> + { + uint32_t arg = get32(input+2*sizeof(uint32_t)); + uint32_t idlen = get32(input+3*sizeof(uint32_t)); + uint8_t *idstr = (uint8_t*)input+4*sizeof(uint32_t); + uint8_t *data_p = idstr+idlen; + + //handle the ethernet cases + if (strncmp((char*)idstr, "ip", idlen) == 0){ + struct ip_addr addr = {get32(data_p)}; + ethernet_set_ip_addr(arg, addr); + } + else if (strncmp((char*)idstr, "mac", idlen) == 0){ + eth_mac_addr_t addr; + memcpy(&addr, data_p, sizeof(addr)); + ethernet_set_mac_addr(arg, &addr); + } + //handle the main board eeprom + else if (strncmp((char*)idstr, "qrrev", idlen) == 0){ + qr_set_revision(get32(data_p)); + } + else if (strncmp((char*)idstr, "qrser", idlen) == 0){ + qr_set_serial(get32(data_p)); + } + else if (strncmp((char*)idstr, "qrmod", idlen) == 0){ + qr_set_model(get32(data_p)); + } + //handle the caldiv eeprom + else if (strncmp((char*)idstr, "cdrev", idlen) == 0){ + if (_caldiv_set_rev) _caldiv_set_rev(get32(data_p)); + } + else if (strncmp((char*)idstr, "cdser", idlen) == 0){ + if (_caldiv_set_ser) _caldiv_set_ser(get32(data_p)); + } + else if (strncmp((char*)idstr, "cdmod", idlen) == 0){ + if (_caldiv_set_ser) _caldiv_set_mod(get32(data_p)); + } + else { + goto done; + } + } + goto ok; +#endif + + default: + status = QLDS_UNKNOWN_CMD; + goto done; + } + + ok: + status = QLDS_OK; + + done: + put32(output, nonce); + put32(output+4, status); + *actual_olen = 2*sizeof(uint32_t); +} |