f064aa3060
Replace direct call to unserialize() on each SimObject with a pair of calls for better control over initialization in both ckpt and non-ckpt cases. If restoring from a checkpoint, loadState(ckpt) is called on each SimObject. The default implementation simply calls unserialize() if there is a corresponding checkpoint section, so we get backward compatibility for existing objects. However, objects can override loadState() to get other behaviors, e.g., doing other programmed initializations after unserialize(), or complaining if no checkpoint section is found. (Note that the default warning for a missing checkpoint section is now gone.) If not restoring from a checkpoint, we call the new initState() method on each SimObject instead. This provides a hook for state initializations that are only required when *not* restoring from a checkpoint. Given this new framework, do some cleanup of LiveProcess subclasses and X86System, which were (in some cases) emulating initState() behavior in startup via a local flag or (in other cases) erroneously doing initializations in startup() that clobbered state loaded earlier by unserialize().
400 lines
14 KiB
C++
400 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2010 ARM Limited
|
|
* All rights reserved
|
|
*
|
|
* The license below extends only to copyright in the software and shall
|
|
* not be construed as granting a license to any other intellectual
|
|
* property including but not limited to intellectual property relating
|
|
* to a hardware implementation of the functionality of the software
|
|
* licensed hereunder. You may use the software subject to the license
|
|
* terms below provided that you ensure that this notice is replicated
|
|
* unmodified and in its entirety in all distributions of the software,
|
|
* modified or unmodified, in source code or in binary form.
|
|
*
|
|
* Copyright (c) 2007-2008 The Florida State University
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Authors: Stephen Hines
|
|
* Ali Saidi
|
|
*/
|
|
|
|
#include "arch/arm/isa_traits.hh"
|
|
#include "arch/arm/process.hh"
|
|
#include "arch/arm/types.hh"
|
|
#include "base/loader/elf_object.hh"
|
|
#include "base/loader/object_file.hh"
|
|
#include "base/misc.hh"
|
|
#include "cpu/thread_context.hh"
|
|
#include "mem/page_table.hh"
|
|
#include "mem/translating_port.hh"
|
|
#include "sim/process_impl.hh"
|
|
#include "sim/system.hh"
|
|
|
|
using namespace std;
|
|
using namespace ArmISA;
|
|
|
|
ArmLiveProcess::ArmLiveProcess(LiveProcessParams *params, ObjectFile *objFile,
|
|
ObjectFile::Arch _arch)
|
|
: LiveProcess(params, objFile), arch(_arch)
|
|
{
|
|
stack_base = 0xbf000000L;
|
|
|
|
// Set pointer for next thread stack. Reserve 8M for main stack.
|
|
next_thread_stack_base = stack_base - (8 * 1024 * 1024);
|
|
|
|
// Set up break point (Top of Heap)
|
|
brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize();
|
|
brk_point = roundUp(brk_point, VMPageSize);
|
|
|
|
// Set up region for mmaps. For now, start at bottom of kuseg space.
|
|
mmap_start = mmap_end = 0x40000000L;
|
|
}
|
|
|
|
void
|
|
ArmLiveProcess::startup()
|
|
{
|
|
LiveProcess::startup();
|
|
argsInit(MachineBytes, VMPageSize);
|
|
}
|
|
|
|
void
|
|
ArmLiveProcess::copyStringArray32(std::vector<std::string> &strings,
|
|
Addr array_ptr, Addr data_ptr,
|
|
TranslatingPort* memPort)
|
|
{
|
|
Addr data_ptr_swap;
|
|
for (int i = 0; i < strings.size(); ++i) {
|
|
data_ptr_swap = htog(data_ptr);
|
|
memPort->writeBlob(array_ptr, (uint8_t*)&data_ptr_swap,
|
|
sizeof(uint32_t));
|
|
memPort->writeString(data_ptr, strings[i].c_str());
|
|
array_ptr += sizeof(uint32_t);
|
|
data_ptr += strings[i].size() + 1;
|
|
}
|
|
// add NULL terminator
|
|
data_ptr = 0;
|
|
|
|
memPort->writeBlob(array_ptr, (uint8_t*)&data_ptr, sizeof(uint32_t));
|
|
}
|
|
|
|
void
|
|
ArmLiveProcess::argsInit(int intSize, int pageSize)
|
|
{
|
|
typedef AuxVector<uint32_t> auxv_t;
|
|
std::vector<auxv_t> auxv;
|
|
|
|
string filename;
|
|
if (argv.size() < 1)
|
|
filename = "";
|
|
else
|
|
filename = argv[0];
|
|
|
|
//We want 16 byte alignment
|
|
uint64_t align = 16;
|
|
|
|
// load object file into target memory
|
|
objFile->loadSections(initVirtMem);
|
|
|
|
enum ArmCpuFeature {
|
|
Arm_Swp = 1 << 0,
|
|
Arm_Half = 1 << 1,
|
|
Arm_Thumb = 1 << 2,
|
|
Arm_26Bit = 1 << 3,
|
|
Arm_FastMult = 1 << 4,
|
|
Arm_Fpa = 1 << 5,
|
|
Arm_Vfp = 1 << 6,
|
|
Arm_Edsp = 1 << 7,
|
|
Arm_Java = 1 << 8,
|
|
Arm_Iwmmxt = 1 << 9,
|
|
Arm_Crunch = 1 << 10,
|
|
Arm_ThumbEE = 1 << 11,
|
|
Arm_Neon = 1 << 12,
|
|
Arm_Vfpv3 = 1 << 13,
|
|
Arm_Vfpv3d16 = 1 << 14
|
|
};
|
|
|
|
//Setup the auxilliary vectors. These will already have endian conversion.
|
|
//Auxilliary vectors are loaded only for elf formatted executables.
|
|
ElfObject * elfObject = dynamic_cast<ElfObject *>(objFile);
|
|
if (elfObject) {
|
|
uint32_t features =
|
|
Arm_Swp |
|
|
Arm_Half |
|
|
Arm_Thumb |
|
|
// Arm_26Bit |
|
|
Arm_FastMult |
|
|
// Arm_Fpa |
|
|
Arm_Vfp |
|
|
Arm_Edsp |
|
|
// Arm_Java |
|
|
// Arm_Iwmmxt |
|
|
// Arm_Crunch |
|
|
Arm_ThumbEE |
|
|
Arm_Neon |
|
|
Arm_Vfpv3 |
|
|
Arm_Vfpv3d16 |
|
|
0;
|
|
|
|
//Bits which describe the system hardware capabilities
|
|
//XXX Figure out what these should be
|
|
auxv.push_back(auxv_t(M5_AT_HWCAP, features));
|
|
//The system page size
|
|
auxv.push_back(auxv_t(M5_AT_PAGESZ, ArmISA::VMPageSize));
|
|
//Frequency at which times() increments
|
|
auxv.push_back(auxv_t(M5_AT_CLKTCK, 0x64));
|
|
// For statically linked executables, this is the virtual address of the
|
|
// program header tables if they appear in the executable image
|
|
auxv.push_back(auxv_t(M5_AT_PHDR, elfObject->programHeaderTable()));
|
|
// This is the size of a program header entry from the elf file.
|
|
auxv.push_back(auxv_t(M5_AT_PHENT, elfObject->programHeaderSize()));
|
|
// This is the number of program headers from the original elf file.
|
|
auxv.push_back(auxv_t(M5_AT_PHNUM, elfObject->programHeaderCount()));
|
|
//This is the address of the elf "interpreter", It should be set
|
|
//to 0 for regular executables. It should be something else
|
|
//(not sure what) for dynamic libraries.
|
|
auxv.push_back(auxv_t(M5_AT_BASE, 0));
|
|
|
|
//XXX Figure out what this should be.
|
|
auxv.push_back(auxv_t(M5_AT_FLAGS, 0));
|
|
//The entry point to the program
|
|
auxv.push_back(auxv_t(M5_AT_ENTRY, objFile->entryPoint()));
|
|
//Different user and group IDs
|
|
auxv.push_back(auxv_t(M5_AT_UID, uid()));
|
|
auxv.push_back(auxv_t(M5_AT_EUID, euid()));
|
|
auxv.push_back(auxv_t(M5_AT_GID, gid()));
|
|
auxv.push_back(auxv_t(M5_AT_EGID, egid()));
|
|
//Whether to enable "secure mode" in the executable
|
|
auxv.push_back(auxv_t(M5_AT_SECURE, 0));
|
|
|
|
// Pointer to 16 bytes of random data
|
|
auxv.push_back(auxv_t(M5_AT_RANDOM, 0));
|
|
|
|
//The filename of the program
|
|
auxv.push_back(auxv_t(M5_AT_EXECFN, 0));
|
|
//The string "v71" -- ARM v7 architecture
|
|
auxv.push_back(auxv_t(M5_AT_PLATFORM, 0));
|
|
}
|
|
|
|
//Figure out how big the initial stack nedes to be
|
|
|
|
// A sentry NULL void pointer at the top of the stack.
|
|
int sentry_size = intSize;
|
|
|
|
string platform = "v71";
|
|
int platform_size = platform.size() + 1;
|
|
|
|
// Bytes for AT_RANDOM above, we'll just keep them 0
|
|
int aux_random_size = 16; // as per the specification
|
|
|
|
// The aux vectors are put on the stack in two groups. The first group are
|
|
// the vectors that are generated as the elf is loaded. The second group
|
|
// are the ones that were computed ahead of time and include the platform
|
|
// string.
|
|
int aux_data_size = filename.size() + 1;
|
|
|
|
int env_data_size = 0;
|
|
for (int i = 0; i < envp.size(); ++i) {
|
|
env_data_size += envp[i].size() + 1;
|
|
}
|
|
int arg_data_size = 0;
|
|
for (int i = 0; i < argv.size(); ++i) {
|
|
arg_data_size += argv[i].size() + 1;
|
|
}
|
|
|
|
int info_block_size =
|
|
sentry_size + env_data_size + arg_data_size +
|
|
aux_data_size + platform_size + aux_random_size;
|
|
|
|
//Each auxilliary vector is two 4 byte words
|
|
int aux_array_size = intSize * 2 * (auxv.size() + 1);
|
|
|
|
int envp_array_size = intSize * (envp.size() + 1);
|
|
int argv_array_size = intSize * (argv.size() + 1);
|
|
|
|
int argc_size = intSize;
|
|
|
|
//Figure out the size of the contents of the actual initial frame
|
|
int frame_size =
|
|
info_block_size +
|
|
aux_array_size +
|
|
envp_array_size +
|
|
argv_array_size +
|
|
argc_size;
|
|
|
|
//There needs to be padding after the auxiliary vector data so that the
|
|
//very bottom of the stack is aligned properly.
|
|
int partial_size = frame_size;
|
|
int aligned_partial_size = roundUp(partial_size, align);
|
|
int aux_padding = aligned_partial_size - partial_size;
|
|
|
|
int space_needed = frame_size + aux_padding;
|
|
|
|
stack_min = stack_base - space_needed;
|
|
stack_min = roundDown(stack_min, align);
|
|
stack_size = stack_base - stack_min;
|
|
|
|
// map memory
|
|
pTable->allocate(roundDown(stack_min, pageSize),
|
|
roundUp(stack_size, pageSize));
|
|
|
|
// map out initial stack contents
|
|
uint32_t sentry_base = stack_base - sentry_size;
|
|
uint32_t aux_data_base = sentry_base - aux_data_size;
|
|
uint32_t env_data_base = aux_data_base - env_data_size;
|
|
uint32_t arg_data_base = env_data_base - arg_data_size;
|
|
uint32_t platform_base = arg_data_base - platform_size;
|
|
uint32_t aux_random_base = platform_base - aux_random_size;
|
|
uint32_t auxv_array_base = aux_random_base - aux_array_size - aux_padding;
|
|
uint32_t envp_array_base = auxv_array_base - envp_array_size;
|
|
uint32_t argv_array_base = envp_array_base - argv_array_size;
|
|
uint32_t argc_base = argv_array_base - argc_size;
|
|
|
|
DPRINTF(Stack, "The addresses of items on the initial stack:\n");
|
|
DPRINTF(Stack, "0x%x - aux data\n", aux_data_base);
|
|
DPRINTF(Stack, "0x%x - env data\n", env_data_base);
|
|
DPRINTF(Stack, "0x%x - arg data\n", arg_data_base);
|
|
DPRINTF(Stack, "0x%x - random data\n", aux_random_base);
|
|
DPRINTF(Stack, "0x%x - platform base\n", platform_base);
|
|
DPRINTF(Stack, "0x%x - auxv array\n", auxv_array_base);
|
|
DPRINTF(Stack, "0x%x - envp array\n", envp_array_base);
|
|
DPRINTF(Stack, "0x%x - argv array\n", argv_array_base);
|
|
DPRINTF(Stack, "0x%x - argc \n", argc_base);
|
|
DPRINTF(Stack, "0x%x - stack min\n", stack_min);
|
|
|
|
// write contents to stack
|
|
|
|
// figure out argc
|
|
uint32_t argc = argv.size();
|
|
uint32_t guestArgc = ArmISA::htog(argc);
|
|
|
|
//Write out the sentry void *
|
|
uint32_t sentry_NULL = 0;
|
|
initVirtMem->writeBlob(sentry_base,
|
|
(uint8_t*)&sentry_NULL, sentry_size);
|
|
|
|
//Fix up the aux vectors which point to other data
|
|
for (int i = auxv.size() - 1; i >= 0; i--) {
|
|
if (auxv[i].a_type == M5_AT_PLATFORM) {
|
|
auxv[i].a_val = platform_base;
|
|
initVirtMem->writeString(platform_base, platform.c_str());
|
|
} else if (auxv[i].a_type == M5_AT_EXECFN) {
|
|
auxv[i].a_val = aux_data_base;
|
|
initVirtMem->writeString(aux_data_base, filename.c_str());
|
|
} else if (auxv[i].a_type == M5_AT_RANDOM) {
|
|
auxv[i].a_val = aux_random_base;
|
|
// Just leave the value 0, we don't want randomness
|
|
}
|
|
}
|
|
|
|
//Copy the aux stuff
|
|
for(int x = 0; x < auxv.size(); x++)
|
|
{
|
|
initVirtMem->writeBlob(auxv_array_base + x * 2 * intSize,
|
|
(uint8_t*)&(auxv[x].a_type), intSize);
|
|
initVirtMem->writeBlob(auxv_array_base + (x * 2 + 1) * intSize,
|
|
(uint8_t*)&(auxv[x].a_val), intSize);
|
|
}
|
|
//Write out the terminating zeroed auxilliary vector
|
|
const uint64_t zero = 0;
|
|
initVirtMem->writeBlob(auxv_array_base + 2 * intSize * auxv.size(),
|
|
(uint8_t*)&zero, 2 * intSize);
|
|
|
|
copyStringArray(envp, envp_array_base, env_data_base, initVirtMem);
|
|
copyStringArray(argv, argv_array_base, arg_data_base, initVirtMem);
|
|
|
|
initVirtMem->writeBlob(argc_base, (uint8_t*)&guestArgc, intSize);
|
|
|
|
ThreadContext *tc = system->getThreadContext(contextIds[0]);
|
|
//Set the stack pointer register
|
|
tc->setIntReg(StackPointerReg, stack_min);
|
|
//A pointer to a function to run when the program exits. We'll set this
|
|
//to zero explicitly to make sure this isn't used.
|
|
tc->setIntReg(ArgumentReg0, 0);
|
|
//Set argument regs 1 and 2 to argv[0] and envp[0] respectively
|
|
if (argv.size() > 0) {
|
|
tc->setIntReg(ArgumentReg1, arg_data_base + arg_data_size -
|
|
argv[argv.size() - 1].size() - 1);
|
|
} else {
|
|
tc->setIntReg(ArgumentReg1, 0);
|
|
}
|
|
if (envp.size() > 0) {
|
|
tc->setIntReg(ArgumentReg2, env_data_base + env_data_size -
|
|
envp[envp.size() - 1].size() - 1);
|
|
} else {
|
|
tc->setIntReg(ArgumentReg2, 0);
|
|
}
|
|
|
|
Addr prog_entry = objFile->entryPoint();
|
|
if (arch == ObjectFile::Thumb)
|
|
prog_entry = (prog_entry & ~mask(1)) | (ULL(1) << PcTBitShift);
|
|
tc->setPC(prog_entry);
|
|
tc->setNextPC(prog_entry + sizeof(MachInst));
|
|
|
|
//Align the "stack_min" to a page boundary.
|
|
stack_min = roundDown(stack_min, pageSize);
|
|
}
|
|
|
|
ArmISA::IntReg
|
|
ArmLiveProcess::getSyscallArg(ThreadContext *tc, int &i)
|
|
{
|
|
assert(i < 6);
|
|
return tc->readIntReg(ArgumentReg0 + i++);
|
|
}
|
|
|
|
uint64_t
|
|
ArmLiveProcess::getSyscallArg(ThreadContext *tc, int &i, int width)
|
|
{
|
|
assert(width == 32 || width == 64);
|
|
if (width == 32)
|
|
return getSyscallArg(tc, i);
|
|
|
|
// 64 bit arguments are passed starting in an even register
|
|
if (i % 2 != 0)
|
|
i++;
|
|
|
|
// Registers r0-r6 can be used
|
|
assert(i < 5);
|
|
uint64_t val;
|
|
val = tc->readIntReg(ArgumentReg0 + i++);
|
|
val |= ((uint64_t)tc->readIntReg(ArgumentReg0 + i++) << 32);
|
|
return val;
|
|
}
|
|
|
|
|
|
void
|
|
ArmLiveProcess::setSyscallArg(ThreadContext *tc,
|
|
int i, ArmISA::IntReg val)
|
|
{
|
|
assert(i < 4);
|
|
tc->setIntReg(ArgumentReg0 + i, val);
|
|
}
|
|
|
|
void
|
|
ArmLiveProcess::setSyscallReturn(ThreadContext *tc,
|
|
SyscallReturn return_value)
|
|
{
|
|
tc->setIntReg(ReturnValueReg, return_value.value());
|
|
}
|