When accessing arguments for a syscall, the position of an argument depends on the policies of the ISA, how much space preceding arguments took up, and the "alignment" of the index for this particular argument into the number of possible storate locations. This change adjusts getSyscallArg to take its index parameter by reference instead of value and to adjust it to point to the possible location of the next argument on the stack, basically just after the current one. This way, the rules for the new argument can be applied locally without knowing about other arguments since those have already been taken into account implicitly. All system calls have also been changed to reflect the new interface. In a number of cases this made the implementation clearer since it encourages arguments to be collected in one place in order and then used as necessary later, as opposed to scattering them throughout the function or using them in place in long expressions. It also discourages using getSyscallArg over and over to retrieve the same value when a temporary would do the job.
794 lines
23 KiB
C++
794 lines
23 KiB
C++
/*
|
|
* Copyright (c) 2001-2005 The Regents of The University of Michigan
|
|
* 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: Nathan Binkert
|
|
* Steve Reinhardt
|
|
* Ali Saidi
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string>
|
|
|
|
#include "arch/remote_gdb.hh"
|
|
#include "base/intmath.hh"
|
|
#include "base/loader/object_file.hh"
|
|
#include "base/loader/symtab.hh"
|
|
#include "base/statistics.hh"
|
|
#include "config/full_system.hh"
|
|
#include "config/the_isa.hh"
|
|
#include "cpu/thread_context.hh"
|
|
#include "mem/page_table.hh"
|
|
#include "mem/physical.hh"
|
|
#include "mem/translating_port.hh"
|
|
#include "params/Process.hh"
|
|
#include "params/LiveProcess.hh"
|
|
#include "sim/debug.hh"
|
|
#include "sim/process.hh"
|
|
#include "sim/process_impl.hh"
|
|
#include "sim/stats.hh"
|
|
#include "sim/syscall_emul.hh"
|
|
#include "sim/system.hh"
|
|
|
|
#if THE_ISA == ALPHA_ISA
|
|
#include "arch/alpha/linux/process.hh"
|
|
#include "arch/alpha/tru64/process.hh"
|
|
#elif THE_ISA == SPARC_ISA
|
|
#include "arch/sparc/linux/process.hh"
|
|
#include "arch/sparc/solaris/process.hh"
|
|
#elif THE_ISA == MIPS_ISA
|
|
#include "arch/mips/linux/process.hh"
|
|
#elif THE_ISA == ARM_ISA
|
|
#include "arch/arm/linux/process.hh"
|
|
#elif THE_ISA == X86_ISA
|
|
#include "arch/x86/linux/process.hh"
|
|
#elif THE_ISA == POWER_ISA
|
|
#include "arch/power/linux/process.hh"
|
|
#else
|
|
#error "THE_ISA not set"
|
|
#endif
|
|
|
|
|
|
using namespace std;
|
|
using namespace TheISA;
|
|
|
|
//
|
|
// The purpose of this code is to fake the loader & syscall mechanism
|
|
// when there's no OS: thus there's no resone to use it in FULL_SYSTEM
|
|
// mode when we do have an OS
|
|
//
|
|
#if FULL_SYSTEM
|
|
#error "process.cc not compatible with FULL_SYSTEM"
|
|
#endif
|
|
|
|
// current number of allocated processes
|
|
int num_processes = 0;
|
|
|
|
template<class IntType>
|
|
AuxVector<IntType>::AuxVector(IntType type, IntType val)
|
|
{
|
|
a_type = TheISA::htog(type);
|
|
a_val = TheISA::htog(val);
|
|
}
|
|
|
|
template class AuxVector<uint32_t>;
|
|
template class AuxVector<uint64_t>;
|
|
|
|
Process::Process(ProcessParams * params)
|
|
: SimObject(params), system(params->system), checkpointRestored(false),
|
|
max_stack_size(params->max_stack_size)
|
|
{
|
|
string in = params->input;
|
|
string out = params->output;
|
|
string err = params->errout;
|
|
|
|
// initialize file descriptors to default: same as simulator
|
|
int stdin_fd, stdout_fd, stderr_fd;
|
|
|
|
if (in == "stdin" || in == "cin")
|
|
stdin_fd = STDIN_FILENO;
|
|
else if (in == "None")
|
|
stdin_fd = -1;
|
|
else
|
|
stdin_fd = Process::openInputFile(in);
|
|
|
|
if (out == "stdout" || out == "cout")
|
|
stdout_fd = STDOUT_FILENO;
|
|
else if (out == "stderr" || out == "cerr")
|
|
stdout_fd = STDERR_FILENO;
|
|
else if (out == "None")
|
|
stdout_fd = -1;
|
|
else
|
|
stdout_fd = Process::openOutputFile(out);
|
|
|
|
if (err == "stdout" || err == "cout")
|
|
stderr_fd = STDOUT_FILENO;
|
|
else if (err == "stderr" || err == "cerr")
|
|
stderr_fd = STDERR_FILENO;
|
|
else if (err == "None")
|
|
stderr_fd = -1;
|
|
else if (err == out)
|
|
stderr_fd = stdout_fd;
|
|
else
|
|
stderr_fd = Process::openOutputFile(err);
|
|
|
|
M5_pid = system->allocatePID();
|
|
// initialize first 3 fds (stdin, stdout, stderr)
|
|
Process::FdMap *fdo = &fd_map[STDIN_FILENO];
|
|
fdo->fd = stdin_fd;
|
|
fdo->filename = in;
|
|
fdo->flags = O_RDONLY;
|
|
fdo->mode = -1;
|
|
fdo->fileOffset = 0;
|
|
|
|
fdo = &fd_map[STDOUT_FILENO];
|
|
fdo->fd = stdout_fd;
|
|
fdo->filename = out;
|
|
fdo->flags = O_WRONLY | O_CREAT | O_TRUNC;
|
|
fdo->mode = 0774;
|
|
fdo->fileOffset = 0;
|
|
|
|
fdo = &fd_map[STDERR_FILENO];
|
|
fdo->fd = stderr_fd;
|
|
fdo->filename = err;
|
|
fdo->flags = O_WRONLY;
|
|
fdo->mode = -1;
|
|
fdo->fileOffset = 0;
|
|
|
|
|
|
// mark remaining fds as free
|
|
for (int i = 3; i <= MAX_FD; ++i) {
|
|
Process::FdMap *fdo = &fd_map[i];
|
|
fdo->fd = -1;
|
|
}
|
|
|
|
mmap_start = mmap_end = 0;
|
|
nxm_start = nxm_end = 0;
|
|
pTable = new PageTable(this);
|
|
// other parameters will be initialized when the program is loaded
|
|
}
|
|
|
|
|
|
void
|
|
Process::regStats()
|
|
{
|
|
using namespace Stats;
|
|
|
|
num_syscalls
|
|
.name(name() + ".PROG:num_syscalls")
|
|
.desc("Number of system calls")
|
|
;
|
|
}
|
|
|
|
//
|
|
// static helper functions
|
|
//
|
|
int
|
|
Process::openInputFile(const string &filename)
|
|
{
|
|
int fd = open(filename.c_str(), O_RDONLY);
|
|
|
|
if (fd == -1) {
|
|
perror(NULL);
|
|
cerr << "unable to open \"" << filename << "\" for reading\n";
|
|
fatal("can't open input file");
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
|
|
int
|
|
Process::openOutputFile(const string &filename)
|
|
{
|
|
int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0664);
|
|
|
|
if (fd == -1) {
|
|
perror(NULL);
|
|
cerr << "unable to open \"" << filename << "\" for writing\n";
|
|
fatal("can't open output file");
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
ThreadContext *
|
|
Process::findFreeContext()
|
|
{
|
|
int size = contextIds.size();
|
|
ThreadContext *tc;
|
|
for (int i = 0; i < size; ++i) {
|
|
tc = system->getThreadContext(contextIds[i]);
|
|
if (tc->status() == ThreadContext::Halted) {
|
|
// inactive context, free to use
|
|
return tc;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
Process::startup()
|
|
{
|
|
if (contextIds.empty())
|
|
fatal("Process %s is not associated with any HW contexts!\n", name());
|
|
|
|
// first thread context for this process... initialize & enable
|
|
ThreadContext *tc = system->getThreadContext(contextIds[0]);
|
|
|
|
// mark this context as active so it will start ticking.
|
|
tc->activate(0);
|
|
|
|
Port *mem_port;
|
|
mem_port = system->physmem->getPort("functional");
|
|
initVirtMem = new TranslatingPort("process init port", this,
|
|
TranslatingPort::Always);
|
|
mem_port->setPeer(initVirtMem);
|
|
initVirtMem->setPeer(mem_port);
|
|
}
|
|
|
|
// map simulator fd sim_fd to target fd tgt_fd
|
|
void
|
|
Process::dup_fd(int sim_fd, int tgt_fd)
|
|
{
|
|
if (tgt_fd < 0 || tgt_fd > MAX_FD)
|
|
panic("Process::dup_fd tried to dup past MAX_FD (%d)", tgt_fd);
|
|
|
|
Process::FdMap *fdo = &fd_map[tgt_fd];
|
|
fdo->fd = sim_fd;
|
|
}
|
|
|
|
|
|
// generate new target fd for sim_fd
|
|
int
|
|
Process::alloc_fd(int sim_fd, string filename, int flags, int mode, bool pipe)
|
|
{
|
|
// in case open() returns an error, don't allocate a new fd
|
|
if (sim_fd == -1)
|
|
return -1;
|
|
|
|
// find first free target fd
|
|
for (int free_fd = 0; free_fd <= MAX_FD; ++free_fd) {
|
|
Process::FdMap *fdo = &fd_map[free_fd];
|
|
if (fdo->fd == -1) {
|
|
fdo->fd = sim_fd;
|
|
fdo->filename = filename;
|
|
fdo->mode = mode;
|
|
fdo->fileOffset = 0;
|
|
fdo->flags = flags;
|
|
fdo->isPipe = pipe;
|
|
fdo->readPipeSource = 0;
|
|
return free_fd;
|
|
}
|
|
}
|
|
|
|
panic("Process::alloc_fd: out of file descriptors!");
|
|
}
|
|
|
|
|
|
// free target fd (e.g., after close)
|
|
void
|
|
Process::free_fd(int tgt_fd)
|
|
{
|
|
Process::FdMap *fdo = &fd_map[tgt_fd];
|
|
if (fdo->fd == -1)
|
|
warn("Process::free_fd: request to free unused fd %d", tgt_fd);
|
|
|
|
fdo->fd = -1;
|
|
fdo->filename = "NULL";
|
|
fdo->mode = 0;
|
|
fdo->fileOffset = 0;
|
|
fdo->flags = 0;
|
|
fdo->isPipe = false;
|
|
fdo->readPipeSource = 0;
|
|
}
|
|
|
|
|
|
// look up simulator fd for given target fd
|
|
int
|
|
Process::sim_fd(int tgt_fd)
|
|
{
|
|
if (tgt_fd > MAX_FD)
|
|
return -1;
|
|
|
|
return fd_map[tgt_fd].fd;
|
|
}
|
|
|
|
Process::FdMap *
|
|
Process::sim_fd_obj(int tgt_fd)
|
|
{
|
|
if (tgt_fd > MAX_FD)
|
|
panic("sim_fd_obj called in fd out of range.");
|
|
|
|
return &fd_map[tgt_fd];
|
|
}
|
|
bool
|
|
Process::checkAndAllocNextPage(Addr vaddr)
|
|
{
|
|
// if this is an initial write we might not have
|
|
if (vaddr >= stack_min && vaddr < stack_base) {
|
|
pTable->allocate(roundDown(vaddr, VMPageSize), VMPageSize);
|
|
return true;
|
|
}
|
|
|
|
// We've accessed the next page of the stack, so extend the stack
|
|
// to cover it.
|
|
if (vaddr < stack_min && vaddr >= stack_base - max_stack_size) {
|
|
while (vaddr < stack_min) {
|
|
stack_min -= TheISA::PageBytes;
|
|
if(stack_base - stack_min > max_stack_size)
|
|
fatal("Maximum stack size exceeded\n");
|
|
if(stack_base - stack_min > 8*1024*1024)
|
|
fatal("Over max stack size for one thread\n");
|
|
pTable->allocate(stack_min, TheISA::PageBytes);
|
|
inform("Increasing stack size by one page.");
|
|
};
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// find all offsets for currently open files and save them
|
|
void
|
|
Process::fix_file_offsets() {
|
|
Process::FdMap *fdo_stdin = &fd_map[STDIN_FILENO];
|
|
Process::FdMap *fdo_stdout = &fd_map[STDOUT_FILENO];
|
|
Process::FdMap *fdo_stderr = &fd_map[STDERR_FILENO];
|
|
string in = fdo_stdin->filename;
|
|
string out = fdo_stdout->filename;
|
|
string err = fdo_stderr->filename;
|
|
|
|
// initialize file descriptors to default: same as simulator
|
|
int stdin_fd, stdout_fd, stderr_fd;
|
|
|
|
if (in == "stdin" || in == "cin")
|
|
stdin_fd = STDIN_FILENO;
|
|
else if (in == "None")
|
|
stdin_fd = -1;
|
|
else{
|
|
//OPEN standard in and seek to the right location
|
|
stdin_fd = Process::openInputFile(in);
|
|
if (lseek(stdin_fd, fdo_stdin->fileOffset, SEEK_SET) < 0)
|
|
panic("Unable to seek to correct location in file: %s", in);
|
|
}
|
|
|
|
if (out == "stdout" || out == "cout")
|
|
stdout_fd = STDOUT_FILENO;
|
|
else if (out == "stderr" || out == "cerr")
|
|
stdout_fd = STDERR_FILENO;
|
|
else if (out == "None")
|
|
stdout_fd = -1;
|
|
else{
|
|
stdout_fd = Process::openOutputFile(out);
|
|
if (lseek(stdout_fd, fdo_stdout->fileOffset, SEEK_SET) < 0)
|
|
panic("Unable to seek to correct location in file: %s", out);
|
|
}
|
|
|
|
if (err == "stdout" || err == "cout")
|
|
stderr_fd = STDOUT_FILENO;
|
|
else if (err == "stderr" || err == "cerr")
|
|
stderr_fd = STDERR_FILENO;
|
|
else if (err == "None")
|
|
stderr_fd = -1;
|
|
else if (err == out)
|
|
stderr_fd = stdout_fd;
|
|
else {
|
|
stderr_fd = Process::openOutputFile(err);
|
|
if (lseek(stderr_fd, fdo_stderr->fileOffset, SEEK_SET) < 0)
|
|
panic("Unable to seek to correct location in file: %s", err);
|
|
}
|
|
|
|
fdo_stdin->fd = stdin_fd;
|
|
fdo_stdout->fd = stdout_fd;
|
|
fdo_stderr->fd = stderr_fd;
|
|
|
|
|
|
for (int free_fd = 3; free_fd <= MAX_FD; ++free_fd) {
|
|
Process::FdMap *fdo = &fd_map[free_fd];
|
|
if (fdo->fd != -1) {
|
|
if (fdo->isPipe){
|
|
if (fdo->filename == "PIPE-WRITE")
|
|
continue;
|
|
else {
|
|
assert (fdo->filename == "PIPE-READ");
|
|
//create a new pipe
|
|
int fds[2];
|
|
int pipe_retval = pipe(fds);
|
|
|
|
if (pipe_retval < 0) {
|
|
// error
|
|
panic("Unable to create new pipe.");
|
|
}
|
|
fdo->fd = fds[0]; //set read pipe
|
|
Process::FdMap *fdo_write = &fd_map[fdo->readPipeSource];
|
|
if (fdo_write->filename != "PIPE-WRITE")
|
|
panic ("Couldn't find write end of the pipe");
|
|
|
|
fdo_write->fd = fds[1];//set write pipe
|
|
}
|
|
} else {
|
|
//Open file
|
|
int fd = open(fdo->filename.c_str(), fdo->flags, fdo->mode);
|
|
|
|
if (fd == -1)
|
|
panic("Unable to open file: %s", fdo->filename);
|
|
fdo->fd = fd;
|
|
|
|
//Seek to correct location before checkpoint
|
|
if (lseek(fd,fdo->fileOffset, SEEK_SET) < 0)
|
|
panic("Unable to seek to correct location in file: %s", fdo->filename);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void
|
|
Process::find_file_offsets(){
|
|
for (int free_fd = 0; free_fd <= MAX_FD; ++free_fd) {
|
|
Process::FdMap *fdo = &fd_map[free_fd];
|
|
if (fdo->fd != -1) {
|
|
fdo->fileOffset = lseek(fdo->fd, 0, SEEK_CUR);
|
|
} else {
|
|
fdo->filename = "NULL";
|
|
fdo->fileOffset = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Process::setReadPipeSource(int read_pipe_fd, int source_fd){
|
|
Process::FdMap *fdo = &fd_map[read_pipe_fd];
|
|
fdo->readPipeSource = source_fd;
|
|
}
|
|
|
|
void
|
|
Process::FdMap::serialize(std::ostream &os)
|
|
{
|
|
SERIALIZE_SCALAR(fd);
|
|
SERIALIZE_SCALAR(isPipe);
|
|
SERIALIZE_SCALAR(filename);
|
|
SERIALIZE_SCALAR(flags);
|
|
SERIALIZE_SCALAR(readPipeSource);
|
|
SERIALIZE_SCALAR(fileOffset);
|
|
}
|
|
|
|
void
|
|
Process::FdMap::unserialize(Checkpoint *cp, const std::string §ion)
|
|
{
|
|
UNSERIALIZE_SCALAR(fd);
|
|
UNSERIALIZE_SCALAR(isPipe);
|
|
UNSERIALIZE_SCALAR(filename);
|
|
UNSERIALIZE_SCALAR(flags);
|
|
UNSERIALIZE_SCALAR(readPipeSource);
|
|
UNSERIALIZE_SCALAR(fileOffset);
|
|
}
|
|
|
|
void
|
|
Process::serialize(std::ostream &os)
|
|
{
|
|
SERIALIZE_SCALAR(initialContextLoaded);
|
|
SERIALIZE_SCALAR(brk_point);
|
|
SERIALIZE_SCALAR(stack_base);
|
|
SERIALIZE_SCALAR(stack_size);
|
|
SERIALIZE_SCALAR(stack_min);
|
|
SERIALIZE_SCALAR(next_thread_stack_base);
|
|
SERIALIZE_SCALAR(mmap_start);
|
|
SERIALIZE_SCALAR(mmap_end);
|
|
SERIALIZE_SCALAR(nxm_start);
|
|
SERIALIZE_SCALAR(nxm_end);
|
|
find_file_offsets();
|
|
pTable->serialize(os);
|
|
for (int x = 0; x <= MAX_FD; x++) {
|
|
nameOut(os, csprintf("%s.FdMap%d", name(), x));
|
|
fd_map[x].serialize(os);
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
Process::unserialize(Checkpoint *cp, const std::string §ion)
|
|
{
|
|
UNSERIALIZE_SCALAR(initialContextLoaded);
|
|
UNSERIALIZE_SCALAR(brk_point);
|
|
UNSERIALIZE_SCALAR(stack_base);
|
|
UNSERIALIZE_SCALAR(stack_size);
|
|
UNSERIALIZE_SCALAR(stack_min);
|
|
UNSERIALIZE_SCALAR(next_thread_stack_base);
|
|
UNSERIALIZE_SCALAR(mmap_start);
|
|
UNSERIALIZE_SCALAR(mmap_end);
|
|
UNSERIALIZE_SCALAR(nxm_start);
|
|
UNSERIALIZE_SCALAR(nxm_end);
|
|
pTable->unserialize(cp, section);
|
|
for (int x = 0; x <= MAX_FD; x++) {
|
|
fd_map[x].unserialize(cp, csprintf("%s.FdMap%d", section, x));
|
|
}
|
|
fix_file_offsets();
|
|
|
|
checkpointRestored = true;
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LiveProcess member definitions
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
LiveProcess::LiveProcess(LiveProcessParams * params, ObjectFile *_objFile)
|
|
: Process(params), objFile(_objFile),
|
|
argv(params->cmd), envp(params->env), cwd(params->cwd)
|
|
{
|
|
__uid = params->uid;
|
|
__euid = params->euid;
|
|
__gid = params->gid;
|
|
__egid = params->egid;
|
|
__pid = params->pid;
|
|
__ppid = params->ppid;
|
|
|
|
prog_fname = params->cmd[0];
|
|
|
|
// load up symbols, if any... these may be used for debugging or
|
|
// profiling.
|
|
if (!debugSymbolTable) {
|
|
debugSymbolTable = new SymbolTable();
|
|
if (!objFile->loadGlobalSymbols(debugSymbolTable) ||
|
|
!objFile->loadLocalSymbols(debugSymbolTable)) {
|
|
// didn't load any symbols
|
|
delete debugSymbolTable;
|
|
debugSymbolTable = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LiveProcess::argsInit(int intSize, int pageSize)
|
|
{
|
|
Process::startup();
|
|
|
|
// load object file into target memory
|
|
objFile->loadSections(initVirtMem);
|
|
|
|
// Calculate how much space we need for arg & env arrays.
|
|
int argv_array_size = intSize * (argv.size() + 1);
|
|
int envp_array_size = intSize * (envp.size() + 1);
|
|
int arg_data_size = 0;
|
|
for (vector<string>::size_type i = 0; i < argv.size(); ++i) {
|
|
arg_data_size += argv[i].size() + 1;
|
|
}
|
|
int env_data_size = 0;
|
|
for (vector<string>::size_type i = 0; i < envp.size(); ++i) {
|
|
env_data_size += envp[i].size() + 1;
|
|
}
|
|
|
|
int space_needed =
|
|
argv_array_size + envp_array_size + arg_data_size + env_data_size;
|
|
if (space_needed < 32*1024)
|
|
space_needed = 32*1024;
|
|
|
|
// set bottom of stack
|
|
stack_min = stack_base - space_needed;
|
|
// align it
|
|
stack_min = roundDown(stack_min, pageSize);
|
|
stack_size = stack_base - stack_min;
|
|
// map memory
|
|
pTable->allocate(stack_min, roundUp(stack_size, pageSize));
|
|
|
|
// map out initial stack contents
|
|
Addr argv_array_base = stack_min + intSize; // room for argc
|
|
Addr envp_array_base = argv_array_base + argv_array_size;
|
|
Addr arg_data_base = envp_array_base + envp_array_size;
|
|
Addr env_data_base = arg_data_base + arg_data_size;
|
|
|
|
// write contents to stack
|
|
uint64_t argc = argv.size();
|
|
if (intSize == 8)
|
|
argc = htog((uint64_t)argc);
|
|
else if (intSize == 4)
|
|
argc = htog((uint32_t)argc);
|
|
else
|
|
panic("Unknown int size");
|
|
|
|
initVirtMem->writeBlob(stack_min, (uint8_t*)&argc, intSize);
|
|
|
|
copyStringArray(argv, argv_array_base, arg_data_base, initVirtMem);
|
|
copyStringArray(envp, envp_array_base, env_data_base, initVirtMem);
|
|
|
|
ThreadContext *tc = system->getThreadContext(contextIds[0]);
|
|
|
|
setSyscallArg(tc, 0, argc);
|
|
setSyscallArg(tc, 1, argv_array_base);
|
|
tc->setIntReg(StackPointerReg, stack_min);
|
|
|
|
Addr prog_entry = objFile->entryPoint();
|
|
tc->setPC(prog_entry);
|
|
tc->setNextPC(prog_entry + sizeof(MachInst));
|
|
|
|
#if THE_ISA != ALPHA_ISA && THE_ISA != POWER_ISA //e.g. MIPS or Sparc
|
|
tc->setNextNPC(prog_entry + (2 * sizeof(MachInst)));
|
|
#endif
|
|
|
|
num_processes++;
|
|
}
|
|
|
|
void
|
|
LiveProcess::syscall(int64_t callnum, ThreadContext *tc)
|
|
{
|
|
num_syscalls++;
|
|
|
|
SyscallDesc *desc = getDesc(callnum);
|
|
if (desc == NULL)
|
|
fatal("Syscall %d out of range", callnum);
|
|
|
|
desc->doSyscall(callnum, this, tc);
|
|
}
|
|
|
|
IntReg
|
|
LiveProcess::getSyscallArg(ThreadContext *tc, int &i, int width)
|
|
{
|
|
return getSyscallArg(tc, i);
|
|
}
|
|
|
|
LiveProcess *
|
|
LiveProcess::create(LiveProcessParams * params)
|
|
{
|
|
LiveProcess *process = NULL;
|
|
|
|
string executable =
|
|
params->executable == "" ? params->cmd[0] : params->executable;
|
|
ObjectFile *objFile = createObjectFile(executable);
|
|
if (objFile == NULL) {
|
|
fatal("Can't load object file %s", executable);
|
|
}
|
|
|
|
if (objFile->isDynamic())
|
|
fatal("Object file is a dynamic executable however only static "
|
|
"executables are supported!\n Please recompile your "
|
|
"executable as a static binary and try again.\n");
|
|
|
|
#if THE_ISA == ALPHA_ISA
|
|
if (objFile->getArch() != ObjectFile::Alpha)
|
|
fatal("Object file architecture does not match compiled ISA (Alpha).");
|
|
|
|
switch (objFile->getOpSys()) {
|
|
case ObjectFile::Tru64:
|
|
process = new AlphaTru64Process(params, objFile);
|
|
break;
|
|
|
|
case ObjectFile::UnknownOpSys:
|
|
warn("Unknown operating system; assuming Linux.");
|
|
// fall through
|
|
case ObjectFile::Linux:
|
|
process = new AlphaLinuxProcess(params, objFile);
|
|
break;
|
|
|
|
default:
|
|
fatal("Unknown/unsupported operating system.");
|
|
}
|
|
#elif THE_ISA == SPARC_ISA
|
|
if (objFile->getArch() != ObjectFile::SPARC64 &&
|
|
objFile->getArch() != ObjectFile::SPARC32)
|
|
fatal("Object file architecture does not match compiled ISA (SPARC).");
|
|
switch (objFile->getOpSys()) {
|
|
case ObjectFile::UnknownOpSys:
|
|
warn("Unknown operating system; assuming Linux.");
|
|
// fall through
|
|
case ObjectFile::Linux:
|
|
if (objFile->getArch() == ObjectFile::SPARC64) {
|
|
process = new Sparc64LinuxProcess(params, objFile);
|
|
} else {
|
|
process = new Sparc32LinuxProcess(params, objFile);
|
|
}
|
|
break;
|
|
|
|
|
|
case ObjectFile::Solaris:
|
|
process = new SparcSolarisProcess(params, objFile);
|
|
break;
|
|
|
|
default:
|
|
fatal("Unknown/unsupported operating system.");
|
|
}
|
|
#elif THE_ISA == X86_ISA
|
|
if (objFile->getArch() != ObjectFile::X86_64 &&
|
|
objFile->getArch() != ObjectFile::I386)
|
|
fatal("Object file architecture does not match compiled ISA (x86).");
|
|
switch (objFile->getOpSys()) {
|
|
case ObjectFile::UnknownOpSys:
|
|
warn("Unknown operating system; assuming Linux.");
|
|
// fall through
|
|
case ObjectFile::Linux:
|
|
if (objFile->getArch() == ObjectFile::X86_64) {
|
|
process = new X86_64LinuxProcess(params, objFile);
|
|
} else {
|
|
process = new I386LinuxProcess(params, objFile);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fatal("Unknown/unsupported operating system.");
|
|
}
|
|
#elif THE_ISA == MIPS_ISA
|
|
if (objFile->getArch() != ObjectFile::Mips)
|
|
fatal("Object file architecture does not match compiled ISA (MIPS).");
|
|
switch (objFile->getOpSys()) {
|
|
case ObjectFile::UnknownOpSys:
|
|
warn("Unknown operating system; assuming Linux.");
|
|
// fall through
|
|
case ObjectFile::Linux:
|
|
process = new MipsLinuxProcess(params, objFile);
|
|
break;
|
|
|
|
default:
|
|
fatal("Unknown/unsupported operating system.");
|
|
}
|
|
#elif THE_ISA == ARM_ISA
|
|
if (objFile->getArch() != ObjectFile::Arm)
|
|
fatal("Object file architecture does not match compiled ISA (ARM).");
|
|
switch (objFile->getOpSys()) {
|
|
case ObjectFile::UnknownOpSys:
|
|
warn("Unknown operating system; assuming Linux.");
|
|
// fall through
|
|
case ObjectFile::Linux:
|
|
process = new ArmLinuxProcess(params, objFile);
|
|
break;
|
|
case ObjectFile::LinuxArmOABI:
|
|
fatal("M5 does not support ARM OABI binaries. Please recompile with an"
|
|
" EABI compiler.");
|
|
default:
|
|
fatal("Unknown/unsupported operating system.");
|
|
}
|
|
#elif THE_ISA == POWER_ISA
|
|
if (objFile->getArch() != ObjectFile::Power)
|
|
fatal("Object file architecture does not match compiled ISA (Power).");
|
|
switch (objFile->getOpSys()) {
|
|
case ObjectFile::UnknownOpSys:
|
|
warn("Unknown operating system; assuming Linux.");
|
|
// fall through
|
|
case ObjectFile::Linux:
|
|
process = new PowerLinuxProcess(params, objFile);
|
|
break;
|
|
|
|
default:
|
|
fatal("Unknown/unsupported operating system.");
|
|
}
|
|
#else
|
|
#error "THE_ISA not set"
|
|
#endif
|
|
|
|
|
|
if (process == NULL)
|
|
fatal("Unknown error creating process object.");
|
|
return process;
|
|
}
|
|
|
|
LiveProcess *
|
|
LiveProcessParams::create()
|
|
{
|
|
return LiveProcess::create(this);
|
|
}
|