FreeCalypso > hg > freecalypso-tools
view loadtools/flamdsec.c @ 997:67513b9446da
doc/Flash-write-protection: new article
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Mon, 04 Dec 2023 01:42:35 +0000 |
parents | ad3041e19884 |
children |
line wrap: on
line source
/* * This module is a place to implement commands and functions for * sector write-protection (locking and unlocking, checking current * lock state) on AMD-style flash chips. */ #include <sys/types.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <time.h> #include <unistd.h> #include "flash.h" extern struct flash_bank_info flash_bank_info[2]; /* * Some common functions for Spansion PL-N flash: common between * flash lock-state retrieval and active PPB programming. */ static pln_special_mode_entry(base_addr, mode_opc) uint32_t base_addr; uint16_t mode_opc; { if (do_w16(base_addr + 0xAAA, 0xAA)) { bad_w16: fprintf(stderr, "unexpected response to w16 in PL-N special mode entry sequence - aborting\n"); return(-1); } if (do_w16(base_addr + 0x554, 0x55)) goto bad_w16; if (do_w16(base_addr + 0xAAA, mode_opc)) goto bad_w16; return(0); } static pln_special_mode_exit(base_addr) uint32_t base_addr; { if (do_w16(base_addr, 0x90)) { bad_w16: fprintf(stderr, "unexpected response to w16 in PL-N special mode exit sequence - aborting\n"); return(-1); } if (do_w16(base_addr, 0x00)) goto bad_w16; return(0); } /* * flash lock-state implementation with its helper functions. */ static issue_read_id(base_addr) uint32_t base_addr; { if (do_w16(base_addr + 0xAAA, 0xAA)) { bad_w16: fprintf(stderr, "unexpected response to w16 in read ID cmd sequence - aborting\n"); return(-1); } if (do_w16(base_addr + 0x554, 0x55)) goto bad_w16; if (do_w16(base_addr + 0xAAA, 0x90)) goto bad_w16; return(0); } static issue_reset_cmd(base_addr) uint32_t base_addr; { if (do_w16(base_addr + 0xAAA, 0xF0)) { fprintf(stderr, "unexpected response to w16 when resetting flash to read mode!\n"); return(-1); } return(0); } static read_mode_lock_word(base_addr, word_offset, retp) uint32_t base_addr, word_offset; uint16_t *retp; { if (do_w16(base_addr + 0xAAA, 0xAA)) { bad_w16: fprintf(stderr, "unexpected response to w16 in mode lock query sequence - aborting\n"); return(-1); } if (do_w16(base_addr + 0x554, 0x55)) goto bad_w16; if (do_w16(base_addr + 0xAAA, 0x60)) goto bad_w16; if (do_w16(base_addr + word_offset, 0x48)) goto bad_w16; if (do_r16(base_addr + word_offset, retp) < 0) return(-1); return(0); } flashcmd_lock_state(argc, argv, bank) char **argv; { struct flash_bank_info *bi; struct amd_lock_info *li; struct lock_group_desc *grp; uint32_t offset, part_addr; uint16_t word; unsigned ng, nb; if (argc > 2) { fprintf(stderr, "error: too many arguments\n"); return(-1); } if (flash_detect(bank, 0) < 0) return(-1); bi = flash_bank_info + bank; li = bi->amd_lock; if (!li) { fprintf(stderr, "Operation not supported for this flash chip type\n"); return(-1); } offset = 0; for (ng = 0; ng < li->ngroups; ng++) { grp = li->groups + ng; if (grp->part_begin) { part_addr = bi->base_addr + offset; if (issue_read_id(part_addr) < 0) return(-1); } if (offset == 0 && li->have_status_word_3) { if (do_r16(bi->base_addr + 6, &word) < 0) return(-1); printf("Global status word 3: %04X\n", word); } if (offset == 0 && li->have_status_word_7) { if (do_r16(bi->base_addr + 0xE, &word) < 0) return(-1); printf("Global status word 7: %04X\n", word); } for (nb = 0; nb < grp->nblocks; nb++) { if (do_r16(bi->base_addr + offset + 4, &word) < 0) return(-1); printf("Sector%s at 0x%X: %s\n", grp->is_group ? " group" : "", offset, (word & 1) ? "locked" : "unlocked"); offset += grp->block_size; } if (grp->part_end) { if (issue_reset_cmd(part_addr) < 0) return(-1); } } if (li->have_mode_lock_bits) { if (read_mode_lock_word(bi->base_addr, 0x14, &word) < 0) return(-1); printf("Password Protection Mode lock: %04X\n", word); if (issue_reset_cmd(bi->base_addr) < 0) return(-1); if (read_mode_lock_word(bi->base_addr, 0x24, &word) < 0) return(-1); printf("Persistent Protection Mode lock: %04X\n", word); if (issue_reset_cmd(bi->base_addr) < 0) return(-1); } if (li->have_pln_lock_reg) { if (pln_special_mode_entry(bi->base_addr, 0x40) < 0) return(-1); if (do_r16(bi->base_addr, &word) < 0) return(-1); printf("PL-N Lock Register: %04X\n", word); if (pln_special_mode_exit(bi->base_addr) < 0) return(-1); } return(0); } /* * Here comes a version of the above lock-state checking code, * modified for use from within ppb-program-all and ppb-erase-all * functions for chips that don't do this work internally. */ static int_lock_state_check(bi, sought_state) struct flash_bank_info *bi; { struct amd_lock_info *li = bi->amd_lock; struct lock_group_desc *grp; uint32_t offset, part_addr; uint16_t word; unsigned ng, nb; int lock_state; offset = 0; for (ng = 0; ng < li->ngroups; ng++) { grp = li->groups + ng; if (grp->part_begin) { part_addr = bi->base_addr + offset; if (issue_read_id(part_addr) < 0) return(-1); } for (nb = 0; nb < grp->nblocks; nb++) { if (do_r16(bi->base_addr + offset + 4, &word) < 0) return(-1); lock_state = word & 1; if (lock_state != sought_state) break; offset += grp->block_size; } if (lock_state != sought_state) { if (issue_reset_cmd(part_addr) < 0) return(-1); return(0); } if (grp->part_end) { if (issue_reset_cmd(part_addr) < 0) return(-1); } } return(1); } /* * Spansion PL-J PPB write functions, referenced from lock_info structures * in fldevs.c device descriptions. */ static plj_ppb_write_op(base_addr, is_erase, retp) uint32_t base_addr; uint16_t *retp; { if (do_w16(base_addr + 0xAAA, 0xAA)) { bad_w16: fprintf(stderr, "unexpected response to w16 in PPB command sequence - aborting\n"); return(-1); } if (do_w16(base_addr + 0x554, 0x55)) goto bad_w16; if (do_w16(base_addr + 0xAAA, 0x60)) goto bad_w16; if (do_w16(base_addr + 4, is_erase ? 0x60 : 0x68)) goto bad_w16; usleep(1200); /* per S29PL-J datasheet */ if (do_w16(base_addr + 4, is_erase ? 0x40 : 0x48)) goto bad_w16; if (do_r16(base_addr + 4, retp) < 0) return(-1); return(0); } plj_ppb_program_one(bi, sector_addr) struct flash_bank_info *bi; uint32_t sector_addr; { uint16_t stat; unsigned pulsecnt; int rc; for (pulsecnt = 0; pulsecnt < 25; ) { rc = plj_ppb_write_op(bi->base_addr + sector_addr, 0, &stat); if (rc < 0) return(rc); pulsecnt++; if (!(stat & 1)) continue; printf("PPB 0x%X programmed with %u pulse%s\n", sector_addr, pulsecnt, pulsecnt > 1 ? "s" : ""); return amd_reset_cmd(bi); } fprintf(stderr, "PPB 0x%X programming FAILED, tried %u pulses\n", sector_addr, pulsecnt); return(-1); } plj_ppb_program_all(bi) struct flash_bank_info *bi; { struct amd_lock_info *li = bi->amd_lock; struct lock_group_desc *grp; uint32_t offset; unsigned ng, nb; int rc; offset = 0; for (ng = 0; ng < li->ngroups; ng++) { grp = li->groups + ng; for (nb = 0; nb < grp->nblocks; nb++) { rc = plj_ppb_program_one(bi, offset); if (rc < 0) return(rc); offset += grp->block_size; } } printf("Verifying PPB programming\n"); rc = int_lock_state_check(bi, 1); if (rc < 0) return(rc); if (rc) return(0); fprintf(stderr, "flash error: one or more PPBs failed to program\n"); return(-1); } static plj_ppb_erase_cycle(bi) struct flash_bank_info *bi; { uint16_t stat; unsigned pulsecnt; int rc; printf("Performing PPB erase cycle\n"); for (pulsecnt = 0; pulsecnt < 1000; ) { rc = plj_ppb_write_op(bi->base_addr, 1, &stat); if (rc < 0) return(rc); pulsecnt++; if (stat & 1) continue; printf("PPB erase cycle succeeded after %u pulse%s\n", pulsecnt, pulsecnt > 1 ? "s" : ""); return amd_reset_cmd(bi); } fprintf(stderr, "PPB erase cycle FAILED, tried %u pulses\n", pulsecnt); return(-1); } plj_ppb_erase_all_single(bi, raw_mode) struct flash_bank_info *bi; { unsigned pulsecnt; uint16_t stat; int rc; if (raw_mode) return plj_ppb_erase_cycle(bi); printf("Programming all PPBs before erase cycle\n"); rc = plj_ppb_program_all(bi); if (rc < 0) return(-1); printf("Entering PPB erase and verify loop\n"); for (pulsecnt = 0; ; ) { if (pulsecnt >= 1000) { fprintf(stderr, "flash error: unable to complete PPB erase after %u pulses\n", pulsecnt); return(-1); } rc = plj_ppb_write_op(bi->base_addr, 1, &stat); if (rc < 0) return(rc); pulsecnt++; if (stat & 1) continue; putchar('.'); fflush(stdout); rc = amd_reset_cmd(bi); if (rc < 0) return(rc); rc = int_lock_state_check(bi, 0); if (rc < 0) return(rc); if (rc) break; } printf("\nPPB erase complete, total pulses: %u\n", pulsecnt); return(0); } plj_ppb_erase_all_dualbank(dummy_bi, raw_mode) void *dummy_bi; { unsigned pulsecnt; uint16_t stat; int rc; if (flash_detect(0, 0) < 0) return(-1); if (flash_detect(1, 0) < 0) return(-1); if (flash_bank_info[0].device != flash_bank_info[1].device) { fprintf(stderr, "error: mismatch between two flash banks\n"); return(-1); } if (raw_mode) return plj_ppb_erase_cycle(&flash_bank_info[0]); printf("Programming all PPBs in flash bank 0\n"); rc = plj_ppb_program_all(&flash_bank_info[0]); if (rc < 0) return(-1); printf("Programming all PPBs in flash bank 1\n"); rc = plj_ppb_program_all(&flash_bank_info[1]); if (rc < 0) return(-1); printf("Entering PPB erase and verify loop\n"); for (pulsecnt = 0; ; ) { if (pulsecnt >= 1000) { fprintf(stderr, "flash error: unable to complete PPB erase after %u pulses\n", pulsecnt); return(-1); } rc = plj_ppb_write_op(flash_bank_info[0].base_addr, 1, &stat); if (rc < 0) return(rc); pulsecnt++; if (stat & 1) continue; putchar('.'); fflush(stdout); rc = amd_reset_cmd(&flash_bank_info[0]); if (rc < 0) return(rc); rc = int_lock_state_check(&flash_bank_info[0], 0); if (rc < 0) return(rc); if (!rc) continue; rc = int_lock_state_check(&flash_bank_info[1], 0); if (rc < 0) return(rc); if (rc) break; } printf("\nPPB erase complete, total pulses: %u\n", pulsecnt); return(0); } /* * Spansion PL-N PPB write functions, referenced from lock_info structures * in fldevs.c device descriptions. */ static pln_ppb_write_op(oper_addr, write1, write2, expect_stat) uint32_t oper_addr; uint16_t write1, write2, expect_stat; { int rc; uint16_t read_stat, prev_stat; time_t start_time, curtime; rc = pln_special_mode_entry(oper_addr, 0xC0); if (rc < 0) return(rc); if (do_w16(oper_addr, write1)) { bad_w16: fprintf(stderr, "unexpected response to w16 in PPB command sequence - aborting\n"); return(-1); } if (do_w16(oper_addr, write2)) goto bad_w16; printf("Polling for completion status\n"); usleep(10000); /* make sure we don't get state before op starts */ start_time = time(0); rc = do_r16(oper_addr, &read_stat); if (rc < 0) return(rc); for (;;) { prev_stat = read_stat; rc = do_r16(oper_addr, &read_stat); if (rc < 0) return(rc); if (read_stat == expect_stat && prev_stat == expect_stat) break; curtime = time(0); if (curtime >= start_time + 10) { fprintf(stderr, "operation timeout, aborting\n"); return(-1); } } printf("Operation completed successfully\n"); return pln_special_mode_exit(oper_addr); } pln_ppb_program_one(bi, sector_addr) struct flash_bank_info *bi; uint32_t sector_addr; { printf("Issuing PPB Program command\n"); return pln_ppb_write_op(bi->base_addr + sector_addr, 0xA0, 0, 0); } pln_ppb_erase_all(bi, raw_mode) struct flash_bank_info *bi; { printf("Issuing All PPB Erase command\n"); return pln_ppb_write_op(bi->base_addr, 0x80, 0x30, 1); } /* * Front end functions for PPB operation commands. */ flashcmd_ppb_program(argc, argv, bank) char **argv; { struct flash_bank_info *bi; struct amd_lock_info *li; u_long offset_arg; struct sector_info *sp; char *strtoul_endp; if (argc != 3) { inv: fprintf(stderr, "usage: %s %s sector-offset\n", argv[0], argv[1]); return(-1); } offset_arg = strtoul(argv[2], &strtoul_endp, 16); if (*strtoul_endp) goto inv; if (flash_detect(bank, 0) < 0) return(-1); bi = flash_bank_info + bank; li = bi->amd_lock; if (!li || !li->ppb_program_one) { fprintf(stderr, "Operation not supported for this flash chip type\n"); return(-1); } if (offset_arg >= bi->geom->total_size) { fprintf(stderr, "error: specified offset exceeds flash bank size (0x%lx)\n", (u_long) bi->geom->total_size); return(-1); } if (get_flash_sector_table(bi) < 0) return(-1); for (sp = bi->sectors; sp->size; sp++) if (sp->start == offset_arg) break; if (!sp->size) { fprintf(stderr, "error: specified offset not aligned to a flash sector boundary\n"); return(-1); } return li->ppb_program_one(bi, sp->start); } flashcmd_ppb_program_all(argc, argv, bank) char **argv; { struct flash_bank_info *bi; struct amd_lock_info *li; if (argc > 2) { fprintf(stderr, "error: too many arguments\n"); return(-1); } if (flash_detect(bank, 0) < 0) return(-1); bi = flash_bank_info + bank; li = bi->amd_lock; if (!li || !li->ppb_program_all) { fprintf(stderr, "Operation not supported for this flash chip type\n"); return(-1); } return li->ppb_program_all(bi); } flashcmd_ppb_erase_all(argc, argv, bank) char **argv; { struct flash_bank_info *bi; struct amd_lock_info *li; int raw_mode; switch (argc) { case 2: raw_mode = 0; break; case 3: if (!strcmp(argv[2], "raw")) { raw_mode = 1; break; } /* FALL THRU */ default: fprintf(stderr, "usage: %s %s [raw]\n", argv[0], argv[1]); return(-1); } if (flash_detect(bank, 0) < 0) return(-1); bi = flash_bank_info + bank; li = bi->amd_lock; if (!li || !li->ppb_erase_all) { fprintf(stderr, "Operation not supported for this flash chip type\n"); return(-1); } return li->ppb_erase_all(bi, raw_mode); }