Implement flash/RTC/BIOS dumping, fix IDE driver

This commit is contained in:
spicyjpeg 2023-07-09 18:12:27 +02:00
parent 7c488e3495
commit 96bb7a2688
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
13 changed files with 268 additions and 27 deletions

View File

@ -27,7 +27,7 @@
"write": "Writing new data to cartridge...",
"error": "An error occurred while writing to the cartridge's EEPROM.\n\nPress the Test button to view debug logs."
},
"hddDumpWorker": {
"cartDumpWorker": {
"save": "Saving cartridge dump...",
"error": "An error occurred while saving the dump to the hard drive. Turn off the system and make sure the drive is connected to the IDE bus properly, set as secondary and formatted with a single FAT16, FAT32 or exFAT partition.\n\nPress the Test button to view debug logs."
},
@ -35,6 +35,16 @@
"compress": "Compressing cartridge dump...",
"generate": "Generating QR code..."
},
"romDumpWorker": {
"init": "Creating dump directory...",
"dumpBIOS": "Dumping BIOS ROM...",
"dumpRTC": "Dumping RTC RAM...",
"dumpFlash": "Dumping internal flash...",
"dumpPCMCIA1": "Dumping PCMCIA card in slot 1...",
"dumpPCMCIA2": "Dumping PCMCIA card in slot 2...",
"initError": "An error occurred while creating the dump directory. Turn off the system and make sure the drive is connected to the IDE bus properly, set as secondary and formatted with a single FAT16, FAT32 or exFAT partition.\n\nPress the Test button to view debug logs.",
"dumpError": "An error occurred while writing dumps to the hard drive. Ensure the drive has at least 32 MB of free space (256 MB if both PCMCIA cards are inserted) and the filesystem is not damaged.\n\nPress the Test button to view debug logs."
},
"rebootWorker": {
"reboot": "Rebooting system..."
}
@ -187,19 +197,20 @@
"MainMenuScreen": {
"title": "{CART_ICON} Main menu",
"itemPrompt": "{RIGHT_ARROW} Select by pressing {START_BUTTON}",
"itemPrompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to move, select by pressing {START_BUTTON}",
"cartInfo": {
"name": "Manage security cartridge",
"prompt": "Display information about, unlock, dump and manipulate the contents of the inserted security cartridge (if any)."
},
"dump": {
"name": "Dump flash, PCMCIA cards, RTC or BIOS ROM",
"prompt": "Dump the contents of the internal ROMs, flash or any PCMCIA cards to the IDE hard drive or CF card connected as secondary drive (if any)."
"name": "Dump flash, PCMCIA cards, RTC and BIOS ROM",
"prompt": "Dump the contents of the internal ROMs and PCMCIA cards to the IDE hard drive or CF card connected as secondary drive (if any).",
"confirm": "The contents of the internal flash memory, RTC RAM, BIOS ROM and any inserted PCMCIA flash cards will be dumped and the dumps saved to a new directory in the root of the hard drive or CF card currently configured as secondary on the IDE bus.\n\nDo you wish to proceed?"
},
"restore": {
"name": "Restore flash, PCMCIA cards or RTC from dump",
"prompt": "Restore the contents of the internal flash or any PCMCIA cards to the IDE hard drive or CF card connected as secondary drive (if any)."
"prompt": "Restore the contents of the internal ROMs or any PCMCIA cards from the IDE hard drive or CF card connected as secondary drive (if any)."
},
"about": {
"name": "About this tool",

View File

@ -232,8 +232,8 @@ bool App::_qrCodeWorker(void) {
return true;
}
bool App::_hddDumpWorker(void) {
_workerStatus.update(0, 1, WSTR("App.hddDumpWorker.save"));
bool App::_cartDumpWorker(void) {
_workerStatus.update(0, 1, WSTR("App.cartDumpWorker.save"));
char code[8], region[8], path[32];
@ -246,7 +246,7 @@ bool App::_hddDumpWorker(void) {
if (_fileProvider->saveStruct(_dump, path) != sizeof(cart::Dump)) {
_errorScreen.setMessage(
_cartInfoScreen, WSTR("App.hddDumpWorker.error")
_cartInfoScreen, WSTR("App.cartDumpWorker.error")
);
_workerStatus.setNextScreen(_errorScreen);
return false;
@ -322,6 +322,7 @@ bool App::_cartReflashWorker(void) {
_parser->flush();
auto error = _driver->setDataKey(_selectedEntry->dataKey);
delayMicroseconds(1000000); // TODO: does this fix ZS01 bricking?
if (error)
LOG("failed to set data key");
@ -347,6 +348,7 @@ bool App::_cartEraseWorker(void) {
_workerStatus.update(0, 1, WSTR("App.cartEraseWorker.erase"));
auto error = _driver->erase();
delayMicroseconds(1000000); // TODO: does this fix ZS01 bricking?
_cartDetectWorker();
@ -363,6 +365,140 @@ bool App::_cartEraseWorker(void) {
return _cartUnlockWorker();
}
struct DumpRegion {
util::Hash prompt;
const char *path;
const uint16_t *ptr;
size_t length;
int bank;
uint32_t inputs;
};
static constexpr int _NUM_DUMP_REGIONS = 5;
static const DumpRegion _DUMP_REGIONS[_NUM_DUMP_REGIONS]{
{
.prompt = "App.romDumpWorker.dumpBIOS"_h,
.path = "dump_%d/bios.bin",
.ptr = reinterpret_cast<const uint16_t *>(DEV2_BASE),
.length = 0x80000,
.bank = -1,
.inputs = 0
}, {
.prompt = "App.romDumpWorker.dumpRTC"_h,
.path = "dump_%d/rtc.bin",
.ptr = reinterpret_cast<const uint16_t *>(DEV0_BASE | 0x620000),
.length = 0x2000,
.bank = -1,
.inputs = 0
}, {
.prompt = "App.romDumpWorker.dumpFlash"_h,
.path = "dump_%d/flash.bin",
.ptr = reinterpret_cast<const uint16_t *>(DEV0_BASE),
.length = 0x1000000,
.bank = SYS573_BANK_FLASH,
.inputs = 0
}, {
.prompt = "App.romDumpWorker.dumpPCMCIA1"_h,
.path = "dump_%d/pcmcia1.bin",
.ptr = reinterpret_cast<const uint16_t *>(DEV0_BASE),
.length = 0x4000000,
.bank = SYS573_BANK_PCMCIA1,
.inputs = io::JAMMA_PCMCIA_CD1
}, {
.prompt = "App.romDumpWorker.dumpPCMCIA2"_h,
.path = "dump_%d/pcmcia2.bin",
.ptr = reinterpret_cast<const uint16_t *>(DEV0_BASE),
.length = 0x4000000,
.bank = SYS573_BANK_PCMCIA2,
.inputs = io::JAMMA_PCMCIA_CD2
}
};
bool App::_romDumpWorker(void) {
_workerStatus.update(0, 1, WSTR("App.romDumpWorker.init"));
// Store all dumps in a directory named "dump_N".
int index = 0;
char path[32];
do {
index++;
snprintf(path, sizeof(path), "dump_%d", index);
} while (_fileProvider->fileExists(path));
LOG("saving dumps to %s", path);
if (!_fileProvider->createDirectory(path)) {
_errorScreen.setMessage(
_mainMenuScreen, WSTR("App.romDumpWorker.initError")
);
_workerStatus.setNextScreen(_errorScreen);
return false;
}
uint32_t inputs = io::getJAMMAInputs();
for (int i = 0; i < _NUM_DUMP_REGIONS; i++) {
auto &region = _DUMP_REGIONS[i];
// Skip PCMCIA slots if a card is not inserted.
if (region.inputs && !(inputs & region.inputs))
continue;
_workerStatus.update(i, _NUM_DUMP_REGIONS, WSTRH(region.prompt));
snprintf(path, sizeof(path), region.path, index);
auto _file = _fileProvider->openFile(
path, file::WRITE | file::ALLOW_CREATE
);
if (!_file)
goto _writeError;
// Read data from the source and write it to the file one 8 KB chunk at
// a time.
uint16_t buffer[0x1000];
const uint16_t *ptr = region.ptr;
size_t length = region.length;
int bank = region.bank;
io::setFlashBank(bank++);
for (; length; length -= sizeof(buffer)) {
uint16_t *output = buffer;
for (int i = sizeof(buffer); i; i -= 2)
*(output++) = *(ptr++);
// TODO TODO
if (ptr >= reinterpret_cast<const void *>(DEV0_BASE | 0x400000)) {
ptr = region.ptr;
io::setFlashBank(bank++);
}
if (_file->write(buffer, sizeof(buffer)) < sizeof(buffer)) {
_file->close();
goto _writeError;
}
}
_file->close();
LOG("%s saved", path);
}
_workerStatus.setNextScreen(_mainMenuScreen);
return true;
_writeError:
_errorScreen.setMessage(
_mainMenuScreen, WSTR("App.romDumpWorker.dumpError")
);
_workerStatus.setNextScreen(_errorScreen);
return false;
}
bool App::_rebootWorker(void) {
_workerStatus.update(0, 1, WSTR("App.rebootWorker.reboot"));
_workerStatus.setStatus(WORKER_REBOOT);

View File

@ -142,10 +142,11 @@ private:
bool _cartDetectWorker(void);
bool _cartUnlockWorker(void);
bool _qrCodeWorker(void);
bool _hddDumpWorker(void);
bool _cartDumpWorker(void);
bool _cartWriteWorker(void);
bool _cartReflashWorker(void);
bool _cartEraseWorker(void);
bool _romDumpWorker(void);
bool _rebootWorker(void);
void _worker(void);

View File

@ -62,7 +62,7 @@ void CartActionsScreen::qrDump(ui::Context &ctx) {
}
void CartActionsScreen::hddDump(ui::Context &ctx) {
APP->_setupWorker(&App::_hddDumpWorker);
APP->_setupWorker(&App::_cartDumpWorker);
ctx.show(APP->_workerStatusScreen, false, true);
}

View File

@ -81,7 +81,7 @@ public:
void (MainMenuScreen::*target)(ui::Context &ctx);
};
static constexpr int _NUM_MENU_ENTRIES = 3;
static constexpr int _NUM_MENU_ENTRIES = 4;
static const MenuEntry _MENU_ENTRIES[_NUM_MENU_ENTRIES]{
{
@ -89,11 +89,11 @@ static const MenuEntry _MENU_ENTRIES[_NUM_MENU_ENTRIES]{
.prompt = "MainMenuScreen.cartInfo.prompt"_h,
.target = &MainMenuScreen::cartInfo
}, {
#if 0
.name = "MainMenuScreen.dump.name"_h,
.prompt = "MainMenuScreen.dump.prompt"_h,
.target = &MainMenuScreen::dump
}, {
#if 0
.name = "MainMenuScreen.restore.name"_h,
.prompt = "MainMenuScreen.restore.prompt"_h,
.target = &MainMenuScreen::restore
@ -123,7 +123,16 @@ void MainMenuScreen::cartInfo(ui::Context &ctx) {
}
void MainMenuScreen::dump(ui::Context &ctx) {
//ctx.show(APP->_dumpMenuScreen, false, true);
APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) {
APP->_setupWorker(&App::_romDumpWorker);
ctx.show(APP->_workerStatusScreen, false, true);
},
STR("MainMenuScreen.dump.confirm")
);
ctx.show(APP->_confirmScreen, false, true);
}
void MainMenuScreen::restore(ui::Context &ctx) {

View File

@ -107,6 +107,16 @@ void FATFile::close(void) {
uint32_t currentSPUOffset = spu::DUMMY_BLOCK_END;
bool Provider::fileExists(const char *path) {
auto file = openFile(path, READ);
if (!file)
return false;
file->close();
return true;
}
size_t Provider::loadData(util::Data &output, const char *path) {
auto file = openFile(path, READ);
@ -135,6 +145,7 @@ size_t Provider::loadData(void *output, size_t length, const char *path) {
return actualLength;
}
size_t Provider::saveData(const void *input, size_t length, const char *path) {
auto file = openFile(path, WRITE | ALLOW_CREATE);
@ -146,6 +157,7 @@ size_t Provider::saveData(const void *input, size_t length, const char *path) {
return actualLength;
}
size_t Provider::loadTIM(gpu::Image &output, const char *path) {
util::Data data;
@ -209,6 +221,18 @@ bool HostProvider::init(void) {
return true;
}
bool HostProvider::createDirectory(const char *path) {
int fd = pcdrvCreate(path, PCDRV_ATTR_DIRECTORY);
if (fd < 0) {
LOG("PCDRV error, code=%d", fd);
return false;
}
pcdrvClose(fd);
return true;
}
File *HostProvider::openFile(const char *path, uint32_t flags) {
PCDRVOpenMode mode = PCDRV_MODE_READ;
@ -237,14 +261,14 @@ bool FATProvider::init(const char *drive) {
auto error = f_mount(&_fs, drive, 1);
if (error) {
LOG("mount error, drive=%s, code=%d", drive, error);
LOG("FAT mount error, drive=%s, code=%d", drive, error);
return false;
}
f_chdrive(drive);
__builtin_strncpy(_drive, drive, sizeof(_drive));
LOG("mount ok, drive=%s", drive);
LOG("FAT mount ok, drive=%s", drive);
return true;
}
@ -252,9 +276,24 @@ void FATProvider::close(void) {
auto error = f_unmount(_drive);
if (error)
LOG("unmount error, drive=%s, code=%d", _drive, error);
LOG("FAT unmount error, drive=%s, code=%d", _drive, error);
else
LOG("unmount ok, drive=%s", _drive);
LOG("FAT unmount ok, drive=%s", _drive);
}
bool FATProvider::fileExists(const char *path) {
return !f_stat(path, nullptr);
}
bool FATProvider::createDirectory(const char *path) {
auto error = f_mkdir(path);
if (error) {
LOG("FAT error, code=%d", error);
return false;
}
return true;
}
File *FATProvider::openFile(const char *path, uint32_t flags) {
@ -320,6 +359,10 @@ void ZIPProvider::close(void) {
_file->close();
}
bool ZIPProvider::fileExists(const char *path) {
return (mz_zip_reader_locate_file(&_zip, path, nullptr, 0) >= 0);
}
size_t ZIPProvider::loadData(util::Data &output, const char *path) {
output.destroy();
output.ptr = mz_zip_reader_extract_file_to_heap(

View File

@ -83,6 +83,9 @@ public:
virtual void close(void) {}
virtual bool fileExists(const char *path);
virtual bool createDirectory(const char *path) { return false; }
virtual File *openFile(const char *path, uint32_t flags) { return nullptr; }
virtual size_t loadData(util::Data &output, const char *path);
virtual size_t loadData(void *output, size_t length, const char *path);
@ -95,6 +98,8 @@ class HostProvider : public Provider {
public:
bool init(void);
bool createDirectory(const char *path);
File *openFile(const char *path, uint32_t flags);
};
@ -107,6 +112,9 @@ public:
bool init(const char *drive);
void close(void);
bool fileExists(const char *path);
bool createDirectory(const char *path);
File *openFile(const char *path, uint32_t flags);
};
@ -121,6 +129,8 @@ public:
bool init(const void *zipData, size_t length);
void close(void);
bool fileExists(const char *path);
size_t loadData(util::Data &output, const char *path);
size_t loadData(void *output, size_t length, const char *path);
};

View File

@ -280,8 +280,9 @@ DeviceError Device::_ideReadWrite(
if (error)
return error;
//error = _transferDMA(reinterpret_cast<void *>(ptr), length, write);
error = _transferPIO(reinterpret_cast<void *>(ptr), length, write);
error = _transferPIO(
reinterpret_cast<void *>(ptr), length * ATA_SECTOR_SIZE, write
);
if (error)
return error;
@ -312,7 +313,7 @@ DeviceError Device::flushCache(void) {
/* FatFs API glue */
extern "C" DSTATUS disk_initialize(uint8_t drive) {
if (!devices[drive].enumerate())
if (devices[drive].enumerate())
return RES_NOTRDY;
return disk_status(drive);
@ -322,7 +323,7 @@ extern "C" DSTATUS disk_status(uint8_t drive) {
auto &dev = devices[drive];
uint32_t flags = 0;
if (dev.flags & DEVICE_READY)
if (!(dev.flags & DEVICE_READY))
flags |= STA_NOINIT;
if (!dev.capacity)
flags |= STA_NODISK;

View File

@ -270,11 +270,11 @@ private:
SYS573_IDE_CS1_BASE[reg] = value;
}
inline void _select(uint8_t flags) {
inline void _select(uint8_t regFlags) {
if (flags & DEVICE_SECONDARY)
_write(CS0_DEVICE_SEL, flags | CS0_DEVICE_SEL_SECONDARY);
_write(CS0_DEVICE_SEL, regFlags | CS0_DEVICE_SEL_SECONDARY);
else
_write(CS0_DEVICE_SEL, flags | CS0_DEVICE_SEL_PRIMARY);
_write(CS0_DEVICE_SEL, regFlags | CS0_DEVICE_SEL_PRIMARY);
}
void _setLBA(uint64_t lba, uint16_t count);

View File

@ -111,6 +111,12 @@ static inline void setCartOutput(CartOutputPin pin, bool value) {
SYS573_CART_OUT = _cartOutputReg;
}
static inline void setFlashBank(int bank) {
_bankSwitchReg = (_bankSwitchReg & (3 << 6)) | bank;
SYS573_BANK_CTRL = _bankSwitchReg;
}
static inline void setCartSDADir(bool dir) {
if (dir)
_bankSwitchReg |= 1 << 6;

View File

@ -17,6 +17,7 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
typedef enum {
PCDRV_MODE_READ = 0,
@ -30,12 +31,20 @@ typedef enum {
PCDRV_SEEK_END = 2
} PCDRVSeekMode;
typedef enum {
PCDRV_ATTR_READ_ONLY = 1 << 0,
PCDRV_ATTR_HIDDEN = 1 << 1,
PCDRV_ATTR_SYSTEM = 1 << 2,
PCDRV_ATTR_DIRECTORY = 1 << 4,
PCDRV_ATTR_ARCHIVE = 1 << 5
} PCDRVAttribute;
#ifdef __cplusplus
extern "C" {
#endif
int pcdrvInit(void);
int pcdrvCreate(const char *path);
int pcdrvCreate(const char *path, uint32_t attributes);
int pcdrvOpen(const char *path, PCDRVOpenMode mode);
int pcdrvClose(int fd);
int pcdrvRead(int fd, void *data, size_t length);

View File

@ -29,7 +29,7 @@ pcdrvInit:
.type pcdrvCreate, @function
pcdrvCreate:
li $a2, 0
move $a2, $a1
move $a1, $a0
break 0, 0x102 # (path, path, 0) -> error, fd

View File

@ -36,7 +36,8 @@ typedef enum {
EXP1_BASE = 0xbf000000,
IO_BASE = 0xbf800000,
EXP2_BASE = 0xbf802000,
EXP3_BASE = 0xbfa00000
EXP3_BASE = 0xbfa00000,
DEV2_BASE = 0xbfc00000
} BaseAddress;
/* Bus interface */
@ -178,6 +179,7 @@ typedef enum {
typedef enum {
DMA_CHCR_READ = 0 << 0,
DMA_CHCR_WRITE = 1 << 0,
DMA_CHCR_REVERSE = 1 << 1,
DMA_CHCR_CHOPPING = 1 << 8,
DMA_CHCR_MODE_BITMASK = 3 << 9,
DMA_CHCR_MODE_BURST = 0 << 9,
@ -190,6 +192,13 @@ typedef enum {
DMA_CHCR_PAUSE = 1 << 29 // Burst mode only
} DMACHCRFlag;
typedef enum {
DMA_DPCR_PRIORITY_BITMASK = 7 << 0,
DMA_DPCR_PRIORITY_MIN = 7 << 0,
DMA_DPCR_PRIORITY_MAX = 0 << 0,
DMA_DPCR_ENABLE = 1 << 3
} DMADPCRFlag;
typedef enum {
DMA_DICR_CH_MODE_BITMASK = 0x7f << 0,
DMA_DICR_BUS_ERROR = 1 << 15,
@ -449,6 +458,12 @@ typedef enum {
SYS573_MISC_IN_SERVICE = 1 << 12
} Sys573MiscInputFlag;
typedef enum {
SYS573_BANK_FLASH = 0,
SYS573_BANK_PCMCIA1 = 16,
SYS573_BANK_PCMCIA2 = 32
} Sys573Bank;
#define SYS573_MISC_OUT _MMIO16(DEV0_BASE | 0x400000)
#define SYS573_DIP_CART _MMIO16(DEV0_BASE | 0x400004)
#define SYS573_MISC_IN _MMIO16(DEV0_BASE | 0x400006)