// 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); }
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; } }