mirror of https://github.com/zeldaret/mm.git
255 lines
7.1 KiB
C
255 lines
7.1 KiB
C
#include "prevent_bss_reordering.h"
|
|
#include "global.h"
|
|
#include "stack.h"
|
|
#include "stackcheck.h"
|
|
#include "system_malloc.h"
|
|
#include "z64thread.h"
|
|
|
|
OSMesgQueue sFlashromMesgQueue;
|
|
OSMesg sFlashromMesg[1];
|
|
s32 sFlashromIsInit;
|
|
s32 sFlashromVendor;
|
|
|
|
STACK(sSysFlashromStack, 0x1000);
|
|
StackEntry sSysFlashromStackInfo;
|
|
OSThread sSysFlashromThread;
|
|
FlashromRequest sFlashromRequest;
|
|
OSMesg sSysFlashromMsgBuf[1];
|
|
|
|
s32 SysFlashrom_IsInit(void) {
|
|
return sFlashromIsInit;
|
|
}
|
|
|
|
const char* SysFlashrom_GetVendorStr(void) {
|
|
switch (sFlashromVendor) {
|
|
case FLASH_VERSION_MX_PROTO_A:
|
|
return "PROTO A";
|
|
|
|
case FLASH_VERSION_MX_A:
|
|
return "A";
|
|
|
|
case FLASH_VERSION_MX_C:
|
|
return "C";
|
|
|
|
case FLASH_VERSION_MEI:
|
|
return "D(NEW)";
|
|
|
|
case FLASH_VERSION_MX_B_AND_D:
|
|
return "B or D";
|
|
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
s32 SysFlashrom_CheckFlashType(void) {
|
|
u32 flashType;
|
|
u32 flashVendor;
|
|
|
|
if (!SysFlashrom_IsInit()) {
|
|
return -1;
|
|
}
|
|
osFlashReadId(&flashType, &flashVendor);
|
|
sFlashromVendor = flashVendor;
|
|
if (flashType == FLASH_TYPE_MAGIC) {
|
|
if ((flashVendor == FLASH_VERSION_MX_PROTO_A) || (flashVendor == FLASH_VERSION_MX_A) ||
|
|
(flashVendor == FLASH_VERSION_MX_C) || (flashVendor == FLASH_VERSION_MEI) ||
|
|
(flashVendor == FLASH_VERSION_MX_B_AND_D)) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
s32 SysFlashrom_InitFlash(void) {
|
|
osCreateMesgQueue(&sFlashromMesgQueue, sFlashromMesg, ARRAY_COUNT(sFlashromMesg));
|
|
osFlashInit();
|
|
sFlashromIsInit = true;
|
|
if (SysFlashrom_CheckFlashType() != 0) {
|
|
sFlashromIsInit = false;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
s32 SysFlashrom_ReadData(void* addr, u32 pageNum, u32 pageCount) {
|
|
OSIoMesg msg;
|
|
|
|
if (!SysFlashrom_IsInit()) {
|
|
return -1;
|
|
}
|
|
osInvalDCache(addr, pageCount * FLASH_PAGE_SIZE);
|
|
osFlashReadArray(&msg, OS_MESG_PRI_NORMAL, pageNum, addr, pageCount, &sFlashromMesgQueue);
|
|
osRecvMesg(&sFlashromMesgQueue, NULL, OS_MESG_BLOCK);
|
|
return 0;
|
|
}
|
|
|
|
s32 SysFlashrom_EraseSector(u32 page) {
|
|
if (!SysFlashrom_IsInit()) {
|
|
return -1;
|
|
}
|
|
return osFlashSectorErase(page);
|
|
}
|
|
|
|
s32 SysFlashrom_ExecWrite(void* addr, u32 pageNum, u32 pageCount) {
|
|
OSIoMesg msg;
|
|
s32 result;
|
|
u32 i;
|
|
|
|
if (!SysFlashrom_IsInit()) {
|
|
return -1;
|
|
}
|
|
// Ensure the page is always aligned to a sector boundary.
|
|
if ((pageNum % FLASH_PAGE_SIZE) != 0) {
|
|
Fault_AddHungupAndCrash("../sys_flashrom.c", 275);
|
|
}
|
|
osWritebackDCache(addr, pageCount * FLASH_PAGE_SIZE);
|
|
for (i = 0; i < pageCount; i++) {
|
|
osFlashWriteBuffer(&msg, OS_MESG_PRI_NORMAL, (u8*)addr + i * FLASH_PAGE_SIZE, &sFlashromMesgQueue);
|
|
osRecvMesg(&sFlashromMesgQueue, NULL, OS_MESG_BLOCK);
|
|
result = osFlashWriteArray(i + pageNum);
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
s32 SysFlashrom_AttemptWrite(void* addr, u32 pageNum, u32 pageCount) {
|
|
s32 result;
|
|
s32 i;
|
|
|
|
if (!SysFlashrom_IsInit()) {
|
|
return -1;
|
|
}
|
|
osWritebackDCache(addr, pageCount * FLASH_PAGE_SIZE);
|
|
i = 0;
|
|
failRetry:
|
|
result = SysFlashrom_EraseSector(pageNum);
|
|
if (result != 0) {
|
|
if (i < 3) {
|
|
i++;
|
|
goto failRetry;
|
|
}
|
|
return result;
|
|
}
|
|
result = SysFlashrom_ExecWrite(addr, pageNum, pageCount);
|
|
if (result != 0) {
|
|
if (i < 3) {
|
|
i++;
|
|
goto failRetry;
|
|
}
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Flash bits can only by flipped with a write from 1 -> 0. Going from 0 -> 1 requires an erasure.
|
|
// This function will try to determine if any sectors need to be erased before writing saving erase cycles.
|
|
s32 SysFlashrom_NeedsToErase(void* data, void* addr, u32 pageCount) {
|
|
u32 size;
|
|
u32 i;
|
|
|
|
for (i = 0; i < pageCount * FLASH_PAGE_SIZE; i += 4) {
|
|
if ((*(s32*)data & *(s32*)addr) != *(s32*)addr) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
s32 SysFlashrom_WriteData(void* addr, u32 pageNum, u32 pageCount) {
|
|
void* data;
|
|
size_t size;
|
|
s32 ret;
|
|
|
|
if (!SysFlashrom_IsInit()) {
|
|
return -1;
|
|
}
|
|
size = pageCount * FLASH_PAGE_SIZE;
|
|
data = SystemArena_Malloc(size);
|
|
if (data == NULL) {
|
|
ret = SysFlashrom_AttemptWrite(addr, pageNum, pageCount);
|
|
} else {
|
|
SysFlashrom_ReadData(data, pageNum, pageCount);
|
|
if (bcmp(data, addr, size) == 0) {
|
|
ret = 0;
|
|
} else {
|
|
// Will always erase the sector even if it wouldn't normally need to.
|
|
if (SysFlashrom_NeedsToErase(data, addr, pageCount)) {
|
|
ret = SysFlashrom_AttemptWrite(addr, pageNum, pageCount);
|
|
} else {
|
|
ret = SysFlashrom_AttemptWrite(addr, pageNum, pageCount);
|
|
}
|
|
if (ret == 0) {
|
|
SysFlashrom_ReadData(data, pageNum, pageCount);
|
|
if (bcmp(data, addr, size) == 0) {
|
|
ret = 0;
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
}
|
|
}
|
|
SystemArena_Free(data);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void SysFlashrom_ThreadEntry(void* arg) {
|
|
FlashromRequest* req = (FlashromRequest*)arg;
|
|
|
|
switch (req->requestType) {
|
|
case FLASHROM_REQUEST_WRITE:
|
|
req->response = SysFlashrom_WriteData(req->addr, req->pageNum, req->pageCount);
|
|
osSendMesg(&req->messageQueue, req->response, OS_MESG_BLOCK);
|
|
break;
|
|
|
|
case FLASHROM_REQUEST_READ:
|
|
req->response = SysFlashrom_ReadData(req->addr, req->pageNum, req->pageCount);
|
|
osSendMesg(&req->messageQueue, req->response, OS_MESG_BLOCK);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SysFlashrom_WriteDataAsync(u8* addr, u32 pageNum, u32 pageCount) {
|
|
FlashromRequest* req = &sFlashromRequest;
|
|
if (SysFlashrom_IsInit()) {
|
|
req->requestType = FLASHROM_REQUEST_WRITE;
|
|
req->addr = addr;
|
|
req->pageNum = pageNum;
|
|
req->pageCount = pageCount;
|
|
osCreateMesgQueue(&req->messageQueue, sSysFlashromMsgBuf, ARRAY_COUNT(sSysFlashromMsgBuf));
|
|
StackCheck_Init(&sSysFlashromStackInfo, sSysFlashromStack, STACK_TOP(sSysFlashromStack), 0, 0x100,
|
|
"sys_flashrom");
|
|
osCreateThread(&sSysFlashromThread, Z_THREAD_ID_FLASHROM, SysFlashrom_ThreadEntry, req,
|
|
STACK_TOP(sSysFlashromStack), Z_PRIORITY_FLASHROM);
|
|
osStartThread(&sSysFlashromThread);
|
|
}
|
|
}
|
|
|
|
s32 SysFlashrom_IsBusy(void) {
|
|
OSMesgQueue* queue = &sFlashromRequest.messageQueue;
|
|
|
|
if (!SysFlashrom_IsInit()) {
|
|
return -1;
|
|
}
|
|
return MQ_IS_FULL(queue);
|
|
}
|
|
|
|
s32 SysFlashrom_AwaitResult(void) {
|
|
if (!SysFlashrom_IsInit()) {
|
|
return -1;
|
|
}
|
|
osRecvMesg(&sFlashromRequest.messageQueue, NULL, OS_MESG_BLOCK);
|
|
osDestroyThread(&sSysFlashromThread);
|
|
StackCheck_Cleanup(&sSysFlashromStackInfo);
|
|
return sFlashromRequest.response;
|
|
}
|
|
|
|
void SysFlashrom_WriteDataSync(void* addr, u32 pageNum, u32 pageCount) {
|
|
SysFlashrom_WriteDataAsync(addr, pageNum, pageCount);
|
|
SysFlashrom_AwaitResult();
|
|
}
|