From 3936c397a7918fa8625355b5af39a371da6adc3a Mon Sep 17 00:00:00 2001 From: Ingo Albrecht Date: Sun, 11 Apr 2010 07:26:42 +0200 Subject: [PATCH] loader: flash locking commands, various flash rework --- src/host/osmocon/osmoload.c | 182 ++++++- src/target/firmware/apps/loader/main.c | 166 ++++-- src/target/firmware/apps/loader/protocol.h | 27 +- src/target/firmware/flash/cfi_flash.c | 590 ++++++++++++--------- src/target/firmware/include/cfi_flash.h | 75 +-- 5 files changed, 673 insertions(+), 367 deletions(-) diff --git a/src/host/osmocon/osmoload.c b/src/host/osmocon/osmoload.c index ef98a3529..ef00c3de1 100644 --- a/src/host/osmocon/osmoload.c +++ b/src/host/osmocon/osmoload.c @@ -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 - Poke at memory"); puts(" memdump - Dump memory to file"); puts(" memload - Load file into memory"); + puts(" finfo - Information about flash chips"); + puts(" funlock
- Unlock flash block"); + puts(" flock
- Lock flash block"); + puts(" flockdown
- Lock down flash block"); + puts(" fgetlock
- Get locking state of block"); puts(" jump - 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; diff --git a/src/target/firmware/apps/loader/main.c b/src/target/firmware/apps/loader/main.c index a3aefa8ec..d1fdb3178 100644 --- a/src/target/firmware/apps/loader/main.c +++ b/src/target/firmware/apps/loader/main.c @@ -48,8 +48,7 @@ #include #include -#include -#include +#include #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); } diff --git a/src/target/firmware/apps/loader/protocol.h b/src/target/firmware/apps/loader/protocol.h index c2ea25ca4..0a61c89ec 100644 --- a/src/target/firmware/apps/loader/protocol.h +++ b/src/target/firmware/apps/loader/protocol.h @@ -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, }; diff --git a/src/target/firmware/flash/cfi_flash.c b/src/target/firmware/flash/cfi_flash.c index a269142f2..624d14333 100644 --- a/src/target/firmware/flash/cfi_flash.c +++ b/src/target/firmware/flash/cfi_flash.c @@ -23,50 +23,103 @@ #include #include #include +#include #include #include /* XXX: memdump_range() */ #include +#include +#include -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<block_erase_timeout_typ; uint32_t block_erase_max = (1<block_erase_timeout_max) * block_erase_typ; uint32_t word_program_typ = 1<word_write_timeout_typ; uint32_t word_program_max = (1<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<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<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; } diff --git a/src/target/firmware/include/cfi_flash.h b/src/target/firmware/include/cfi_flash.h index 45435c41b..9d8b33aef 100644 --- a/src/target/firmware/include/cfi_flash.h +++ b/src/target/firmware/include/cfi_flash.h @@ -4,67 +4,38 @@ #include - -#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