arch-power: Add multi-mode support
This adds multi-mode support and allows the simulator to read, interpret and execute 32bit and 64-bit, big and little endian binaries in syscall emulation mode. During process initialization, a minimal set of hardware capabilities are also advertised by the simulator to show support for 64-bit mode and little endian byte order. This also adds some fixups specific to 64-bit ELF ABI v1 that readjust the entry point and symbol table due to the use of function descriptors. Change-Id: I124339eff7b70dbd14e50ff970340c88c13bd0ad Signed-off-by: Sandipan Das <sandipan@linux.ibm.com> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/40944 Reviewed-by: Boris Shingarov <shingarov@labware.com> Maintainer: Boris Shingarov <shingarov@labware.com> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
committed by
Boris Shingarov
parent
be3a57e68a
commit
480effb522
@@ -40,5 +40,5 @@ class PowerEmuLinux(PowerSEWorkload):
|
||||
|
||||
@classmethod
|
||||
def _is_compatible_with(cls, obj):
|
||||
return obj.get_arch() == 'power' and \
|
||||
return obj.get_arch() in ('power', 'power64') and \
|
||||
obj.get_op_sys() in ('linux', 'unknown')
|
||||
|
||||
@@ -50,7 +50,9 @@ class LinuxLoader : public Process::Loader
|
||||
Process *
|
||||
load(const ProcessParams ¶ms, loader::ObjectFile *obj) override
|
||||
{
|
||||
if (obj->getArch() != loader::Power)
|
||||
auto arch = obj->getArch();
|
||||
|
||||
if (arch != loader::Power && arch != loader::Power64)
|
||||
return nullptr;
|
||||
|
||||
auto opsys = obj->getOpSys();
|
||||
@@ -60,7 +62,10 @@ class LinuxLoader : public Process::Loader
|
||||
opsys = loader::Linux;
|
||||
}
|
||||
|
||||
if (opsys != loader::Linux)
|
||||
if ((arch == loader::Power && opsys != loader::Linux) ||
|
||||
(arch == loader::Power64 &&
|
||||
opsys != loader::LinuxPower64ABIv1 &&
|
||||
opsys != loader::LinuxPower64ABIv2))
|
||||
return nullptr;
|
||||
|
||||
return new PowerProcess(params, obj);
|
||||
|
||||
@@ -82,7 +82,55 @@ PowerProcess::initState()
|
||||
{
|
||||
Process::initState();
|
||||
|
||||
argsInit<uint32_t>(PageBytes);
|
||||
if (objFile->getArch() == loader::Power)
|
||||
argsInit<uint32_t>(PageBytes);
|
||||
else
|
||||
argsInit<uint64_t>(PageBytes);
|
||||
|
||||
// Fix up entry point and symbol table for 64-bit ELF ABI v1
|
||||
if (objFile->getOpSys() != loader::LinuxPower64ABIv1)
|
||||
return;
|
||||
|
||||
// Fix entry point address and the base TOC pointer by looking the
|
||||
// the function descriptor in the .opd section
|
||||
Addr entryPoint, tocBase;
|
||||
ByteOrder byteOrder = objFile->getByteOrder();
|
||||
ThreadContext *tc = system->threads[contextIds[0]];
|
||||
|
||||
// The first doubleword of the descriptor contains the address of the
|
||||
// entry point of the function
|
||||
initVirtMem->readBlob(getStartPC(), &entryPoint, sizeof(Addr));
|
||||
|
||||
// Update the PC state
|
||||
auto pc = tc->pcState();
|
||||
pc.byteOrder(byteOrder);
|
||||
pc.set(gtoh(entryPoint, byteOrder));
|
||||
tc->pcState(pc);
|
||||
|
||||
// The second doubleword of the descriptor contains the TOC base
|
||||
// address for the function
|
||||
initVirtMem->readBlob(getStartPC() + 8, &tocBase, sizeof(Addr));
|
||||
tc->setIntReg(TOCPointerReg, gtoh(tocBase, byteOrder));
|
||||
|
||||
// Fix symbol table entries as they would otherwise point to the
|
||||
// function descriptor rather than the actual entry point address
|
||||
auto *symbolTable = new loader::SymbolTable;
|
||||
|
||||
for (auto sym : loader::debugSymbolTable) {
|
||||
Addr entry;
|
||||
loader::Symbol symbol = sym;
|
||||
|
||||
// Try to read entry point from function descriptor
|
||||
if (initVirtMem->tryReadBlob(sym.address, &entry, sizeof(Addr)))
|
||||
symbol.address = gtoh(entry, byteOrder);
|
||||
|
||||
symbolTable->insert(symbol);
|
||||
}
|
||||
|
||||
// Replace the current debug symbol table
|
||||
loader::debugSymbolTable.clear();
|
||||
loader::debugSymbolTable.insert(*symbolTable);
|
||||
delete symbolTable;
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
@@ -91,6 +139,8 @@ PowerProcess::argsInit(int pageSize)
|
||||
{
|
||||
int intSize = sizeof(IntType);
|
||||
ByteOrder byteOrder = objFile->getByteOrder();
|
||||
bool is64bit = (objFile->getArch() == loader::Power64);
|
||||
bool isLittleEndian = (byteOrder == ByteOrder::little);
|
||||
std::vector<gem5::auxv::AuxVector<IntType>> auxv;
|
||||
|
||||
std::string filename;
|
||||
@@ -110,13 +160,21 @@ PowerProcess::argsInit(int pageSize)
|
||||
//Auxilliary vectors are loaded only for elf formatted executables.
|
||||
auto *elfObject = dynamic_cast<loader::ElfObject *>(objFile);
|
||||
if (elfObject) {
|
||||
IntType features = 0;
|
||||
IntType features = HWCAP_FEATURE_32;
|
||||
|
||||
// Check if running in 64-bit mode
|
||||
if (is64bit)
|
||||
features |= HWCAP_FEATURE_64;
|
||||
|
||||
// Check if running in little endian mode
|
||||
if (isLittleEndian)
|
||||
features |= HWCAP_FEATURE_PPC_LE | HWCAP_FEATURE_TRUE_LE;
|
||||
|
||||
//Bits which describe the system hardware capabilities
|
||||
//XXX Figure out what these should be
|
||||
auxv.emplace_back(gem5::auxv::Hwcap, features);
|
||||
//The system page size
|
||||
auxv.emplace_back(gem5::auxv::Pagesz, PowerISA::PageBytes);
|
||||
auxv.emplace_back(gem5::auxv::Pagesz, pageSize);
|
||||
//Frequency at which times() increments
|
||||
auxv.emplace_back(gem5::auxv::Clktck, 0x64);
|
||||
// For statically linked executables, this is the virtual address of
|
||||
@@ -282,7 +340,7 @@ PowerProcess::argsInit(int pageSize)
|
||||
|
||||
//Set the machine status for a typical userspace
|
||||
Msr msr = 0;
|
||||
msr.sf = (intSize == 8);
|
||||
msr.sf = is64bit;
|
||||
msr.hv = 1;
|
||||
msr.ee = 1;
|
||||
msr.pr = 1;
|
||||
@@ -290,10 +348,13 @@ PowerProcess::argsInit(int pageSize)
|
||||
msr.ir = 1;
|
||||
msr.dr = 1;
|
||||
msr.ri = 1;
|
||||
msr.le = (byteOrder == ByteOrder::little);
|
||||
msr.le = isLittleEndian;
|
||||
tc->setIntReg(INTREG_MSR, msr);
|
||||
|
||||
tc->pcState(getStartPC());
|
||||
auto pc = tc->pcState();
|
||||
pc.set(getStartPC());
|
||||
pc.byteOrder(byteOrder);
|
||||
tc->pcState(pc);
|
||||
|
||||
//Align the "stack_min" to a page boundary.
|
||||
memState->setStackMin(roundDown(stack_min, pageSize));
|
||||
|
||||
@@ -53,6 +53,7 @@ const int ArgumentReg3 = 6;
|
||||
const int ArgumentReg4 = 7;
|
||||
const int ArgumentReg5 = 8;
|
||||
const int StackPointerReg = 1;
|
||||
const int TOCPointerReg = 2;
|
||||
|
||||
enum MiscIntRegNums
|
||||
{
|
||||
|
||||
@@ -252,15 +252,8 @@ ElfObject::determineArch()
|
||||
arch = (eclass == ELFCLASS64) ? Riscv64 : Riscv32;
|
||||
} else if (emach == EM_PPC && eclass == ELFCLASS32) {
|
||||
arch = Power;
|
||||
if (edata != ELFDATA2MSB) {
|
||||
fatal("The binary you're trying to load is compiled for "
|
||||
"little endian Power.\ngem5 only supports big "
|
||||
"endian Power. Please recompile your binary.\n");
|
||||
}
|
||||
} else if (emach == EM_PPC64) {
|
||||
fatal("The binary you're trying to load is compiled for 64-bit "
|
||||
"Power. M5\n only supports 32-bit Power. Please "
|
||||
"recompile your binary.\n");
|
||||
} else if (emach == EM_PPC64 && eclass == ELFCLASS64) {
|
||||
arch = Power64;
|
||||
} else {
|
||||
warn("Unknown architecture: %d\n", emach);
|
||||
}
|
||||
@@ -269,6 +262,21 @@ ElfObject::determineArch()
|
||||
void
|
||||
ElfObject::determineOpSys()
|
||||
{
|
||||
// For 64-bit Power, EI_OSABI and EI_ABIVERSION cannot be used to
|
||||
// determine the ABI version used by the ELF object
|
||||
if (ehdr.e_machine == EM_PPC64) {
|
||||
switch (ehdr.e_flags & 0x3) {
|
||||
case 0x1: opSys = LinuxPower64ABIv1; return;
|
||||
case 0x2: opSys = LinuxPower64ABIv2; return;
|
||||
default:
|
||||
if (ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
opSys = LinuxPower64ABIv1;
|
||||
if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
opSys = LinuxPower64ABIv2;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Detect the operating system
|
||||
switch (ehdr.e_ident[EI_OSABI]) {
|
||||
case ELFOSABI_LINUX:
|
||||
|
||||
@@ -66,6 +66,8 @@ archToString(Arch arch)
|
||||
return "thumb";
|
||||
case Power:
|
||||
return "power";
|
||||
case Power64:
|
||||
return "power64";
|
||||
case Riscv64:
|
||||
return "riscv64";
|
||||
case Riscv32:
|
||||
@@ -84,6 +86,8 @@ opSysToString(OpSys op_sys)
|
||||
case Tru64:
|
||||
return "tru64";
|
||||
case Linux:
|
||||
case LinuxPower64ABIv1:
|
||||
case LinuxPower64ABIv2:
|
||||
return "linux";
|
||||
case Solaris:
|
||||
return "solaris";
|
||||
|
||||
@@ -59,6 +59,7 @@ enum Arch
|
||||
Arm,
|
||||
Thumb,
|
||||
Power,
|
||||
Power64,
|
||||
Riscv64,
|
||||
Riscv32
|
||||
};
|
||||
@@ -72,6 +73,8 @@ enum OpSys
|
||||
Linux,
|
||||
Solaris,
|
||||
LinuxArmOABI,
|
||||
LinuxPower64ABIv1,
|
||||
LinuxPower64ABIv2,
|
||||
FreeBSD
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user