fxos/lib/os.cpp

246 lines
6.0 KiB
C++

#include <fxos/os.h>
#include <fxos/vspace.h>
#include <fxos/memory.h>
#include <fxos/util/log.h>
#include <stdexcept>
#include <cstring>
namespace FxOS {
OS::OS(VirtualSpace &space): type {UNKNOWN}, m_space {space}
{
if(!space.covers(0x80000000, (256 << 10))) {
FxOS_log(ERR,
"OS analysis failed: space doesn't have at least 256 kB "
"of ROM to analyze from");
return;
}
/* Detect OS type by heuristic */
if(space.read_str(0x80010000, 8).value == "CASIOWIN")
this->type = FX;
else if(space.read_str(0x80020000, 8).value == "CASIOWIN")
this->type = CG;
else {
FxOS_log(ERR, "OS analysis failed: cannot determine OS type");
return;
}
parse_header();
parse_syscall_table();
parse_footer();
}
void OS::parse_header()
{
VirtualSpace &s = m_space;
if(this->type == FX) {
/* Bootcode timestamp at the very end of the bootcode */
this->bootcode_timestamp = s.read_str(0x8000ffb0, 14);
this->bootcode_checksum = s.read_u32(0x8000fffc);
this->version = s.read_str(0x80010020, 10);
this->serial_number = s.read_str(0x8000ffd0, 8);
}
else if(this->type == CG) {
this->bootcode_timestamp = s.read_str(0x8001ffb0, 14);
this->bootcode_checksum = s.read_u32(0x8001fffc);
this->version = s.read_str(0x80020020, 10);
this->serial_number = s.read_str(0x8001ffd0, 8);
}
/* Version has format MM.mm.pppp */
version_major = std::stoi(this->version.value.substr(0, 2));
version_minor = std::stoi(this->version.value.substr(3, 2));
version_patch = std::stoi(this->version.value.substr(6, 4));
}
bool OS::version_lt(int major, int minor, int patch) const noexcept
{
if(this->version_major < major)
return true;
if(this->version_major > major)
return false;
if(this->version_minor < minor)
return true;
if(this->version_minor > minor)
return false;
return (this->version_patch < patch);
}
bool OS::version_le(int major, int minor, int patch) const noexcept
{
if(this->version_major < major)
return true;
if(this->version_major > major)
return false;
if(this->version_minor < minor)
return true;
if(this->version_minor > minor)
return false;
return (this->version_patch <= patch);
}
bool OS::version_gt(int major, int minor, int patch) const noexcept
{
if(this->version_major < major)
return false;
if(this->version_major > major)
return true;
if(this->version_minor < minor)
return false;
if(this->version_minor > minor)
return true;
return (this->version_patch > patch);
}
bool OS::version_ge(int major, int minor, int patch) const noexcept
{
if(this->version_major < major)
return false;
if(this->version_major > major)
return true;
if(this->version_minor < minor)
return false;
if(this->version_minor > minor)
return true;
return (this->version_patch >= patch);
}
//---
// Syscall resolution
//---
int OS::syscall_count() const noexcept
{
return m_syscall_table.size();
}
uint32_t OS::syscall(int id) const
{
return m_syscall_table[id];
}
int OS::find_syscall(uint32_t entry) const noexcept
{
try {
return m_syscall_addresses.at(entry);
}
catch(std::out_of_range &e) {
return -1;
}
}
uint32_t OS::syscall_table_address() const noexcept
{
uint32_t address = (this->type == FX) ? 0x8001007c : 0x8002007c;
return m_space.read_u32(address);
}
void OS::parse_syscall_table()
{
/* Traverse the syscall table */
uint32_t syscall_table = syscall_table_address();
int id = 0;
while(1) {
uint32_t entry = m_space.read_u32(syscall_table + 4 * id);
MemoryRegion const *r = MemoryRegion::region_for(entry);
if(!r)
break;
m_syscall_table.push_back(entry);
m_syscall_addresses[entry] = id;
id++;
}
}
//---
// Footer search
//---
void OS::parse_footer()
{
VirtualSpace &s = m_space;
/* Find the footer address (occurrence of "CASIOABSLangdata") */
uint32_t start = MemoryRegion::ROM.start;
uint32_t end = MemoryRegion::ROM.end;
this->footer = s.search(start, end, "CASIOABSLangdata", 16);
if(this->footer == end) {
this->footer = -1;
this->timestamp = std::string("");
this->langdata = 0;
this->computed_checksum = -1;
return;
}
uint32_t addr = this->footer + 8;
this->langdata = 0;
while(1) {
void const *entry = s.translate(addr, 8);
if(!entry || memcmp(entry, "Langdata", 8) != 0)
break;
this->langdata++;
addr += 0x30;
}
this->timestamp = s.read_str(addr, 14);
this->checksum = s.read_u32(addr + 0x18);
this->computed_checksum = this->compute_checksum();
}
//---
// Checksum
//---
static uint32_t accumulate_range(
VirtualSpace &m_space, uint32_t start, uint32_t end)
{
uint32_t sum = 0;
/* Read from dynamically-sized bindings; read_u8() would be too slow */
while(start < end) {
int size;
uint8_t *buf = (uint8_t *)m_space.translate_dynamic(start, &size);
if(!buf || size <= 0)
return -1;
if(start + size > end)
size = end - start;
for(int i = 0; i < size; i++)
sum += buf[i];
start += size;
}
return sum;
}
uint32_t OS::compute_checksum() const
{
if(this->type == FX) {
return accumulate_range(m_space, 0x80010000, this->checksum.address);
}
else if(this->type == CG) {
if(this->version_lt(2, 2, 0)) {
return accumulate_range(m_space, 0x80020000, 0x80b5feb0)
+ accumulate_range(m_space, 0x80b5fef0, 0x80b5fff8);
}
else {
return accumulate_range(m_space, 0x80020000, 0x80b20000)
+ accumulate_range(
m_space, this->footer + 8, this->checksum.address);
}
}
return -1;
}
} /* namespace FxOS */