Ejemplo n.º 1
0
// Load the program image into the memory
std::pair<MemAddr, bool>
LoadProgram(const std::string& msg_prefix, vector<ActiveROM::LoadableRange>& ranges, IMemoryAdmin& memory, char* data, MemSize size, bool verbose)
{
    Verify(size >= sizeof(Elf_Ehdr), "ELF file too short or truncated");

    Elf_Ehdr& ehdr = *static_cast<Elf_Ehdr*>(static_cast<void*>(data));

    // Unmarshall header
    ehdr.e_type      = elftohh(ehdr.e_type);
    ehdr.e_machine   = elftohh(ehdr.e_machine);
    ehdr.e_version   = elftohw(ehdr.e_version);
    ehdr.e_entry     = elftoha(ehdr.e_entry);
    ehdr.e_phoff     = elftoho(ehdr.e_phoff);
    ehdr.e_shoff     = elftoho(ehdr.e_shoff);
    ehdr.e_flags     = elftohw(ehdr.e_flags);
    ehdr.e_ehsize    = elftohh(ehdr.e_ehsize);
    ehdr.e_phentsize = elftohh(ehdr.e_phentsize);
    ehdr.e_phnum     = elftohh(ehdr.e_phnum);
    ehdr.e_shentsize = elftohh(ehdr.e_shentsize);
    ehdr.e_shnum     = elftohh(ehdr.e_shnum);
    ehdr.e_shstrndx  = elftohh(ehdr.e_shstrndx);

    // Check file signature
    Verify(ehdr.e_ident[EI_MAG0] == ELFMAG0 && ehdr.e_ident[EI_MAG1] == ELFMAG1 &&
           ehdr.e_ident[EI_MAG2] == ELFMAG2 && ehdr.e_ident[EI_MAG3] == ELFMAG3,
           "invalid ELF file signature");

    // Check that this file is for our 'architecture'
    Verify(ehdr.e_ident[EI_VERSION] == EV_CURRENT,  "ELF version mismatch");
    Verify(ehdr.e_ident[EI_CLASS]   == ELFCLASS,    "file is not of proper bitsize");
    Verify(ehdr.e_ident[EI_DATA]    == ELFDATA,     "file is not of proper endianness");
    Verify(ehdr.e_machine == MACHINE_NORMAL ||
           ehdr.e_machine == MACHINE_LEGACY,       "target architecture is not supported");
    Verify(ehdr.e_type              == ET_EXEC,    "file is not an executable file");
    Verify(ehdr.e_phoff != 0 && ehdr.e_phnum != 0, "file has no program header");
    Verify(ehdr.e_phentsize == sizeof(Elf_Phdr),   "file has an invalid program header");
    Verify(ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize <= size, "file has an invalid program header");
    Verify(ehdr.e_shentsize == sizeof(Elf_Shdr),   "file has an invalid section header");
    Verify(ehdr.e_shoff + ehdr.e_shnum * ehdr.e_shentsize <= size, "file has an invalid section header");

    Elf_Shdr* shdr = static_cast<Elf_Shdr*>(static_cast<void*>(data + ehdr.e_shoff));

    // Load section information
    for (Elf_Half i = 0; i < ehdr.e_shnum; ++i)
    {
        shdr[i].sh_name      = elftohw (shdr[i].sh_name);
        shdr[i].sh_type      = elftohw (shdr[i].sh_type);
        shdr[i].sh_addr      = elftoha (shdr[i].sh_addr);
        shdr[i].sh_offset    = elftoho (shdr[i].sh_offset);
        shdr[i].sh_link      = elftohw (shdr[i].sh_link);
        shdr[i].sh_info      = elftohw (shdr[i].sh_info);
#if ELFCLASS == ELFCLASS64
        shdr[i].sh_flags     = elftohxw(shdr[i].sh_flags);
        shdr[i].sh_size      = elftohxw(shdr[i].sh_size);
        shdr[i].sh_addralign = elftohxw(shdr[i].sh_addralign);
        shdr[i].sh_entsize   = elftohxw(shdr[i].sh_entsize);
#else
        shdr[i].sh_flags     = elftohw (shdr[i].sh_flags);
        shdr[i].sh_size      = elftohw (shdr[i].sh_size);
        shdr[i].sh_addralign = elftohw (shdr[i].sh_addralign);
        shdr[i].sh_entsize   = elftohw (shdr[i].sh_entsize);
#endif
    }


    // Find symbol table & corresponding string table
    Elf_Sym* elf_sym_table = 0;
    size_t elf_sym_table_len = 0;
    const char *str_table = 0;
    size_t str_table_len = 0;

    for (Elf_Half i = 0; i < ehdr.e_shnum; ++i)
    {
        if (shdr[i].sh_type == SHT_SYMTAB)
        {
            Verify(shdr[i].sh_entsize == sizeof(Elf_Sym), "file has an invalid symtable");
            Verify(shdr[i].sh_offset + shdr[i].sh_size <= size, "file has an invalid symtable");

            elf_sym_table = static_cast<Elf_Sym*>(static_cast<void*>(data + shdr[i].sh_offset));
            elf_sym_table_len = shdr[i].sh_size / sizeof(Elf_Sym);

            Verify(shdr[i].sh_link != 0 /*SHN_UNDEF*/, "symtable has no string table");
            Verify(shdr[i].sh_link < ehdr.e_shnum, "symtable has an invalid string table");

            Elf_Shdr& strsh = shdr[shdr[i].sh_link];

            Verify(strsh.sh_type == SHT_STRTAB, "file has an invalid string table");
            Verify(strsh.sh_offset + strsh.sh_size <= size, "file has an invalid string table");

            str_table = data + strsh.sh_offset;
            str_table_len = strsh.sh_size;

            break;
        }
    }

    SymbolTable& symtable = memory.GetSymbolTable();
    for (size_t i = 1 /* first entry is unused */; i < elf_sym_table_len; ++i)
    {
        elf_sym_table[i].st_name = elftohw(elf_sym_table[i].st_name);
        elf_sym_table[i].st_value = elftoha(elf_sym_table[i].st_value);
#if ELFCLASS == ELFCLASS64
        elf_sym_table[i].st_size = elftohxw(elf_sym_table[i].st_size);
#else
        elf_sym_table[i].st_size = elftohw(elf_sym_table[i].st_size);
#endif

        Verify(elf_sym_table[i].st_name < str_table_len, "file specifies an invalid symbol");

        const char* name = str_table + elf_sym_table[i].st_name;
        MemAddr addr = elf_sym_table[i].st_value;
        if (addr != 0 && strlen(name) > 0)
        {
            symtable.AddSymbol(addr, name, elf_sym_table[i].st_size);
        }
    }

    Elf_Phdr* phdr = static_cast<Elf_Phdr*>(static_cast<void*>(data + ehdr.e_phoff));

    // Determine base address and check for loadable segments
    bool     hasLoadable = false;
    Elf_Addr base = 0;
    for (Elf_Half i = 0; i < ehdr.e_phnum; ++i)
    {
        phdr[i].p_type   = elftohw (phdr[i].p_type);
        phdr[i].p_flags  = elftohw (phdr[i].p_flags);
        phdr[i].p_offset = elftoho (phdr[i].p_offset);
        phdr[i].p_vaddr  = elftoha (phdr[i].p_vaddr);
        phdr[i].p_paddr  = elftoha (phdr[i].p_paddr);
        phdr[i].p_filesz = elftohxw(phdr[i].p_filesz);
        phdr[i].p_memsz  = elftohxw(phdr[i].p_memsz);
        phdr[i].p_align  = elftohxw(phdr[i].p_align);

        if (phdr[i].p_type == PT_LOAD)
        {
            if (!hasLoadable || phdr[i].p_vaddr < base) {
                base = phdr[i].p_vaddr;
            }
            hasLoadable = true;
        }
    }
    // Verify(hasLoadable, "file has no loadable segments");
    base = base & -PAGE_SIZE;

    // Then copy the LOAD segments into their right locations
    for (Elf_Half i = 0; i < ehdr.e_phnum; ++i)
    {
        if (phdr[i].p_type == PT_LOAD && phdr[i].p_memsz > 0)
        {
            Verify(phdr[i].p_memsz >= phdr[i].p_filesz, "file has an invalid segment");

            int perm = 0;
            if (phdr[i].p_flags & PF_R) perm |= IMemory::PERM_READ|IMemory::PERM_DCA_READ;
            if (phdr[i].p_flags & PF_W) perm |= IMemory::PERM_WRITE|IMemory::PERM_DCA_WRITE;
            if (phdr[i].p_flags & PF_X) perm |= IMemory::PERM_EXECUTE;

            if (phdr[i].p_filesz > 0)
            {
                Verify(phdr[i].p_offset + phdr[i].p_filesz <= size, "file has an invalid segment");

                ActiveROM::LoadableRange r;
                r.rom_offset = phdr[i].p_offset;
                r.rom_size = phdr[i].p_filesz;
                r.vaddr = phdr[i].p_vaddr;
                r.vsize = phdr[i].p_memsz;
                r.perm = (IMemory::Permissions)perm;
                ranges.push_back(r);

                // We do not reserve here because this
                // will be taken care of by the ActiveROM during loading.
                if (verbose)
                {
                    clog << msg_prefix << ": " << dec << phdr[i].p_filesz << " bytes loadable at virtual address 0x" << hex << phdr[i].p_vaddr << endl;
                }
            }
            else
            {
                memory.Reserve(phdr[i].p_vaddr, phdr[i].p_memsz, 0, perm);
                if (verbose)
                {
                    clog << msg_prefix << ": " << dec << phdr[i].p_memsz << " bytes reserved at virtual address 0x" << hex << phdr[i].p_vaddr << endl;
                }
            }
        }
    }

    if (verbose)
    {
        const char* type = (ehdr.e_machine == MACHINE_LEGACY)
            ? "legacy"
            : "microthreaded";

        clog << msg_prefix << ": loaded " << type << " ELF binary with virtual base address 0x" << hex << base
             << ", entry point at 0x" << hex << ehdr.e_entry << endl;
    }
    return make_pair(ehdr.e_entry, ehdr.e_machine == MACHINE_LEGACY);
}
Ejemplo n.º 2
0
MGSystem::MGSystem(Config& config, bool quiet)
    : m_kernel(m_breakpoints),
      m_clock(m_kernel.CreateClock(config.getValue<unsigned long>("CoreFreq"))),
      m_root("", m_clock),
      m_procs(),
      m_fpus(),
      m_iobuses(),
      m_devices(),
      m_procbusmapping(),
      m_symtable(),
      m_breakpoints(m_kernel),
      m_memory(0),
      m_objdump_cmd(),
      m_config(config),
      m_bootrom(0),
      m_selector(0)
{

    if (!quiet)
    {
        clog << endl
             << "Instanciating components..." << endl;
    }

    PSize numProcessors = m_config.getValue<PSize>("NumProcessors");

    const size_t numProcessorsPerFPU = config.getValue<size_t>("NumProcessorsPerFPU");
    const PSize  numFPUs             = (numProcessors + numProcessorsPerFPU - 1) / numProcessorsPerFPU;

    string memory_type = config.getValue<string>("MemoryType");
    transform(memory_type.begin(), memory_type.end(), memory_type.begin(), ::toupper);

    Clock& memclock = m_kernel.CreateClock(config.getValue<size_t>("MemoryFreq"));

    IMemoryAdmin *memadmin;

    if (memory_type == "SERIAL") {
        SerialMemory* memory = new SerialMemory("memory", m_root, memclock, config);
        memadmin = memory;
        m_memory = memory;
    } else if (memory_type == "PARALLEL") {
        ParallelMemory* memory = new ParallelMemory("memory", m_root, memclock, config);
        memadmin = memory;
        m_memory = memory;
    } else if (memory_type == "BANKED") {
        BankedMemory* memory = new BankedMemory("memory", m_root, memclock, config, "DIRECT");
        memadmin = memory;
        m_memory = memory;
    } else if (memory_type == "RANDOMBANKED") {
        BankedMemory* memory = new BankedMemory("memory", m_root, memclock, config, "RMIX");
        memadmin = memory;
        m_memory = memory;
    } else if (memory_type == "DDR") {
        DDRMemory* memory = new DDRMemory("memory", m_root, memclock, config, "DIRECT");
        memadmin = memory;
        m_memory = memory;
    } else if (memory_type == "RANDOMDDR") {
        DDRMemory* memory = new DDRMemory("memory", m_root, memclock, config, "RMIX");
        memadmin = memory;
        m_memory = memory;
    } else if (memory_type == "COMA") {
        COMA* memory = new TwoLevelCOMA("memory", m_root, memclock, config);
        memadmin = memory;
        m_memory = memory;
    } else if (memory_type == "ZLCOMA") {
        ZLCOMA* memory = new ZLCOMA("memory", m_root, memclock, config);
        memadmin = memory;
        m_memory = memory;
    } else if (memory_type == "FLATCOMA") {
        COMA* memory = new OneLevelCOMA("memory", m_root, memclock, config);
        memadmin = memory;
        m_memory = memory;
    } else {
        throw runtime_error("Unknown memory type: " + memory_type);
    }
    if (!quiet)
    {
        clog << "memory: " << memory_type << endl;
    }
    memadmin->SetSymbolTable(m_symtable);
    m_breakpoints.SetSymbolTable(m_symtable);

    // Create the event selector
    Clock& selclock = m_kernel.CreateClock(config.getValue<unsigned long>("EventCheckFreq"));
    m_selector = new Selector("selector", m_root, selclock, config);

    // Create the I/O Buses
    const size_t numIOBuses = config.getValue<size_t>("NumIOBuses");
    m_iobuses.resize(numIOBuses);
    for (size_t b = 0; b < numIOBuses; ++b)
    {
        stringstream ss;
        ss << "iobus" << b;
        string name = ss.str();

        string bus_type = config.getValue<string>(m_root, name, "Type");
        Clock& ioclock = m_kernel.CreateClock(config.getValue<unsigned long>(m_root, name, "Freq"));

        if (bus_type == "NULLIO") {
            NullIO* bus = new NullIO(name, m_root, ioclock);
            m_iobuses[b] = bus;
            config.registerObject(*bus, "nullio");
            config.registerProperty(*bus, "freq", (uint32_t)ioclock.GetFrequency());
        } else {
            throw runtime_error("Unknown I/O bus type for " + name + ": " + bus_type);
        }

        if (!quiet)
        {
            clog << name << ": " << bus_type << endl;
        }
    }

    // Create the FPUs
    m_fpus.resize(numFPUs);
    for (size_t f = 0; f < numFPUs; ++f)
    {
        stringstream name;
        name << "fpu" << f;
        m_fpus[f] = new FPU(name.str(), m_root, m_clock, config, numProcessorsPerFPU);

        config.registerObject(*m_fpus[f], "fpu");
        config.registerProperty(*m_fpus[f], "freq", (uint32_t)m_clock.GetFrequency());
    }
    if (!quiet)
    {
        clog << numFPUs << " FPUs instantiated." << endl;
    }

    // Create processor grid
    m_procs.resize(numProcessors);
    for (size_t i = 0; i < numProcessors; ++i)
    {
        FPU& fpu = *m_fpus[i / numProcessorsPerFPU];

        stringstream ss;
        ss << "cpu" << i;
        string name = ss.str();

        IIOBus* iobus = NULL;
        if (config.getValueOrDefault<bool>(m_root, name, "EnableIO", false)) // I/O disabled unless specified
        {
            size_t busid = config.getValue<size_t>(m_root, name, "BusID");
            if (busid >= m_iobuses.size())
            {
                throw runtime_error("Processor " + name + " set to connect to non-existent bus");
            }

            m_procbusmapping[i] = busid;
            iobus = m_iobuses[busid];

            if (!quiet)
            {
                clog << name << ": connected to " << dynamic_cast<Object*>(iobus)->GetName() << endl;
            }
        }

        m_procs[i]   = new Processor(name, m_root, m_clock, i, m_procs, *m_memory, *memadmin, fpu, iobus, config);
    }
    if (!quiet)
    {
        clog << numProcessors << " cores instantiated." << endl;
    }

    // Create the I/O devices
    vector<string> dev_names = config.getWordList("IODevices");
    size_t numIODevices = dev_names.size();

    m_devices.resize(numIODevices);
    vector<ActiveROM*> aroms;

    UnixInterface *uif = new UnixInterface("unix_if", m_root);
    m_devices.push_back(uif);

    for (size_t i = 0; i < numIODevices; ++i)
    {
        string name = dev_names[i];

        bool enable_dev = config.getValueOrDefault<bool>(m_root, name, "EnableDevice", true);
        if (!enable_dev)
            continue;

        size_t busid = config.getValue<size_t>(m_root, name, "BusID");

        if (busid >= m_iobuses.size())
        {
            throw runtime_error("Device " + name + " set to connect to non-existent bus");
        }

        IIOBus& iobus = *m_iobuses[busid];

        IODeviceID devid = config.getValueOrDefault<IODeviceID>(m_root, name, "DeviceID", iobus.GetNextAvailableDeviceID());

        string dev_type = config.getValue<string>(m_root, name, "Type");

        if (!quiet)
        {
            clog << name << ": connected to " << dynamic_cast<Object&>(iobus).GetName() << " (type " << dev_type << ", devid " << dec << devid << ')' << endl;
        }

        if (dev_type == "LCD") {
            LCD *lcd = new LCD(name, m_root, iobus, devid, config);
            m_devices[i] = lcd;
            config.registerObject(*lcd, "lcd");
        } else if (dev_type == "RTC") {
            Clock& rtcclock = m_kernel.CreateClock(config.getValue<size_t>(m_root, name, "RTCUpdateFreq"));
            RTC *rtc = new RTC(name, m_root, rtcclock, iobus, devid, config);
            m_devices[i] = rtc;
            config.registerObject(*rtc, "rtc");
        } else if (dev_type == "GFX") {
            size_t fbdevid = config.getValueOrDefault<size_t>(m_root, name, "GfxFrameBufferDeviceID", devid + 1);
            Display *disp = new Display(name, m_root, iobus, devid, fbdevid, config);
            m_devices[i] = disp;
            config.registerObject(*disp, "gfx");
        } else if (dev_type == "AROM") {
            ActiveROM *rom = new ActiveROM(name, m_root, *memadmin, iobus, devid, config, quiet);
            m_devices[i] = rom;
            aroms.push_back(rom);
            config.registerObject(*rom, "arom");
        } else if (dev_type == "UART") {
            UART *uart = new UART(name, m_root, iobus, devid, config);
            m_devices[i] = uart;
            config.registerObject(*uart, "uart");
        } else if (dev_type == "SMC") {
            SMC * smc = new SMC(name, m_root, iobus, devid, config);
            m_devices[i] = smc;
            config.registerObject(*smc, "smc");
        } else if (dev_type == "RPC") {
            RPCInterface* rpc = new RPCInterface(name, m_root, iobus, devid, config, *uif);
            m_devices[i] = rpc;
            config.registerObject(*rpc, "rpc");
        } else {
            throw runtime_error("Unknown I/O device type: " + dev_type);
        }

        config.registerBidiRelation(iobus, *m_devices[i], "client", (uint32_t)devid);
    }



    // We need to register the master frequency into the
    // configuration, because both in-program and external monitoring
    // want to know it.
    unsigned long masterfreq = m_kernel.GetMasterFrequency();
    config.getValueOrDefault("MasterFreq", masterfreq); // The lookup will set the config key as side effect

    config.registerObject(m_root, "system");
    config.registerProperty(m_root, "version", PACKAGE_VERSION);
    config.registerProperty(m_root, "masterfreq", (uint32_t)masterfreq);

    if (!quiet)
    {
        clog << endl
             << "Initializing components..." << endl;
    }

    // Initialize the memory
    m_memory->Initialize();

    // Connect processors in the link
    for (size_t i = 0; i < numProcessors; ++i)
    {
        Processor* prev = (i == 0)                 ? NULL : m_procs[i - 1];
        Processor* next = (i == numProcessors - 1) ? NULL : m_procs[i + 1];
        m_procs[i]->Initialize(prev, next);
        if (next)
            config.registerRelation(*m_procs[i], *next, "link", true);
    }

    // Initialize the buses. This initializes the devices as well.
    for (auto iob : m_iobuses)
        iob->Initialize();

    // Check for bootable ROMs. This must happen after I/O bus
    // initialization because the ROM contents are loaded then.
    for (auto rom : aroms)
        if (rom->IsBootable())
        {
            if (m_bootrom != NULL)
            {
                throw runtime_error("More than one bootable ROM detected: " + rom->GetName() + ", " + m_bootrom->GetFQN());
            }
            m_bootrom = rom;
        }

    if (m_bootrom == NULL)
    {
        cerr << "Warning: No bootable ROM configured." << endl;
    }

    if (!quiet)
    {
        clog << endl
             << "Final configuration..." << endl;
    }

    // Set up the initial memory ranges
    size_t numRanges = config.getValue<size_t>("NumMemoryRanges");
    for (size_t i = 0; i < numRanges; ++i)
    {
        stringstream ss;
        ss << "MemoryRange" << i;
        string name = ss.str();

        MemAddr address = config.getValue<MemAddr>(name, "Address");
        MemSize size = config.getValue<MemSize>(name, "Size");
        string mode = config.getValue<string>(name, "Mode");
        ProcessID pid = config.getValue<ProcessID>(name, "PID");
        int perm = 0;
        if (mode.find("R") != string::npos)
            perm |= IMemory::PERM_READ;
        if (mode.find("W") != string::npos)
            perm |= IMemory::PERM_WRITE;
        if (mode.find("X") != string::npos)
            perm |= IMemory::PERM_EXECUTE;

        memadmin->Reserve(address, size, pid, perm);
    }

    // Set program debugging per default
    m_kernel.SetDebugMode(Kernel::DEBUG_PROG);

    // Find objdump command
#if defined(TARGET_MTALPHA)
# define OBJDUMP_VAR "MTALPHA_OBJDUMP"
# if defined(OBJDUMP_MTALPHA)
#  define OBJDUMP_CMD OBJDUMP_MTALPHA
# endif
#elif defined(TARGET_MTSPARC)
# define OBJDUMP_VAR "MTSPARC_OBJDUMP"
# if defined(OBJDUMP_MTSPARC)
#  define OBJDUMP_CMD OBJDUMP_MTSPARC
# endif
#elif defined(TARGET_MIPS32)
# define OBJDUMP_VAR "MIPS32_OBJDUMP"
# if defined(OBJDUMP_MIPS32)
#  define OBJDUMP_CMD OBJDUMP_MIPS32
# endif
#elif defined(TARGET_MIPS32EL)
# define OBJDUMP_VAR "MIPS32EL_OBJDUMP"
# if defined(OBJDUMP_MIPS32EL)
#  define OBJDUMP_CMD OBJDUMP_MIPS32EL
# endif
#endif
    const char *v = 0;
#ifdef OBJDUMP_VAR
    v = getenv(OBJDUMP_VAR);
#endif
    if (!v)
    {
#ifdef OBJDUMP_CMD
        v = OBJDUMP_CMD;
#else
        if (!quiet)
            cerr << "# Warning: platform-specific 'objdump' was not found and " OBJDUMP_VAR " is not set; cannot disassemble code." << endl;
        v = "unknown-objdump";
#endif
    }
    m_objdump_cmd = v;

    if (!quiet)
    {
        clog << "Location of `objdump': " << m_objdump_cmd << endl;

        static char const qual[] = {'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
        unsigned int q;
        for (q = 0; masterfreq % 1000 == 0 && q < sizeof(qual)/sizeof(qual[0]); ++q)
        {
            masterfreq /= 1000;
        }
        clog << "Created Microgrid: "
             << dec
             << CountComponents(m_root) << " components, "
             << Process::GetAllProcesses().size() << " processes, "
             << "simulation running at " << dec << masterfreq << " " << qual[q] << "Hz" << endl;
    }
}