1198 lines
28 KiB
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;
|
|
}
|