578 lines
12 KiB
C
578 lines
12 KiB
C
/* control utility for the Calypso bootloader */
|
|
|
|
/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de>
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.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 <osmocore/msgb.h>
|
|
#include <osmocore/select.h>
|
|
|
|
#include <loader/protocol.h>
|
|
|
|
#define MSGB_MAX 256
|
|
|
|
#define DEFAULT_SOCKET "/tmp/osmocom_loader"
|
|
|
|
static struct bsc_fd connection;
|
|
|
|
enum {
|
|
OPERATION_QUERY,
|
|
OPERATION_MEMDUMP,
|
|
OPERATION_MEMLOAD,
|
|
};
|
|
|
|
static struct {
|
|
unsigned char print_requests;
|
|
unsigned char print_replies;
|
|
|
|
unsigned char quit;
|
|
int operation;
|
|
uint8_t command;
|
|
|
|
FILE *binfile;
|
|
|
|
uint32_t req_length;
|
|
uint32_t req_address;
|
|
|
|
uint32_t cur_length;
|
|
uint32_t cur_address;
|
|
} osmoload;
|
|
|
|
static int usage(const char *name)
|
|
{
|
|
printf("\nUsage: %s [ -v | -h ] [ -d tr ] [ -m {c123,c155} ] [ -l /tmp/osmocom_loader ] COMMAND ...\n", name);
|
|
puts(" memread <hex-length> <hex-address>");
|
|
puts(" memdump <hex-length> <hex-address> <file>");
|
|
puts(" memload <hex-address> <file>");
|
|
puts(" flashloader");
|
|
puts(" romloader");
|
|
puts(" ping");
|
|
puts(" reset");
|
|
puts(" off");
|
|
|
|
exit(2);
|
|
}
|
|
|
|
static int version(const char *name)
|
|
{
|
|
//printf("\n%s version %s\n", name, VERSION);
|
|
exit(2);
|
|
}
|
|
|
|
static void hexdump(const uint8_t *data, unsigned int len)
|
|
{
|
|
const uint8_t *bufptr = data;
|
|
int n;
|
|
|
|
for (n=0; n < len; n++, bufptr++)
|
|
printf("%02x ", *bufptr);
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
loader_send_request(struct msgb *msg) {
|
|
int rc;
|
|
u_int16_t len = htons(msg->len);
|
|
|
|
if(osmoload.print_requests) {
|
|
printf("Sending %d bytes:\n", msg->len);
|
|
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();
|
|
static void loader_do_memload();
|
|
|
|
static void
|
|
loader_handle_reply(struct msgb *msg) {
|
|
int rc;
|
|
|
|
if(osmoload.print_replies) {
|
|
printf("Received %d bytes:\n", msg->len);
|
|
hexdump(msg->data, msg->len);
|
|
}
|
|
|
|
uint8_t cmd = msgb_get_u8(msg);
|
|
|
|
uint8_t length;
|
|
uint32_t address;
|
|
|
|
void *data;
|
|
|
|
switch(cmd) {
|
|
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_get_u8(msg);
|
|
address = msgb_get_u32(msg);
|
|
data = msgb_get(msg, length);
|
|
break;
|
|
case LOADER_MEM_WRITE:
|
|
length = msgb_get_u8(msg);
|
|
address = msgb_get_u32(msg);
|
|
break;
|
|
default:
|
|
printf("Received unknown reply %d:\n", cmd);
|
|
hexdump(msg->data, msg->len);
|
|
osmoload.quit = 1;
|
|
break;
|
|
}
|
|
|
|
switch(osmoload.operation) {
|
|
case OPERATION_QUERY:
|
|
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);
|
|
hexdump(data, length);
|
|
break;
|
|
case LOADER_MEM_WRITE:
|
|
printf("Confirmed memory write of %d bytes at 0x%x.\n", length, address);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if(osmoload.command == cmd) {
|
|
osmoload.quit = 1;
|
|
}
|
|
break;
|
|
case OPERATION_MEMDUMP:
|
|
if(cmd == LOADER_MEM_READ) {
|
|
putchar('.');
|
|
fflush(stdout);
|
|
rc = fwrite(data, 1, length, osmoload.binfile);
|
|
if(ferror(osmoload.binfile)) {
|
|
printf("Error writing to dump file: %s\n", strerror(errno));
|
|
}
|
|
loader_do_memdump();
|
|
}
|
|
break;
|
|
case OPERATION_MEMLOAD:
|
|
if(cmd == LOADER_MEM_WRITE) {
|
|
putchar('.');
|
|
fflush(stdout);
|
|
loader_do_memload();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
loader_read_cb(struct bsc_fd *fd, unsigned int flags) {
|
|
struct msgb *msg;
|
|
u_int16_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 sockaddr_un local;
|
|
struct bsc_fd *conn = &connection;
|
|
|
|
local.sun_family = AF_UNIX;
|
|
strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
|
|
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
|
|
|
|
conn->fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (conn->fd < 0) {
|
|
fprintf(stderr, "Failed to create unix domain socket.\n");
|
|
exit(1);
|
|
}
|
|
|
|
rc = connect(conn->fd, (struct sockaddr *) &local,
|
|
sizeof(local.sun_family) + strlen(local.sun_path));
|
|
if (rc < 0) {
|
|
fprintf(stderr, "Failed to connect to '%s'.\n", local.sun_path);
|
|
exit(1);
|
|
}
|
|
|
|
conn->when = BSC_FD_READ;
|
|
conn->cb = loader_read_cb;
|
|
conn->data = NULL;
|
|
|
|
if (bsc_register_fd(conn) != 0) {
|
|
fprintf(stderr, "Failed to register fd.\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
loader_send_query(uint8_t command) {
|
|
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
|
|
msgb_put_u8(msg, command);
|
|
loader_send_request(msg);
|
|
msgb_free(msg);
|
|
|
|
osmoload.operation = OPERATION_QUERY;
|
|
osmoload.command = command;
|
|
}
|
|
|
|
static void
|
|
loader_send_memread(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.operation = OPERATION_QUERY;
|
|
osmoload.command = LOADER_MEM_READ;
|
|
}
|
|
|
|
static void
|
|
loader_send_memwrite(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_u32(msg, address);
|
|
memcpy(msgb_put(msg, length), data, length);
|
|
loader_send_request(msg);
|
|
msgb_free(msg);
|
|
|
|
osmoload.operation = OPERATION_QUERY;
|
|
osmoload.command = LOADER_MEM_WRITE;
|
|
}
|
|
|
|
#define MEM_MSG_MAX (MSGB_MAX - 16)
|
|
|
|
static void
|
|
loader_do_memdump() {
|
|
uint32_t rembytes = osmoload.req_length - osmoload.cur_length;
|
|
|
|
if(!rembytes) {
|
|
puts("done.");
|
|
osmoload.quit = 1;
|
|
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.cur_address);
|
|
loader_send_request(msg);
|
|
msgb_free(msg);
|
|
|
|
osmoload.cur_address += reqbytes;
|
|
osmoload.cur_length += reqbytes;
|
|
}
|
|
|
|
static void
|
|
loader_do_memload() {
|
|
uint32_t rembytes = osmoload.req_length - osmoload.cur_length;
|
|
|
|
if(!rembytes) {
|
|
puts("done.");
|
|
osmoload.quit = 1;
|
|
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_WRITE);
|
|
msgb_put_u8(msg, reqbytes);
|
|
msgb_put_u32(msg, osmoload.cur_address);
|
|
|
|
unsigned c = 0;
|
|
unsigned char *p = msgb_put(msg, reqbytes);
|
|
while(c < reqbytes) {
|
|
int rc = fread(p, 1, reqbytes, osmoload.binfile);
|
|
if(ferror(osmoload.binfile)) {
|
|
printf("Could not read from file: %s\n", strerror(errno));
|
|
}
|
|
c -= rc;
|
|
p += rc;
|
|
}
|
|
|
|
loader_send_request(msg);
|
|
|
|
msgb_free(msg);
|
|
|
|
osmoload.cur_address += reqbytes;
|
|
osmoload.cur_length += reqbytes;
|
|
}
|
|
|
|
static void
|
|
loader_start_memdump(uint32_t length, uint32_t address, char *file) {
|
|
printf("Dumping %u bytes of memory at 0x%u to file %s\n", length, address, file);
|
|
|
|
osmoload.operation = OPERATION_MEMDUMP;
|
|
|
|
osmoload.binfile = fopen(file, "wb");
|
|
if(!osmoload.binfile) {
|
|
printf("Could not open %s: %s\n", file, strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
osmoload.req_length = length;
|
|
osmoload.req_address = address;
|
|
|
|
osmoload.cur_length = 0;
|
|
osmoload.cur_address = address;
|
|
|
|
loader_do_memdump();
|
|
}
|
|
|
|
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 at 0x%u to file %s\n", length, address, file);
|
|
|
|
osmoload.operation = OPERATION_MEMLOAD;
|
|
|
|
osmoload.binfile = fopen(file, "rb");
|
|
if(!osmoload.binfile) {
|
|
printf("Could not open %s: %s\n", file, strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
osmoload.req_length = length;
|
|
osmoload.req_address = address;
|
|
|
|
osmoload.cur_length = 0;
|
|
osmoload.cur_address = address;
|
|
|
|
loader_do_memload();
|
|
}
|
|
|
|
static void
|
|
loader_command(char *name, int cmdc, char **cmdv) {
|
|
if(!cmdc) {
|
|
usage(name);
|
|
}
|
|
|
|
char *cmd = cmdv[0];
|
|
|
|
char buf[256];
|
|
memset(buf, 23, sizeof(buf));
|
|
|
|
if(!strcmp(cmd, "ping")) {
|
|
loader_send_query(LOADER_PING);
|
|
} else if(!strcmp(cmd, "memread")) {
|
|
uint8_t length;
|
|
uint32_t address;
|
|
|
|
if(cmdc < 3) {
|
|
usage(name);
|
|
}
|
|
|
|
length = strtoul(cmdv[1], NULL, 16);
|
|
address = strtoul(cmdv[2], NULL, 16);
|
|
|
|
loader_send_memread(length, address);
|
|
} else if(!strcmp(cmd, "memdump")) {
|
|
uint32_t length;
|
|
uint32_t address;
|
|
|
|
if(cmdc < 4) {
|
|
usage(name);
|
|
}
|
|
|
|
length = strtoul(cmdv[1], NULL, 16);
|
|
address = strtoul(cmdv[2], NULL, 16);
|
|
|
|
loader_start_memdump(length, address, cmdv[3]);
|
|
} else if(!strcmp(cmd, "memload")) {
|
|
uint32_t length;
|
|
uint32_t address;
|
|
|
|
if(cmdc < 3) {
|
|
usage(name);
|
|
}
|
|
|
|
address = strtoul(cmdv[1], NULL, 16);
|
|
|
|
loader_start_memload(address, cmdv[2]);
|
|
} else if(!strcmp(cmd, "memwrite")) {
|
|
loader_send_memwrite(128, 0x810000, buf);
|
|
} else if(!strcmp(cmd, "off")) {
|
|
loader_send_query(LOADER_POWEROFF);
|
|
} else if(!strcmp(cmd, "reset")) {
|
|
loader_send_query(LOADER_RESET);
|
|
} else if(!strcmp(cmd, "romload")) {
|
|
loader_send_query(LOADER_ENTER_ROM_LOADER);
|
|
} else if(!strcmp(cmd, "flashload")) {
|
|
loader_send_query(LOADER_ENTER_FLASH_LOADER);
|
|
} else if(!strcmp(cmd, "help")) {
|
|
usage(name);
|
|
} else {
|
|
printf("Unknown command '%s'\n", cmd);
|
|
usage(name);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
bsc_select_main(0);
|
|
}
|
|
|
|
if(osmoload.binfile) {
|
|
fclose(osmoload.binfile);
|
|
}
|
|
|
|
return 0;
|
|
}
|