loader: flash locking commands, various flash rework
This commit is contained in:
parent
d602dcaafb
commit
3936c397a7
|
@ -66,25 +66,26 @@ static struct {
|
|||
/* quit flag for main loop */
|
||||
unsigned char quit;
|
||||
|
||||
/* state machine */
|
||||
/* main state machine */
|
||||
int state;
|
||||
|
||||
/* pending query command */
|
||||
uint8_t command;
|
||||
|
||||
/* general timeout */
|
||||
struct timer_list timeout;
|
||||
|
||||
/* binary i/o for firmware images */
|
||||
FILE *binfile;
|
||||
|
||||
/* buffer containing binfile data */
|
||||
char *binbuf;
|
||||
|
||||
/* memory operation state */
|
||||
uint32_t membase;
|
||||
uint32_t memlen;
|
||||
uint32_t memoff;
|
||||
uint16_t memcrc;
|
||||
uint16_t memreq;
|
||||
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 */
|
||||
} osmoload;
|
||||
|
||||
static int usage(const char *name)
|
||||
|
@ -94,6 +95,11 @@ static int usage(const char *name)
|
|||
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(" finfo - Information about flash chips");
|
||||
puts(" funlock <chip> <address> - Unlock flash block");
|
||||
puts(" flock <chip> <address> - Lock flash block");
|
||||
puts(" flockdown <chip> <address> - Lock down flash block");
|
||||
puts(" fgetlock <chip> <address> - Get locking state of block");
|
||||
puts(" jump <hex-address> - Jump to address");
|
||||
puts(" jumpflash - Jump to flash loader");
|
||||
puts(" jumprom - Jump to rom loader");
|
||||
|
@ -187,6 +193,36 @@ static void memop_timeout(void *dummy) {
|
|||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
loader_dump_flash_info(struct msgb *msg) {
|
||||
uint8_t nchips;
|
||||
|
||||
nchips = msgb_get_u8(msg);
|
||||
|
||||
int chip;
|
||||
for(chip = 0; chip < nchips; chip++) {
|
||||
|
||||
uint32_t address;
|
||||
address = msgb_get_u32(msg);
|
||||
|
||||
uint32_t chipsize;
|
||||
chipsize = msgb_get_u32(msg);
|
||||
|
||||
uint8_t nregions;
|
||||
nregions = msgb_get_u8(msg);
|
||||
|
||||
printf("chip %d at 0x%8.8x of %d bytes in %d regions\n", chip, address, chipsize, nregions);
|
||||
|
||||
int region;
|
||||
for(region = 0; region < nregions; region++) {
|
||||
uint16_t c = msgb_get_u32(msg);
|
||||
uint32_t s = msgb_get_u32(msg);
|
||||
|
||||
printf(" region %d with %d blocks of %d bytes each\n", region, c, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
loader_handle_reply(struct msgb *msg) {
|
||||
if(osmoload.print_replies) {
|
||||
|
@ -196,10 +232,12 @@ loader_handle_reply(struct msgb *msg) {
|
|||
|
||||
uint8_t cmd = msgb_get_u8(msg);
|
||||
|
||||
uint8_t chip;
|
||||
uint8_t length;
|
||||
uint16_t crc;
|
||||
uint32_t address;
|
||||
uint32_t entrypoint;
|
||||
uint32_t status;
|
||||
|
||||
void *data;
|
||||
|
||||
|
@ -217,6 +255,7 @@ loader_handle_reply(struct msgb *msg) {
|
|||
break;
|
||||
case LOADER_MEM_READ:
|
||||
length = msgb_get_u8(msg);
|
||||
crc = msgb_get_u16(msg);
|
||||
address = msgb_get_u32(msg);
|
||||
data = msgb_get(msg, length);
|
||||
break;
|
||||
|
@ -228,6 +267,17 @@ loader_handle_reply(struct msgb *msg) {
|
|||
case LOADER_JUMP:
|
||||
address = msgb_get_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_get_u8(msg);
|
||||
address = msgb_get_u32(msg);
|
||||
status = msgb_get_u32(msg);
|
||||
break;
|
||||
default:
|
||||
printf("Received unknown reply %d:\n", cmd);
|
||||
hexdump(msg->data, msg->len);
|
||||
|
@ -264,6 +314,32 @@ loader_handle_reply(struct msgb *msg) {
|
|||
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_dump_flash_info(msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -275,6 +351,13 @@ loader_handle_reply(struct msgb *msg) {
|
|||
break;
|
||||
case STATE_DUMP_IN_PROGRESS:
|
||||
if(cmd == LOADER_MEM_READ) {
|
||||
osmoload.memcrc = 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('.');
|
||||
}
|
||||
loader_do_memdump(data, length);
|
||||
}
|
||||
break;
|
||||
|
@ -282,7 +365,9 @@ loader_handle_reply(struct msgb *msg) {
|
|||
if(cmd == LOADER_MEM_WRITE) {
|
||||
if(osmoload.memcrc != crc) {
|
||||
osmoload.memoff -= osmoload.memreq;
|
||||
printf("bad CRC %x\n", crc);
|
||||
printf("\nbad crc %4.4x (not %4.4x) at offset 0x%8.8x", crc, osmoload.memcrc, osmoload.memoff);
|
||||
} else {
|
||||
putchar('.');
|
||||
}
|
||||
loader_do_memload();
|
||||
}
|
||||
|
@ -290,6 +375,8 @@ loader_handle_reply(struct msgb *msg) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -376,6 +463,19 @@ loader_send_query(uint8_t command) {
|
|||
osmoload.command = command;
|
||||
}
|
||||
|
||||
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.state = STATE_QUERY_PENDING;
|
||||
osmoload.command = command;
|
||||
}
|
||||
|
||||
static void
|
||||
loader_send_memget(uint8_t length, uint32_t address) {
|
||||
struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
|
||||
|
@ -438,6 +538,7 @@ loader_do_memdump(void *data, size_t length) {
|
|||
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;
|
||||
|
@ -555,6 +656,7 @@ loader_start_memload(uint32_t address, char *file) {
|
|||
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;
|
||||
|
@ -598,6 +700,68 @@ loader_command(char *name, int cmdc, char **cmdv) {
|
|||
loader_send_query(LOADER_ENTER_ROM_LOADER);
|
||||
} else if(!strcmp(cmd, "jumpflash")) {
|
||||
loader_send_query(LOADER_ENTER_FLASH_LOADER);
|
||||
} else if(!strcmp(cmd, "flashinfo")) {
|
||||
loader_send_query(LOADER_FLASH_INFO);
|
||||
} else if(!strcmp(cmd, "flashgetlock")) {
|
||||
uint8_t chip;
|
||||
uint32_t address;
|
||||
|
||||
if(cmdc < 3) {
|
||||
usage(name);
|
||||
}
|
||||
|
||||
chip = strtoul(cmdv[1], NULL, 10);
|
||||
address = strtoul(cmdv[2], NULL, 16);
|
||||
|
||||
loader_send_flash_query(LOADER_FLASH_GETLOCK, chip, address);
|
||||
} else if(!strcmp(cmd, "flashunlock")) {
|
||||
uint8_t chip;
|
||||
uint32_t address;
|
||||
|
||||
if(cmdc < 3) {
|
||||
usage(name);
|
||||
}
|
||||
|
||||
chip = strtoul(cmdv[1], NULL, 10);
|
||||
address = strtoul(cmdv[2], NULL, 16);
|
||||
|
||||
loader_send_flash_query(LOADER_FLASH_UNLOCK, chip, address);
|
||||
} else if(!strcmp(cmd, "flashlock")) {
|
||||
uint8_t chip;
|
||||
uint32_t address;
|
||||
|
||||
if(cmdc < 3) {
|
||||
usage(name);
|
||||
}
|
||||
|
||||
chip = strtoul(cmdv[1], NULL, 10);
|
||||
address = strtoul(cmdv[2], NULL, 16);
|
||||
|
||||
loader_send_flash_query(LOADER_FLASH_LOCK, chip, address);
|
||||
} else if(!strcmp(cmd, "flashlockdown")) {
|
||||
uint8_t chip;
|
||||
uint32_t address;
|
||||
|
||||
if(cmdc < 3) {
|
||||
usage(name);
|
||||
}
|
||||
|
||||
chip = strtoul(cmdv[1], NULL, 10);
|
||||
address = strtoul(cmdv[2], NULL, 16);
|
||||
|
||||
loader_send_flash_query(LOADER_FLASH_LOCKDOWN, chip, address);
|
||||
} else if(!strcmp(cmd, "flasherase")) {
|
||||
uint8_t chip;
|
||||
uint32_t address;
|
||||
|
||||
if(cmdc < 3) {
|
||||
usage(name);
|
||||
}
|
||||
|
||||
chip = strtoul(cmdv[1], NULL, 10);
|
||||
address = strtoul(cmdv[2], NULL, 16);
|
||||
|
||||
loader_send_flash_query(LOADER_FLASH_ERASE, chip, address);
|
||||
} else if(!strcmp(cmd, "memput")) {
|
||||
uint32_t address;
|
||||
|
||||
|
@ -690,7 +854,7 @@ loader_command(char *name, int cmdc, char **cmdv) {
|
|||
|
||||
if(osmoload.state == STATE_QUERY_PENDING) {
|
||||
osmoload.timeout.cb = &query_timeout;
|
||||
bsc_schedule_timer(&osmoload.timeout, 0, 500000);
|
||||
bsc_schedule_timer(&osmoload.timeout, 0, 5000000);
|
||||
}
|
||||
if(osmoload.state == STATE_LOAD_IN_PROGRESS) {
|
||||
osmoload.timeout.cb = &memop_timeout;
|
||||
|
|
|
@ -48,8 +48,7 @@
|
|||
#include <calypso/uart.h>
|
||||
#include <calypso/timer.h>
|
||||
|
||||
#include <layer1/sync.h>
|
||||
#include <layer1/tpu_window.h>
|
||||
#include <cfi_flash.h>
|
||||
|
||||
#include "protocol.h"
|
||||
|
||||
|
@ -95,11 +94,7 @@ static void device_jump(void *entry) {
|
|||
}
|
||||
|
||||
static void
|
||||
loader_send_simple(uint8_t dlci, uint8_t command) {
|
||||
struct msgb *msg = sercomm_alloc_msgb(1);
|
||||
if(!msg) {
|
||||
puts("Failed to allocate message buffer!\n");
|
||||
}
|
||||
loader_send_simple(struct msgb *msg, uint8_t dlci, uint8_t command) {
|
||||
msgb_put_u8(msg, command);
|
||||
sercomm_sendmsg(dlci, msg);
|
||||
}
|
||||
|
@ -108,16 +103,15 @@ extern unsigned char _start;
|
|||
|
||||
static void
|
||||
loader_send_init(uint8_t dlci) {
|
||||
struct msgb *msg = sercomm_alloc_msgb(1);
|
||||
if(!msg) {
|
||||
puts("Failed to allocate message buffer!\n");
|
||||
}
|
||||
struct msgb *msg = sercomm_alloc_msgb(9);
|
||||
msgb_put_u8(msg, LOADER_INIT);
|
||||
msgb_put_u32(msg, 0);
|
||||
msgb_put_u32(msg, &_start);
|
||||
sercomm_sendmsg(dlci, msg);
|
||||
}
|
||||
|
||||
flash_t the_flash;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Always disable wdt (some platforms enable it on boot) */
|
||||
|
@ -137,6 +131,11 @@ int main(void)
|
|||
puts("\n\nOSMOCOM Calypso loader (revision " GIT_REVISION ")\n");
|
||||
puts(hr);
|
||||
|
||||
/* Initialize flash driver */
|
||||
if(flash_init(&the_flash, 0)) {
|
||||
puts("Failed to initialize flash!\n");
|
||||
}
|
||||
|
||||
/* Identify environment */
|
||||
printf("Running on %s in environment %s\n", manifest_board, manifest_environment);
|
||||
|
||||
|
@ -167,42 +166,45 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
|
|||
|
||||
uint8_t command = msgb_get_u8(msg);
|
||||
|
||||
printf("command %u: ", command);
|
||||
int res;
|
||||
|
||||
flash_lock_t lock;
|
||||
|
||||
uint8_t chip;
|
||||
uint8_t nbytes;
|
||||
uint16_t crc;
|
||||
uint32_t address;
|
||||
|
||||
struct msgb *reply;
|
||||
struct msgb *reply = sercomm_alloc_msgb(256); // XXX
|
||||
|
||||
if(!reply) {
|
||||
printf("Failed to allocate reply buffer!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch(command) {
|
||||
|
||||
case LOADER_PING:
|
||||
puts("ping\n");
|
||||
loader_send_simple(dlci, LOADER_PING);
|
||||
loader_send_simple(reply, dlci, LOADER_PING);
|
||||
break;
|
||||
|
||||
case LOADER_RESET:
|
||||
puts("reset\n");
|
||||
loader_send_simple(dlci, LOADER_RESET);
|
||||
loader_send_simple(reply, dlci, LOADER_RESET);
|
||||
device_reset();
|
||||
break;
|
||||
|
||||
case LOADER_POWEROFF:
|
||||
puts("poweroff\n");
|
||||
loader_send_simple(dlci, LOADER_POWEROFF);
|
||||
loader_send_simple(reply, dlci, LOADER_POWEROFF);
|
||||
device_poweroff();
|
||||
break;
|
||||
|
||||
case LOADER_ENTER_ROM_LOADER:
|
||||
puts("jump to rom loader\n");
|
||||
loader_send_simple(dlci, LOADER_ENTER_ROM_LOADER);
|
||||
loader_send_simple(reply, dlci, LOADER_ENTER_ROM_LOADER);
|
||||
device_enter_loader(1);
|
||||
break;
|
||||
|
||||
case LOADER_ENTER_FLASH_LOADER:
|
||||
puts("jump to flash loader\n");
|
||||
loader_send_simple(dlci, LOADER_ENTER_FLASH_LOADER);
|
||||
loader_send_simple(reply, dlci, LOADER_ENTER_FLASH_LOADER);
|
||||
device_enter_loader(0);
|
||||
break;
|
||||
|
||||
|
@ -211,16 +213,11 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
|
|||
nbytes = msgb_get_u8(msg);
|
||||
address = msgb_get_u32(msg);
|
||||
|
||||
printf("mem read %u @ %p\n", nbytes, (void*)address);
|
||||
|
||||
reply = sercomm_alloc_msgb(6 + nbytes);
|
||||
|
||||
if(!reply) {
|
||||
printf("Failed to allocate reply buffer!\n");
|
||||
}
|
||||
crc = crc16(0, (void*)address, nbytes);
|
||||
|
||||
msgb_put_u8(reply, LOADER_MEM_READ);
|
||||
msgb_put_u8(reply, nbytes);
|
||||
msgb_put_u16(reply, crc);
|
||||
msgb_put_u32(reply, address);
|
||||
|
||||
memcpy(msgb_put(reply, nbytes), (void*)address, nbytes);
|
||||
|
@ -235,27 +232,14 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
|
|||
crc = msgb_get_u16(msg);
|
||||
address = msgb_get_u32(msg);
|
||||
|
||||
printf("mem write %u @ %p\n", nbytes, (void*)address);
|
||||
|
||||
void *data = msgb_get(msg, nbytes);
|
||||
|
||||
uint16_t mycrc = crc16(0, data, nbytes);
|
||||
|
||||
#if 0
|
||||
printf("crc %x got %x\n", mycrc, crc);
|
||||
hexdump(data, nbytes);
|
||||
#endif
|
||||
|
||||
if(mycrc == crc) {
|
||||
memcpy((void*)address, data, nbytes);
|
||||
}
|
||||
|
||||
reply = sercomm_alloc_msgb(8);
|
||||
|
||||
if(!reply) {
|
||||
printf("Failed to allocate reply buffer!\n");
|
||||
}
|
||||
|
||||
msgb_put_u8(reply, LOADER_MEM_WRITE);
|
||||
msgb_put_u8(reply, nbytes);
|
||||
msgb_put_u16(reply, mycrc);
|
||||
|
@ -269,14 +253,6 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
|
|||
|
||||
address = msgb_get_u32(msg);
|
||||
|
||||
printf("jump to 0x%x\n", address);
|
||||
|
||||
reply = sercomm_alloc_msgb(5);
|
||||
|
||||
if(!reply) {
|
||||
printf("Failed to allocate reply buffer!\n");
|
||||
}
|
||||
|
||||
msgb_put_u8(reply, LOADER_JUMP);
|
||||
msgb_put_u32(reply, address);
|
||||
|
||||
|
@ -286,12 +262,96 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
|
|||
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unknown command %d\n", command);
|
||||
case LOADER_FLASH_INFO:
|
||||
|
||||
msgb_put_u8(reply, LOADER_FLASH_INFO);
|
||||
msgb_put_u8(reply, 1); // nchips
|
||||
|
||||
// chip 1
|
||||
msgb_put_u32(reply, the_flash.f_base);
|
||||
msgb_put_u32(reply, the_flash.f_size);
|
||||
msgb_put_u8(reply, the_flash.f_nregions);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < the_flash.f_nregions; i++) {
|
||||
msgb_put_u32(reply, the_flash.f_regions[i].fr_bnum);
|
||||
msgb_put_u32(reply, the_flash.f_regions[i].fr_bsize);
|
||||
}
|
||||
|
||||
sercomm_sendmsg(dlci, reply);
|
||||
|
||||
break;
|
||||
|
||||
case LOADER_FLASH_ERASE:
|
||||
case LOADER_FLASH_UNLOCK:
|
||||
case LOADER_FLASH_LOCK:
|
||||
case LOADER_FLASH_LOCKDOWN:
|
||||
|
||||
chip = msgb_get_u8(msg);
|
||||
address = msgb_get_u32(msg);
|
||||
|
||||
if(command == LOADER_FLASH_ERASE) {
|
||||
res = flash_block_erase(&the_flash, address);
|
||||
}
|
||||
if(command == LOADER_FLASH_UNLOCK) {
|
||||
res = flash_block_unlock(&the_flash, address);
|
||||
}
|
||||
if(command == LOADER_FLASH_LOCK) {
|
||||
res = flash_block_lock(&the_flash, address);
|
||||
}
|
||||
if(command == LOADER_FLASH_LOCKDOWN) {
|
||||
res = flash_block_lockdown(&the_flash, address);
|
||||
}
|
||||
|
||||
msgb_put_u8(reply, command);
|
||||
msgb_put_u8(reply, chip);
|
||||
msgb_put_u32(reply, address);
|
||||
msgb_put_u32(reply, (res != 0));
|
||||
|
||||
sercomm_sendmsg(dlci, reply);
|
||||
|
||||
break;
|
||||
|
||||
case LOADER_FLASH_GETLOCK:
|
||||
|
||||
chip = msgb_get_u8(msg);
|
||||
address = msgb_get_u32(msg);
|
||||
|
||||
lock = flash_block_getlock(&the_flash, address);
|
||||
|
||||
msgb_put_u8(reply, command);
|
||||
msgb_put_u8(reply, chip);
|
||||
msgb_put_u32(reply, address);
|
||||
|
||||
switch(lock) {
|
||||
case FLASH_UNLOCKED:
|
||||
msgb_put_u32(reply, LOADER_FLASH_UNLOCKED);
|
||||
break;
|
||||
case FLASH_LOCKED:
|
||||
msgb_put_u32(reply, LOADER_FLASH_LOCKED);
|
||||
break;
|
||||
case FLASH_LOCKED_DOWN:
|
||||
msgb_put_u32(reply, LOADER_FLASH_LOCKED_DOWN);
|
||||
break;
|
||||
default:
|
||||
msgb_put_u32(reply, 0xFFFFFFFF);
|
||||
break;
|
||||
}
|
||||
|
||||
sercomm_sendmsg(dlci, reply);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unknown command %d\n", command);
|
||||
|
||||
msgb_free(reply);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,37 @@
|
|||
|
||||
enum loader_command {
|
||||
/* init message from loader */
|
||||
LOADER_INIT,
|
||||
|
||||
/* ping / pong */
|
||||
LOADER_PING,
|
||||
|
||||
/* lifecycle requests */
|
||||
LOADER_RESET,
|
||||
LOADER_POWEROFF,
|
||||
|
||||
/* jumps */
|
||||
LOADER_JUMP,
|
||||
LOADER_ENTER_ROM_LOADER,
|
||||
LOADER_ENTER_FLASH_LOADER,
|
||||
|
||||
/* generic memory ops */
|
||||
LOADER_MEM_READ,
|
||||
LOADER_MEM_WRITE,
|
||||
LOADER_JUMP,
|
||||
|
||||
/* flash operations */
|
||||
LOADER_FLASH_INFO,
|
||||
LOADER_FLASH_ERASE,
|
||||
LOADER_FLASH_UNLOCK,
|
||||
LOADER_FLASH_LOCK,
|
||||
LOADER_FLASH_LOCKDOWN,
|
||||
LOADER_FLASH_GETLOCK,
|
||||
LOADER_FLASH_PROGRAM,
|
||||
|
||||
};
|
||||
|
||||
enum loader_flash_lock {
|
||||
LOADER_FLASH_UNLOCKED = 0,
|
||||
LOADER_FLASH_LOCKED,
|
||||
LOADER_FLASH_LOCKED_DOWN,
|
||||
};
|
||||
|
|
|
@ -23,50 +23,103 @@
|
|||
#include <debug.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <memory.h>
|
||||
#include <cfi_flash.h>
|
||||
|
||||
/* XXX: memdump_range() */
|
||||
#include <calypso/misc.h>
|
||||
#include <calypso/uart.h>
|
||||
#include <comm/sercomm.h>
|
||||
|
||||
enum flash_cmd {
|
||||
FLASH_CMD_RESET = 0xff,
|
||||
FLASH_CMD_READ_ID = 0x90,
|
||||
FLASH_CMD_CFI = 0x98,
|
||||
FLASH_CMD_READ_STATUS = 0x70,
|
||||
FLASH_CMD_CLEAR_STATUS = 0x50,
|
||||
FLASH_CMD_WRITE = 0x40,
|
||||
FLASH_CMD_BLOCK_ERASE = 0x20,
|
||||
FLASH_CMD_ERASE_CONFIRM = 0xD0,
|
||||
FLASH_CMD_PROTECT = 0x60,
|
||||
/* global definitions */
|
||||
#define CFI_FLASH_MAX_ERASE_REGIONS 4
|
||||
|
||||
/* structure of erase region descriptor */
|
||||
struct cfi_region {
|
||||
uint16_t b_count;
|
||||
uint16_t b_size;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* structure of cfi query response */
|
||||
struct cfi_query {
|
||||
uint8_t qry[3];
|
||||
uint16_t p_id;
|
||||
uint16_t p_adr;
|
||||
uint16_t a_id;
|
||||
uint16_t a_adr;
|
||||
uint8_t vcc_min;
|
||||
uint8_t vcc_max;
|
||||
uint8_t vpp_min;
|
||||
uint8_t vpp_max;
|
||||
uint8_t word_write_timeout_typ;
|
||||
uint8_t buf_write_timeout_typ;
|
||||
uint8_t block_erase_timeout_typ;
|
||||
uint8_t chip_erase_timeout_typ;
|
||||
uint8_t word_write_timeout_max;
|
||||
uint8_t buf_write_timeout_max;
|
||||
uint8_t block_erase_timeout_max;
|
||||
uint8_t chip_erase_timeout_max;
|
||||
uint8_t dev_size;
|
||||
uint16_t interface_desc;
|
||||
uint16_t max_buf_write_size;
|
||||
uint8_t num_erase_regions;
|
||||
struct cfi_region erase_regions[CFI_FLASH_MAX_ERASE_REGIONS];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* manufacturer ids */
|
||||
enum cfi_manuf {
|
||||
CFI_MANUF_INTEL = 0x0089,
|
||||
};
|
||||
|
||||
/* algorithm ids */
|
||||
enum cfi_algo {
|
||||
CFI_ALGO_INTEL_3 = 0x03
|
||||
};
|
||||
|
||||
/* various command bytes */
|
||||
enum cfi_flash_cmd {
|
||||
CFI_CMD_RESET = 0xff,
|
||||
CFI_CMD_READ_ID = 0x90,
|
||||
CFI_CMD_CFI = 0x98,
|
||||
CFI_CMD_READ_STATUS = 0x70,
|
||||
CFI_CMD_CLEAR_STATUS = 0x50,
|
||||
CFI_CMD_WRITE = 0x40,
|
||||
CFI_CMD_BLOCK_ERASE = 0x20,
|
||||
CFI_CMD_ERASE_CONFIRM = 0xD0,
|
||||
CFI_CMD_PROTECT = 0x60,
|
||||
};
|
||||
|
||||
/* protection commands */
|
||||
enum flash_prot_cmd {
|
||||
FLASH_PROT_LOCK = 0x01,
|
||||
FLASH_PROT_UNLOCK = 0xD0,
|
||||
FLASH_PROT_LOCKDOWN = 0x2F
|
||||
CFI_PROT_LOCK = 0x01,
|
||||
CFI_PROT_UNLOCK = 0xD0,
|
||||
CFI_PROT_LOCKDOWN = 0x2F
|
||||
};
|
||||
|
||||
/* offsets from base */
|
||||
enum flash_offset {
|
||||
FLASH_OFFSET_MANUFACTURER_ID = 0x00,
|
||||
FLASH_OFFSET_DEVICE_ID = 0x01,
|
||||
FLASH_OFFSET_INTEL_PROTECTION = 0x81,
|
||||
FLASH_OFFSET_CFI_RESP = 0x10
|
||||
CFI_OFFSET_MANUFACTURER_ID = 0x00,
|
||||
CFI_OFFSET_DEVICE_ID = 0x01,
|
||||
CFI_OFFSET_INTEL_PROTECTION = 0x81,
|
||||
CFI_OFFSET_CFI_RESP = 0x10
|
||||
};
|
||||
|
||||
/* offsets from block base */
|
||||
enum flash_block_offset {
|
||||
FLASH_OFFSET_BLOCK_LOCKSTATE = 0x02
|
||||
CFI_OFFSET_BLOCK_LOCKSTATE = 0x02
|
||||
};
|
||||
|
||||
/* status masks */
|
||||
enum flash_status {
|
||||
FLASH_STATUS_READY = 0x80,
|
||||
FLASH_STATUS_ERASE_SUSPENDED = 0x40,
|
||||
FLASH_STATUS_ERASE_ERROR = 0x20,
|
||||
FLASH_STATUS_PROGRAM_ERROR = 0x10,
|
||||
FLASH_STATUS_VPP_LOW = 0x08,
|
||||
FLASH_STATUS_PROGRAM_SUSPENDED = 0x04,
|
||||
FLASH_STATUS_LOCKED_ERROR = 0x02,
|
||||
FLASH_STATUS_RESERVED = 0x01
|
||||
CFI_STATUS_READY = 0x80,
|
||||
CFI_STATUS_ERASE_SUSPENDED = 0x40,
|
||||
CFI_STATUS_ERASE_ERROR = 0x20,
|
||||
CFI_STATUS_PROGRAM_ERROR = 0x10,
|
||||
CFI_STATUS_VPP_LOW = 0x08,
|
||||
CFI_STATUS_PROGRAM_SUSPENDED = 0x04,
|
||||
CFI_STATUS_LOCKED_ERROR = 0x02,
|
||||
CFI_STATUS_RESERVED = 0x01
|
||||
};
|
||||
|
||||
static inline void flash_write_cmd(const void *base_addr, uint16_t cmd)
|
||||
|
@ -83,105 +136,158 @@ static char flash_protected(uint32_t block_offset) {
|
|||
return block_offset < 64*1024;
|
||||
}
|
||||
|
||||
uint8_t flash_block_getlock(cfi_flash_t *flash, uint32_t block_offset) {
|
||||
|
||||
flash_lock_t flash_block_getlock(flash_t *flash, uint32_t block_offset) {
|
||||
const void *base_addr = flash->f_base;
|
||||
|
||||
uint8_t lockstate;
|
||||
flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
|
||||
lockstate = flash_read16(base_addr, block_offset + FLASH_OFFSET_BLOCK_LOCKSTATE);
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
return lockstate;
|
||||
flash_write_cmd(base_addr, CFI_CMD_READ_ID);
|
||||
lockstate = flash_read16(base_addr, (block_offset>>1) + CFI_OFFSET_BLOCK_LOCKSTATE);
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
if(lockstate & 0x2) {
|
||||
return FLASH_LOCKED_DOWN;
|
||||
} else if(lockstate & 0x01) {
|
||||
return FLASH_LOCKED;
|
||||
} else {
|
||||
return FLASH_UNLOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
void flash_block_unlock(cfi_flash_t *flash, uint32_t block_offset) {
|
||||
int flash_block_unlock(flash_t *flash, uint32_t block_offset) {
|
||||
const void *base_addr = flash->f_base;
|
||||
printf("Unlocking block at 0x%08x\n", block_offset);
|
||||
|
||||
if(flash_protected(block_offset)) {
|
||||
puts("error: block is soft-protected\n");
|
||||
return;
|
||||
if(block_offset >= flash->f_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_PROTECT);
|
||||
flash_write_cmd(base_addr + block_offset, FLASH_PROT_UNLOCK);
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
if(flash_protected(block_offset)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
printf("Unlocking block at 0x%08x, meaning %08x\n", block_offset, base_addr + block_offset);
|
||||
|
||||
flash_write_cmd(base_addr, CFI_CMD_PROTECT);
|
||||
flash_write_cmd(base_addr + block_offset, CFI_PROT_UNLOCK);
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flash_block_lock(cfi_flash_t *flash, uint32_t block_offset) {
|
||||
int flash_block_lock(flash_t *flash, uint32_t block_offset) {
|
||||
const void *base_addr = flash->f_base;
|
||||
|
||||
if(block_offset >= flash->f_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
printf("Locking block at 0x%08x\n", block_offset);
|
||||
flash_write_cmd(base_addr, FLASH_CMD_PROTECT);
|
||||
flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCK);
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
|
||||
flash_write_cmd(base_addr, CFI_CMD_PROTECT);
|
||||
flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCK);
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flash_block_lockdown(cfi_flash_t *flash, uint32_t block_offset) {
|
||||
int flash_block_lockdown(flash_t *flash, uint32_t block_offset) {
|
||||
const void *base_addr = flash->f_base;
|
||||
|
||||
if(block_offset >= flash->f_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
printf("Locking down block at 0x%08x\n", block_offset);
|
||||
flash_write_cmd(base_addr, FLASH_CMD_PROTECT);
|
||||
flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCKDOWN);
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
|
||||
flash_write_cmd(base_addr, CFI_CMD_PROTECT);
|
||||
flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCKDOWN);
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flash_block_erase(cfi_flash_t *flash, uint32_t block_offset) {
|
||||
int flash_block_erase(flash_t *flash, uint32_t block_offset) {
|
||||
const void *base_addr = flash->f_base;
|
||||
printf("Erasing block 0x%08x...", block_offset);
|
||||
|
||||
if(block_offset >= flash->f_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if(flash_protected(block_offset)) {
|
||||
puts("error: block is soft-protected\n");
|
||||
return;
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
printf("Erasing block 0x%08x...", block_offset);
|
||||
|
||||
void *block_addr = ((uint8_t*)base_addr) + block_offset;
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS);
|
||||
flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS);
|
||||
|
||||
flash_write_cmd(block_addr, FLASH_CMD_BLOCK_ERASE);
|
||||
flash_write_cmd(block_addr, FLASH_CMD_ERASE_CONFIRM);
|
||||
flash_write_cmd(block_addr, CFI_CMD_BLOCK_ERASE);
|
||||
flash_write_cmd(block_addr, CFI_CMD_ERASE_CONFIRM);
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS);
|
||||
flash_write_cmd(base_addr, CFI_CMD_READ_STATUS);
|
||||
uint16_t status;
|
||||
do {
|
||||
status = flash_read16(base_addr, 0);
|
||||
} while(!(status&FLASH_STATUS_READY));
|
||||
} while(!(status&CFI_STATUS_READY));
|
||||
|
||||
if(status&FLASH_STATUS_ERASE_ERROR) {
|
||||
int res = 0;
|
||||
if(status&CFI_STATUS_ERASE_ERROR) {
|
||||
puts("error: ");
|
||||
if(status&FLASH_STATUS_VPP_LOW) {
|
||||
if(status&CFI_STATUS_VPP_LOW) {
|
||||
puts("vpp insufficient\n");
|
||||
}
|
||||
if(status&FLASH_STATUS_LOCKED_ERROR) {
|
||||
res = -EFAULT;
|
||||
} else if(status&CFI_STATUS_LOCKED_ERROR) {
|
||||
puts("block is lock-protected\n");
|
||||
res = -EPERM;
|
||||
} else {
|
||||
puts("unknown fault\n");
|
||||
res = -EFAULT;
|
||||
}
|
||||
} else {
|
||||
puts("done\n");
|
||||
}
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes) {
|
||||
int flash_program(flash_t *flash, uint32_t dst, void *src, uint32_t nbytes) {
|
||||
const void *base_addr = flash->f_base;
|
||||
int res = 0;
|
||||
uint32_t i;
|
||||
|
||||
/* check destination bounds */
|
||||
if(dst >= flash->f_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if(dst + nbytes > flash->f_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check destination alignment */
|
||||
if(dst%2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if(nbytes%2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check permissions */
|
||||
if(flash_protected(dst)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* say something */
|
||||
printf("Programming %u bytes to 0x%08x from 0x%p...", nbytes, dst, src);
|
||||
|
||||
if(dst%2) {
|
||||
puts("error: unaligned destination\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(nbytes%2) {
|
||||
puts("error: unaligned count\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(flash_protected(dst)) {
|
||||
puts("error: block is soft-protected\n");
|
||||
return;
|
||||
}
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS);
|
||||
/* clear status register */
|
||||
flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS);
|
||||
|
||||
/* write the words */
|
||||
puts("writing...");
|
||||
for(i = 0; i < nbytes; i += 2) {
|
||||
uint16_t *src_addr = (uint16_t*)(src + i);
|
||||
|
@ -189,248 +295,228 @@ void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes)
|
|||
|
||||
uint16_t data = *src_addr;
|
||||
|
||||
flash_write_cmd(dst_addr, FLASH_CMD_WRITE);
|
||||
flash_write_cmd(dst_addr, CFI_CMD_WRITE);
|
||||
flash_write_cmd(dst_addr, data);
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS);
|
||||
flash_write_cmd(base_addr, CFI_CMD_READ_STATUS);
|
||||
uint16_t status;
|
||||
do {
|
||||
status = flash_read16(base_addr, 0);
|
||||
} while(!(status&FLASH_STATUS_READY));
|
||||
} while(!(status&CFI_STATUS_READY));
|
||||
|
||||
if(status&FLASH_STATUS_PROGRAM_ERROR) {
|
||||
if(status&CFI_STATUS_PROGRAM_ERROR) {
|
||||
puts("error: ");
|
||||
if(status&FLASH_STATUS_VPP_LOW) {
|
||||
if(status&CFI_STATUS_VPP_LOW) {
|
||||
puts("vpp insufficient");
|
||||
}
|
||||
if(status&FLASH_STATUS_LOCKED_ERROR) {
|
||||
res = -EFAULT;
|
||||
} else if(status&CFI_STATUS_LOCKED_ERROR) {
|
||||
puts("block is lock-protected");
|
||||
res = -EPERM;
|
||||
} else {
|
||||
puts("unknown fault");
|
||||
res = -EFAULT;
|
||||
}
|
||||
goto err_reset;
|
||||
}
|
||||
}
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
/* verify the result */
|
||||
puts("verifying...");
|
||||
for(i = 0; i < nbytes; i += 2) {
|
||||
uint16_t *src_addr = (uint16_t*)(src + i);
|
||||
uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i);
|
||||
if(*src_addr != *dst_addr) {
|
||||
puts("error: verification failed");
|
||||
res = -EFAULT;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
puts("done\n");
|
||||
|
||||
return;
|
||||
return res;
|
||||
|
||||
err_reset:
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
err:
|
||||
printf(" at offset 0x%x\n", i);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef void (*flash_block_cb_t)(cfi_flash_t *flash,
|
||||
uint32_t block_offset,
|
||||
uint32_t block_size);
|
||||
/* Internal: retrieve manufacturer and device id from id space */
|
||||
static int get_id(void *base_addr, uint16_t *manufacturer_id, uint16_t *device_id) {
|
||||
flash_write_cmd(base_addr, CFI_CMD_READ_ID);
|
||||
|
||||
void flash_iterate_blocks(cfi_flash_t *flash, struct cfi_query *qry,
|
||||
uint32_t start_offset, uint32_t end_offset,
|
||||
flash_block_cb_t callback)
|
||||
{
|
||||
int region, block;
|
||||
*manufacturer_id = flash_read16(base_addr, CFI_OFFSET_MANUFACTURER_ID);
|
||||
*device_id = flash_read16(base_addr, CFI_OFFSET_DEVICE_ID);
|
||||
|
||||
uint32_t block_start = 0;
|
||||
for(region = 0; region < qry->num_erase_regions; region++) {
|
||||
uint16_t actual_count = qry->erase_regions[region].b_count + 1;
|
||||
uint32_t actual_size = qry->erase_regions[region].b_size * 256;
|
||||
for(block = 0; block < actual_count; block++) {
|
||||
uint32_t block_end = block_start + actual_size;
|
||||
if(block_start >= start_offset && block_end-1 <= end_offset) {
|
||||
callback(flash, block_start, actual_size);
|
||||
}
|
||||
block_start = block_end;
|
||||
}
|
||||
}
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_id(void *base_addr, uint16_t *manufacturer_id, uint16_t *device_id) {
|
||||
flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
|
||||
/* Internal: retrieve cfi query response data */
|
||||
static int get_query(void *base_addr, struct cfi_query *query) {
|
||||
int res = 0;
|
||||
int i;
|
||||
|
||||
*manufacturer_id = flash_read16(base_addr, FLASH_OFFSET_MANUFACTURER_ID);
|
||||
*device_id = flash_read16(base_addr, FLASH_OFFSET_DEVICE_ID);
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
}
|
||||
|
||||
static void get_query(void *base_addr, struct cfi_query *query) {
|
||||
unsigned int i;
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_CFI);
|
||||
flash_write_cmd(base_addr, CFI_CMD_CFI);
|
||||
|
||||
for(i = 0; i < sizeof(struct cfi_query); i++) {
|
||||
uint16_t byte = flash_read16(base_addr, FLASH_OFFSET_CFI_RESP+i);
|
||||
uint16_t byte = flash_read16(base_addr, CFI_OFFSET_CFI_RESP+i);
|
||||
*(((unsigned char*)query)+i) = byte;
|
||||
}
|
||||
|
||||
if(query->qry[0] != 'Q' || query->qry[1] != 'R' || query->qry[2] != 'Y') {
|
||||
puts("Error: CFI query signature not found\n");
|
||||
res = -ENOENT;
|
||||
}
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void dump_query(void *base_addr, struct cfi_query *query) {
|
||||
unsigned int i;
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_CFI);
|
||||
|
||||
for(i = 0; i < sizeof(struct cfi_query); i++) {
|
||||
uint8_t byte = *(((uint8_t*)query)+i);
|
||||
printf("%04X: %02X\n", FLASH_OFFSET_CFI_RESP+i, byte);
|
||||
}
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
}
|
||||
|
||||
static void dump_layout(void *base_addr, const struct cfi_query *qry) {
|
||||
int region;
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
|
||||
for(region = 0; region < qry->num_erase_regions; region++) {
|
||||
uint16_t actual_count = qry->erase_regions[region].b_count + 1;
|
||||
uint32_t actual_size = qry->erase_regions[region].b_size * 256;
|
||||
printf("Region of 0x%04x times 0x%6x bytes\n", actual_count,
|
||||
actual_size);
|
||||
}
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
}
|
||||
|
||||
static void dump_locks(void *base_addr, const struct cfi_query *qry) {
|
||||
int region, block;
|
||||
|
||||
uint32_t block_addr = 0;
|
||||
flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
|
||||
for(region = 0; region < qry->num_erase_regions; region++) {
|
||||
uint16_t actual_count = qry->erase_regions[region].b_count + 1;
|
||||
uint32_t actual_size = qry->erase_regions[region].b_size * 256;
|
||||
for(block = 0; block < actual_count; block++) {
|
||||
uint8_t lock = flash_read16(base_addr, block_addr+2);
|
||||
printf("Block 0x%08x lock 0x%02x\n", block_addr*2, lock);
|
||||
block_addr += actual_size / 2;
|
||||
}
|
||||
}
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
}
|
||||
|
||||
static void dump_protection(void *base_addr) {
|
||||
flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
|
||||
|
||||
uint16_t lock = flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION);
|
||||
printf("Protection Lock: 0x%04x\n", lock);
|
||||
|
||||
puts("Protection Data: ");
|
||||
/* Internal: retrieve intel protection data */
|
||||
static int get_intel_protection(void *base_addr, uint16_t *lockp, uint8_t protp[8]) {
|
||||
int i;
|
||||
for(i = 0; i < 8; i++) {
|
||||
printf("%04x", flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION + 1 + i));
|
||||
}
|
||||
putchar('\n');
|
||||
|
||||
flash_write_cmd(base_addr, FLASH_CMD_RESET);
|
||||
/* check args */
|
||||
if(!lockp) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if(!protp) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* enter read id mode */
|
||||
flash_write_cmd(base_addr, CFI_CMD_READ_ID);
|
||||
|
||||
/* get lock */
|
||||
*lockp = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION);
|
||||
|
||||
/* get data */
|
||||
for(i = 0; i < 8; i++) {
|
||||
protp[i] = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION + 1 + i);
|
||||
}
|
||||
|
||||
/* leave read id mode */
|
||||
flash_write_cmd(base_addr, CFI_CMD_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_timing(void *base_addr, struct cfi_query *qry) {
|
||||
#if 0
|
||||
|
||||
static void dump_intel_protection(uint16_t lock, uint8_t data[8]) {
|
||||
printf(" protection lock 0x%4.4x data 0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
|
||||
lock, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
|
||||
}
|
||||
|
||||
static void dump_query_algorithms(struct cfi_query *qry) {
|
||||
printf(" primary algorithm 0x%4.4x\n", qry->p_id);
|
||||
printf(" primary extended query 0x%4.4x\n", qry->p_adr);
|
||||
printf(" alternate algorithm 0x%4.4x\n", qry->a_id);
|
||||
printf(" alternate extended query 0x%4.4x\n", qry->a_adr);
|
||||
}
|
||||
|
||||
static void dump_query_timing(struct cfi_query *qry) {
|
||||
uint32_t block_erase_typ = 1<<qry->block_erase_timeout_typ;
|
||||
uint32_t block_erase_max = (1<<qry->block_erase_timeout_max) * block_erase_typ;
|
||||
uint32_t word_program_typ = 1<<qry->word_write_timeout_typ;
|
||||
uint32_t word_program_max = (1<<qry->word_write_timeout_max) * word_program_typ;
|
||||
printf("Block Erase Typical: %u ms\n", block_erase_typ);
|
||||
printf("Block Erase Maximum: %u ms\n", block_erase_max);
|
||||
printf("Word Program Typical: %u us\n", word_program_typ);
|
||||
printf("Word Program Maximum: %u us\n", word_program_max);
|
||||
printf(" block erase typ %u ms\n", block_erase_typ);
|
||||
printf(" block erase max %u ms\n", block_erase_max);
|
||||
printf(" word program typ %u us\n", word_program_typ);
|
||||
printf(" word program max %u us\n", word_program_max);
|
||||
}
|
||||
|
||||
static void dump_algorithms(void *base_addr, struct cfi_query *qry) {
|
||||
printf("Primary Algorithm ID: %04x\n", qry->p_id);
|
||||
printf("Primary Extended Query: %04x\n", qry->p_adr);
|
||||
void flash_dump_info(flash_t *flash) {
|
||||
int i;
|
||||
printf("flash at 0x%p of %d bytes with %d regions\n", flash->f_base, flash->f_size, flash->f_nregions);
|
||||
|
||||
printf("Alternate Algorithm ID: %04x\n", qry->a_id);
|
||||
printf("Alternate Extended Query: %04x\n", qry->a_adr);
|
||||
uint16_t m_id, d_id;
|
||||
if(get_id(flash->f_base, &m_id, &d_id)) {
|
||||
puts(" failed to get id\n");
|
||||
} else {
|
||||
printf(" manufacturer 0x%4.4x device 0x%4.4x\n", m_id, d_id);
|
||||
}
|
||||
|
||||
uint16_t plock;
|
||||
uint8_t pdata[8];
|
||||
if(get_intel_protection(flash->f_base, &plock, pdata)) {
|
||||
puts(" failed to get protection data\n");
|
||||
} else {
|
||||
dump_intel_protection(plock, pdata);
|
||||
}
|
||||
|
||||
struct cfi_query qry;
|
||||
if(get_query(flash->f_base, &qry)) {
|
||||
puts(" failed to get cfi query response\n");
|
||||
} else {
|
||||
dump_query_algorithms(&qry);
|
||||
dump_query_timing(&qry);
|
||||
}
|
||||
|
||||
for(i = 0; i < flash->f_nregions; i++) {
|
||||
flash_region_t *fr = &flash->f_regions[i];
|
||||
printf(" region %d: %d blocks of %d bytes at 0x%p\n", i, fr->fr_bnum, fr->fr_bsize, fr->fr_base);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lockdown_block_cb(cfi_flash_t *flash,
|
||||
uint32_t block_offset,
|
||||
uint32_t block_size)
|
||||
{
|
||||
flash_block_lockdown(flash, block_offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
print_block_cb(cfi_flash_t *flash,
|
||||
uint32_t block_offset,
|
||||
uint32_t block_size)
|
||||
{
|
||||
printf("%08x size %08x\n", block_offset, block_size);
|
||||
}
|
||||
int flash_init(flash_t *flash, void *base_addr) {
|
||||
int res, i;
|
||||
uint16_t m_id, d_id;
|
||||
uint32_t base;
|
||||
struct cfi_query qry;
|
||||
|
||||
void flash_dump_info(cfi_flash_t *flash) {
|
||||
void *base_addr = flash->f_base;
|
||||
struct cfi_query *qry = &flash->f_query;
|
||||
/* retrieve and check manufacturer and device id */
|
||||
res = get_id(base_addr, &m_id, &d_id);
|
||||
if(res) {
|
||||
return res;
|
||||
}
|
||||
if(m_id != CFI_MANUF_INTEL) {
|
||||
/* we only support intel devices */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
printf("Flash Manufacturer ID: %04x\n", flash->f_manuf_id);
|
||||
printf("Flash Device ID: %04x\n", flash->f_dev_id);
|
||||
|
||||
printf("Flash Size: 0x%08x bytes\n", flash->f_size);
|
||||
|
||||
dump_algorithms(base_addr, qry);
|
||||
|
||||
dump_timing(base_addr, qry);
|
||||
|
||||
dump_protection(base_addr);
|
||||
|
||||
dump_layout(base_addr, qry);
|
||||
|
||||
dump_locks(base_addr, qry);
|
||||
}
|
||||
|
||||
void flash_init(cfi_flash_t *flash, void *base_addr) {
|
||||
printd("Initializing CFI flash at 0x%p\n", base_addr);
|
||||
/* retrieve and check query response */
|
||||
res = get_query(base_addr, &qry);
|
||||
if(res) {
|
||||
return res;
|
||||
}
|
||||
if(qry.p_id != CFI_ALGO_INTEL_3) {
|
||||
/* we only support algo 3 */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if(qry.num_erase_regions > FLASH_MAX_REGIONS) {
|
||||
/* we have a hard limit on the number of regions */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* fill in basic information */
|
||||
flash->f_base = base_addr;
|
||||
flash->f_size = 1<<qry.dev_size;
|
||||
|
||||
get_id(base_addr, &flash->f_manuf_id, &flash->f_dev_id);
|
||||
/* determine number of erase regions */
|
||||
flash->f_nregions = qry.num_erase_regions;
|
||||
|
||||
get_query(base_addr, &flash->f_query);
|
||||
/* compute actual erase region info from cfi junk */
|
||||
base = 0;
|
||||
for(i = 0; i < flash->f_nregions; i++) {
|
||||
flash_region_t *fr = &flash->f_regions[i];
|
||||
|
||||
flash->f_size = 1<<flash->f_query.dev_size;
|
||||
}
|
||||
|
||||
void flash_test() {
|
||||
/* block iterator test */
|
||||
#if 0
|
||||
flash_iterate_blocks(flash, qry, 0x0000, 0xFFFF, &lockdown_block_cb);
|
||||
#endif
|
||||
|
||||
/* programming test */
|
||||
#if 0
|
||||
static uint8_t magic[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||
|
||||
memdump_range(&magic, sizeof(magic));
|
||||
|
||||
#if 0
|
||||
#define ADDR 0x001E0000
|
||||
flash_block_unlock(flash, ADDR);
|
||||
memdump_range(ADDR, 16);
|
||||
flash_block_erase(flash, ADDR);
|
||||
memdump_range(ADDR, 16);
|
||||
flash_program(flash, ADDR, &magic, sizeof(magic));
|
||||
memdump_range(ADDR, 16);
|
||||
#undef ADDR
|
||||
#endif
|
||||
|
||||
#endif
|
||||
fr->fr_base = base;
|
||||
fr->fr_bnum = qry.erase_regions[i].b_count + 1;
|
||||
fr->fr_bsize = qry.erase_regions[i].b_size * 256;
|
||||
|
||||
base += fr->fr_bnum * fr->fr_bsize;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,67 +4,38 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define CFI_FLASH_MAX_ERASE_REGIONS 4
|
||||
|
||||
/* structure of erase region descriptor */
|
||||
struct cfi_region {
|
||||
uint16_t b_count;
|
||||
uint16_t b_size;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/* structure of cfi query response */
|
||||
struct cfi_query {
|
||||
uint8_t qry[3];
|
||||
uint16_t p_id;
|
||||
uint16_t p_adr;
|
||||
uint16_t a_id;
|
||||
uint16_t a_adr;
|
||||
uint8_t vcc_min;
|
||||
uint8_t vcc_max;
|
||||
uint8_t vpp_min;
|
||||
uint8_t vpp_max;
|
||||
uint8_t word_write_timeout_typ;
|
||||
uint8_t buf_write_timeout_typ;
|
||||
uint8_t block_erase_timeout_typ;
|
||||
uint8_t chip_erase_timeout_typ;
|
||||
uint8_t word_write_timeout_max;
|
||||
uint8_t buf_write_timeout_max;
|
||||
uint8_t block_erase_timeout_max;
|
||||
uint8_t chip_erase_timeout_max;
|
||||
uint8_t dev_size;
|
||||
uint16_t interface_desc;
|
||||
uint16_t max_buf_write_size;
|
||||
uint8_t num_erase_regions;
|
||||
struct cfi_region erase_regions[CFI_FLASH_MAX_ERASE_REGIONS];
|
||||
} __attribute__((packed));
|
||||
#define FLASH_MAX_REGIONS 4
|
||||
|
||||
typedef struct {
|
||||
void *f_base;
|
||||
void *fr_base;
|
||||
size_t fr_bnum;
|
||||
size_t fr_bsize;
|
||||
} flash_region_t;
|
||||
|
||||
uint32_t f_size;
|
||||
typedef struct {
|
||||
void *f_base;
|
||||
size_t f_size;
|
||||
|
||||
uint16_t f_manuf_id;
|
||||
uint16_t f_dev_id;
|
||||
size_t f_nregions;
|
||||
flash_region_t f_regions[FLASH_MAX_REGIONS];
|
||||
} flash_t;
|
||||
|
||||
struct cfi_query f_query;
|
||||
} cfi_flash_t;
|
||||
typedef enum {
|
||||
FLASH_UNLOCKED = 0,
|
||||
FLASH_LOCKED,
|
||||
FLASH_LOCKED_DOWN
|
||||
} flash_lock_t;
|
||||
|
||||
typedef uint8_t flash_lock;
|
||||
int flash_init(flash_t *flash, void *base_addr);
|
||||
|
||||
void flash_init(cfi_flash_t *flash, void *base_addr);
|
||||
flash_lock_t flash_block_getlock(flash_t *flash, uint32_t block_offset);
|
||||
|
||||
void flash_dump_info(cfi_flash_t *flash);
|
||||
int flash_block_unlock(flash_t *flash, uint32_t block_offset);
|
||||
int flash_block_lock(flash_t *flash, uint32_t block_offset);
|
||||
int flash_block_lockdown(flash_t *flash, uint32_t block_offset);
|
||||
|
||||
flash_lock flash_block_getlock(cfi_flash_t *flash, uint32_t block_offset);
|
||||
int flash_block_erase(flash_t *flash, uint32_t block_offset);
|
||||
|
||||
void flash_block_unlock(cfi_flash_t *flash, uint32_t block_offset);
|
||||
void flash_block_lock(cfi_flash_t *flash, uint32_t block_offset);
|
||||
void flash_block_lockdown(cfi_flash_t *flash, uint32_t block_offset);
|
||||
|
||||
void flash_block_erase(cfi_flash_t *flash, uint32_t block_addr);
|
||||
|
||||
void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes);
|
||||
int flash_program(flash_t *flash, uint32_t dst_offset, void *src, uint32_t nbytes);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue