From ae0458fc2ed2f5c3aa90c92c38d6053824b854d4 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Sat, 6 Feb 2021 17:39:51 +0530 Subject: [PATCH] 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 --- src/arch/power/PowerSeWorkload.py | 2 +- src/arch/power/linux/se_workload.cc | 9 +++- src/arch/power/process.cc | 73 ++++++++++++++++++++++++++--- src/arch/power/registers.hh | 1 + src/base/loader/elf_object.cc | 26 ++++++---- src/base/loader/object_file.cc | 4 ++ src/base/loader/object_file.hh | 3 ++ 7 files changed, 100 insertions(+), 18 deletions(-) diff --git a/src/arch/power/PowerSeWorkload.py b/src/arch/power/PowerSeWorkload.py index 2d3d3cb7f..40e989c0e 100644 --- a/src/arch/power/PowerSeWorkload.py +++ b/src/arch/power/PowerSeWorkload.py @@ -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') diff --git a/src/arch/power/linux/se_workload.cc b/src/arch/power/linux/se_workload.cc index 864468ff6..091fd35b8 100644 --- a/src/arch/power/linux/se_workload.cc +++ b/src/arch/power/linux/se_workload.cc @@ -47,7 +47,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(); @@ -57,7 +59,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); diff --git a/src/arch/power/process.cc b/src/arch/power/process.cc index 409f980ae..735b3d9de 100644 --- a/src/arch/power/process.cc +++ b/src/arch/power/process.cc @@ -76,7 +76,55 @@ PowerProcess::initState() { Process::initState(); - argsInit(PageBytes); + if (objFile->getArch() == ::Loader::Power) + argsInit(PageBytes); + else + argsInit(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 @@ -85,6 +133,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> auxv; std::string filename; @@ -104,13 +154,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 = PPC_FEATURE_32; + + // Check if running in 64-bit mode + if (is64bit) + features |= PPC_FEATURE_64; + + // Check if running in little endian mode + if (isLittleEndian) + features |= PPC_FEATURE_PPC_LE | PPC_FEATURE_TRUE_LE; //Bits which describe the system hardware capabilities //XXX Figure out what these should be auxv.emplace_back(M5_AT_HWCAP, features); //The system page size - auxv.emplace_back(M5_AT_PAGESZ, PowerISA::PageBytes); + auxv.emplace_back(M5_AT_PAGESZ, pageSize); //Frequency at which times() increments auxv.emplace_back(M5_AT_CLKTCK, 0x64); // For statically linked executables, this is the virtual address of @@ -276,7 +334,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; @@ -284,10 +342,13 @@ PowerProcess::argsInit(int pageSize) msr.ir = 1; msr.dr = 1; msr.ri = 1; - msr.le = (byteOrder == ByteOrder::little); + msr.le = isLittleEndian; tc->setMiscReg(MISCREG_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)); diff --git a/src/arch/power/registers.hh b/src/arch/power/registers.hh index c9bdf1c8b..2745a92c2 100644 --- a/src/arch/power/registers.hh +++ b/src/arch/power/registers.hh @@ -85,6 +85,7 @@ const int ArgumentReg3 = 6; const int ArgumentReg4 = 7; const int ArgumentReg5 = 8; const int StackPointerReg = 1; +const int TOCPointerReg = 2; // There isn't one in Power, but we need to define one somewhere const int ZeroReg = NumIntRegs - 1; diff --git a/src/base/loader/elf_object.cc b/src/base/loader/elf_object.cc index 419887536..66bd066f2 100644 --- a/src/base/loader/elf_object.cc +++ b/src/base/loader/elf_object.cc @@ -247,15 +247,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); } @@ -264,6 +257,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: diff --git a/src/base/loader/object_file.cc b/src/base/loader/object_file.cc index 6fdf228d4..d8075a520 100644 --- a/src/base/loader/object_file.cc +++ b/src/base/loader/object_file.cc @@ -62,6 +62,8 @@ archToString(Arch arch) return "thumb"; case Power: return "power"; + case Power64: + return "power64"; case Riscv64: return "riscv64"; case Riscv32: @@ -80,6 +82,8 @@ opSysToString(OpSys op_sys) case Tru64: return "tru64"; case Linux: + case LinuxPower64ABIv1: + case LinuxPower64ABIv2: return "linux"; case Solaris: return "solaris"; diff --git a/src/base/loader/object_file.hh b/src/base/loader/object_file.hh index 7d542a98e..19d9bd6fe 100644 --- a/src/base/loader/object_file.hh +++ b/src/base/loader/object_file.hh @@ -53,6 +53,7 @@ enum Arch { Arm, Thumb, Power, + Power64, Riscv64, Riscv32 }; @@ -65,6 +66,8 @@ enum OpSys { Linux, Solaris, LinuxArmOABI, + LinuxPower64ABIv1, + LinuxPower64ABIv2, FreeBSD }; -- 2.30.2