/* XBR-Flash for Linux by trancy Thanks goes to tmbinc, Redline99 and other I forgot History ------- v0.1 initial by tmbinc v0.1.x used sources from Redline99 (Xell mods) v0.1.1 modified by trancy v0.2 Added Bad Block Management (BBM) Added KV and Config Injection */ #include #include #include #include #include #include #include #include #include volatile unsigned int *flash; time_t start,end; double time_dif; //****** Defines **************************************************************************** //NAND SIZES #define NAND_SIZE_16MB 0x1000000 #define NAND_SIZE_64MB 0x4000000 #define NAND_SIZE_128MB 0x8000000 #define NAND_SIZE_256MB 0x10000000 #define NAND_SIZE_512MB 0x20000000 //Registers #define SFCX_CONFIG 0x00 #define SFCX_STATUS 0x01 #define SFCX_COMMAND 0x02 #define SFCX_ADDRESS 0x03 #define SFCX_DATA 0x04 #define SFCX_LOGICAL 0x05 #define SFCX_PHYSICAL 0x06 #define SFCX_DPHYSADDR 0x07 #define SFCX_MPHYSADDR 0x08 //Commands for Command Register #define PAGE_BUF_TO_REG 0x00 //Read page buffer to data register #define REG_TO_PAGE_BUF 0x01 //Write data register to page buffer #define LOG_PAGE_TO_BUF 0x02 //Read logical page into page buffer #define PHY_PAGE_TO_BUF 0x03 //Read physical page into page buffer #define WRITE_PAGE_TO_PHY 0x04 //Write page buffer to physical page #define BLOCK_ERASE 0x05 //Block Erase #define DMA_LOG_TO_RAM 0x06 //DMA logical flash to main memory #define DMA_PHY_TO_RAM 0x07 //DMA physical flash to main memory #define DMA_RAM_TO_PHY 0x08 //DMA main memory to physical flash #define UNLOCK_CMD_0 0x55 //Unlock command 0; Kernel does 0x5500 #define UNLOCK_CMD_1 0xAA //Unlock command 1; Kernel does 0xAA00 // API Consumers should use these two defines to // use for creating static buffers at compile time #define MAX_PAGE_SZ 0x210 //Max known hardware physical page size #define MAX_BLOCK_SZ 0x42000 //Max known hardware physical block size #define BLOCK_16K_SZ 0x4200 //Max known hardware physical page size #define BLOCK_128K_SZ 0x21000 //Max known hardware physical block size struct sfc { int initialized; int large_block; int page_sz; int meta_sz; int page_sz_phys; int pages_in_block; int block_sz; int block_sz_phys; int size_mb; int size_bytes; int size_bytes_phys; int size_bytes_available; int size_pages; int size_blocks; int size_blocks_available; int offset_kv_phys; int offset_config_phys; int last_bad_block_pos; }; //****** Prototypes **************************************************************************** unsigned int sfcx_init(void); void sfcx_writereg( int reg, unsigned long value ); unsigned int sfcx_readreg( int reg ); int sfcx_erase_block(int address); int sfcx_read_page(unsigned char *data, int sector, int raw); int sfcx_write_page(unsigned char *data, int address); int sfcx_read_block(unsigned char *data, int address, int raw); int sfcx_write_block(unsigned char *data, int address); void sfcx_calcecc(unsigned int *data); int sfcx_get_blocknumber(unsigned char *data); void sfcx_set_blocknumber(unsigned char *data, int num); int sfcx_get_blockversion(unsigned char *data); void sfcx_set_blockversion(unsigned char *data, int ver); void sfcx_set_pagevalid(unsigned char *data); void sfcx_set_pageinvalid(unsigned char *data); int sfcx_is_pagevalid(unsigned char *data); int sfcx_block_to_address(int block); int sfcx_address_to_block(int address); extern void *mmap64( void *__addr, size_t __len, int __prot, int __flags, int __fd, __off64_t __offset) __THROW; extern volatile void * ioremap( unsigned long long physaddr, unsigned size, int sync ); extern int iounmap( volatile void *start, size_t length ); int flash_from_file( const char *filename, int raw ); int dump_flash_to_file( const char *filename ); int verify_flash_with_file( const char *filename, int raw ); int inject_kv_config_to_flash( const char *orig_filename , const char *xbr_filename ); struct sfc sfc; int main( int argc, char **argv ) { const char *orig = argv[1], *image = argv[2]; int res; printf("\nXBR-Flash for Linux v0.2 beta by trancy\nThanks goes to tmbinc and Redline99\n\n"); flash = ioremap(0xea00c000, 0x1000, 1); sfcx_init(); //Set new Bablock reserve position to the top of the Nand sfc.last_bad_block_pos = sfc.size_blocks_available - 1; #if 0 flash_from_file(image, -1); res = verify_flash_with_file(image, -1); return 0; #endif if ( argc != 2 && argc != 3 && argc != 4) { printf("\nusage: %s backup.bin xbr.bin -i\nbackup.bin Backup from your current Nand\nxbr.bin XBReboot file\n-i [optional] Injecting KV and Config\n\n", *argv); return 2; } res = verify_flash_with_file(orig, -1); if ( res == -1 ) { dump_flash_to_file(orig); res = verify_flash_with_file(orig, -1); } if ( res != sfc.size_bytes ) { if ( res == -2 ) printf("Verify failed!\n"); else if ( res > 0 ) printf("Verified correctly, but only %d bytes.\n", res); else printf("Error: Original image invalid\n"); printf("Info: I won't flash if you don't have a full, working backup, sorry.\n"); return 1; } printf("Verify ok.\n"); if ( argc > 3 ) { if (inject_kv_config_to_flash(orig, image) == -1) { printf("Error: Injecting failed! (%d)\n", res); return 0; } } //Write from file to flash if ( argc > 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("Error: Verify failed! (%d)\n", res); } } return 0; } int inject_kv_config_to_flash( const char *orig_filename , const char *xbr_filename ) { int status; unsigned char *page; FILE *f_org = fopen(orig_filename, "rb"); if ( !f_org ) { printf("\nError: Failed to open file: %s\n", orig_filename); return -1; } FILE *f_xbr = fopen(xbr_filename, "r+b"); if ( !f_xbr ) { printf("\nError: Failed to open file: %s\n", xbr_filename); return -1; } page = (unsigned char*) malloc (BLOCK_16K_SZ * 2); //inject kv and config printf("\nInjecting KV and Config from %s to %s...\n", orig_filename, xbr_filename); //write KV fseek(f_org, sfc.offset_kv_phys, SEEK_SET); if ( fread(page, 1, BLOCK_16K_SZ, f_org) != BLOCK_16K_SZ ) return -1; fseek(f_xbr, sfc.offset_kv_phys, SEEK_SET); if ( fwrite(page, 1, BLOCK_16K_SZ, f_xbr) != BLOCK_16K_SZ ) return -1; //write Config; 2 pages (16) to write fseek(f_org, sfc.offset_config_phys, SEEK_SET); if ( fread(page, 1, BLOCK_16K_SZ * 2, f_org) != BLOCK_16K_SZ * 2) return -1; fseek(f_xbr, sfc.offset_config_phys, SEEK_SET); if ( fwrite(page, 1, BLOCK_16K_SZ * 2, f_xbr) != BLOCK_16K_SZ * 2) return -1; fclose(f_xbr); fclose(f_org); free(page); printf("Injecting done.\n"); return 0; } int dump_flash_to_file( const char *filename ) { int i; unsigned char *block; block = (unsigned char*) malloc (sfc.block_sz_phys); printf("\nDumping to %s...\n", filename); printf("0x%x block's to dump...\n", sfc.size_blocks ); time (&start); FILE *f = fopen(filename, "wb"); for ( i = 0; i < sfc.size_bytes; i += sfc.block_sz ) { sfcx_read_block(block, i, 1); if ( !(i & 0x3fff) ) { printf("Reading block: 0x%x of 0x%x\r", i / sfc.block_sz + 1 , sfc.size_blocks ); fflush(stdout); } if ( fwrite(block, 1, sfc.block_sz_phys, f) != sfc.block_sz_phys ) return -1; } time (&end); time_dif = difftime (end,start); printf("\nReading done in %.2lf sec.\nSpeed: %.2lf MB/s\n", time_dif, sfc.size_bytes / time_dif / 1024 / 1024); fclose(f); free(block); return 0; } int verify_flash_with_file( const char *filename, int raw ) { int i, status, nadnsize; unsigned char *block, *block_flash; FILE *f = fopen(filename, "rb"); if ( !f ) { return -1; } printf("\nVerifying flash with %s...\n", filename); printf("0x%x block's to verify...\n", sfc.size_blocks); if ( raw == -1 ) /* auto */ { fseek(f, 0, SEEK_END); nadnsize = ftell(f); //64MB XBR-File if( nadnsize == NAND_SIZE_64MB / sfc.page_sz * sfc.page_sz_phys ) sfc.size_bytes = NAND_SIZE_64MB; if ( nadnsize == sfc.size_bytes / sfc.page_sz * sfc.page_sz_phys ) { raw = 1; printf("Detected RAW nand file, verifying in raw mode.\n"); } else if ( nadnsize == NAND_SIZE_16MB || nadnsize == NAND_SIZE_64MB || nadnsize == NAND_SIZE_128MB || nadnsize == NAND_SIZE_256MB || nadnsize == NAND_SIZE_512MB) { raw = 0; printf("Detected short nand file, verifying in cooked mode.\n"); } else { printf("Wrong filesize (%s)\n", filename); return -1; } fseek(f, 0, SEEK_SET); } block = (unsigned char*) malloc (sfc.block_sz_phys); block_flash = (unsigned char*) malloc (sfc.block_sz_phys); time (&start); for ( i = 0; i < sfc.size_bytes; i += sfc.block_sz ) { if(sfc.last_bad_block_pos +1 == sfcx_address_to_block(i)) { //We reach the the last entry bad block in reserved area printf("\nVerifying only to last entry bad block in the reserved area."); i = sfc.size_bytes; break; } if ( !(i & 0x3fff) ) { printf("Verifying block: 0x%x of 0x%x \r", sfcx_address_to_block(i) + 1, sfc.size_blocks); fflush(stdout); } if ( fread(block, 1, sfc.block_sz_phys, f) != sfc.block_sz_phys ) return i; status = sfcx_read_block(block_flash, i, raw); if (status & 0x40) { printf("Ignoring bad block @ 0x%x \n ", sfcx_address_to_block(i) ); continue; } if ( memcmp(block, block_flash, sfc.block_sz_phys) ) { printf("Verify error @ block: 0x%x \n", sfcx_address_to_block(i) ); return -2; } } time (&end); time_dif = difftime (end,start); printf("\nVerify done in %.2lf sec.\nSpeed: %.2lf MB/s.\n", time_dif, sfc.size_bytes / time_dif / 1024 / 1024); fclose(f); free(block); free(block_flash); return i; } int flash_from_file( const char *filename, int raw ) { int i, j, phys_pos, status = 0x200; unsigned char *block, *block_flash, *bad_block; FILE *f = fopen(filename, "rb"); if ( !f ) { printf("\nError: Failed to open file: %s\n", filename); return -1; } printf("\nFlashing from %s...\n", filename); printf("0x%x block's to write...\n", sfc.size_blocks); //Set new Bablock reserve position to the top of the Nand sfc.last_bad_block_pos = sfc.size_blocks_available -1; if ( !f ) return -1; if ( raw == -1 ) /* auto */ { fseek(f, 0, SEEK_END); //64MB XBR-File if( ftell(f) == NAND_SIZE_64MB / sfc.page_sz * sfc.page_sz_phys ) sfc.size_bytes = NAND_SIZE_64MB; if ( ftell(f) == sfc.size_bytes / sfc.page_sz * sfc.page_sz_phys ) { 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); } block = (unsigned char*) malloc (sfc.block_sz_phys); block_flash = (unsigned char*) malloc (sfc.block_sz_phys); bad_block = (unsigned char*) malloc (sfc.block_sz_phys); time(&start); for ( i = 0; i < sfc.size_bytes; i += sfc.block_sz ) { memset(block, 0xFF, sizeof(block)); if ( !fread(block, 1, sfc.page_sz_phys * sfc.pages_in_block, f) ) return i; printf("Writing block: 0x%x of 0x%x\r", sfcx_address_to_block(i) + 1, sfc.size_blocks ); fflush(stdout); //Check for bad block sfcx_writereg(SFCX_STATUS, sfcx_readreg(SFCX_STATUS)); sfcx_writereg(SFCX_ADDRESS, i); sfcx_writereg(SFCX_COMMAND, raw ? PHY_PAGE_TO_BUF : LOG_PAGE_TO_BUF); // Wait Busy while ((status = sfcx_readreg(SFCX_STATUS)) & 1); if ( (!raw)) { phys_pos = sfcx_readreg(SFCX_PHYSICAL); if ( !(phys_pos & 0x04000000) ) /* shouldn't happen, unless the existing image is broken. just assume the block is okay. */ { printf("Error: Uh, oh, don't know. Reading at %08x failed. phys_pos: %i\n", i, phys_pos); phys_pos = i; } phys_pos &= 0x3fffe00; if ( phys_pos != i ) { printf("Relocating/moving bad block from position 0x%x to 0x%x\n", sfcx_address_to_block(i) , sfcx_address_to_block(phys_pos)); } } else if (status != 0x200) //Bad Block Management { phys_pos = sfc.last_bad_block_pos * sfc.block_sz; sfc.last_bad_block_pos --; if ( phys_pos != i ) { printf("Relocating/moving bad block from position 0x%x to 0x%x\n", sfcx_address_to_block(i), phys_pos / sfc.block_sz); } continue; } else { phys_pos = i; } if(sfc.last_bad_block_pos == sfcx_address_to_block(i)) { //We reach the the last entry bad block in reserved area printf("\nWriting only to last entry bad block in the reserved area."); break; } //Erase block in Nand sfcx_erase_block(phys_pos); //Write block to Nand sfcx_write_block(block, phys_pos); } time(&end); time_dif = difftime(end,start); printf("\nWrite done in %.2lf sec.\nSpeed: %.2lf MB/s\n", time_dif, sfc.size_bytes / time_dif / 1024 / 1024); fclose(f); free(block); free(block_flash); free(bad_block); return 0; } void sfcx_writereg( int reg, unsigned long value ) { flash[reg] = bswap_32(value); } unsigned int sfcx_readreg( int reg ) { return bswap_32(flash[reg]); } unsigned int sfcx_init(void) { sfc.initialized = 0; sfc.large_block = 0; sfc.page_sz = 0x200; sfc.meta_sz = 0x10; sfc.page_sz_phys = sfc.page_sz + sfc.meta_sz; sfc.offset_kv_phys = 0x4200; unsigned int config = sfcx_readreg(SFCX_CONFIG); //Turn off interrupts, turn off WP_EN, and set DMA pages to 0 sfcx_writereg(SFCX_CONFIG, config &~ (4|8|0x3c0)); switch ((config >> 17) & 0x03) { case 0: // Small block original SFC (pre jasper) switch ((config >> 4) & 0x3) { case 0: // Unsupported 8MB? printf("Error: Unsupported Type A-0\n"); return -1; //sfc.block_sz = 0x4000; // 16 KB //sfc.size_blocks = 0x200; //sfc.size_bytes = sfc.size_blocks << 0xE; case 1: // 16MB sfc.block_sz = 0x4000; // 16 KB sfc.size_blocks = 0x400; sfc.size_blocks_available = 0x400; sfc.size_bytes = sfc.size_blocks << 0xE; sfc.size_bytes_available = sfc.size_blocks << 0xE; sfc.offset_config_phys = 0xFF7E00; break; case 2: // 32MB sfc.block_sz = 0x4000; // 16 KB sfc.size_blocks = 0x800; sfc.size_blocks_available = 0x800; sfc.size_bytes = sfc.size_blocks << 0xE; sfc.size_bytes_available = sfc.size_blocks << 0xE; sfc.offset_config_phys = 0x1FF3E00; break; case 3: // 64MB sfc.block_sz = 0x4000; // 16 KB sfc.size_blocks = 0x1000; sfc.size_blocks_available = 0x1000; sfc.size_bytes = sfc.size_blocks << 0xE; sfc.size_bytes_available = sfc.size_blocks << 0xE; sfc.offset_config_phys = 0x3FEBE00; break; } break; case 1: // New SFC/Southbridge: Codename "Panda"? switch ((config >> 4) & 0x3) { case 0: // Unsupported printf("Error: Unsupported Type B-0\n"); return -2; case 1: // Small block 16MB setup sfc.block_sz = 0x4000; // 16 KB sfc.size_blocks = 0x400; sfc.size_blocks_available = 0x400; sfc.size_bytes = sfc.size_blocks << 0xE; sfc.size_bytes_available = sfc.size_blocks << 0xE; sfc.offset_config_phys = 0xFF7E00; break; case 2: // Large Block: Current Jasper 256MB and 512MB sfc.block_sz = 0x20000; // 128KB sfc.size_bytes_available = 0x1 << (((config >> 19) & 0x3) + ((config >> 21) & 0xF) + 0x17); sfc.size_bytes = NAND_SIZE_64MB; sfc.size_blocks = sfc.size_bytes >> 0x11; sfc.size_blocks_available = sfc.size_bytes_available >> 0x11; sfc.large_block = 2; sfc.offset_config_phys = 0x3DBF000; break; case 3: // Large Block: Future or unknown hardware sfc.block_sz = 0x40000; // 256KB sfc.size_bytes = NAND_SIZE_128MB; sfc.size_bytes_available = 0x1 << (((config >> 19) & 0x3) + ((config >> 21) & 0xF) + 0x17); sfc.size_blocks = sfc.size_bytes >> 0x12; sfc.size_blocks_available = sfc.size_bytes_available >> 0x12; sfc.large_block = 3; sfc.offset_config_phys = 0x3D9E000; break; } break; default: printf("Error: Unsupported Type\n"); return -3; } sfc.pages_in_block = sfc.block_sz / sfc.page_sz; sfc.block_sz_phys = sfc.pages_in_block * sfc.page_sz_phys; sfc.size_pages = sfc.size_bytes / sfc.page_sz; sfc.size_blocks = sfc.size_bytes / sfc.block_sz; sfc.size_bytes_phys = sfc.block_sz_phys * sfc.size_blocks; sfc.size_mb = sfc.size_bytes >> 20; printf("Nandsize: %d MB detected\n", sfc.size_bytes_available >> 20); printf("Read and write: %d MB\n", sfc.size_mb); sfc.initialized = 1; return config; } int sfcx_read_page(unsigned char *data, int address, int raw) { int status; sfcx_writereg(SFCX_STATUS, sfcx_readreg(SFCX_STATUS)); // Set flash address (logical) //address &= 0x3fffe00; // Align to page sfcx_writereg(SFCX_ADDRESS, address); // Command the read // Either a logical read (0x200 bytes, no meta data) // or a Physical read (0x210 bytes with meta data) sfcx_writereg(SFCX_COMMAND, raw ? PHY_PAGE_TO_BUF : LOG_PAGE_TO_BUF); // Wait Busy while ((status = sfcx_readreg(SFCX_STATUS)) & 1); if (status != 0x200) { if (status & 0x40) { printf("Bad block found (index: 0x%x) \n", sfcx_address_to_block(address)); } else if (status & 0x1c) { //printf(" ! SFCX: (Corrected) ECC error at address %08x: %08x\n", address, status); } else if (!raw && (status & 0x800)) { //printf(" ! SFCX: Illegal logical block at %08x (status: %08x)\n", sfcx_address_to_block(address), status); } else { printf("Error: Unknown error at address 0x%x: 0x%x. Please worry.\n", address, status); } } // Set internal page buffer pointer to 0 sfcx_writereg(SFCX_ADDRESS, 0); int i; int page_sz = raw ? sfc.page_sz_phys : sfc.page_sz; for (i = 0; i < page_sz ; i += 4) { // Transfer data from buffer to register sfcx_writereg(SFCX_COMMAND, PAGE_BUF_TO_REG); // Read out our data through the register *(int*)(data + i) = bswap_32(sfcx_readreg(SFCX_DATA)); } return status; } int sfcx_write_page(unsigned char *data, int address) { sfcx_writereg(SFCX_STATUS, 0xFF); // Enable Writes sfcx_writereg(SFCX_CONFIG, sfcx_readreg(SFCX_CONFIG) | 8); // Set internal page buffer pointer to 0 sfcx_writereg(SFCX_ADDRESS, 0); int i; for (i = 0; i < sfc.page_sz_phys; i+=4) { // Write out our data through the register sfcx_writereg(SFCX_DATA, bswap_32(*(int*)(data + i))); // Transfer data from register to buffer sfcx_writereg(SFCX_COMMAND, REG_TO_PAGE_BUF); } // Set flash address (logical) //address &= 0x3fffe00; // Align to page sfcx_writereg(SFCX_ADDRESS, address); // Unlock sequence (for write) sfcx_writereg(SFCX_COMMAND, UNLOCK_CMD_0); sfcx_writereg(SFCX_COMMAND, UNLOCK_CMD_1); // Wait Busy while (sfcx_readreg(SFCX_STATUS) & 1); // Command the write sfcx_writereg(SFCX_COMMAND, WRITE_PAGE_TO_PHY); // Wait Busy while (sfcx_readreg(SFCX_STATUS) & 1); int status = sfcx_readreg(SFCX_STATUS); if (status != 0x200) printf("Unexpected sfcx_write_page status 0x%x\n", status); // Disable Writes sfcx_writereg(SFCX_CONFIG, sfcx_readreg(SFCX_CONFIG) & ~8); return status; } int sfcx_read_block(unsigned char *data, int address, int raw) { int p; int status = 0; int page_sz = raw ? sfc.page_sz_phys : sfc.page_sz; for (p = 0; p < sfc.pages_in_block; p++) { status |= sfcx_read_page(&data[p * page_sz], address + (p * sfc.page_sz), raw); //if (status != 0x200) // break; } return status; } int sfcx_write_block(unsigned char *data, int address) { int p; int status = 0; for (p = 0; p < sfc.pages_in_block; p++) { status |= sfcx_write_page(&data[p * sfc.page_sz_phys], address + (p * sfc.page_sz)); //if (status != 0x200) // break; } return status; } int sfcx_erase_block(int address) { // Enable Writes sfcx_writereg(SFCX_CONFIG, sfcx_readreg(SFCX_CONFIG) | 8); sfcx_writereg(SFCX_STATUS, 0xFF); // Set flash address (logical) //address &= 0x3fffe00; // Align to page sfcx_writereg(SFCX_ADDRESS, address); // Wait Busy while (sfcx_readreg(SFCX_STATUS) & 1); // Unlock sequence (for erase) sfcx_writereg(SFCX_COMMAND, UNLOCK_CMD_1); sfcx_writereg(SFCX_COMMAND, UNLOCK_CMD_0); // Wait Busy while (sfcx_readreg(SFCX_STATUS) & 1); // Command the block erase sfcx_writereg(SFCX_COMMAND, BLOCK_ERASE); // Wait Busy while (sfcx_readreg(SFCX_STATUS) & 1); int status = sfcx_readreg(SFCX_STATUS); if (status != 0x200) printf("Unexpected erase at block 0x%x with status 0x%x\n", sfcx_address_to_block(address), status); sfcx_writereg(SFCX_STATUS, 0xFF); // Disable Writes sfcx_writereg(SFCX_CONFIG, sfcx_readreg(SFCX_CONFIG) & ~8); return status; } void sfcx_calcecc(unsigned int *data) { unsigned int i=0, val=0; unsigned char *edc = ((unsigned char*)data) + sfc.page_sz; unsigned int v=0; for (i = 0; i < 0x1066; i++) { if (!(i & 31)) v = ~bswap_32(*data++); val ^= v & 1; v>>=1; if (val & 1) val ^= 0x6954559; val >>= 1; } val = ~val; // 26 bit ecc data edc[0xC] |= (val << 6) & 0xC0; edc[0xD] = (val >> 2) & 0xFF; edc[0xE] = (val >> 10) & 0xFF; edc[0xF] = (val >> 18) & 0xFF; } int sfcx_get_blocknumber(unsigned char *data) { if(sfc.large_block) { return (data[sfc.page_sz + 0x2] << 8) | (data[sfc.page_sz + 0x1]); } else { return (data[sfc.page_sz + 0x1] << 8) | (data[sfc.page_sz + 0x0]); } } void sfcx_set_blocknumber(unsigned char *data, int num) { if(sfc.large_block) { data[sfc.page_sz + 0x2] = (num >> 8) & 0xFF; data[sfc.page_sz + 0x1] = (num >> 0) & 0xFF; } else { data[sfc.page_sz + 0x1] = (num >> 8) & 0xFF; data[sfc.page_sz + 0x0] = (num >> 0) & 0xFF; } } int sfcx_get_blockversion(unsigned char *data) { if(sfc.large_block) { return (data[sfc.page_sz + 0x6] << 24) | (data[sfc.page_sz + 0x4] << 16) | (data[sfc.page_sz + 0x3] << 8) | (data[sfc.page_sz + 0x5]); } else { return (data[sfc.page_sz + 0x6] << 24) | (data[sfc.page_sz + 0x4] << 16) | (data[sfc.page_sz + 0x3] << 8) | (data[sfc.page_sz + 0x2]); } } void sfcx_set_blockversion(unsigned char *data, int ver) { // NOTE This is only doing a single page if(sfc.large_block) { data[sfc.page_sz + 0x5] = (ver >> 0) & 0xFF; data[sfc.page_sz + 0x3] = (ver >> 8) & 0xFF; data[sfc.page_sz + 0x4] = (ver >> 16) & 0xFF; data[sfc.page_sz + 0x6] = (ver >> 24) & 0xFF; } else { data[sfc.page_sz + 0x2] = (ver >> 0) & 0xFF; data[sfc.page_sz + 0x3] = (ver >> 8) & 0xFF; data[sfc.page_sz + 0x4] = (ver >> 16) & 0xFF; data[sfc.page_sz + 0x6] = (ver >> 24) & 0xFF; } } void sfcx_set_pagevalid(unsigned char *data) { if(sfc.large_block) { data[sfc.page_sz + 0x0] = 0xFF; } else { data[sfc.page_sz + 0x5] = 0xFF; } } void sfcx_set_pageinvalid(unsigned char *data) { if(sfc.large_block) { data[sfc.page_sz + 0x0] = 0x00; } else { data[sfc.page_sz + 0x5] = 0x00; } } int sfcx_is_pagevalid(unsigned char *data) { if(sfc.large_block) { return data[sfc.page_sz + 0x0] == 0xFF; } else { return data[sfc.page_sz + 0x5] == 0xFF; } } // TODO: Some sort of block/file system driver to make use of // the block allocation table and the rest of the nand int sfcx_block_to_address(int block) { return block * sfc.block_sz; } int sfcx_address_to_block(int address) { return address / sfc.block_sz; } 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); }