osmocom-bb/src/host/osmocon/osmoload.c

1198 lines
28 KiB
C

/* control utility for the Calypso bootloader */
/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de>
* (C) 2018 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* 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 2 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.
*
*/
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/crc16.h>
#include <osmocom/core/socket.h>
#include <loader/protocol.h>
#define MSGB_MAX 256
#define MEM_MSG_MAX (MSGB_MAX - 16)
#define DEFAULT_SOCKET "/tmp/osmocom_loader"
static struct osmo_fd connection;
enum {
STATE_INIT,
STATE_QUERY_PENDING,
STATE_DUMP_IN_PROGRESS,
STATE_LOAD_IN_PROGRESS,
STATE_FLASHRANGE_GET_INFO,
STATE_FLASHRANGE_IN_PROGRESS,
STATE_PROGRAM_GET_INFO,
STATE_PROGRAM_IN_PROGRESS,
STATE_DUMPING,
};
struct flashblock {
uint8_t fb_chip;
uint32_t fb_offset;
uint32_t fb_addr;
uint32_t fb_size;
};
static struct {
/* debug flags */
unsigned char print_requests;
unsigned char print_replies;
/* quit flag for main loop */
unsigned char quit;
/* main state machine */
int state;
/* pending query command */
uint8_t command;
/* general timeout */
struct osmo_timer_list timeout;
/* binary i/o for firmware images */
FILE *binfile;
/* buffer containing binfile data */
char *binbuf;
/* memory operation state */
uint8_t memchip; /* target chip (for flashes) */
uint32_t membase; /* target base address of operation */
uint32_t memlen; /* length of entire operation */
uint32_t memoff; /* offset for next request */
uint16_t memcrc; /* crc for current request */
uint16_t memreq; /* length of current request */
/* array of all flash blocks */
uint8_t flashcommand;
uint32_t numblocks;
struct flashblock blocks[512];
} osmoload;
static int usage(const char *name)
{
printf("Usage: %s [ -v | -h ] [ -d tr ] [ -m {c123,c155} ] [ -l /tmp/osmocom_loader ] COMMAND ...\n", name);
puts("\n Memory commands:");
puts(" memget <hex-address> <hex-length> - Peek at memory");
puts(" memput <hex-address> <hex-bytes> - Poke at memory");
puts(" memdump <hex-address> <hex-length> <file>- Dump memory to file");
puts(" memload <hex-address> <file> - Load file into memory");
puts("\n Flash commands:");
puts(" finfo - Information about flash chips");
puts(" funlock <address> <length> - Unlock flash block");
puts(" flock <address> <length> - Lock flash block");
puts(" flockdown <address> <length> - Lock down flash block");
puts(" fgetlock <address> <length> - Get locking state of block");
puts(" ferase <address> <length> - Erase flash range");
puts(" fprogram <chip> <address> <file> - Program file into flash");
puts("\n Execution commands:");
puts(" jump <hex-address> - Jump to address");
puts(" jumpflash - Jump to flash loader");
puts(" jumprom - Jump to rom loader");
puts("\n Device lifecycle:");
puts(" ping - Ping the loader");
puts(" reset - Reset device");
puts(" off - Power off device");
puts("\n Debug:");
puts(" dump - Dump loader traffic to console");
exit(2);
}
static int version(const char *name)
{
//printf("\n%s version %s\n", name, VERSION);
exit(2);
}
static void osmoload_osmo_hexdump(const uint8_t *data, unsigned int len)
{
const uint8_t *bufptr = data;
const uint8_t *endptr = bufptr + len;
int n, m, i, hexchr;
for (n=0; n < len; n+=32, bufptr += 32) {
hexchr = 0;
for(m = 0; m < 32 && bufptr < endptr; m++, bufptr++) {
if((m) && !(m%4)) {
putchar(' ');
hexchr++;
}
printf("%02x", *bufptr);
hexchr+=2;
}
bufptr -= m;
int n = 71 - hexchr;
for(i = 0; i < n; i++) {
putchar(' ');
}
putchar(' ');
for(m = 0; m < 32 && bufptr < endptr; m++, bufptr++) {
if(isgraph(*bufptr)) {
putchar(*bufptr);
} else {
putchar('.');
}
}
bufptr -= m;
putchar('\n');
}
}
static void
loader_send_request(struct msgb *msg) {
int rc;
uint16_t len = htons(msg->len);
if(osmoload.print_requests) {
printf("Sending %d bytes:\n", msg->len);
osmoload_osmo_hexdump(msg->data, msg->len);
}
rc = write(connection.fd, &len, sizeof(len));
if(rc != sizeof(len)) {
fprintf(stderr, "Error writing.\n");
exit(2);
}
rc = write(connection.fd, msg->data, msg->len);
if(rc != msg->len) {
fprintf(stderr, "Error writing.\n");
exit(2);
}
}
static void loader_do_memdump(uint16_t crc, void *address, size_t length);
static void loader_do_memload();
static void loader_do_fprogram();
static void loader_do_flashrange(uint8_t cmd, struct msgb *msg, uint8_t chip, uint32_t address, uint32_t status);
static void memop_timeout(void *dummy) {
switch(osmoload.state) {
case STATE_LOAD_IN_PROGRESS:
printf("Timeout. Repeating.");
osmoload.memoff -= osmoload.memreq;
loader_do_memload();
break;
default:
break;
}
return;
}
static void
loader_parse_flash_info(struct msgb *msg) {
uint8_t nchips;
nchips = msgb_pull_u8(msg);
osmoload.numblocks = 0;
int chip;
for(chip = 0; chip < nchips; chip++) {
uint32_t address;
address = msgb_pull_u32(msg);
uint32_t chipsize;
chipsize = msgb_pull_u32(msg);
uint8_t nregions;
nregions = msgb_pull_u8(msg);
printf(" chip %d at 0x%8.8x of %d bytes in %d regions\n", chip, address, chipsize, nregions);
uint32_t curoffset = 0;
int region;
for(region = 0; region < nregions; region++) {
uint16_t blockcount = msgb_pull_u32(msg);
uint32_t blocksize = msgb_pull_u32(msg);
printf(" region %d with %d blocks of %d bytes each\n", region, blockcount, blocksize);
int block;
for(block = 0; block < blockcount; block++) {
osmoload.blocks[osmoload.numblocks].fb_chip = chip;
osmoload.blocks[osmoload.numblocks].fb_offset = curoffset;
osmoload.blocks[osmoload.numblocks].fb_addr = address + curoffset;
osmoload.blocks[osmoload.numblocks].fb_size = blocksize;
printf(" block %d with %d bytes at 0x%8.8x on chip %d\n",
osmoload.numblocks, blocksize, address + curoffset, chip);
curoffset += blocksize;
osmoload.numblocks++;
}
}
}
}
static void
loader_handle_reply(struct msgb *msg) {
if(osmoload.print_replies) {
printf("Received %d bytes:\n", msg->len);
osmoload_osmo_hexdump(msg->data, msg->len);
}
uint8_t cmd = msgb_pull_u8(msg);
uint8_t chip;
uint8_t length;
uint16_t crc;
uint32_t address;
uint32_t entrypoint;
uint32_t status;
void *data;
switch(cmd) {
case LOADER_INIT:
address = msgb_pull_u32(msg);
entrypoint = msgb_pull_u32(msg);
printf("Loader at entry %x has been started, requesting load to %x\n", entrypoint, address);
break;
case LOADER_PING:
case LOADER_RESET:
case LOADER_POWEROFF:
case LOADER_ENTER_ROM_LOADER:
case LOADER_ENTER_FLASH_LOADER:
break;
case LOADER_MEM_READ:
length = msgb_pull_u8(msg);
crc = msgb_pull_u16(msg);
address = msgb_pull_u32(msg);
data = msgb_pull(msg, length) - length;
break;
case LOADER_MEM_WRITE:
length = msgb_pull_u8(msg);
crc = msgb_pull_u16(msg);
address = msgb_pull_u32(msg);
break;
case LOADER_JUMP:
address = msgb_pull_u32(msg);
break;
case LOADER_FLASH_INFO:
break;
case LOADER_FLASH_GETLOCK:
case LOADER_FLASH_ERASE:
case LOADER_FLASH_UNLOCK:
case LOADER_FLASH_LOCK:
case LOADER_FLASH_LOCKDOWN:
chip = msgb_pull_u8(msg);
address = msgb_pull_u32(msg);
status = msgb_pull_u32(msg);
break;
case LOADER_FLASH_PROGRAM:
length = msgb_pull_u8(msg);
crc = msgb_pull_u16(msg);
msgb_pull_u8(msg); // XXX align
chip = msgb_pull_u8(msg);
address = msgb_pull_u32(msg);
status = msgb_pull_u32(msg);
break;
default:
printf("Received unknown reply %d:\n", cmd);
osmoload_osmo_hexdump(msg->data, msg->len);
osmoload.quit = 1;
return;
}
switch(osmoload.state) {
case STATE_QUERY_PENDING:
case STATE_DUMPING:
switch(cmd) {
case LOADER_PING:
printf("Received pong.\n");
break;
case LOADER_RESET:
printf("Reset confirmed.\n");
break;
case LOADER_POWEROFF:
printf("Poweroff confirmed.\n");
break;
case LOADER_ENTER_ROM_LOADER:
printf("Jump to ROM loader confirmed.\n");
break;
case LOADER_ENTER_FLASH_LOADER:
printf("Jump to flash loader confirmed.\n");
break;
case LOADER_MEM_READ:
printf("Received memory dump of %d bytes at 0x%x:\n", length, address);
osmoload_osmo_hexdump(data, length);
break;
case LOADER_MEM_WRITE:
printf("Confirmed memory write of %d bytes at 0x%x.\n", length, address);
break;
case LOADER_JUMP:
printf("Confirmed jump to 0x%x.\n", address);
break;
case LOADER_FLASH_ERASE:
printf("Confirmed flash erase of chip %d address 0x%8.8x, status %s\n",
chip, address, status ? "FAILED" : "ok");
break;
case LOADER_FLASH_GETLOCK:
printf("Lock state of chip %d address 0x%8.8x is %s\n",
chip, address, (status == LOADER_FLASH_LOCKED ? "locked"
: (status == LOADER_FLASH_LOCKED_DOWN ? "locked down"
: (status == LOADER_FLASH_UNLOCKED ? "unlocked"
: "UNKNOWN"))));
break;
case LOADER_FLASH_UNLOCK:
printf("Confirmed flash unlock of chip %d address 0x%8.8x, status %s\n",
chip, address, status ? "FAILED" : "ok");
break;
case LOADER_FLASH_LOCK:
printf("Confirmed flash lock of chip %d address 0x%8.8x, status %s\n",
chip, address, status ? "FAILED" : "ok");
break;
case LOADER_FLASH_LOCKDOWN:
printf("Confirmed flash lockdown of chip %d address 0x%8.8x, status %s\n",
chip, address, status ? "FAILED" : "ok");
break;
case LOADER_FLASH_INFO:
loader_parse_flash_info(msg);
break;
default:
break;
}
if(osmoload.state == STATE_QUERY_PENDING) {
if(osmoload.command == cmd) {
osmoload.quit = 1;
}
}
break;
case STATE_DUMP_IN_PROGRESS:
if(cmd == LOADER_MEM_READ) {
loader_do_memdump(crc, data, length);
}
break;
case STATE_LOAD_IN_PROGRESS:
if(cmd == LOADER_MEM_WRITE) {
if(osmoload.memcrc != crc) {
osmoload.memoff -= osmoload.memreq;
printf("\nbad crc %4.4x (not %4.4x) at offset 0x%8.8x", crc, osmoload.memcrc, osmoload.memoff);
} else {
putchar('.');
}
loader_do_memload();
}
break;
case STATE_PROGRAM_GET_INFO:
case STATE_PROGRAM_IN_PROGRESS:
if(cmd == LOADER_FLASH_PROGRAM) {
if(osmoload.memcrc != crc) {
osmoload.memoff -= osmoload.memreq;
printf("\nbad crc %4.4x (not %4.4x) at offset 0x%8.8x", crc, osmoload.memcrc, osmoload.memoff);
} else {
putchar('.');
}
if(((int)status) != 0) {
printf("\nstatus %d, aborting\n", status);
exit(1);
}
loader_do_fprogram();
}
break;
case STATE_FLASHRANGE_GET_INFO:
case STATE_FLASHRANGE_IN_PROGRESS:
loader_do_flashrange(cmd, msg, chip, address, status);
break;
default:
break;
}
fflush(stdout);
}
static int
loader_read_cb(struct osmo_fd *fd, unsigned int flags) {
struct msgb *msg;
uint16_t len;
int rc;
msg = msgb_alloc(MSGB_MAX, "loader");
if (!msg) {
fprintf(stderr, "Failed to allocate msg.\n");
return -1;
}
rc = read(fd->fd, &len, sizeof(len));
if (rc < sizeof(len)) {
fprintf(stderr, "Short read. Error.\n");
exit(2);
}
if (ntohs(len) > MSGB_MAX) {
fprintf(stderr, "Length is too big: %u\n", ntohs(len));
msgb_free(msg);
return -1;
}
/* blocking read for the poor... we can starve in here... */
msg->l2h = msgb_put(msg, ntohs(len));
rc = read(fd->fd, msg->l2h, msgb_l2len(msg));
if (rc != msgb_l2len(msg)) {
fprintf(stderr, "Can not read data: rc: %d errno: %d\n", rc, errno);
msgb_free(msg);
return -1;
}
loader_handle_reply(msg);
msgb_free(msg);
return 0;
}
static void
loader_connect(const char *socket_path) {
int rc;
struct osmo_fd *conn = &connection;
rc = osmo_sock_unix_init_ofd(conn, SOCK_STREAM, 0, socket_path, OSMO_SOCK_F_CONNECT);
if (rc < 0) {
fprintf(stderr, "Failed to create unix domain socket %s: %s\n",
socket_path, strerror(-rc));
exit(1);
}
conn->cb = loader_read_cb;
conn->data = NULL;
}
static void
loader_send_simple(uint8_t command) {
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
msgb_put_u8(msg, command);
loader_send_request(msg);
msgb_free(msg);
osmoload.command = command;
}
static void
loader_start_query(uint8_t command) {
loader_send_simple(command);
osmoload.state = STATE_QUERY_PENDING;
}
static void
loader_send_flash_query(uint8_t command, uint8_t chip, uint32_t address) {
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
msgb_put_u8(msg, command);
msgb_put_u8(msg, chip);
msgb_put_u32(msg, address);
loader_send_request(msg);
msgb_free(msg);
osmoload.command = command;
}
static void __attribute__((__unused__))
loader_start_flash_query(uint8_t command, uint8_t chip, uint32_t address) {
loader_send_flash_query(command, chip, address);
osmoload.state = STATE_QUERY_PENDING;
}
static void
loader_start_memget(uint8_t length, uint32_t address) {
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
msgb_put_u8(msg, LOADER_MEM_READ);
msgb_put_u8(msg, length);
msgb_put_u32(msg, address);
loader_send_request(msg);
msgb_free(msg);
osmoload.state = STATE_QUERY_PENDING;
osmoload.command = LOADER_MEM_READ;
}
static void
loader_start_memput(uint8_t length, uint32_t address, void *data) {
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
msgb_put_u8(msg, LOADER_MEM_WRITE);
msgb_put_u8(msg, length);
msgb_put_u16(msg, osmo_crc16(0, data, length));
msgb_put_u32(msg, address);
memcpy(msgb_put(msg, length), data, length);
loader_send_request(msg);
msgb_free(msg);
osmoload.state = STATE_QUERY_PENDING;
osmoload.command = LOADER_MEM_WRITE;
}
static void
loader_start_jump(uint32_t address) {
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
msgb_put_u8(msg, LOADER_JUMP);
msgb_put_u32(msg, address);
loader_send_request(msg);
msgb_free(msg);
osmoload.state = STATE_QUERY_PENDING;
osmoload.command = LOADER_JUMP;
}
static void
loader_do_memdump(uint16_t crc, void *data, size_t length) {
int rc;
if(data && length) {
osmoload.memcrc = osmo_crc16(0, data, length);
if(osmoload.memcrc != crc) {
osmoload.memoff -= osmoload.memreq;
printf("\nbad crc %4.4x (not %4.4x) at offset 0x%8.8x", crc, osmoload.memcrc, osmoload.memoff);
} else {
putchar('.');
}
memcpy(osmoload.binbuf + osmoload.memoff, data, length);
osmoload.memoff += length;
}
uint32_t rembytes = osmoload.memlen - osmoload.memoff;
if(!rembytes) {
puts("done.");
osmoload.quit = 1;
unsigned c = osmoload.memlen;
char *p = osmoload.binbuf;
while(c) {
rc = fwrite(p, 1, c, osmoload.binfile);
if(ferror(osmoload.binfile)) {
printf("Could not read from file: %s\n", strerror(errno));
exit(1);
}
c -= rc;
p += rc;
}
fclose(osmoload.binfile);
osmoload.binfile = NULL;
free(osmoload.binbuf);
return;
}
uint8_t reqbytes = (rembytes < MEM_MSG_MAX) ? rembytes : MEM_MSG_MAX;
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
msgb_put_u8(msg, LOADER_MEM_READ);
msgb_put_u8(msg, reqbytes);
msgb_put_u32(msg, osmoload.membase + osmoload.memoff);
loader_send_request(msg);
msgb_free(msg);
}
static void
loader_do_memload() {
uint32_t rembytes = osmoload.memlen - osmoload.memoff;
if(!rembytes) {
puts("done.");
osmoload.quit = 1;
return;
}
osmo_timer_schedule(&osmoload.timeout, 0, 500000);
uint8_t reqbytes = (rembytes < MEM_MSG_MAX) ? rembytes : MEM_MSG_MAX;
osmoload.memcrc = osmo_crc16(0, (uint8_t *) osmoload.binbuf + osmoload.memoff, reqbytes);
osmoload.memreq = reqbytes;
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
msgb_put_u8(msg, LOADER_MEM_WRITE);
msgb_put_u8(msg, reqbytes);
msgb_put_u16(msg, osmoload.memcrc);
msgb_put_u32(msg, osmoload.membase + osmoload.memoff);
unsigned char *p = msgb_put(msg, reqbytes);
memcpy(p, osmoload.binbuf + osmoload.memoff, reqbytes);
#if 0
printf("Sending %u bytes at offset %u to address %x with crc %x\n",
reqbytes, osmoload.memoff, osmoload.membase + osmoload.memoff,
osmoload.memcrc);
#endif
loader_send_request(msg);
msgb_free(msg);
osmoload.memoff += reqbytes;
}
static void
loader_do_fprogram() {
uint32_t rembytes = osmoload.memlen - osmoload.memoff;
if(!rembytes) {
puts("done.");
osmoload.quit = 1;
return;
}
osmo_timer_schedule(&osmoload.timeout, 0, 10000000);
uint8_t reqbytes = (rembytes < MEM_MSG_MAX) ? rembytes : MEM_MSG_MAX;
osmoload.memcrc = osmo_crc16(0, (uint8_t *) osmoload.binbuf + osmoload.memoff, reqbytes);
osmoload.memreq = reqbytes;
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
msgb_put_u8(msg, LOADER_FLASH_PROGRAM);
msgb_put_u8(msg, reqbytes);
msgb_put_u16(msg, osmoload.memcrc);
msgb_put_u8(msg, 0); // XXX: align data to 16bit
msgb_put_u8(msg, osmoload.memchip);
msgb_put_u32(msg, osmoload.membase + osmoload.memoff);
unsigned char *p = msgb_put(msg, reqbytes);
memcpy(p, osmoload.binbuf + osmoload.memoff, reqbytes);
#if 0
printf("Sending %u bytes at offset %u to address %x with crc %x\n",
reqbytes, osmoload.memoff, osmoload.membase + osmoload.memoff,
osmoload.memcrc);
#endif
loader_send_request(msg);
msgb_free(msg);
osmoload.memoff += reqbytes;
}
static void
loader_start_memdump(uint32_t length, uint32_t address, char *file) {
printf("Dumping %u bytes of memory at 0x%x to file %s\n", length, address, file);
osmoload.binbuf = malloc(length);
if(!osmoload.binbuf) {
printf("Could not allocate %u bytes for %s.\n", length, file);
exit(1);
}
osmoload.binfile = fopen(file, "wb");
if(!osmoload.binfile) {
printf("Could not open %s: %s\n", file, strerror(errno));
exit(1);
}
osmoload.membase = address;
osmoload.memlen = length;
osmoload.memoff = 0;
osmoload.state = STATE_DUMP_IN_PROGRESS;
loader_do_memdump(0, NULL, 0);
}
static void
loader_start_memload(uint32_t address, char *file) {
int rc;
struct stat st;
rc = stat(file, &st);
if(rc < 0) {
printf("Could not stat %s: %s\n", file, strerror(errno));
exit(1);
}
uint32_t length = st.st_size;
printf("Loading %u bytes of memory to address 0x%x from file %s\n", length, address, file);
osmoload.binbuf = malloc(length);
if(!osmoload.binbuf) {
printf("Could not allocate %u bytes for %s.\n", length, file);
exit(1);
}
osmoload.binfile = fopen(file, "rb");
if(!osmoload.binfile) {
printf("Could not open %s: %s\n", file, strerror(errno));
exit(1);
}
unsigned c = length;
char *p = osmoload.binbuf;
while(c) {
rc = fread(p, 1, c, osmoload.binfile);
if(ferror(osmoload.binfile)) {
printf("Could not read from file: %s\n", strerror(errno));
exit(1);
}
c -= rc;
p += rc;
}
fclose(osmoload.binfile);
osmoload.binfile = NULL;
osmoload.membase = address;
osmoload.memlen = length;
osmoload.memoff = 0;
osmoload.state = STATE_LOAD_IN_PROGRESS;
loader_do_memload();
}
static void
loader_start_flashrange(uint8_t command, uint32_t address, uint32_t length) {
switch(command) {
case LOADER_FLASH_ERASE:
printf("Erasing %u bytes of flash at 0x%x\n", length, address);
break;
case LOADER_FLASH_LOCK:
printf("Locking %u bytes of flash at 0x%x\n", length, address);
break;
case LOADER_FLASH_LOCKDOWN:
printf("Locking down %u bytes of flash at 0x%x\n", length, address);
break;
case LOADER_FLASH_UNLOCK:
printf("Unlocking %u bytes of flash at 0x%x\n", length, address);
break;
case LOADER_FLASH_GETLOCK:
printf("Getlocking %u bytes of flash at 0x%x\n", length, address);
break;
default:
puts("Unknown range command");
abort();
break;
}
osmoload.flashcommand = command;
osmoload.membase = address;
osmoload.memlen = length;
osmoload.memoff = 0;
printf(" requesting flash info to determine block layout\n");
osmoload.state = STATE_FLASHRANGE_GET_INFO;
loader_send_simple(LOADER_FLASH_INFO);
}
static void
loader_do_flashrange(uint8_t cmd, struct msgb *msg, uint8_t chip, uint32_t address, uint32_t status) {
switch(osmoload.state) {
case STATE_FLASHRANGE_GET_INFO:
if(cmd == LOADER_FLASH_INFO) {
loader_parse_flash_info(msg);
osmoload.state = STATE_FLASHRANGE_IN_PROGRESS;
loader_do_flashrange(0, NULL, 0, 0, 0);
}
break;
case STATE_FLASHRANGE_IN_PROGRESS:
{
if(msg) {
if(cmd == osmoload.flashcommand) {
if(cmd == LOADER_FLASH_GETLOCK) {
printf(" lock state of chip %d address 0x%8.8x is %s\n",
chip, address, (status == LOADER_FLASH_LOCKED ? "locked"
: (status == LOADER_FLASH_LOCKED_DOWN ? "locked down"
: (status == LOADER_FLASH_UNLOCKED ? "unlocked"
: "UNKNOWN"))));
} else {
printf(" confirmed operation on chip %d address 0x%8.8x, status %s\n",
chip, address, status ? "FAILED" : "ok");
}
} else {
break;
}
}
uint32_t addr = osmoload.membase + osmoload.memoff;
if(osmoload.memoff >= osmoload.memlen) {
puts(" operation done");
osmoload.quit = 1;
break;
}
uint8_t found = 0;
int i;
for(i = 0; i < osmoload.numblocks; i++) {
struct flashblock *b = &osmoload.blocks[i];
if(b->fb_addr == addr) {
loader_send_flash_query(osmoload.flashcommand, b->fb_chip, b->fb_offset);
osmoload.memoff += b->fb_size;
found = 1;
break;
}
}
if(!found) {
puts("Oops!? Block not found?"); // XXX message
abort();
}
}
break;
}
}
static void
loader_start_fprogram(uint8_t chip, uint32_t address, char *file) {
int rc;
struct stat st;
rc = stat(file, &st);
if(rc < 0) {
printf("Could not stat %s: %s\n", file, strerror(errno));
exit(1);
}
uint32_t length = st.st_size;
printf("Loading %u bytes of memory at 0x%x in chip %d from file %s\n", length, address, chip, file);
osmoload.binbuf = malloc(length);
if(!osmoload.binbuf) {
printf("Could not allocate %u bytes for %s.\n", length, file);
exit(1);
}
osmoload.binfile = fopen(file, "rb");
if(!osmoload.binfile) {
printf("Could not open %s: %s\n", file, strerror(errno));
exit(1);
}
unsigned c = length;
char *p = osmoload.binbuf;
while(c) {
rc = fread(p, 1, c, osmoload.binfile);
if(ferror(osmoload.binfile)) {
printf("Could not read from file: %s\n", strerror(errno));
exit(1);
}
c -= rc;
p += rc;
}
fclose(osmoload.binfile);
osmoload.binfile = NULL;
osmoload.memchip = chip;
osmoload.membase = address;
osmoload.memlen = length;
osmoload.memoff = 0;
osmoload.state = STATE_PROGRAM_IN_PROGRESS;
loader_do_fprogram();
}
static void
query_timeout(void *dummy) {
puts("Query timed out.");
exit(2);
}
static void
loader_command(char *name, int cmdc, char **cmdv) {
if(!cmdc) {
usage(name);
}
char *cmd = cmdv[0];
char buf[MEM_MSG_MAX];
memset(buf, 23, sizeof(buf));
if(!strcmp(cmd, "dump")) {
osmoload.state = STATE_DUMPING;
} else if(!strcmp(cmd, "ping")) {
loader_start_query(LOADER_PING);
} else if(!strcmp(cmd, "off")) {
loader_start_query(LOADER_POWEROFF);
} else if(!strcmp(cmd, "reset")) {
loader_start_query(LOADER_RESET);
} else if(!strcmp(cmd, "jumprom")) {
loader_start_query(LOADER_ENTER_ROM_LOADER);
} else if(!strcmp(cmd, "jumpflash")) {
loader_start_query(LOADER_ENTER_FLASH_LOADER);
} else if(!strcmp(cmd, "finfo")) {
puts("Requesting flash layout info");
loader_start_query(LOADER_FLASH_INFO);
} else if(!strcmp(cmd, "memput")) {
uint32_t address;
if(cmdc < 3) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
unsigned int i;
char *hex = cmdv[2];
if(strlen(hex)&1) {
puts("Invalid hex string.");
exit(2);
}
for(i = 0; i <= sizeof(buf) && i < strlen(hex)/2; i++) {
if(i >= sizeof(buf)) {
puts("Value too long for single message");
exit(2);
}
unsigned int byte;
int count = sscanf(hex + i * 2, "%02x", &byte);
if(count != 1) {
puts("Invalid hex string.");
exit(2);
}
buf[i] = byte & 0xFF;
}
loader_start_memput(i & 0xFF, address, buf);
} else if(!strcmp(cmd, "memget")) {
uint32_t address;
uint8_t length;
if(cmdc < 3) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
length = strtoul(cmdv[2], NULL, 16);
if(length > MEM_MSG_MAX) {
puts("Too many bytes");
exit(2);
}
loader_start_memget(length, address);
} else if(!strcmp(cmd, "jump")) {
uint32_t address;
if(cmdc < 2) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
loader_start_jump(address);
} else if(!strcmp(cmd, "memdump")) {
uint32_t address;
uint32_t length;
if(cmdc < 4) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
length = strtoul(cmdv[2], NULL, 16);
loader_start_memdump(length, address, cmdv[3]);
} else if(!strcmp(cmd, "memload")) {
uint32_t address;
if(cmdc < 3) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
loader_start_memload(address, cmdv[2]);
} else if(!strcmp(cmd, "fprogram")) {
uint8_t chip;
uint32_t address;
if(cmdc < 4) {
usage(name);
}
chip = strtoul(cmdv[1], NULL, 10);
address = strtoul(cmdv[2], NULL, 16);
loader_start_fprogram(chip, address, cmdv[3]);
} else if(!strcmp(cmd, "ferase")) {
uint32_t address;
uint32_t length;
if(cmdc < 3) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
length = strtoul(cmdv[2], NULL, 16);
loader_start_flashrange(LOADER_FLASH_ERASE, address, length);
} else if(!strcmp(cmd, "flock")) {
uint32_t address;
uint32_t length;
if(cmdc < 3) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
length = strtoul(cmdv[2], NULL, 16);
loader_start_flashrange(LOADER_FLASH_LOCK, address, length);
} else if(!strcmp(cmd, "flockdown")) {
uint32_t address;
uint32_t length;
if(cmdc < 3) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
length = strtoul(cmdv[2], NULL, 16);
loader_start_flashrange(LOADER_FLASH_LOCKDOWN, address, length);
} else if(!strcmp(cmd, "funlock")) {
uint32_t address;
uint32_t length;
if(cmdc < 3) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
length = strtoul(cmdv[2], NULL, 16);
loader_start_flashrange(LOADER_FLASH_UNLOCK, address, length);
} else if(!strcmp(cmd, "fgetlock")) {
uint32_t address;
uint32_t length;
if(cmdc < 3) {
usage(name);
}
address = strtoul(cmdv[1], NULL, 16);
length = strtoul(cmdv[2], NULL, 16);
loader_start_flashrange(LOADER_FLASH_GETLOCK, address, length);
} else if(!strcmp(cmd, "help")) {
usage(name);
} else {
printf("Unknown command '%s'\n", cmd);
usage(name);
}
if(osmoload.state == STATE_QUERY_PENDING) {
osmoload.timeout.cb = &query_timeout;
osmo_timer_schedule(&osmoload.timeout, 0, 5000000);
}
if(osmoload.state == STATE_LOAD_IN_PROGRESS) {
osmoload.timeout.cb = &memop_timeout;
}
}
void
setdebug(const char *name, char c) {
switch(c) {
case 't':
osmoload.print_requests = 1;
break;
case 'r':
osmoload.print_replies = 1;
break;
default:
usage(name);
break;
}
}
int
main(int argc, char **argv) {
int opt;
char *loader_un_path = "/tmp/osmocom_loader";
const char *debugopt;
while((opt = getopt(argc, argv, "d:hl:m:v")) != -1) {
switch(opt) {
case 'd':
debugopt = optarg;
while(*debugopt) {
setdebug(argv[0], *debugopt);
debugopt++;
}
break;
case 'l':
loader_un_path = optarg;
break;
case 'm':
puts("model selection not implemented");
exit(2);
break;
case 'v':
version(argv[0]);
break;
case 'h':
default:
usage(argv[0]);
break;
}
}
osmoload.quit = 0;
loader_connect(loader_un_path);
loader_command(argv[0], argc - optind, argv + optind);
while(!osmoload.quit) {
osmo_select_main(0);
}
if(osmoload.binfile) {
fclose(osmoload.binfile);
}
return 0;
}