mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-09-07 18:44:17 +00:00
287 lines
8.7 KiB
C
287 lines
8.7 KiB
C
#include "flashmem.h"
|
|
#include "pmflash.h"
|
|
#include "string.h"
|
|
#include "ticks_apis.h"
|
|
|
|
#ifndef AS_BOOTROM
|
|
#include "dbprint.h"
|
|
#endif // AS_BOOTROM
|
|
|
|
// default is 0, first set when FlashInit() call.
|
|
static uint32_t flashmem_spibaudrate = 0;
|
|
|
|
#ifndef AS_BOOTROM
|
|
|
|
// flash ids, first set when FlashInit() call.
|
|
static spi_flash_t spi_flash_data = {0};
|
|
// The capacity information calculated after the flash information is detected.
|
|
// This variable is referenced in many places, so it cannot be modified with static.
|
|
uint8_t spi_flash_pages64k = 4;
|
|
|
|
// Get spi baudrate
|
|
uint32_t Flash_GetSpiBaudrate(void) {
|
|
return flashmem_spibaudrate;
|
|
}
|
|
|
|
// Set spi baudrate, not updated immediately.
|
|
// The new baud rate will take effect the next time the FlashSetup function is executed.
|
|
// And depending on the platform, the baud rate that is finally set may not be your expected value.
|
|
// Maybe some platforms can only communicate at certain fixed baud rates.
|
|
void Flash_SetSpiBaudrate(uint32_t baudrate) {
|
|
flashmem_spibaudrate = baudrate;
|
|
Dbprintf("Spi Baudrate : %dMHz", flashmem_spibaudrate / 1000000);
|
|
}
|
|
|
|
// WARNING -- if callers are using a file system (such as SPIFFS),
|
|
// they should inform the file system of this change
|
|
// e.g., rdv40_spiffs_check()
|
|
bool Flash_WipeMemoryPage(uint8_t page) {
|
|
if (!FlashInit()) {
|
|
if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_WriteData init fail");
|
|
return false;
|
|
}
|
|
|
|
// Each block is 64Kb. One block erase takes 1s ( 1000ms )
|
|
Flash_WriteEnable();
|
|
Flash_Erase64k(page);
|
|
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
|
|
FlashStop();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Wipes flash memory completely, fills with 0xFF
|
|
bool Flash_WipeMemory(void) {
|
|
if (!FlashInit()) {
|
|
if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_WriteData init fail");
|
|
return false;
|
|
}
|
|
|
|
// Each block is 64Kb. Four blocks
|
|
// one block erase takes 1s ( 1000ms )
|
|
for (uint8_t i = 0; i < spi_flash_pages64k; i++) {
|
|
Flash_WriteEnable();
|
|
Flash_Erase64k(i);
|
|
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
}
|
|
|
|
FlashStop();
|
|
return true;
|
|
}
|
|
|
|
// ReadData with spi initialization
|
|
uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len) {
|
|
|
|
if (!FlashInit()) return 0;
|
|
|
|
// check busy only
|
|
if (Flash_CheckBusy(BUSY_TIMEOUT)) return 0;
|
|
|
|
// function reused, length check inside.
|
|
len = Flash_ReadDataCont(address, out, len);
|
|
|
|
FlashStop();
|
|
return len;
|
|
}
|
|
|
|
// Write data can only program one page. A page has 256 bytes.
|
|
// if len > 256, it might wrap around and overwrite pos 0.
|
|
uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len) {
|
|
|
|
if (!FlashInit()) {
|
|
if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_WriteData init fail");
|
|
return 0;
|
|
}
|
|
|
|
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
Flash_WriteEnable();
|
|
|
|
// function reused, len and addr check inside.
|
|
len = Flash_WriteDataCont(address, in, len);
|
|
|
|
FlashStop();
|
|
return len;
|
|
}
|
|
|
|
// assumes valid start 256 based 00 address
|
|
// Start writing flash from the specified location.
|
|
// Write FLASH_MEM_BLOCK_SIZE bytes at most each time. If the writing is nearly complete, write it as bytes_remaining bytes.
|
|
uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len) {
|
|
|
|
bool isok;
|
|
uint16_t res, bytes_sent = 0, bytes_remaining = len;
|
|
uint8_t buf[FLASH_MEM_BLOCK_SIZE];
|
|
while (bytes_remaining > 0) {
|
|
|
|
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
Flash_WriteEnable();
|
|
|
|
uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining);
|
|
|
|
memcpy(buf, in + bytes_sent, bytes_in_packet);
|
|
|
|
res = Flash_WriteDataCont(address + bytes_sent, buf, bytes_in_packet);
|
|
|
|
bytes_remaining -= bytes_in_packet;
|
|
bytes_sent += bytes_in_packet;
|
|
|
|
isok = (res == bytes_in_packet);
|
|
|
|
if (!isok)
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
FlashStop();
|
|
return len;
|
|
}
|
|
|
|
void Flashmem_print_status(void) {
|
|
DbpString(_CYAN_("Flash memory"));
|
|
Dbprintf(" Baudrate................ " _GREEN_("%d MHz"), flashmem_spibaudrate / 1000000);
|
|
|
|
if (FlashInit() == false) {
|
|
DbpString(" Init.................... " _RED_("failed"));
|
|
return;
|
|
}
|
|
DbpString(" Init.................... " _GREEN_("ok"));
|
|
|
|
if (spi_flash_data.device_id > 0) {
|
|
Dbprintf(" Mfr ID / Dev ID......... " _YELLOW_("%02X / %02X"),
|
|
spi_flash_data.manufacturer_id,
|
|
spi_flash_data.device_id
|
|
);
|
|
}
|
|
|
|
if (spi_flash_data.jedec_id > 0) {
|
|
Dbprintf(" JEDEC Mfr ID / Dev ID... " _YELLOW_("%02X / %04X"),
|
|
spi_flash_data.manufacturer_id,
|
|
spi_flash_data.jedec_id
|
|
);
|
|
}
|
|
|
|
Dbprintf(" Memory size............. " _YELLOW_("%d Kb") " ( %d pages * 64k )", spi_flash_pages64k * 64, spi_flash_pages64k);
|
|
|
|
uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
Flash_UniqueID(uid);
|
|
Dbprintf(" Unique ID (be).......... " _YELLOW_("0x%02X%02X%02X%02X%02X%02X%02X%02X"),
|
|
uid[0], uid[1], uid[2], uid[3],
|
|
uid[4], uid[5], uid[6], uid[7]
|
|
);
|
|
if (g_dbglevel > DBG_DEBUG) {
|
|
Dbprintf(" Unique ID (le).......... " _YELLOW_("0x%02X%02X%02X%02X%02X%02X%02X%02X"),
|
|
uid[7], uid[6], uid[5], uid[4],
|
|
uid[3], uid[2], uid[1], uid[0]
|
|
);
|
|
}
|
|
FlashStop();
|
|
}
|
|
|
|
spi_flash_t *flash_get_info(void) {
|
|
return &spi_flash_data;
|
|
}
|
|
|
|
bool FlashDetect(void) {
|
|
|
|
flash_device_type_t flash_data = {0};
|
|
bool ret = false;
|
|
// read using 0x9F (JEDEC)
|
|
if (Flash_ReadID(&flash_data, true)) {
|
|
spi_flash_data.manufacturer_id = flash_data.manufacturer_id;
|
|
spi_flash_data.jedec_id = (flash_data.device_id << 8) + flash_data.device_id2;
|
|
ret = true;
|
|
} else {
|
|
if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_ReadID failed reading JEDEC (0x9F)");
|
|
}
|
|
|
|
// read using 0x90 (Manufacturer / Device ID)
|
|
if (Flash_ReadID(&flash_data, false)) {
|
|
if (spi_flash_data.manufacturer_id == 0) {
|
|
spi_flash_data.manufacturer_id = flash_data.manufacturer_id;
|
|
}
|
|
spi_flash_data.device_id = flash_data.device_id;
|
|
ret = true;
|
|
} else {
|
|
if (g_dbglevel > DBG_DEBUG) Dbprintf("Flash_ReadID failed reading Mfr/Dev (0x90)");
|
|
}
|
|
|
|
// Check JEDEC data is valid, compare the reported device types and then calculate the number of pages
|
|
// It is covering the most (known) cases of devices but probably there are vendors with different data
|
|
// They will be handled when there is such cases
|
|
if (ret) {
|
|
if (spi_flash_data.jedec_id > 0 && spi_flash_data.jedec_id < 0xFFFF) {
|
|
if (((spi_flash_data.device_id + 1) & 0x0F) == (spi_flash_data.jedec_id & 0x000F)) {
|
|
spi_flash_pages64k = 1 << (spi_flash_data.jedec_id & 0x000F);
|
|
}
|
|
}
|
|
}
|
|
|
|
spi_flash_data.pages64k = spi_flash_pages64k;
|
|
return ret;
|
|
}
|
|
|
|
#endif // #ifndef AS_BOOTROM
|
|
|
|
// initialize
|
|
bool FlashInit(void) {
|
|
// set default baud rate from platform specific
|
|
if (!flashmem_spibaudrate) { // only set if current value == 0
|
|
flashmem_spibaudrate = Flash_DefaultBaudrate();
|
|
}
|
|
|
|
// Prioritize call the StartTicks, as the subsequent initialization process may rely on the counter
|
|
// to determine if there is a communication timeout.
|
|
StartTicks();
|
|
|
|
// If it is a QSPI communication interface, an attempt will be made to enable 4-wire communication at this stage.
|
|
// If the enable fails, it indicates that the chip does not support QSPI or has poor soldering.
|
|
// Tip: Some platform related steps only need to be executed once during initialization, which will be done in this function.
|
|
if (!FlashSetup(flashmem_spibaudrate)) {
|
|
StopTicks();
|
|
return false;
|
|
}
|
|
|
|
if (Flash_CheckBusy(BUSY_TIMEOUT)) {
|
|
StopTicks();
|
|
return false;
|
|
}
|
|
|
|
#ifndef AS_BOOTROM
|
|
if (spi_flash_data.manufacturer_id == 0) {
|
|
if (FlashDetect() == false) {
|
|
return false;
|
|
}
|
|
}
|
|
#endif // #ifndef AS_BOOTROM
|
|
|
|
return true;
|
|
}
|
|
|
|
// check flash write/erase working.
|
|
bool Flash_CheckBusy(uint32_t timeout) {
|
|
WaitUS(WINBOND_WRITE_DELAY);
|
|
StartCountUS();
|
|
uint32_t _time = GetCountUS();
|
|
uint8_t status;
|
|
|
|
do {
|
|
// Read status register failed!
|
|
if (!Flash_ReadStat1(&status)) {
|
|
// The chip may not be working properly, so it is meaningless to determine whether it is busy.
|
|
// We will return false first. If we consider returning true in the future, please modify it.
|
|
return false;
|
|
}
|
|
// Flash is busy for wipe/write
|
|
if (!(status & BUSY)) {
|
|
return false;
|
|
}
|
|
} while ((GetCountUS() - _time) < timeout);
|
|
|
|
if (timeout <= (GetCountUS() - _time)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|