Implement "strict mode".

This commit is contained in:
Christian Speckner 2024-08-11 14:53:53 +02:00
parent 0567eb18b0
commit 22af25e77c
4 changed files with 128 additions and 18 deletions

View File

@ -26,6 +26,7 @@
#include "ElfEnvironment.hxx" #include "ElfEnvironment.hxx"
#include "Logger.hxx" #include "Logger.hxx"
#include "Props.hxx" #include "Props.hxx"
#include "Settings.hxx"
#include "exception/FatalEmulationError.hxx" #include "exception/FatalEmulationError.hxx"
#include "CartELF.hxx" #include "CartELF.hxx"
@ -220,7 +221,7 @@ CartridgeELF::CartridgeELF(const ByteBuffer& image, size_t size, string_view md5
createRomAccessArrays(0x1000); createRomAccessArrays(0x1000);
parseAndLinkElf(); parseAndLinkElf();
setupMemoryMap(); allocationSections();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -229,6 +230,9 @@ CartridgeELF::~CartridgeELF() = default;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeELF::reset() void CartridgeELF::reset()
{ {
const bool devMode = mySettings.getBool("dev.settings");
const bool strictMode = devMode && mySettings.getBool("dev.thumb.trapfatal");
std::fill_n(myLastPeekResult.get(), 0x1000, 0); std::fill_n(myLastPeekResult.get(), 0x1000, 0);
myIsBusDriven = false; myIsBusDriven = false;
myDriveBusValue = 0; myDriveBusValue = 0;
@ -251,6 +255,7 @@ void CartridgeELF::reset()
myLinker->getSegmentSize(ElfLinker::SegmentType::rodata)); myLinker->getSegmentSize(ElfLinker::SegmentType::rodata));
std::memcpy(mySectionTables.get(), LOOKUP_TABLES, sizeof(LOOKUP_TABLES)); std::memcpy(mySectionTables.get(), LOOKUP_TABLES, sizeof(LOOKUP_TABLES));
setupMemoryMap(strictMode);
myCortexEmu.reset(); myCortexEmu.reset();
myTransactionQueue myTransactionQueue
@ -349,20 +354,76 @@ uInt8 CartridgeELF::overdrivePoke(uInt16 address, uInt8 value)
return driveBus(address, value); return driveBus(address, value);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CortexM0::err_t CartridgeELF::BusFallbackDelegate::fetch16( CortexM0::err_t CartridgeELF::BusFallbackDelegate::fetch16(
uInt32 address, uInt16& value, uInt8& op, CortexM0& cortex uInt32 address, uInt16& value, uInt8& op, CortexM0& cortex
) { ) {
return address == (RETURN_ADDR & ~1) if (address == (RETURN_ADDR & ~1)) return CortexM0::errCustom(ERR_RETURN);
? CortexM0::errCustom(ERR_RETURN)
: CortexM0::errIntrinsic(CortexM0::ERR_UNMAPPED_FETCH16, address); return handleError("fetch16", address, CortexM0::ERR_UNMAPPED_FETCH16, cortex);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CortexM0::err_t CartridgeELF::BusFallbackDelegate::read8(uInt32 address, uInt8& value, CortexM0& cortex) CortexM0::err_t CartridgeELF::BusFallbackDelegate::read8(uInt32 address, uInt8& value, CortexM0& cortex)
{ {
// TODO: remove this hack and replace it with a setting. return handleError("read8", address, CortexM0::ERR_UNMAPPED_READ8, cortex);
value = 0; }
return 0;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CortexM0::err_t CartridgeELF::BusFallbackDelegate::read16(uInt32 address, uInt16& value, CortexM0& cortex)
{
return handleError("read16", address, CortexM0::ERR_UNMAPPED_READ16, cortex);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CortexM0::err_t CartridgeELF::BusFallbackDelegate::read32(uInt32 address, uInt32& value, CortexM0& cortex)
{
return handleError("read32", address, CortexM0::ERR_UNMAPPED_READ32, cortex);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CortexM0::err_t CartridgeELF::BusFallbackDelegate::write8(uInt32 address, uInt8 value, CortexM0& cortex)
{
return handleError("write8", address, CortexM0::ERR_UNMAPPED_WRITE8, cortex);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CortexM0::err_t CartridgeELF::BusFallbackDelegate::write16(uInt32 address, uInt16 value, CortexM0& cortex)
{
return handleError("write16", address, CortexM0::ERR_UNMAPPED_WRITE16, cortex);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CortexM0::err_t CartridgeELF::BusFallbackDelegate::write32(uInt32 address, uInt32 value, CortexM0& cortex)
{
return handleError("write32", address, CortexM0::ERR_UNMAPPED_WRITE32, cortex);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeELF::BusFallbackDelegate::setErrorsAreFatal(bool fatal)
{
myErrorsAreFatal = fatal;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CortexM0::err_t CartridgeELF::BusFallbackDelegate::handleError(
string_view accessType, uInt32 address, CortexM0::err_t err, CortexM0& cortex
) const {
if (myErrorsAreFatal) return CortexM0::errIntrinsic(err, address);
stringstream s;
s
<< "invalid " << accessType << " access to 0x"
<< std::hex << std::setw(8) << std::setfill('0') << address
<< " (PC = 0x"
<< std::hex << std::setw(8) << std::setfill('0') << cortex.getRegister(15)
<< ")";
Logger::error(s.str());
return CortexM0::ERR_NONE;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -438,28 +499,35 @@ void CartridgeELF::parseAndLinkElf()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeELF::setupMemoryMap() void CartridgeELF::allocationSections()
{ {
mySectionStack = make_unique<uInt8[]>(STACK_SIZE); mySectionStack = make_unique<uInt8[]>(STACK_SIZE);
mySectionText = make_unique<uInt8[]>(TEXT_SIZE); mySectionText = make_unique<uInt8[]>(TEXT_SIZE);
mySectionData = make_unique<uInt8[]>(DATA_SIZE); mySectionData = make_unique<uInt8[]>(DATA_SIZE);
mySectionRodata = make_unique<uInt8[]>(RODATA_SIZE); mySectionRodata = make_unique<uInt8[]>(RODATA_SIZE);
mySectionTables = make_unique<uInt8[]>(TABLES_SIZE); mySectionTables = make_unique<uInt8[]>(TABLES_SIZE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeELF::setupMemoryMap(bool strictMode)
{
myCortexEmu myCortexEmu
.resetMappings()
.mapRegionData(ADDR_STACK_BASE / CortexM0::PAGE_SIZE, .mapRegionData(ADDR_STACK_BASE / CortexM0::PAGE_SIZE,
STACK_SIZE / CortexM0::PAGE_SIZE, false, mySectionStack.get()) STACK_SIZE / CortexM0::PAGE_SIZE, false, mySectionStack.get())
.mapRegionCode(ADDR_TEXT_BASE / CortexM0::PAGE_SIZE, .mapRegionCode(ADDR_TEXT_BASE / CortexM0::PAGE_SIZE,
TEXT_SIZE / CortexM0::PAGE_SIZE, true, mySectionText.get()) TEXT_SIZE / CortexM0::PAGE_SIZE, strictMode, mySectionText.get())
.mapRegionData(ADDR_DATA_BASE / CortexM0::PAGE_SIZE, .mapRegionData(ADDR_DATA_BASE / CortexM0::PAGE_SIZE,
DATA_SIZE / CortexM0::PAGE_SIZE, false, mySectionData.get()) DATA_SIZE / CortexM0::PAGE_SIZE, false, mySectionData.get())
.mapRegionData(ADDR_RODATA_BASE / CortexM0::PAGE_SIZE, .mapRegionData(ADDR_RODATA_BASE / CortexM0::PAGE_SIZE,
RODATA_SIZE / CortexM0::PAGE_SIZE, true, mySectionRodata.get()) RODATA_SIZE / CortexM0::PAGE_SIZE, strictMode, mySectionRodata.get())
.mapRegionData(ADDR_TABLES_BASE / CortexM0::PAGE_SIZE, .mapRegionData(ADDR_TABLES_BASE / CortexM0::PAGE_SIZE,
TABLES_SIZE / CortexM0::PAGE_SIZE, true, mySectionTables.get()) TABLES_SIZE / CortexM0::PAGE_SIZE, strictMode, mySectionTables.get())
.mapRegionDelegate(ADDR_STUB_BASE / CortexM0::PAGE_SIZE, .mapRegionDelegate(ADDR_STUB_BASE / CortexM0::PAGE_SIZE,
STUB_SIZE / CortexM0::PAGE_SIZE, true, &myVcsLib) STUB_SIZE / CortexM0::PAGE_SIZE, true, &myVcsLib)
.mapDefault(&myFallbackDelegate); .mapDefault(&myFallbackDelegate);
myFallbackDelegate.setErrorsAreFatal(strictMode);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -572,7 +640,15 @@ void CartridgeELF::runArm()
} }
} }
if (CortexM0::getErrCustom(err) != ERR_STOP_EXECUTION) if (CortexM0::getErrCustom(err) != ERR_STOP_EXECUTION) {
FatalEmulationError::raise("error executing ARM code: " + CortexM0::describeError(err)); ostringstream s;
s
<< "error executing ARM code (PC = 0x"
<< std::hex << std::setw(8) << std::setfill('0') << myCortexEmu.getRegister(15)
<< "): " << CortexM0::describeError(err);
FatalEmulationError::raise(s.str());
}
} }
} }

View File

@ -69,8 +69,24 @@ class CartridgeELF: public Cartridge {
private: private:
class BusFallbackDelegate: public CortexM0::BusTransactionDelegate { class BusFallbackDelegate: public CortexM0::BusTransactionDelegate {
CortexM0::err_t fetch16(uInt32 address, uInt16& value, uInt8& op, CortexM0& cortex) override; public:
CortexM0::err_t read8(uInt32 address, uInt8& value, CortexM0& cortex) override; CortexM0::err_t fetch16(uInt32 address, uInt16& value, uInt8& op, CortexM0& cortex) override;
CortexM0::err_t read8(uInt32 address, uInt8& value, CortexM0& cortex) override;
CortexM0::err_t read16(uInt32 address, uInt16& value, CortexM0& cortex) override;
CortexM0::err_t read32(uInt32 address, uInt32& value, CortexM0& cortex) override;
CortexM0::err_t write8(uInt32 address, uInt8 value, CortexM0& cortex) override;
CortexM0::err_t write16(uInt32 address, uInt16 value, CortexM0& cortex) override;
CortexM0::err_t write32(uInt32 address, uInt32 value, CortexM0& cortex) override;
void setErrorsAreFatal(bool fatal);
private:
CortexM0::err_t handleError(
string_view accessType, uInt32 address, CortexM0::err_t err, CortexM0& cortex
) const;
private:
bool myErrorsAreFatal{false};
}; };
enum class ExecutionStage: uInt8 { enum class ExecutionStage: uInt8 {
@ -83,7 +99,8 @@ class CartridgeELF: public Cartridge {
uInt8 driveBus(uInt16 address, uInt8 value); uInt8 driveBus(uInt16 address, uInt8 value);
void parseAndLinkElf(); void parseAndLinkElf();
void setupMemoryMap(); void allocationSections();
void setupMemoryMap(bool strictMode);
uInt32 getCoreClock() const; uInt32 getCoreClock() const;

View File

@ -546,9 +546,23 @@ string CortexM0::describeError(err_t err) {
CortexM0::CortexM0() CortexM0::CortexM0()
{ {
myPageMap = make_unique<uInt8[]>(PAGEMAP_SIZE); myPageMap = make_unique<uInt8[]>(PAGEMAP_SIZE);
resetMappings();
reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CortexM0& CortexM0::resetMappings()
{
for (auto& region: myRegions) {
region.type = MemoryRegionType::unmapped;
region.access.emplace<std::monostate>();
}
myNextRegionIndex = 0;
std::fill_n(myPageMap.get(), PAGEMAP_SIZE, 0xff); std::fill_n(myPageMap.get(), PAGEMAP_SIZE, 0xff);
reset(); return *this;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -103,6 +103,8 @@ class CortexM0
CortexM0(); CortexM0();
~CortexM0() = default; ~CortexM0() = default;
CortexM0& resetMappings();
CortexM0& mapRegionData(uInt32 pageBase, uInt32 pageCount, CortexM0& mapRegionData(uInt32 pageBase, uInt32 pageCount,
bool readOnly, uInt8* backingStore); bool readOnly, uInt8* backingStore);
@ -166,7 +168,8 @@ class CortexM0
std::variant< std::variant<
MemoryRegionAccessData, // ::get<0>, directData MemoryRegionAccessData, // ::get<0>, directData
MemoryRegionAccessCode, // ::get<1>, directCode MemoryRegionAccessCode, // ::get<1>, directCode
BusTransactionDelegate* // ::get<2>, delegate BusTransactionDelegate*, // ::get<2>, delegate
std::monostate
> access; > access;
private: private: