Bump to 0.4.5, add flash executable loading, fix bugs

This commit is contained in:
spicyjpeg 2024-06-05 18:50:00 +02:00
parent aac5f4abb1
commit f4a8d16b20
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
17 changed files with 177 additions and 78 deletions

View File

@ -6,7 +6,7 @@ set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/cmake/toolchain.cmake")
project(
cart-tool
LANGUAGES C CXX ASM
VERSION 0.4.4
VERSION 0.4.5
DESCRIPTION "Konami System 573 security cartridge tool"
)

View File

@ -7,8 +7,8 @@
extern "C" const uint8_t _resourceArchive[];
extern "C" const size_t _resourceArchiveLength;
static char _ptrArg[]{ "resource.ptr=xxxxxxxx" };
static char _lengthArg[]{ "resource.length=xxxxxxxx" };
static char _ptrArg[]{ "resource.ptr=xxxxxxxx\0" };
static char _lengthArg[]{ "resource.length=xxxxxxxx\0" };
struct [[gnu::packed]] ZIPFileHeader {
public:

View File

@ -87,8 +87,8 @@ bool ExecutableLauncherArgs::parseArgument(const char *arg) {
loadAddress = reinterpret_cast<void *>(strtol(&arg[5], nullptr, 16));
return true;
case "drive"_h:
drive = int(strtol(&arg[6], nullptr, 0));
case "device"_h:
device = int(strtol(&arg[6], nullptr, 0));
return true;
case "frag"_h:

View File

@ -52,7 +52,7 @@ public:
void *entryPoint, *initialGP, *stackTop;
void *loadAddress;
int drive;
int device; // 0-63 = flash, -1 or -2 = IDE
size_t numArgs, numFragments;
const char *executableArgs[util::MAX_EXECUTABLE_ARGS];
@ -60,7 +60,7 @@ public:
inline ExecutableLauncherArgs(void)
: entryPoint(nullptr), initialGP(nullptr), stackTop(nullptr),
loadAddress(nullptr), drive(0), numArgs(0), numFragments(0) {}
loadAddress(nullptr), device(0), numArgs(0), numFragments(0) {}
bool parseArgument(const char *arg);
};

View File

@ -104,6 +104,9 @@ void FATDirectory::close(void) {
/* FAT filesystem provider */
bool FATProvider::init(int drive) {
if (type)
return false;
_drive[0] = drive + '0';
auto error = f_mount(&_fs, _drive, 1);
@ -123,6 +126,9 @@ bool FATProvider::init(int drive) {
}
void FATProvider::close(void) {
if (!type)
return;
auto error = f_unmount(_drive);
if (error) {

View File

@ -20,11 +20,12 @@ enum FileSystemType {
FAT12 = 1,
FAT16 = 2,
FAT32 = 3,
ISO9660 = 4,
ZIP_MEMORY = 5,
ZIP_FILE = 6,
HOST = 7,
VFS = 8
EXFAT = 4,
ISO9660 = 5,
ZIP_MEMORY = 6,
ZIP_FILE = 7,
HOST = 8,
VFS = 9
};
// These are functionally equivalent to the FA_* flags used by FatFs.

View File

@ -180,24 +180,33 @@ VFSMountPoint *VFSProvider::_getMounted(const char *path) {
bool VFSProvider::mount(const char *prefix, Provider *provider, bool force) {
auto hash = util::hash(prefix, VFS_PREFIX_SEPARATOR);
VFSMountPoint *freeMP = nullptr;
for (auto &mp : _mountPoints) {
if (force) {
if (mp.prefix && (mp.prefix != hash))
continue;
} else {
if (mp.prefix)
continue;
if (!mp.prefix) {
freeMP = &mp;
} else if (mp.prefix == hash) {
if (force) {
freeMP = &mp;
break;
}
LOG_FS("%s was already mapped", prefix);
return false;
}
mp.prefix = hash;
mp.pathOffset = __builtin_strlen(prefix);
mp.provider = provider;
LOG_FS("mapped %s", prefix);
return true;
}
return false;
if (!freeMP) {
LOG_FS("no mount points left for %s", prefix);
return false;
}
freeMP->prefix = hash;
freeMP->pathOffset = __builtin_strlen(prefix);
freeMP->provider = provider;
LOG_FS("mapped %s", prefix);
return true;
}
bool VFSProvider::unmount(const char *prefix) {
@ -215,6 +224,7 @@ bool VFSProvider::unmount(const char *prefix) {
return true;
}
LOG_FS("%s was not mapped", prefix);
return false;
}

View File

@ -97,6 +97,9 @@ static constexpr uint32_t _ZIP_FLAGS = 0
| MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY;
bool ZIPProvider::init(File *file) {
if (type)
return false;
mz_zip_zero_struct(&_zip);
_file = file;
@ -128,6 +131,9 @@ bool ZIPProvider::init(File *file) {
}
bool ZIPProvider::init(const void *zipData, size_t length) {
if (type)
return false;
mz_zip_zero_struct(&_zip);
_file = nullptr;
@ -146,6 +152,9 @@ bool ZIPProvider::init(const void *zipData, size_t length) {
}
void ZIPProvider::close(void) {
if (!type)
return;
mz_zip_reader_end(&_zip);
#if 0

View File

@ -607,8 +607,9 @@ DeviceError Device::enumerate(void) {
// actually present. A strict timeout is used in the commands below in order
// to prevent blocking for too long.
IdentifyBlock block;
auto signature = _getCylinder();
if (_getCylinder() == _ATAPI_SIGNATURE) {
if (signature == _ATAPI_SIGNATURE) {
flags |= DEVICE_ATAPI;
_write(CS0_COMMAND, ATA_IDENTIFY_PACKET);
@ -660,6 +661,13 @@ DeviceError Device::enumerate(void) {
// Find out the fastest PIO transfer mode supported and enable it.
int mode = block.getHighestPIOMode();
_select(0);
error = _waitForIdle();
if (error)
return error;
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
_write(CS0_COUNT, TRANSFER_MODE_PIO | mode);
_write(CS0_COMMAND, ATA_SET_FEATURES);

View File

@ -36,12 +36,12 @@ void init(void) {
| ( 4 << 24) // DMA read/write delay
| BIU_CTRL_DMA_DELAY;
#if 0
// Revision D of the main board has footprints for either eight 8-bit RAM
// chips wired as two 32-bit banks, or two 16-bit chips wired as a single
// bank. Normally the kernel takes care of setting up the memory controller
// appropriately, but this makes sure the configuration is correct if e.g.
// the tool is booted through OpenBIOS instead.
// bank.
DRAM_CTRL = isDualBankRAM() ? 0x0c80 : 0x4788;
#endif
_bankSwitchReg = 0;
_cartOutputReg = 0;

View File

@ -29,9 +29,21 @@ void Region::read(void *data, uint32_t offset, size_t length) const {
auto source = reinterpret_cast<const uint32_t *>(ptr + offset);
auto dest = reinterpret_cast<uint32_t *>(data);
// TODO: use memcpy() instead once an optimized implementation is added
util::assertAligned<uint32_t>(source);
util::assertAligned<uint32_t>(dest);
for (; length >= 32; length -= 32, dest += 8, source += 8) {
dest[0] = source[0];
dest[1] = source[1];
dest[2] = source[2];
dest[3] = source[3];
dest[4] = source[4];
dest[5] = source[5];
dest[6] = source[6];
dest[7] = source[7];
}
for (; length; length -= 4)
*(dest++) = *(source++);
}
@ -64,8 +76,19 @@ void RTCRegion::read(void *data, uint32_t offset, size_t length) const {
// The RTC is an 8-bit device connected to a 16-bit bus, i.e. each byte must
// be read as a 16-bit value and then the upper 8 bits must be discarded.
for (; length >= 8; length -= 8, dest += 8, source += 8) {
dest[0] = uint8_t(source[0]);
dest[1] = uint8_t(source[1]);
dest[2] = uint8_t(source[2]);
dest[3] = uint8_t(source[3]);
dest[4] = uint8_t(source[4]);
dest[5] = uint8_t(source[5]);
dest[6] = uint8_t(source[6]);
dest[7] = uint8_t(source[7]);
}
for (; length; length--)
*(dest++) = *(source++) & 0xff;
*(dest++) = uint8_t(*(source++));
}
uint32_t RTCRegion::zipCRC32(
@ -120,18 +143,11 @@ uint16_t *FlashRegion::getRawPtr(uint32_t offset, bool alignToChip) const {
void FlashRegion::read(void *data, uint32_t offset, size_t length) const {
// FIXME: this implementation will not handle unaligned reads and reads that
// cross bank boundaries properly
int bankOffset = offset / FLASH_BANK_LENGTH;
int ptrOffset = offset % FLASH_BANK_LENGTH;
auto bankOffset = offset / FLASH_BANK_LENGTH;
auto ptrOffset = offset % FLASH_BANK_LENGTH;
auto source = reinterpret_cast<const uint32_t *>(ptr + ptrOffset);
auto dest = reinterpret_cast<uint32_t *>(data);
util::assertAligned<uint32_t>(source);
util::assertAligned<uint32_t>(dest);
io::setFlashBank(bank + bankOffset);
for (; length; length -= 4)
*(dest++) = *(source++);
Region::read(data, ptrOffset, length);
}
uint32_t FlashRegion::zipCRC32(
@ -139,8 +155,8 @@ uint32_t FlashRegion::zipCRC32(
) const {
// FIXME: this implementation will not handle unaligned reads and reads that
// cross bank boundaries properly
int bankOffset = offset / FLASH_BANK_LENGTH;
int ptrOffset = offset % FLASH_BANK_LENGTH;
auto bankOffset = offset / FLASH_BANK_LENGTH;
auto ptrOffset = offset % FLASH_BANK_LENGTH;
auto source = reinterpret_cast<const uint32_t *>(ptr + ptrOffset);
auto table = reinterpret_cast<const uint32_t *>(CACHE_BASE);
@ -176,7 +192,7 @@ enum FlashIdentifier : uint16_t {
_ID_28F640J5 = 0x89 | (0x15 << 8)
};
bool FlashRegion::hasBootExecutable(void) const {
const util::ExecutableHeader *FlashRegion::getBootExecutableHeader(void) const {
// FIXME: this implementation will not detect executables that cross bank
// boundaries (but it shouldn't matter as executables must be <4 MB anyway)
auto data = reinterpret_cast<const uint8_t *>(ptr + FLASH_EXECUTABLE_OFFSET);
@ -185,10 +201,10 @@ bool FlashRegion::hasBootExecutable(void) const {
io::setFlashBank(bank);
auto &header = *reinterpret_cast<const util::ExecutableHeader *>(data);
auto header = reinterpret_cast<const util::ExecutableHeader *>(data);
if (!header.validateMagic())
return false;
if (!header->validateMagic())
return nullptr;
// The integrity of the executable is verified by calculating the CRC32 of
// its bytes whose offsets are powers of 2 (i.e. the bytes at indices 0, 1,
@ -196,7 +212,7 @@ bool FlashRegion::hasBootExecutable(void) const {
// header.textLength + util::EXECUTABLE_BODY_OFFSET, as the CRC is also
// calculated on the header, but Konami's shell ignores the last 2048 bytes
// due to a bug.
size_t length = header.textLength;
size_t length = header->textLength;
uint32_t crc = ~0;
crc = (crc >> 8) ^ table[(crc ^ *data) & 0xff];
@ -204,7 +220,10 @@ bool FlashRegion::hasBootExecutable(void) const {
for (size_t i = 1; i < length; i <<= 1)
crc = (crc >> 8) ^ table[(crc ^ data[i]) & 0xff];
return (~crc == *crcPtr);
if (~crc != *crcPtr)
return nullptr;
return header;
}
uint32_t FlashRegion::getJEDECID(void) const {

View File

@ -32,7 +32,9 @@ public:
uint32_t offset, size_t length, uint32_t crc = 0
) const;
virtual bool hasBootExecutable(void) const { return false; }
virtual const util::ExecutableHeader *getBootExecutableHeader(void) const {
return nullptr;
}
virtual uint32_t getJEDECID(void) const { return 0; }
virtual Driver *newDriver(void) const { return nullptr; }
};
@ -67,7 +69,7 @@ public:
void read(void *data, uint32_t offset, size_t length) const;
uint32_t zipCRC32(uint32_t offset, size_t length, uint32_t crc = 0) const;
bool hasBootExecutable(void) const;
const util::ExecutableHeader *getBootExecutableHeader(void) const;
uint32_t getJEDECID(void) const;
Driver *newDriver(void) const;
};

View File

@ -7,30 +7,42 @@
extern "C" uint8_t _textStart[];
int main(int argc, const char **argv) {
io::init();
static constexpr size_t _LOAD_CHUNK_LENGTH = 0x8000;
args::ExecutableLauncherArgs args;
static int _loadFromFlash(args::ExecutableLauncherArgs &args) {
io::setFlashBank(args.device);
for (; argc > 0; argc--)
args.parseArgument(*(argv++));
// The executable's offset and length are always passed as a single
// fragment.
auto ptr = reinterpret_cast<uintptr_t>(args.loadAddress);
auto source = uintptr_t(args.fragments[0].lba);
auto length = size_t(args.fragments[0].length);
#if defined(ENABLE_APP_LOGGING) || defined(ENABLE_IDE_LOGGING)
util::logger.setupSyslog(args.baudRate);
#endif
while (length) {
size_t chunkLength = util::min(length, _LOAD_CHUNK_LENGTH);
if (!args.entryPoint || !args.loadAddress || !args.numFragments) {
LOG_APP("required arguments missing");
return 1;
__builtin_memcpy(
reinterpret_cast<void *>(ptr),
reinterpret_cast<const void *>(source), chunkLength
);
io::clearWatchdog();
ptr += chunkLength;
source += chunkLength;
length -= chunkLength;
}
if (!args.stackTop)
args.stackTop = _textStart - 16;
return 0;
}
auto &dev = ide::devices[args.drive];
static int _loadFromIDE(args::ExecutableLauncherArgs &args) {
int drive = -(args.device + 1);
auto &dev = ide::devices[drive];
if (dev.enumerate()) {
LOG_APP("drive %d initialization failed", args.drive);
auto error = dev.enumerate();
if (error) {
LOG_APP("drive %d: %s", drive, ide::getErrorString(error));
return 2;
}
@ -57,8 +69,10 @@ int main(int argc, const char **argv) {
length -= skipSectors;
}
if (dev.readData(reinterpret_cast<void *>(ptr), lba, length)) {
LOG_APP("read failed, lba=0x%08x", lba);
error = dev.readData(reinterpret_cast<void *>(ptr), lba, length);
if (error) {
LOG_APP("drive %d: %s", drive, ide::getErrorString(error));
return 3;
}
@ -66,6 +80,36 @@ int main(int argc, const char **argv) {
ptr += length * sectorSize;
}
return 0;
}
int main(int argc, const char **argv) {
io::init();
args::ExecutableLauncherArgs args;
for (; argc > 0; argc--)
args.parseArgument(*(argv++));
#if defined(ENABLE_APP_LOGGING) || defined(ENABLE_IDE_LOGGING)
util::logger.setupSyslog(args.baudRate);
#endif
if (!args.entryPoint || !args.loadAddress || !args.numFragments) {
LOG_APP("required arguments missing");
return 1;
}
if (!args.stackTop)
args.stackTop = _textStart - 16;
int error = (args.device >= 0)
? _loadFromFlash(args)
: _loadFromIDE(args);
if (error)
return error;
// Launch the executable.
util::ExecutableLoader loader(
args.entryPoint, args.initialGP, args.stackTop

View File

@ -78,7 +78,7 @@ FileIOManager::FileIOManager(void)
void FileIOManager::initIDE(void) {
closeIDE();
char name[6]{ "ide#:" };
char name[8]{ "ide#:\0" };
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
auto &dev = ide::devices[i];
@ -117,7 +117,7 @@ void FileIOManager::initIDE(void) {
}
void FileIOManager::closeIDE(void) {
char name[6]{ "ide#:" };
char name[8]{ "ide#:\0" };
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
if (ide[i]) {

View File

@ -41,7 +41,7 @@ bool App::_ideInitWorker(void) {
#ifdef ENABLE_AUTOBOOT
// Only try to autoboot if DIP switch 1 is on.
if (io::getDIPSwitch(0)) {
_workerStatus.update(3, 4, WSTR("App.fileInitWorker.autoboot"));
_workerStatus.update(3, 4, WSTR("App.ideInitWorker.autoboot"));
for (auto path : _AUTOBOOT_PATHS) {
file::FileInfo info;
@ -62,14 +62,14 @@ bool App::_ideInitWorker(void) {
}
bool App::_fileInitWorker(void) {
_workerStatus.update(0, 4, WSTR("App.fileInitWorker.unmount"));
_workerStatus.update(0, 3, WSTR("App.fileInitWorker.unmount"));
_fileIO.closeResourceFile();
_fileIO.close();
_workerStatus.update(1, 4, WSTR("App.fileInitWorker.mount"));
_workerStatus.update(1, 3, WSTR("App.fileInitWorker.mount"));
_fileIO.initIDE();
_workerStatus.update(2, 4, WSTR("App.fileInitWorker.loadResources"));
_workerStatus.update(2, 3, WSTR("App.fileInitWorker.loadResources"));
if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip"))
_loadResources();

View File

@ -224,7 +224,7 @@ void FilePickerScreen::update(ui::Context &ctx) {
}
#endif
char name[6]{ "ide#:" };
char name[8]{ "ide#:\0" };
int drive = _drives[index];
auto &dev = ide::devices[drive];

View File

@ -72,7 +72,7 @@ void StorageInfoScreen::show(ui::Context &ctx, bool goBack) {
(id >> 24) & 0xff
);
if (rom::flash.hasBootExecutable())
if (rom::flash.getBootExecutableHeader())
_PRINT(STR("StorageInfoScreen.flash.bootable"));
// TODO: show information about currently installed game
@ -96,7 +96,7 @@ void StorageInfoScreen::show(ui::Context &ctx, bool goBack) {
(id >> 24) & 0xff
);
if (card.hasBootExecutable())
if (card.getBootExecutableHeader())
_PRINT(STR("StorageInfoScreen.pcmcia.bootable"));
} else {
_PRINT(STR("StorageInfoScreen.pcmcia.noCard"));