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:
Sandipan Das
2021-02-06 17:39:51 +05:30
committed by Boris Shingarov
parent be3a57e68a
commit 480effb522
7 changed files with 100 additions and 18 deletions

View File

@@ -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')

View File

@@ -50,7 +50,9 @@ class LinuxLoader : public Process::Loader
Process *
load(const ProcessParams &params, 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);

View File

@@ -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));

View File

@@ -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
{

View File

@@ -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:

View File

@@ -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";

View File

@@ -59,6 +59,7 @@ enum Arch
Arm,
Thumb,
Power,
Power64,
Riscv64,
Riscv32
};
@@ -72,6 +73,8 @@ enum OpSys
Linux,
Solaris,
LinuxArmOABI,
LinuxPower64ABIv1,
LinuxPower64ABIv2,
FreeBSD
};