/* placed in public domain, written by Felix Domke */ /* USE ON YOUR OWN RISK. */ #include #include #include #include #include #include #include int nandsize; int bsec; extern void *mmap64 (void *__addr, size_t __len, int __prot, int __flags, int __fd, __off64_t __offset) __THROW; volatile void * ioremap(unsigned long long physaddr, unsigned size, int sync) { int axs_mem_fd = -1; unsigned long long page_addr, ofs_addr, reg, pgmask; void* reg_mem = NULL; /* * looks like mmap wants aligned addresses? */ pgmask = getpagesize()-1; page_addr = physaddr & ~pgmask; ofs_addr = physaddr & pgmask; /* * Don't forget O_SYNC, esp. if address is in RAM region. * Note: if you do know you'll access in Read Only mode, * pass O_RDONLY to open, and PROT_READ only to mmap */ if (axs_mem_fd == -1) { axs_mem_fd = open("/dev/mem", O_RDWR|(sync ? O_SYNC : 0)); if (axs_mem_fd < 0) { perror("AXS: can't open /dev/mem"); return NULL; } } /* memory map */ reg_mem = mmap64( (caddr_t)reg_mem, size+ofs_addr, PROT_READ|PROT_WRITE, MAP_SHARED, axs_mem_fd, page_addr ); if (reg_mem == MAP_FAILED) { perror("AXS: mmap error"); close(axs_mem_fd); return NULL; } reg = (unsigned long )reg_mem + ofs_addr; return (volatile void *)reg; } int iounmap(volatile void *start, size_t length) { unsigned long ofs_addr; ofs_addr = (unsigned long)start & (getpagesize()-1); /* do some cleanup when you're done with it */ return munmap((unsigned char*)start-ofs_addr, length+ofs_addr); } #define STATUS 1 #define COMMAND 2 #define ADDRESS 3 #define DATA 4 #define LOGICAL 5 #define PHYSICAL 6 volatile unsigned int *flash; void sfcx_writereg(int reg, int value) { flash[reg] = bswap_32(value); } unsigned int sfcx_readreg(int reg) { return bswap_32(flash[reg]); } void readsector(unsigned char *data, int sector, int raw) { int status; sfcx_writereg(STATUS, sfcx_readreg(STATUS)); sfcx_writereg(ADDRESS, sector); sfcx_writereg(COMMAND, raw ? 3 : 2); while ((status = sfcx_readreg(STATUS))&1); if (status != 0x200) { if (status & 0x40) printf(" * Bad block found at %08x\n", sector); else if (status & 0x1c) printf(" * (corrected) ECC error %08x: %08x\n", sector, status); else if (!raw) printf(" * illegal logical block %08x\n", sector); else printf(" * Unknown error at %08x: %08x. Please worry.\n", sector, status); } sfcx_writereg(ADDRESS, 0); int i; for (i = 0; i < 0x210; i+=4) { sfcx_writereg(COMMAND, 0); *(int*)(data + i) = bswap_32(sfcx_readreg(DATA)); } } void flash_erase(int address) { sfcx_writereg(0, sfcx_readreg(0) | 8); sfcx_writereg(STATUS, 0xFF); sfcx_writereg(ADDRESS, address); while (sfcx_readreg(STATUS) & 1); sfcx_writereg(COMMAND, 0xAA); sfcx_writereg(COMMAND, 0x55); while (sfcx_readreg(STATUS) & 1); sfcx_writereg(COMMAND, 0x5); while (sfcx_readreg(STATUS) & 1); int status = sfcx_readreg(STATUS); if (status != 0x200) printf("[%08x]", status); sfcx_writereg(STATUS, 0xFF); sfcx_writereg(0, sfcx_readreg(0) & ~8); } void write_page(int address, unsigned char *data) { sfcx_writereg(STATUS, 0xFF); sfcx_writereg(0, sfcx_readreg(0) | 8); sfcx_writereg(ADDRESS, 0); int i; for (i = 0; i < 0x210; i+=4) { sfcx_writereg(DATA, bswap_32(*(int*)(data + i))); sfcx_writereg(COMMAND, 1); } sfcx_writereg(ADDRESS, address); sfcx_writereg(COMMAND, 0x55); while (sfcx_readreg(STATUS) & 1); sfcx_writereg(COMMAND, 0xAA); while (sfcx_readreg(STATUS) & 1); sfcx_writereg(COMMAND, 0x4); while (sfcx_readreg(STATUS) & 1); int status = sfcx_readreg(STATUS); if (status != 0x200) printf("[%08x]", status); sfcx_writereg(0, sfcx_readreg(0) & ~8); } extern volatile void * ioremap(unsigned long long physaddr, unsigned size, int sync); extern int iounmap(volatile void *start, size_t length); int dump_flash_to_file(const char *filename) { printf(" * Dumping to %s...\n", filename); FILE *f = fopen(filename, "wb"); int i; for (i = 0; i < nandsize; i += 0x200) { unsigned char sector[0x210]; readsector(sector, i, 1); if (!(i&0x3fff)) { printf("%08x\r", i); fflush(stdout); } if (fwrite(sector, 1, 0x210, f) != 0x210) return -1; } printf("done! \n"); fclose(f); return 0; } int verify_flash_with_file(const char *filename, int raw) { FILE *f = fopen(filename, "rb"); if (!f) return -1; if (raw == -1) /* auto */ { fseek(f, 0, SEEK_END); if (ftell(f) == nandsize / 0x200 * 0x210) { raw = 1; printf(" * detected RAW nand file, verifying in raw mode.\n"); } else { raw = 0; printf(" * detected short nand file, verifying in cooked mode.\n"); } fseek(f, 0, SEEK_SET); } printf(" * Verifying flash with %s...\n", filename); int i; for (i = 0; i < nandsize; i += 0x200) { unsigned char sector[0x210], sector_flash[0x210]; if (!(i&0x3fff)) { printf("%08x\r", i); fflush(stdout); } if (fread(sector, 1, 0x210, f) != 0x210) return i; readsector(sector_flash, i, raw); if (sector_flash[bsec] != 0xFF) /* bad sector */ { printf(" * ignoring bad sector at %08x\n", i); continue; } if (memcmp(sector, sector_flash, 0x210)) { printf(" * VERIFY error at %08x\n", i); return -2; } } printf("done! \n"); fclose(f); return i; } int flash_from_file(const char *filename, int raw) { printf(" * Flashing from %s...\n", filename); FILE *f = fopen(filename, "rb"); if (!f) return -1; if (raw == -1) /* auto */ { fseek(f, 0, SEEK_END); if (ftell(f) == nandsize / 0x200 * 0x210) { raw = 1; printf(" * detected RAW nand file, flashing in raw mode.\n"); } else { raw = 0; printf(" * detected short nand file, flashing in cooked mode.\n"); } fseek(f, 0, SEEK_SET); } int i; for (i = 0; i < nandsize; i += 0x4000) { unsigned char sector[0x210*32], sector_flash[0x210*32]; memset(sector, 0xFF, sizeof(sector)); if (!fread(sector, 1, 0x210*32, f)) return i; printf("%08x\r", i); fflush(stdout); int phys_pos; if (!raw) { readsector(sector_flash, i, 0); phys_pos = sfcx_readreg(PHYSICAL); if (!(phys_pos & 0x04000000)) /* shouldn't happen, unless the existing image is broken. just assume the sector is okay. */ { printf(" * Uh, oh, don't know. Reading at %08x failed.\n", i); phys_pos = i; } phys_pos &= 0x3fffe00; if (phys_pos != i) printf(" * relocating sector %08x to %08x...\n", i, phys_pos); } else phys_pos = i; flash_erase(phys_pos); int j; for (j = 0; j < 32; ++j) write_page(phys_pos + j * 0x200, sector + j * 0x210); } return 0; } int main(int argc, char **argv) { flash = ioremap(0xea00c000, 0x1000, 1); printf(" * flash config: %08x\n", sfcx_readreg(0)); sfcx_writereg(0, sfcx_readreg(0) &~ (4|8|0x3c0)); int reg = sfcx_readreg(0); switch(reg) { // 512MB/256MB NAND case 0x00AA3020: nandsize = 512 * 1024 * 1024; bsec = 0x200; break; case 0x008A3020: nandsize = 256 * 1024 * 1024; bsec = 0x200; break; // 16 MB NAND case 0x00023010: case 0x01198010: nandsize = 16 * 1024 * 1024; bsec = 0x205; break; default: printf(" * unknown flash config %08x\n", reg); return 1; } if (argc != 2 && argc != 3) { printf("usage: %s []\n", *argv); return 2; } const char *orig = argv[1]; int res = verify_flash_with_file(orig, 1); if (res == -1) { dump_flash_to_file(orig); res = verify_flash_with_file(orig, 1); } if (res != nandsize) { if (res == -2) printf(" * verify failed!\n"); else if (res > 0) printf(" * verified correctly, but only %d bytes.\n", res); else printf(" * original image invalid\n"); printf(" * I won't flash if you don't have a full, working backup, sorry.\n"); return 1; } printf(" * verify ok.\n"); if (argc > 2) { const char *image = argv[2]; flash_from_file(image, -1); res = verify_flash_with_file(image, -1); if (res > 0) printf(" * verified %d bytes ok\n", res); else printf(" * verify failed! (%d)\n", res); } return 0; }