diff options
57 files changed, 10630 insertions, 92 deletions
diff --git a/arch/alpha/alpha_memory.cc b/arch/alpha/alpha_memory.cc index 23815bf01..31f5a9b20 100644 --- a/arch/alpha/alpha_memory.cc +++ b/arch/alpha/alpha_memory.cc @@ -44,6 +44,11 @@ using namespace std; // // Alpha TLB // +#ifdef DEBUG +bool uncacheBit39 = false; +bool uncacheBit40 = false; +#endif + AlphaTLB::AlphaTLB(const string &name, int s) : SimObject(name), size(s), nlu(0) { @@ -87,24 +92,27 @@ AlphaTLB::checkCacheability(MemReqPtr &req) { // in Alpha, cacheability is controlled by upper-level bits of the // physical address - if (req->paddr & PA_UNCACHED_BIT) { - if (PA_IPR_SPACE(req->paddr)) { - // IPR memory space not implemented - if (!req->xc->misspeculating()) { - switch (req->paddr) { - case ULL(0xFFFFF00188): - req->data = 0; - break; - - default: - panic("IPR memory space not implemented! PA=%x\n", - req->paddr); - } - } - } else { - // mark request as uncacheable - req->flags |= UNCACHEABLE; - } + + /* + * We support having the uncacheable bit in either bit 39 or bit 40. + * The Turbolaser platform (and EV5) support having the bit in 39, but + * Tsunami (which Linux assumes uses an EV6) generates accesses with + * the bit in 40. So we must check for both, but we have debug flags + * to catch a weird case where both are used, which shouldn't happen. + */ + + if (req->paddr & PA_UNCACHED_BIT_43) { + // IPR memory space not implemented + if (PA_IPR_SPACE(req->paddr)) + if (!req->xc->misspeculating()) + panic("IPR memory space not implemented! PA=%x\n", + req->paddr); + + // mark request as uncacheable + req->flags |= UNCACHEABLE; + + // Clear bits 42:35 of the physical address (10-2 in Tsunami manual) + req->paddr &= PA_UNCACHED_MASK; } } @@ -290,10 +298,10 @@ AlphaITB::translate(MemReqPtr &req) const return ITB_Acv_Fault; } - // Check for "superpage" mapping: when SP<1> is set, and - // VA<42:41> == 2, VA<39:13> maps directly to PA<39:13>. - if ((MCSR_SP(ipr[AlphaISA::IPR_MCSR]) & 2) && - VA_SPACE(req->vaddr) == 2) { + + // VA<42:41> == 2, VA<39:13> maps directly to PA<39:13> for EV5 + // VA<47:41> == 0x7e, VA<40:13> maps directly to PA<40:13> for EV6 + if (VA_SPACE_EV6(req->vaddr) == 0x7e) { // only valid in kernel mode if (ICM_CM(ipr[AlphaISA::IPR_ICM]) != AlphaISA::mode_kernel) { @@ -303,6 +311,13 @@ AlphaITB::translate(MemReqPtr &req) const } req->paddr = req->vaddr & PA_IMPL_MASK; + + // sign extend the physical address properly + if (req->paddr & PA_UNCACHED_BIT_40) + req->paddr |= ULL(0xf0000000000); + else + req->paddr &= ULL(0xffffffffff); + } else { // not a physical address: need to look up pte AlphaISA::PTE *pte = lookup(VA_VPN(req->vaddr), @@ -468,10 +483,8 @@ AlphaDTB::translate(MemReqPtr &req, bool write) const return DTB_Fault_Fault; } - // Check for "superpage" mapping: when SP<1> is set, and - // VA<42:41> == 2, VA<39:13> maps directly to PA<39:13>. - if ((MCSR_SP(ipr[AlphaISA::IPR_MCSR]) & 2) && - VA_SPACE(req->vaddr) == 2) { + // Check for "superpage" mapping + if (VA_SPACE_EV6(req->vaddr) == 0x7e) { // only valid in kernel mode if (DTB_CM_CM(ipr[AlphaISA::IPR_DTB_CM]) != @@ -484,6 +497,13 @@ AlphaDTB::translate(MemReqPtr &req, bool write) const } req->paddr = req->vaddr & PA_IMPL_MASK; + + // sign extend the physical address properly + if (req->paddr & PA_UNCACHED_BIT_40) + req->paddr |= ULL(0xf0000000000); + else + req->paddr &= ULL(0xffffffffff); + } else { if (write) write_accesses++; diff --git a/arch/alpha/ev5.hh b/arch/alpha/ev5.hh index 6947ef708..517e1111f 100644 --- a/arch/alpha/ev5.hh +++ b/arch/alpha/ev5.hh @@ -32,8 +32,8 @@ #define ALT_MODE_AM(X) (((X) >> 3) & 0x3) #define DTB_CM_CM(X) (((X) >> 3) & 0x3) -#define DTB_ASN_ASN(X) (((X) >> 57) & 0x7f) -#define DTB_PTE_PPN(X) (((X) >> 32) & 0x07ffffff) +#define DTB_ASN_ASN(X) (((X) >> 57) & 0xff) +#define DTB_PTE_PPN(X) (((X) >> 32) & 0x07fffffff) #define DTB_PTE_XRE(X) (((X) >> 8) & 0xf) #define DTB_PTE_XWE(X) (((X) >> 12) & 0xf) #define DTB_PTE_FONR(X) (((X) >> 1) & 0x1) @@ -42,8 +42,8 @@ #define DTB_PTE_ASMA(X) (((X) >> 4) & 0x1) #define ICM_CM(X) (((X) >> 3) & 0x3) -#define ITB_ASN_ASN(X) (((X) >> 4) & 0x7f) -#define ITB_PTE_PPN(X) (((X) >> 32) & 0x07ffffff) +#define ITB_ASN_ASN(X) (((X) >> 4) & 0xff) +#define ITB_PTE_PPN(X) (((X) >> 32) & 0x07fffffff) #define ITB_PTE_XRE(X) (((X) >> 8) & 0xf) #define ITB_PTE_FONR(X) (((X) >> 1) & 0x1) #define ITB_PTE_FONW(X) (((X) >> 2) & 0x1) @@ -54,12 +54,16 @@ #define VA_IMPL_MASK ULL(0x000007ffffffffff) #define VA_IMPL(X) ((X) & VA_IMPL_MASK) #define VA_VPN(X) (VA_IMPL(X) >> 13) -#define VA_SPACE(X) (((X) >> 41) & 0x3) +#define VA_SPACE_EV5(X) (((X) >> 41) & 0x3) +#define VA_SPACE_EV6(X) (((X) >> 41) & 0x7f) #define VA_POFS(X) ((X) & 0x1fff) -#define PA_IMPL_MASK ULL(0xffffffffff) -#define PA_UNCACHED_BIT ULL(0x8000000000) -#define PA_IPR_SPACE(X) ((X) >= ULL(0xFFFFF00000)) +#define PA_IMPL_MASK ULL(0xfffffffffff) // for Tsunami +#define PA_UNCACHED_BIT_39 ULL(0x8000000000) +#define PA_UNCACHED_BIT_40 ULL(0x10000000000) +#define PA_UNCACHED_BIT_43 ULL(0x80000000000) +#define PA_UNCACHED_MASK ULL(0x807ffffffff) // Clear PA<42:35> +#define PA_IPR_SPACE(X) ((X) >= ULL(0xFFFFFF00000)) #define PA_PFN2PA(X) ((X) << 13) diff --git a/arch/alpha/vtophys.cc b/arch/alpha/vtophys.cc index 5e14b06d3..d91d80c83 100644 --- a/arch/alpha/vtophys.cc +++ b/arch/alpha/vtophys.cc @@ -96,20 +96,20 @@ vtophys(ExecContext *xc, Addr vaddr) { Addr ptbr = xc->regs.ipr[AlphaISA::IPR_PALtemp20]; Addr paddr = 0; - if (PC_PAL(vaddr)) { - paddr = vaddr & ~ULL(1); - } else if (!ptbr) { - paddr = vaddr; - } else { +// if (PC_PAL(vaddr)) { +// paddr = vaddr & ~ULL(1); +// } else { if (vaddr >= ALPHA_K0SEG_BASE && vaddr <= ALPHA_K0SEG_END) { paddr = ALPHA_K0SEG_TO_PHYS(vaddr); + } else if (!ptbr) { + paddr = vaddr; } else { Addr pte = kernel_pte_lookup(xc->physmem, ptbr, vaddr); uint64_t entry = xc->physmem->phys_read_qword(pte); if (pte && entry_valid(entry)) paddr = PMAP_PTE_PA(entry) | (vaddr & PGOFSET); } - } +// } DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); diff --git a/base/cprintf_formats.hh b/base/cprintf_formats.hh index d8a8a552b..120dd94b1 100644 --- a/base/cprintf_formats.hh +++ b/base/cprintf_formats.hh @@ -279,8 +279,6 @@ template <typename T> inline void format_integer(std::ostream &out, const T &data, Format &fmt) { _format_integer(out, data, fmt); } - -#if 0 inline void format_integer(std::ostream &out, char data, Format &fmt) { _format_integer(out, data, fmt); } @@ -290,6 +288,7 @@ format_integer(std::ostream &out, unsigned char data, Format &fmt) inline void format_integer(std::ostream &out, signed char data, Format &fmt) { _format_integer(out, data, fmt); } +#if 0 inline void format_integer(std::ostream &out, short data, Format &fmt) { _format_integer(out, data, fmt); } diff --git a/base/inifile.cc b/base/inifile.cc index 74d47204e..e01032d02 100644 --- a/base/inifile.cc +++ b/base/inifile.cc @@ -142,7 +142,7 @@ IniFile::loadCPP(const string &file, vector<char *> &cppArgs) close(STDOUT_FILENO); if (dup2(fd[1], STDOUT_FILENO) == -1) - return 1; + exit(1); execvp("g++", args); diff --git a/base/loader/elf_object.cc b/base/loader/elf_object.cc index b8ffd2b10..a0c0c0551 100644 --- a/base/loader/elf_object.cc +++ b/base/loader/elf_object.cc @@ -191,7 +191,7 @@ bool ElfObject::loadSomeSymbols(SymbolTable *symtab, int binding) { Elf *elf; - int secidx = 1; // there is a 0 but it is nothing, go figure + int sec_idx = 1; // there is a 0 but it is nothing, go figure Elf_Scn *section; GElf_Shdr shdr; Elf_Data *data; @@ -211,7 +211,7 @@ ElfObject::loadSomeSymbols(SymbolTable *symtab, int binding) assert(elf != NULL); // Get the first section - section = elf_getscn(elf, secidx); + section = elf_getscn(elf, sec_idx); // While there are no more sections while (section != NULL) { @@ -226,14 +226,14 @@ ElfObject::loadSomeSymbols(SymbolTable *symtab, int binding) // loop through all the symbols, only loading global ones for (ii = 0; ii < count; ++ii) { gelf_getsym(data, ii, &sym); - if (GELF_ST_BIND(sym.st_info) & binding) { + if (GELF_ST_BIND(sym.st_info) == binding) { symtab->insert(sym.st_value, elf_strptr(elf, shdr.sh_link, sym.st_name)); } } } - ++secidx; - section = elf_getscn(elf, secidx); + ++sec_idx; + section = elf_getscn(elf, sec_idx); } elf_end(elf); diff --git a/base/loader/symtab.cc b/base/loader/symtab.cc index 075c197a6..cb18d499c 100644 --- a/base/loader/symtab.cc +++ b/base/loader/symtab.cc @@ -95,6 +95,31 @@ SymbolTable::load(const string &filename) } bool +SymbolTable::findNearestSymbol(Addr address, string &symbol) const +{ + ATable::const_iterator i = addrTable.lower_bound(address); + + // check for PALCode + if (address & 0x1) + return false; + + // first check for the end + if (i == addrTable.end()) + i--; + else if (i == addrTable.begin() && (*i).first != address) + return false; + else if ((*i).first != address) + i--; + + symbol = (*i).second; + + if (address != (*i).first) + symbol += csprintf("+%d", address - (*i).first); + + return true; +} + +bool SymbolTable::findSymbol(Addr address, string &symbol) const { ATable::const_iterator i = addrTable.find(address); diff --git a/base/loader/symtab.hh b/base/loader/symtab.hh index 49a811018..1502e4250 100644 --- a/base/loader/symtab.hh +++ b/base/loader/symtab.hh @@ -29,14 +29,14 @@ #ifndef __SYMTAB_HH__ #define __SYMTAB_HH__ -#include "base/hashmap.hh" +#include <map> #include "targetarch/isa_traits.hh" // for Addr class SymbolTable { private: - typedef m5::hash_map<Addr, std::string> ATable; - typedef m5::hash_map<std::string, Addr> STable; + typedef std::map<Addr, std::string> ATable; + typedef std::map<std::string, Addr> STable; ATable addrTable; STable symbolTable; @@ -49,6 +49,7 @@ class SymbolTable bool insert(Addr address, std::string symbol); bool load(const std::string &file); + bool findNearestSymbol(Addr address, std::string &symbol) const; bool findSymbol(Addr address, std::string &symbol) const; bool findAddress(const std::string &symbol, Addr &address) const; diff --git a/base/range.hh b/base/range.hh index d72aa9755..2c4a43f48 100644 --- a/base/range.hh +++ b/base/range.hh @@ -225,7 +225,7 @@ inline bool operator==(const T &pos, const Range<U> &range) { assert(range.valid()); - return pos >= range.start && pos < range.end; + return pos >= range.start && pos <= range.end; } /** diff --git a/base/remote_gdb.cc b/base/remote_gdb.cc index e20800d12..7b73d60e9 100644 --- a/base/remote_gdb.cc +++ b/base/remote_gdb.cc @@ -332,23 +332,21 @@ RemoteGDB::acc(Addr va, size_t len) last_va = alpha_round_page(va + len); do { - if (va < ALPHA_K0SEG_BASE) { - DPRINTF(GDBAcc, "acc: Mapping is invalid %#x < K0SEG\n", va); - return false; - } - - if (va < ALPHA_K1SEG_BASE) { + if (va >= ALPHA_K0SEG_BASE && va < ALPHA_K1SEG_BASE) { if (va < (ALPHA_K0SEG_BASE + pmem->size())) { DPRINTF(GDBAcc, "acc: Mapping is valid K0SEG <= " "%#x < K0SEG + size\n", va); return true; } else { - DPRINTF(GDBAcc, "acc: Mapping is invalid %#x < K0SEG\n", + DPRINTF(GDBAcc, "acc: Mapping invalid %#x > K0SEG + size\n", va); return false; } } + if (PC_PAL(va) || va < 0x10000) + return true; + Addr ptbr = context->regs.ipr[AlphaISA::IPR_PALtemp20]; pte = kernel_pte_lookup(pmem, ptbr, va); if (!pte || !entry_valid(pmem->phys_read_qword(pte))) { diff --git a/base/stats/mysql.hh b/base/stats/mysql.hh index 4671b89dd..4aa8858b1 100644 --- a/base/stats/mysql.hh +++ b/base/stats/mysql.hh @@ -34,6 +34,7 @@ #include "base/stats/output.hh" +namespace MySQL { class Connection; } namespace Stats { class DistDataData; diff --git a/base/traceflags.py b/base/traceflags.py index 69f4e7ab8..3d7623965 100644 --- a/base/traceflags.py +++ b/base/traceflags.py @@ -57,6 +57,7 @@ baseFlags = [ 'Mbox', 'PCIA', 'PCIDEV', + 'PciConfigAll', 'ISP', 'BADADDR', 'Console', @@ -104,7 +105,11 @@ baseFlags = [ 'Context', 'Config', 'Sampler', - 'WriteBarrier' + 'WriteBarrier', + 'IdeCtrl', + 'IdeDisk', + 'Tsunami', + 'TsunamiUart' ] # @@ -119,7 +124,8 @@ compoundFlagMap = { 'GDBAll' : [ 'GDBMisc', 'GDBAcc', 'GDBRead', 'GDBWrite', 'GDBSend', 'GDBRecv', 'GDBExtra' ], 'ScsiAll' : [ 'ScsiDisk', 'ScsiCtrl', 'ScsiNone' ], 'DiskImageAll' : [ 'DiskImage', 'DiskImageRead', 'DiskImageWrite' ], - 'EthernetAll' : [ 'Ethernet', 'EthernetPIO', 'EthernetDMA', 'EthernetData' ] + 'EthernetAll' : [ 'Ethernet', 'EthernetPIO', 'EthernetDMA', 'EthernetData' ], + 'IdeAll' : [ 'IdeCtrl', 'IdeDisk' ] } ############################################################# diff --git a/cpu/base_cpu.cc b/cpu/base_cpu.cc index 702a9afe8..4c162986e 100644 --- a/cpu/base_cpu.cc +++ b/cpu/base_cpu.cc @@ -209,7 +209,7 @@ BaseCPU::post_interrupt(int int_num, int index) if (int_num < 0 || int_num >= NumInterruptLevels) panic("int_num out of bounds\n"); - if (index < 0 || index >= sizeof(uint8_t) * 8) + if (index < 0 || index >= sizeof(uint64_t) * 8) panic("int_num out of bounds\n"); AlphaISA::check_interrupts = 1; @@ -225,7 +225,7 @@ BaseCPU::clear_interrupt(int int_num, int index) if (int_num < 0 || int_num >= NumInterruptLevels) panic("int_num out of bounds\n"); - if (index < 0 || index >= sizeof(uint8_t) * 8) + if (index < 0 || index >= sizeof(uint64_t) * 8) panic("int_num out of bounds\n"); interrupts[int_num] &= ~(1 << index); diff --git a/cpu/base_cpu.hh b/cpu/base_cpu.hh index 9c4026784..9e55d5d3c 100644 --- a/cpu/base_cpu.hh +++ b/cpu/base_cpu.hh @@ -48,7 +48,7 @@ class BaseCPU : public SimObject #ifdef FULL_SYSTEM protected: Tick frequency; - uint8_t interrupts[NumInterruptLevels]; + uint64_t interrupts[NumInterruptLevels]; uint64_t intstatus; public: diff --git a/cpu/exetrace.cc b/cpu/exetrace.cc index 3e8877e93..a4aaa19db 100644 --- a/cpu/exetrace.cc +++ b/cpu/exetrace.cc @@ -48,11 +48,12 @@ using namespace std; // -const SymbolTable *debugSymbolTable = NULL; +SymbolTable *debugSymbolTable = NULL; void Trace::InstRecord::dump(ostream &outs) { + if (flags[PRINT_CYCLE]) ccprintf(outs, "%7d: ", cycle); @@ -64,7 +65,12 @@ Trace::InstRecord::dump(ostream &outs) if (flags[PRINT_THREAD_NUM]) outs << "T" << thread << " : "; - outs << "0x" << hex << PC << " : "; + + std::string str; + if ((debugSymbolTable) && (debugSymbolTable->findNearestSymbol(PC, str))) + outs << "@" << setw(17) << str << " : "; + else + outs << "0x" << hex << PC << " : "; // // Print decoded instruction diff --git a/dev/alpha_console.cc b/dev/alpha_console.cc index 04046557a..111b23c81 100644 --- a/dev/alpha_console.cc +++ b/dev/alpha_console.cc @@ -49,11 +49,13 @@ #include "mem/functional_mem/memory_control.hh" #include "sim/builder.hh" #include "sim/system.hh" +#include "dev/tsunami_io.hh" +#include "sim/sim_object.hh" using namespace std; AlphaConsole::AlphaConsole(const string &name, SimConsole *cons, SimpleDisk *d, - System *system, BaseCPU *cpu, TlaserClock *clock, + System *system, BaseCPU *cpu, SimObject *clock, int num_cpus, MemoryController *mmu, Addr a, HierParams *hier, Bus *bus) : PioDevice(name), disk(d), console(cons), addr(a) @@ -78,8 +80,14 @@ AlphaConsole::AlphaConsole(const string &name, SimConsole *cons, SimpleDisk *d, alphaAccess->numCPUs = num_cpus; alphaAccess->mem_size = system->physmem->size(); alphaAccess->cpuClock = cpu->getFreq() / 1000000; - alphaAccess->intrClockFrequency = clock->frequency(); - + TsunamiIO *clock_linux = dynamic_cast<TsunamiIO *>(clock); + TlaserClock *clock_tru64 = dynamic_cast<TlaserClock *>(clock); + if (clock_linux) + alphaAccess->intrClockFrequency = clock_linux->frequency(); + else if (clock_tru64) + alphaAccess->intrClockFrequency = clock_tru64->frequency(); + else + panic("clock must be of type TlaserClock or TsunamiIO\n"); alphaAccess->diskUnit = 1; } @@ -89,7 +97,7 @@ AlphaConsole::read(MemReqPtr &req, uint8_t *data) memset(data, 0, req->size); uint64_t val; - Addr daddr = req->paddr - addr; + Addr daddr = req->paddr - (addr & PA_IMPL_MASK); switch (daddr) { case offsetof(AlphaAccess, inputChar): @@ -137,7 +145,7 @@ AlphaConsole::write(MemReqPtr &req, const uint8_t *data) return Machine_Check_Fault; } - Addr daddr = req->paddr - addr; + Addr daddr = req->paddr - (addr & PA_IMPL_MASK); ExecContext *other_xc; switch (daddr) { @@ -266,7 +274,7 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaConsole) Param<Addr> addr; SimObjectParam<System *> system; SimObjectParam<BaseCPU *> cpu; - SimObjectParam<TlaserClock *> clock; + SimObjectParam<SimObject *> clock; SimObjectParam<Bus*> io_bus; SimObjectParam<HierParams *> hier; @@ -281,7 +289,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaConsole) INIT_PARAM(addr, "Device Address"), INIT_PARAM(system, "system object"), INIT_PARAM(cpu, "Processor"), - INIT_PARAM(clock, "Turbolaser Clock"), + INIT_PARAM(clock, "Clock"), INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to", NULL), INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams) diff --git a/dev/alpha_console.hh b/dev/alpha_console.hh index b617b64e7..4986d0e5f 100644 --- a/dev/alpha_console.hh +++ b/dev/alpha_console.hh @@ -37,6 +37,8 @@ #include "dev/alpha_access.h" #include "dev/io_device.hh" #include "sim/host.hh" +#include "dev/tsunami_io.hh" +#include "sim/sim_object.hh" class BaseCPU; class SimConsole; @@ -89,7 +91,7 @@ class AlphaConsole : public PioDevice public: /** Standard Constructor */ AlphaConsole(const std::string &name, SimConsole *cons, SimpleDisk *d, - System *system, BaseCPU *cpu, TlaserClock *clock, + System *system, BaseCPU *cpu, SimObject *clock, int num_cpus, MemoryController *mmu, Addr addr, HierParams *hier, Bus *bus); diff --git a/dev/baddev.cc b/dev/baddev.cc new file mode 100644 index 000000000..8a5d68533 --- /dev/null +++ b/dev/baddev.cc @@ -0,0 +1,67 @@ +/* $Id$ */ + +/* @file + * BadDevice implemenation + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "cpu/exec_context.hh" +#include "dev/scsi_ctrl.hh" +#include "dev/baddev.hh" +#include "dev/tsunamireg.h" +#include "dev/tsunami.hh" +#include "mem/functional_mem/memory_control.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; + +BadDevice::BadDevice(const string &name, Addr a, MemoryController *mmu, + const string &devicename) + : FunctionalMemory(name), addr(a), devname(devicename) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); +} + +Fault +BadDevice::read(MemReqPtr &req, uint8_t *data) +{ + + panic("Device %s not imlpmented\n", devname); + return No_Fault; +} + +Fault +BadDevice::write(MemReqPtr &req, const uint8_t *data) +{ + panic("Device %s not imlpmented\n", devname); + return No_Fault; +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(BadDevice) + + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + Param<string> devicename; + +END_DECLARE_SIM_OBJECT_PARAMS(BadDevice) + +BEGIN_INIT_SIM_OBJECT_PARAMS(BadDevice) + + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(addr, "Device Address"), + INIT_PARAM(devicename, "Name of device to error on") + +END_INIT_SIM_OBJECT_PARAMS(BadDevice) + +CREATE_SIM_OBJECT(BadDevice) +{ + return new BadDevice(getInstanceName(), addr, mmu, devicename); +} + +REGISTER_SIM_OBJECT("BadDevice", BadDevice) diff --git a/dev/baddev.hh b/dev/baddev.hh new file mode 100644 index 000000000..e0304d5d5 --- /dev/null +++ b/dev/baddev.hh @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * This devices just panics when touched. For example if you have a + * kernel that touches the frame buffer which isn't allowed. + */ + +#ifndef __BADDEV_HH__ +#define __BADDEV_HH__ + +#include "mem/functional_mem/functional_memory.hh" + +/** + * BadDevice + * This device just panics when accessed. It is supposed to warn + * the user that the kernel they are running has unsupported + * options (i.e. frame buffer) + */ +class BadDevice : public FunctionalMemory +{ + private: + Addr addr; + static const Addr size = 0xf; + + std::string devname; + + public: + /** + * Constructor for the Baddev Class. + * @param name name of the object + * @param a base address of the write + * @param mmu the memory controller + * @param devicename device that is not implemented + */ + BadDevice(const std::string &name, Addr a, MemoryController *mmu, + const std::string &devicename); + + /** + * On a read event we just panic aand hopefully print a + * meaningful error message. + * @param req Contains the address to read from. + * @param data A pointer to write the read data to. + * @return The fault condition of the access. + */ + virtual Fault read(MemReqPtr &req, uint8_t *data); + + /** + * On a write event we just panic aand hopefully print a + * meaningful error message. + * @param req Contains the address to write to. + * @param data The data to write. + * @return The fault condition of the access. + */ + virtual Fault write(MemReqPtr &req, const uint8_t *data); + + /** @todo add serialize/unserialize */ +}; + +#endif // __BADDEV_HH__ diff --git a/dev/console.cc b/dev/console.cc index 5e7b0abf6..ef3f64d8d 100644 --- a/dev/console.cc +++ b/dev/console.cc @@ -49,6 +49,7 @@ #include "mem/functional_mem/memory_control.hh" #include "sim/builder.hh" #include "targetarch/ev5.hh" +#include "dev/platform.hh" using namespace std; @@ -76,7 +77,7 @@ SimConsole::SimConsole(const string &name, const string &file, int num) #if TRACING_ON == 1 linebuf(16384), #endif - _status(0), _enable(0), intr(NULL) + _status(0), _enable(0), intr(NULL), platform(NULL) { if (!file.empty()) outfile = new ofstream(file.c_str()); @@ -322,8 +323,8 @@ SimConsole::clearInt(int i) { int old = _status; _status &= ~i; - if (MaskStatus(old, _enable) != MaskStatus(_status, _enable) && intr) - intr->clear(TheISA::INTLEVEL_IRQ0); + //if (MaskStatus(old, _enable) != MaskStatus(_status, _enable) && intr) + platform->clearConsoleInt(); return old; } @@ -331,10 +332,10 @@ SimConsole::clearInt(int i) void SimConsole::raiseInt(int i) { - int old = _status; + //int old = _status; _status |= i; - if (MaskStatus(old, _enable) != MaskStatus(_status, _enable) && intr) - intr->post(TheISA::INTLEVEL_IRQ0); + //if (MaskStatus(old, _enable) != MaskStatus(_status, _enable) && intr) + platform->postConsoleInt(); } void @@ -357,14 +358,21 @@ SimConsole::setInt(int bits) old = _enable; _enable |= bits; - if (MaskStatus(_status, old) != MaskStatus(_status, _enable) && intr) { + //if (MaskStatus(_status, old) != MaskStatus(_status, _enable) && intr) { + if (intr) { if (MaskStatus(_status, _enable)) - intr->post(TheISA::INTLEVEL_IRQ0); + platform->postConsoleInt(); else - intr->clear(TheISA::INTLEVEL_IRQ0); + platform->clearConsoleInt(); } } +void +SimConsole::setPlatform(Platform *p) +{ + platform = p; + platform->cons = this; +} void SimConsole::serialize(ostream &os) @@ -381,6 +389,7 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimConsole) SimObjectParam<ConsoleListener *> listener; SimObjectParam<IntrControl *> intr_control; + SimObjectParam<Platform *> platform; Param<string> output; Param<bool> append_name; Param<int> number; @@ -391,6 +400,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(SimConsole) INIT_PARAM(listener, "console listener"), INIT_PARAM(intr_control, "interrupt controller"), + INIT_PARAM(platform, "platform"), INIT_PARAM_DFLT(output, "file to dump output to", ""), INIT_PARAM_DFLT(append_name, "append name() to filename", true), INIT_PARAM_DFLT(number, "console number", 0) @@ -413,8 +423,9 @@ CREATE_SIM_OBJECT(SimConsole) SimConsole *console = new SimConsole(getInstanceName(), filename, number); ((ConsoleListener *)listener)->add(console); ((SimConsole *)console)->initInt(intr_control); - ((SimConsole *)console)->setInt(SimConsole::TransmitInterrupt | - SimConsole::ReceiveInterrupt); + ((SimConsole *)console)->setPlatform(platform); + //((SimConsole *)console)->setInt(SimConsole::TransmitInterrupt | + // SimConsole::ReceiveInterrupt); return console; } diff --git a/dev/console.hh b/dev/console.hh index d2bba4612..703d05d51 100644 --- a/dev/console.hh +++ b/dev/console.hh @@ -103,6 +103,8 @@ class SimConsole : public SimObject // interrupt handle IntrControl *intr; + // Platform so we can post interrupts + Platform *platform; public: ///////////////// @@ -143,6 +145,8 @@ class SimConsole : public SimObject void initInt(IntrControl *i); void setInt(int bits); + void setPlatform(Platform *p); + virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); }; diff --git a/dev/etherpkt.cc b/dev/etherpkt.cc index cb6087c89..9eda89e9d 100644 --- a/dev/etherpkt.cc +++ b/dev/etherpkt.cc @@ -41,7 +41,7 @@ EtherPacket::serialize(ostream &os) } void -EtherPacket::unserialize(Checkpoint *cp, const std::string §ion) +EtherPacket::unserialize(Checkpoint *cp, const string §ion) { UNSERIALIZE_SCALAR(length); data = new uint8_t[length]; diff --git a/dev/etherpkt.hh b/dev/etherpkt.hh index 27ac526d6..09516c427 100644 --- a/dev/etherpkt.hh +++ b/dev/etherpkt.hh @@ -39,8 +39,66 @@ #include "sim/host.hh" #include "base/refcnt.hh" +#define EADDR_LEN 6 + class Checkpoint; +struct pseudo_header +{ + uint32_t src_ip_addr; + uint32_t dest_ip_addr; + uint16_t protocol; + uint16_t len; +}; + +/** Ethernet header struct for casting purposes */ +struct eth_header +{ + uint8_t dest[EADDR_LEN]; + uint8_t src[EADDR_LEN]; + uint16_t type; +}; + +struct ip_header +{ + uint8_t vers_len; + uint8_t service_type; + uint16_t dgram_len; + uint16_t ID; + uint16_t flags_frag_offset; + uint8_t TTL; + uint8_t protocol; + uint16_t hdr_chksum; + uint32_t src_ip_addr; + uint32_t dest_ip_addr; + uint8_t *options; + uint8_t *transport_header; +}; + +struct tcp_header +{ + uint16_t src_port_num; + uint16_t dest_port_num; + uint32_t seq_num; + uint32_t ack_num; + uint8_t hdr_len; + uint8_t flags; + uint16_t rcv_window; + uint16_t chksum; + uint16_t urgent; + uint8_t *options; + uint8_t *data; +}; + +struct udp_header +{ + uint16_t src_port_num; + uint16_t dest_port_num; + uint16_t len; + uint16_t chksum; + uint8_t *data; +}; + /* * Reference counted class containing ethernet packet data */ @@ -61,6 +119,16 @@ class EtherPacket : public RefCounted bool IsMulticast() { return data[0] == 0x01; } bool IsBroadcast() { return data[0] == 0xff; } + ip_header *getIpHdr() { return (ip_header *) (data + 14); } + + void *getTransportHdr() { + ip_header *ip = getIpHdr(); + return (void *) (ip + (ip->vers_len & 0xf)); + } + + + typedef RefCountingPtr<EtherPacket> PacketPtr; + void serialize(std::ostream &os); void unserialize(Checkpoint *cp, const std::string §ion); }; diff --git a/dev/ide_ctrl.cc b/dev/ide_ctrl.cc new file mode 100644 index 000000000..f78a8e1ef --- /dev/null +++ b/dev/ide_ctrl.cc @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2003 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. + */ + +#include <cstddef> +#include <cstdlib> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "cpu/intr_control.hh" +#include "dev/dma.hh" +#include "dev/pcireg.h" +#include "dev/pciconfigall.hh" +#include "dev/ide_disk.hh" +#include "dev/ide_ctrl.hh" +#include "dev/tsunami_cchip.hh" +#include "mem/bus/bus.hh" +#include "mem/bus/pio_interface.hh" +#include "mem/bus/pio_interface_impl.hh" +#include "mem/bus/dma_interface.hh" +#include "dev/tsunami.hh" +#include "mem/functional_mem/memory_control.hh" +#include "mem/functional_mem/physical_memory.hh" +#include "sim/builder.hh" +#include "sim/sim_object.hh" + +using namespace std; + +//// +// Initialization and destruction +//// + +IdeController::IdeController(const string &name, IntrControl *ic, + const vector<IdeDisk *> &new_disks, + MemoryController *mmu, PciConfigAll *cf, + PciConfigData *cd, Tsunami *t, uint32_t bus_num, + uint32_t dev_num, uint32_t func_num, + Bus *host_bus, HierParams *hier) + : PciDev(name, mmu, cf, cd, bus_num, dev_num, func_num), tsunami(t) +{ + // put back pointer into Tsunami + tsunami->disk_controller = this; + + // initialize the PIO interface addresses + pri_cmd_addr = 0; + pri_cmd_size = BARSize[0]; + + pri_ctrl_addr = 0; + pri_ctrl_size = BARSize[1]; + + sec_cmd_addr = 0; + sec_cmd_size = BARSize[2]; + + sec_ctrl_addr = 0; + sec_ctrl_size = BARSize[3]; + + // initialize the bus master interface (BMI) address to be configured + // via PCI + bmi_addr = 0; + bmi_size = BARSize[4]; + + // zero out all of the registers + memset(bmi_regs, 0, sizeof(bmi_regs)); + memset(pci_regs, 0, sizeof(pci_regs)); + + // setup initial values + *(uint32_t *)&pci_regs[IDETIM] = 0x80008000; // enable both channels + *(uint8_t *)&bmi_regs[BMIS0] = 0x60; + *(uint8_t *)&bmi_regs[BMIS1] = 0x60; + + // reset all internal variables + io_enabled = false; + bm_enabled = false; + memset(cmd_in_progress, 0, sizeof(cmd_in_progress)); + + // create the PIO and DMA interfaces + if (host_bus) { + pioInterface = newPioInterface(name, hier, host_bus, this, + &IdeController::cacheAccess); + + dmaInterface = new DMAInterface<Bus>(name + ".dma", host_bus, + host_bus, 1); + } + + // setup the disks attached to controller + memset(disks, 0, sizeof(IdeDisk *) * 4); + + if (new_disks.size() > 3) + panic("IDE controllers support a maximum of 4 devices attached!\n"); + + for (int i = 0; i < new_disks.size(); i++) { + disks[i] = new_disks[i]; + disks[i]->setController(this, dmaInterface); + } +} + +IdeController::~IdeController() +{ + for (int i = 0; i < 4; i++) + if (disks[i]) + delete disks[i]; +} + +//// +// Utility functions +/// + +void +IdeController::parseAddr(const Addr &addr, Addr &offset, bool &primary, + RegType_t &type) +{ + offset = addr; + + if (addr >= pri_cmd_addr && addr < (pri_cmd_addr + pri_cmd_size)) { + offset -= pri_cmd_addr; + type = COMMAND_BLOCK; + primary = true; + } else if (addr >= pri_ctrl_addr && + addr < (pri_ctrl_addr + pri_ctrl_size)) { + offset -= pri_ctrl_addr; + type = CONTROL_BLOCK; + primary = true; + } else if (addr >= sec_cmd_addr && + addr < (sec_cmd_addr + sec_cmd_size)) { + offset -= sec_cmd_addr; + type = COMMAND_BLOCK; + primary = false; + } else if (addr >= sec_ctrl_addr && + addr < (sec_ctrl_addr + sec_ctrl_size)) { + offset -= sec_ctrl_addr; + type = CONTROL_BLOCK; + primary = false; + } else if (addr >= bmi_addr && addr < (bmi_addr + bmi_size)) { + offset -= bmi_addr; + type = BMI_BLOCK; + primary = (offset < BMIC1) ? true : false; + } else { + panic("IDE controller access to invalid address: %#x\n", addr); + } +} + +int +IdeController::getDisk(bool primary) +{ + int disk = 0; + uint8_t *devBit = &dev[0]; + + if (!primary) { + disk += 2; + devBit = &dev[1]; + } + + disk += *devBit; + + assert(*devBit == 0 || *devBit == 1); + + return disk; +} + +int +IdeController::getDisk(IdeDisk *diskPtr) +{ + for (int i = 0; i < 4; i++) { + if ((long)diskPtr == (long)disks[i]) + return i; + } + return -1; +} + +//// +// Command completion +//// + +void +IdeController::setDmaComplete(IdeDisk *disk) +{ + int diskNum = getDisk(disk); + + if (diskNum < 0) + panic("Unable to find disk based on pointer %#x\n", disk); + + if (diskNum < 2) { + // clear the start/stop bit in the command register + bmi_regs[BMIC0] &= ~SSBM; + // clear the bus master active bit in the status register + bmi_regs[BMIS0] &= ~BMIDEA; + // set the interrupt bit + bmi_regs[BMIS0] |= IDEINTS; + } else { + // clear the start/stop bit in the command register + bmi_regs[BMIC1] &= ~SSBM; + // clear the bus master active bit in the status register + bmi_regs[BMIS1] &= ~BMIDEA; + // set the interrupt bit + bmi_regs[BMIS1] |= IDEINTS; + } +} + +//// +// Interrupt handling +//// + +void +IdeController::intrPost() +{ + tsunami->cchip->postDRIR(configData->config.hdr.pci0.interruptLine); +} + +void +IdeController::intrClear() +{ + tsunami->cchip->clearDRIR(configData->config.hdr.pci0.interruptLine); +} + +//// +// Bus timing and bus access functions +//// + +Tick +IdeController::cacheAccess(MemReqPtr &req) +{ + // @todo Add more accurate timing to cache access + return curTick + 1000; +} + +//// +// Read and write handling +//// + +void +IdeController::ReadConfig(int offset, int size, uint8_t *data) +{ + Addr origOffset = offset; + + if (offset < PCI_DEVICE_SPECIFIC) { + PciDev::ReadConfig(offset, size, data); + } else { + if (offset >= PCI_IDE_TIMING && offset < (PCI_IDE_TIMING + 4)) { + offset -= PCI_IDE_TIMING; + offset += IDETIM; + + if ((offset + size) > (IDETIM + 4)) + panic("PCI read of IDETIM with invalid size\n"); + } else if (offset == PCI_SLAVE_TIMING) { + offset -= PCI_SLAVE_TIMING; + offset += SIDETIM; + + if ((offset + size) > (SIDETIM + 1)) + panic("PCI read of SIDETIM with invalid size\n"); + } else if (offset == PCI_UDMA33_CTRL) { + offset -= PCI_UDMA33_CTRL; + offset += UDMACTL; + + if ((offset + size) > (UDMACTL + 1)) + panic("PCI read of UDMACTL with invalid size\n"); + } else if (offset >= PCI_UDMA33_TIMING && + offset < (PCI_UDMA33_TIMING + 2)) { + offset -= PCI_UDMA33_TIMING; + offset += UDMATIM; + + if ((offset + size) > (UDMATIM + 2)) + panic("PCI read of UDMATIM with invalid size\n"); + } else { + panic("PCI read of unimplemented register: %x\n", offset); + } + + memcpy((void *)data, (void *)&pci_regs[offset], size); + } + + DPRINTF(IdeCtrl, "IDE PCI read offset: %#x (%#x) size: %#x data: %#x\n", + origOffset, offset, size, *(uint32_t *)data); +} + +void +IdeController::WriteConfig(int offset, int size, uint32_t data) +{ + DPRINTF(IdeCtrl, "IDE PCI write offset: %#x size: %#x data: %#x\n", + offset, size, data); + + // do standard write stuff if in standard PCI space + if (offset < PCI_DEVICE_SPECIFIC) { + PciDev::WriteConfig(offset, size, data); + } else { + if (offset >= PCI_IDE_TIMING && offset < (PCI_IDE_TIMING + 4)) { + offset -= PCI_IDE_TIMING; + offset += IDETIM; + + if ((offset + size) > (IDETIM + 4)) + panic("PCI write to IDETIM with invalid size\n"); + } else if (offset == PCI_SLAVE_TIMING) { + offset -= PCI_SLAVE_TIMING; + offset += SIDETIM; + + if ((offset + size) > (SIDETIM + 1)) + panic("PCI write to SIDETIM with invalid size\n"); + } else if (offset == PCI_UDMA33_CTRL) { + offset -= PCI_UDMA33_CTRL; + offset += UDMACTL; + + if ((offset + size) > (UDMACTL + 1)) + panic("PCI write to UDMACTL with invalid size\n"); + } else if (offset >= PCI_UDMA33_TIMING && + offset < (PCI_UDMA33_TIMING + 2)) { + offset -= PCI_UDMA33_TIMING; + offset += UDMATIM; + + if ((offset + size) > (UDMATIM + 2)) + panic("PCI write to UDMATIM with invalid size\n"); + } else { + panic("PCI write to unimplemented register: %x\n", offset); + } + + memcpy((void *)&pci_regs[offset], (void *)&data, size); + } + + if (offset == PCI_COMMAND) { + if (config.data[offset] & IOSE) + io_enabled = true; + else + io_enabled = false; + + if (config.data[offset] & BME) + bm_enabled = true; + else + bm_enabled = false; + + } else if (data != 0xffffffff) { + switch (offset) { + case PCI0_BASE_ADDR0: + pri_cmd_addr = BARAddrs[0]; + if (pioInterface) + pioInterface->addAddrRange(pri_cmd_addr, + pri_cmd_addr + pri_cmd_size - 1); + + pri_cmd_addr = pri_cmd_addr & PA_UNCACHED_MASK; + break; + + case PCI0_BASE_ADDR1: + pri_ctrl_addr = BARAddrs[1]; + if (pioInterface) + pioInterface->addAddrRange(pri_ctrl_addr, + pri_ctrl_addr + pri_ctrl_size - 1); + + pri_ctrl_addr = pri_ctrl_addr & PA_UNCACHED_MASK; + break; + + case PCI0_BASE_ADDR2: + sec_cmd_addr = BARAddrs[2]; + if (pioInterface) + pioInterface->addAddrRange(sec_cmd_addr, + sec_cmd_addr + sec_cmd_size - 1); + + sec_cmd_addr = sec_cmd_addr & PA_UNCACHED_MASK; + break; + + case PCI0_BASE_ADDR3: + sec_ctrl_addr = BARAddrs[3]; + if (pioInterface) + pioInterface->addAddrRange(sec_ctrl_addr, + sec_ctrl_addr + sec_ctrl_size - 1); + + sec_ctrl_addr = sec_ctrl_addr & PA_UNCACHED_MASK; + break; + + case PCI0_BASE_ADDR4: + bmi_addr = BARAddrs[4]; + if (pioInterface) + pioInterface->addAddrRange(bmi_addr, bmi_addr + bmi_size - 1); + + bmi_addr = bmi_addr & PA_UNCACHED_MASK; + break; + } + } +} + +Fault +IdeController::read(MemReqPtr &req, uint8_t *data) +{ + Addr offset; + bool primary; + bool byte; + bool cmdBlk; + RegType_t type; + int disk; + + parseAddr(req->paddr, offset, primary, type); + byte = (req->size == sizeof(uint8_t)) ? true : false; + cmdBlk = (type == COMMAND_BLOCK) ? true : false; + + if (!io_enabled) + return No_Fault; + + // sanity check the size (allows byte, word, or dword access) + if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) && + req->size != sizeof(uint32_t)) + panic("IDE controller read of invalid size: %#x\n", req->size); + + if (type != BMI_BLOCK) { + assert(req->size != sizeof(uint32_t)); + + disk = getDisk(primary); + if (disks[disk]) + disks[disk]->read(offset, byte, cmdBlk, data); + } else { + memcpy((void *)data, &bmi_regs[offset], req->size); + } + + DPRINTF(IdeCtrl, "IDE read from offset: %#x size: %#x data: %#x\n", + offset, req->size, *(uint32_t *)data); + + return No_Fault; +} + +Fault +IdeController::write(MemReqPtr &req, const uint8_t *data) +{ + Addr offset; + bool primary; + bool byte; + bool cmdBlk; + RegType_t type; + int disk; + + parseAddr(req->paddr, offset, primary, type); + byte = (req->size == sizeof(uint8_t)) ? true : false; + cmdBlk = (type == COMMAND_BLOCK) ? true : false; + + DPRINTF(IdeCtrl, "IDE write from offset: %#x size: %#x data: %#x\n", + offset, req->size, *(uint32_t *)data); + + uint8_t oldVal, newVal; + + if (!io_enabled) + return No_Fault; + + if (type == BMI_BLOCK && !bm_enabled) + return No_Fault; + + if (type != BMI_BLOCK) { + // shadow the dev bit + if (type == COMMAND_BLOCK && offset == IDE_SELECT_OFFSET) { + uint8_t *devBit = (primary ? &dev[0] : &dev[1]); + *devBit = ((*data & IDE_SELECT_DEV_BIT) ? 1 : 0); + } + + assert(req->size != sizeof(uint32_t)); + + disk = getDisk(primary); + if (disks[disk]) + disks[disk]->write(offset, byte, cmdBlk, data); + } else { + switch (offset) { + // Bus master IDE command register + case BMIC1: + case BMIC0: + if (req->size != sizeof(uint8_t)) + panic("Invalid BMIC write size: %x\n", req->size); + + // select the current disk based on DEV bit + disk = getDisk(primary); + + oldVal = bmi_regs[offset]; + newVal = *data; + + // if a DMA transfer is in progress, R/W control cannot change + if (oldVal & SSBM) { + if ((oldVal & RWCON) ^ (newVal & RWCON)) { + (oldVal & RWCON) ? newVal |= RWCON : newVal &= ~RWCON; + } + } + + // see if the start/stop bit is being changed + if ((oldVal & SSBM) ^ (newVal & SSBM)) { + if (oldVal & SSBM) { + // stopping DMA transfer + DPRINTF(IdeCtrl, "Stopping DMA transfer\n"); + + // clear the BMIDEA bit + bmi_regs[offset + 0x2] &= ~BMIDEA; + + if (disks[disk] == NULL) + panic("DMA stop for disk %d which does not exist\n", + disk); + + // inform the disk of the DMA transfer abort + disks[disk]->abortDma(); + } else { + // starting DMA transfer + DPRINTF(IdeCtrl, "Starting DMA transfer\n"); + + // set the BMIDEA bit + bmi_regs[offset + 0x2] |= BMIDEA; + + if (disks[disk] == NULL) + panic("DMA start for disk %d which does not exist\n", + disk); + + // inform the disk of the DMA transfer start + if (primary) + disks[disk]->startDma(*(uint32_t *)&bmi_regs[BMIDTP0]); + else + disks[disk]->startDma(*(uint32_t *)&bmi_regs[BMIDTP1]); + } + } + + // update the register value + bmi_regs[offset] = newVal; + break; + + // Bus master IDE status register + case BMIS0: + case BMIS1: + if (req->size != sizeof(uint8_t)) + panic("Invalid BMIS write size: %x\n", req->size); + + oldVal = bmi_regs[offset]; + newVal = *data; + + // the BMIDEA bit is RO + newVal |= (oldVal & BMIDEA); + + // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each + if ((oldVal & IDEINTS) && (newVal & IDEINTS)) + newVal &= ~IDEINTS; // clear the interrupt? + else + (oldVal & IDEINTS) ? newVal |= IDEINTS : newVal &= ~IDEINTS; + + if ((oldVal & IDEDMAE) && (newVal & IDEDMAE)) + newVal &= ~IDEDMAE; + else + (oldVal & IDEDMAE) ? newVal |= IDEDMAE : newVal &= ~IDEDMAE; + + bmi_regs[offset] = newVal; + break; + + // Bus master IDE descriptor table pointer register + case BMIDTP0: + case BMIDTP1: + if (req->size != sizeof(uint32_t)) + panic("Invalid BMIDTP write size: %x\n", req->size); + + *(uint32_t *)&bmi_regs[offset] = *(uint32_t *)data & ~0x3; + break; + + default: + if (req->size != sizeof(uint8_t) && + req->size != sizeof(uint16_t) && + req->size != sizeof(uint32_t)) + panic("IDE controller write of invalid write size: %x\n", + req->size); + + // do a default copy of data into the registers + memcpy((void *)&bmi_regs[offset], data, req->size); + } + } + + return No_Fault; +} + +//// +// Serialization +//// + +void +IdeController::serialize(std::ostream &os) +{ + // Serialize register addresses and sizes + SERIALIZE_SCALAR(pri_cmd_addr); + SERIALIZE_SCALAR(pri_cmd_size); + SERIALIZE_SCALAR(pri_ctrl_addr); + SERIALIZE_SCALAR(pri_ctrl_size); + SERIALIZE_SCALAR(sec_cmd_addr); + SERIALIZE_SCALAR(sec_cmd_size); + SERIALIZE_SCALAR(sec_ctrl_addr); + SERIALIZE_SCALAR(sec_ctrl_size); + SERIALIZE_SCALAR(bmi_addr); + SERIALIZE_SCALAR(bmi_size); + + // Serialize registers + SERIALIZE_ARRAY(bmi_regs, 16); + SERIALIZE_ARRAY(dev, 2); + SERIALIZE_ARRAY(pci_regs, 8); + + // Serialize internal state + SERIALIZE_SCALAR(io_enabled); + SERIALIZE_SCALAR(bm_enabled); + SERIALIZE_ARRAY(cmd_in_progress, 4); +} + +void +IdeController::unserialize(Checkpoint *cp, const std::string §ion) +{ + // Unserialize register addresses and sizes + UNSERIALIZE_SCALAR(pri_cmd_addr); + UNSERIALIZE_SCALAR(pri_cmd_size); + UNSERIALIZE_SCALAR(pri_ctrl_addr); + UNSERIALIZE_SCALAR(pri_ctrl_size); + UNSERIALIZE_SCALAR(sec_cmd_addr); + UNSERIALIZE_SCALAR(sec_cmd_size); + UNSERIALIZE_SCALAR(sec_ctrl_addr); + UNSERIALIZE_SCALAR(sec_ctrl_size); + UNSERIALIZE_SCALAR(bmi_addr); + UNSERIALIZE_SCALAR(bmi_size); + + // Unserialize registers + UNSERIALIZE_ARRAY(bmi_regs, 16); + UNSERIALIZE_ARRAY(dev, 2); + UNSERIALIZE_ARRAY(pci_regs, 8); + + // Unserialize internal state + UNSERIALIZE_SCALAR(io_enabled); + UNSERIALIZE_SCALAR(bm_enabled); + UNSERIALIZE_ARRAY(cmd_in_progress, 4); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeController) + + SimObjectParam<IntrControl *> intr_ctrl; + SimObjectVectorParam<IdeDisk *> disks; + SimObjectParam<MemoryController *> mmu; + SimObjectParam<PciConfigAll *> configspace; + SimObjectParam<PciConfigData *> configdata; + SimObjectParam<Tsunami *> tsunami; + Param<uint32_t> pci_bus; + Param<uint32_t> pci_dev; + Param<uint32_t> pci_func; + SimObjectParam<Bus *> host_bus; + SimObjectParam<HierParams *> hier; + +END_DECLARE_SIM_OBJECT_PARAMS(IdeController) + +BEGIN_INIT_SIM_OBJECT_PARAMS(IdeController) + + INIT_PARAM(intr_ctrl, "Interrupt Controller"), + INIT_PARAM(disks, "IDE disks attached to this controller"), + INIT_PARAM(mmu, "Memory controller"), + INIT_PARAM(configspace, "PCI Configspace"), + INIT_PARAM(configdata, "PCI Config data"), + INIT_PARAM(tsunami, "Tsunami chipset pointer"), + INIT_PARAM(pci_bus, "PCI bus ID"), + INIT_PARAM(pci_dev, "PCI device number"), + INIT_PARAM(pci_func, "PCI function code"), + INIT_PARAM_DFLT(host_bus, "Host bus to attach to", NULL), + INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams) + +END_INIT_SIM_OBJECT_PARAMS(IdeController) + +CREATE_SIM_OBJECT(IdeController) +{ + return new IdeController(getInstanceName(), intr_ctrl, disks, mmu, + configspace, configdata, tsunami, pci_bus, + pci_dev, pci_func, host_bus, hier); +} + +REGISTER_SIM_OBJECT("IdeController", IdeController) + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/dev/ide_ctrl.hh b/dev/ide_ctrl.hh new file mode 100644 index 000000000..9698724c1 --- /dev/null +++ b/dev/ide_ctrl.hh @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2003 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. + */ + +/** @file + * Simple PCI IDE controller with bus mastering capability and UDMA + * modeled after controller in the Intel PIIX4 chip + */ + +#ifndef __IDE_CTRL_HH__ +#define __IDE_CTRL_HH__ + +#include "dev/pcidev.hh" +#include "dev/pcireg.h" +#include "dev/io_device.hh" + +#define BMIC0 0x0 // Bus master IDE command register +#define BMIS0 0x2 // Bus master IDE status register +#define BMIDTP0 0x4 // Bus master IDE descriptor table pointer register +#define BMIC1 0x8 // Bus master IDE command register +#define BMIS1 0xa // Bus master IDE status register +#define BMIDTP1 0xc // Bus master IDE descriptor table pointer register + +// Bus master IDE command register bit fields +#define RWCON 0x08 // Bus master read/write control +#define SSBM 0x01 // Start/stop bus master + +// Bus master IDE status register bit fields +#define DMA1CAP 0x40 // Drive 1 DMA capable +#define DMA0CAP 0x20 // Drive 0 DMA capable +#define IDEINTS 0x04 // IDE Interrupt Status +#define IDEDMAE 0x02 // IDE DMA error +#define BMIDEA 0x01 // Bus master IDE active + +// IDE Command byte fields +#define IDE_SELECT_OFFSET (6) +#define IDE_SELECT_DEV_BIT 0x10 + +#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET +#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET + +// PCI device specific register byte offsets +#define PCI_IDE_TIMING 0x40 +#define PCI_SLAVE_TIMING 0x44 +#define PCI_UDMA33_CTRL 0x48 +#define PCI_UDMA33_TIMING 0x4a + +#define IDETIM (0) +#define SIDETIM (4) +#define UDMACTL (5) +#define UDMATIM (6) + +// PCI Command bit fields +#define BME 0x04 // Bus master function enable +#define IOSE 0x01 // I/O space enable + +typedef enum RegType { + COMMAND_BLOCK = 0, + CONTROL_BLOCK, + BMI_BLOCK +} RegType_t; + +class IdeDisk; +class IntrControl; +class PciConfigAll; +class Tsunami; +class PhysicalMemory; +class BaseInterface; +class HierParams; +class Bus; + +/** + * Device model for an Intel PIIX4 IDE controller + */ + +class IdeController : public PciDev +{ + private: + /** Primary command block registers */ + Addr pri_cmd_addr; + Addr pri_cmd_size; + /** Primary control block registers */ + Addr pri_ctrl_addr; + Addr pri_ctrl_size; + /** Secondary command block registers */ + Addr sec_cmd_addr; + Addr sec_cmd_size; + /** Secondary control block registers */ + Addr sec_ctrl_addr; + Addr sec_ctrl_size; + /** Bus master interface (BMI) registers */ + Addr bmi_addr; + Addr bmi_size; + + private: + /** Registers used for bus master interface */ + uint8_t bmi_regs[16]; + /** Shadows of the device select bit */ + uint8_t dev[2]; + /** Registers used in PCI configuration */ + uint8_t pci_regs[8]; + + // Internal management variables + bool io_enabled; + bool bm_enabled; + bool cmd_in_progress[4]; + + public: + /** Pointer to the chipset */ + Tsunami *tsunami; + + private: + /** IDE disks connected to controller */ + IdeDisk *disks[4]; + + private: + /** Parse the access address to pass on to device */ + void parseAddr(const Addr &addr, Addr &offset, bool &primary, + RegType_t &type); + + /** Select the disk based on the channel and device bit */ + int getDisk(bool primary); + + /** Select the disk based on a pointer */ + int getDisk(IdeDisk *diskPtr); + + public: + /** + * Constructs and initializes this controller. + * @param name The name of this controller. + * @param ic The interrupt controller. + * @param mmu The memory controller + * @param cf PCI config space + * @param cd PCI config data + * @param bus_num The PCI bus number + * @param dev_num The PCI device number + * @param func_num The PCI function number + * @param host_bus The host bus to connect to + * @param hier The hierarchy parameters + */ + IdeController(const std::string &name, IntrControl *ic, + const std::vector<IdeDisk *> &new_disks, + MemoryController *mmu, PciConfigAll *cf, + PciConfigData *cd, Tsunami *t, + uint32_t bus_num, uint32_t dev_num, uint32_t func_num, + Bus *host_bus, HierParams *hier); + + /** + * Deletes the connected devices. + */ + ~IdeController(); + + virtual void WriteConfig(int offset, int size, uint32_t data); + virtual void ReadConfig(int offset, int size, uint8_t *data); + + void intrPost(); + void intrClear(); + + void setDmaComplete(IdeDisk *disk); + + /** + * Read a done field for a given target. + * @param req Contains the address of the field to read. + * @param data Return the field read. + * @return The fault condition of the access. + */ + virtual Fault read(MemReqPtr &req, uint8_t *data); + + /** + * Write to the mmapped I/O control registers. + * @param req Contains the address to write to. + * @param data The data to write. + * @return The fault condition of the access. + */ + virtual Fault write(MemReqPtr &req, const uint8_t *data); + + /** + * Cache access timing specific to device + * @param req Memory request + */ + Tick cacheAccess(MemReqPtr &req); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +}; +#endif // __IDE_CTRL_HH_ diff --git a/dev/ide_disk.cc b/dev/ide_disk.cc new file mode 100644 index 000000000..ddd4a09e7 --- /dev/null +++ b/dev/ide_disk.cc @@ -0,0 +1,1165 @@ +/* + * Copyright (c) 2003 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. + */ + +/** @file + * Device model implementation for an IDE disk + */ + +#include <cerrno> +#include <cstring> +#include <deque> +#include <string> + +#include "arch/alpha/pmap.h" +#include "base/cprintf.hh" // csprintf +#include "base/trace.hh" +#include "dev/disk_image.hh" +#include "dev/ide_disk.hh" +#include "dev/ide_ctrl.hh" +#include "dev/tsunami.hh" +#include "dev/tsunami_pchip.hh" +#include "mem/functional_mem/physical_memory.hh" +#include "mem/bus/bus.hh" +#include "mem/bus/dma_interface.hh" +#include "mem/bus/pio_interface.hh" +#include "mem/bus/pio_interface_impl.hh" +#include "sim/builder.hh" +#include "sim/sim_object.hh" +#include "sim/universe.hh" + +using namespace std; + +IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys, + int id, int delay) + : SimObject(name), ctrl(NULL), image(img), physmem(phys), + dmaTransferEvent(this), dmaReadWaitEvent(this), + dmaWriteWaitEvent(this), dmaPrdReadEvent(this), + dmaReadEvent(this), dmaWriteEvent(this) +{ + // calculate disk delay in microseconds + diskDelay = (delay * ticksPerSecond / 100000); + + // initialize the data buffer and shadow registers + dataBuffer = new uint8_t[MAX_DMA_SIZE]; + + memset(dataBuffer, 0, MAX_DMA_SIZE); + memset(&cmdReg, 0, sizeof(CommandReg_t)); + memset(&curPrd.entry, 0, sizeof(PrdEntry_t)); + + dmaInterfaceBytes = 0; + curPrdAddr = 0; + curSector = 0; + curCommand = 0; + cmdBytesLeft = 0; + drqBytesLeft = 0; + dmaRead = false; + intrPending = false; + + // fill out the drive ID structure + memset(&driveID, 0, sizeof(struct hd_driveid)); + + // Calculate LBA and C/H/S values + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + + uint32_t lba_size = image->size(); + if (lba_size >= 16383*16*63) { + cylinders = 16383; + heads = 16; + sectors = 63; + } else { + if (lba_size >= 63) + sectors = 63; + else + sectors = lba_size; + + if ((lba_size / sectors) >= 16) + heads = 16; + else + heads = (lba_size / sectors); + + cylinders = lba_size / (heads * sectors); + } + + // Setup the model name + sprintf((char *)driveID.model, "5MI EDD si k"); + // Set the maximum multisector transfer size + driveID.max_multsect = MAX_MULTSECT; + // IORDY supported, IORDY disabled, LBA enabled, DMA enabled + driveID.capability = 0x7; + // UDMA support, EIDE support + driveID.field_valid = 0x6; + // Setup default C/H/S settings + driveID.cyls = cylinders; + driveID.sectors = sectors; + driveID.heads = heads; + // Setup the current multisector transfer size + driveID.multsect = MAX_MULTSECT; + driveID.multsect_valid = 0x1; + // Number of sectors on disk + driveID.lba_capacity = lba_size; + // Multiword DMA mode 2 and below supported + driveID.dma_mword = 0x400; + // Set PIO mode 4 and 3 supported + driveID.eide_pio_modes = 0x3; + // Set DMA mode 4 and below supported + driveID.dma_ultra = 0x10; + // Statically set hardware config word + driveID.hw_config = 0x4001; + + // set the device state to idle + dmaState = Dma_Idle; + + if (id == DEV0) { + devState = Device_Idle_S; + devID = DEV0; + } else if (id == DEV1) { + devState = Device_Idle_NS; + devID = DEV1; + } else { + panic("Invalid device ID: %#x\n", id); + } + + // set the device ready bit + cmdReg.status |= STATUS_DRDY_BIT; +} + +IdeDisk::~IdeDisk() +{ + // destroy the data buffer + delete [] dataBuffer; +} + +//// +// Utility functions +//// + +Addr +IdeDisk::pciToDma(Addr pciAddr) +{ + if (ctrl) + return ctrl->tsunami->pchip->translatePciToDma(pciAddr); + else + panic("Access to unset controller!\n"); +} + +uint32_t +IdeDisk::bytesInDmaPage(Addr curAddr, uint32_t bytesLeft) +{ + uint32_t bytesInPage = 0; + + // First calculate how many bytes could be in the page + if (bytesLeft > ALPHA_PGBYTES) + bytesInPage = ALPHA_PGBYTES; + else + bytesInPage = bytesLeft; + + // Next, see if we have crossed a page boundary, and adjust + Addr upperBound = curAddr + bytesInPage; + Addr pageBound = alpha_trunc_page(curAddr) + ALPHA_PGBYTES; + + assert(upperBound >= curAddr && "DMA read wraps around address space!\n"); + + if (upperBound >= pageBound) + bytesInPage = pageBound - curAddr; + + return bytesInPage; +} + +//// +// Device registers read/write +//// + +void +IdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data) +{ + DevAction_t action = ACT_NONE; + + if (cmdBlk) { + if (offset < 0 || offset > sizeof(CommandReg_t)) + panic("Invalid disk command register offset: %#x\n", offset); + + if (!byte && offset != DATA_OFFSET) + panic("Invalid 16-bit read, only allowed on data reg\n"); + + if (!byte) + *(uint16_t *)data = *(uint16_t *)&cmdReg.data0; + else + *data = ((uint8_t *)&cmdReg)[offset]; + + // determine if an action needs to be taken on the state machine + if (offset == STATUS_OFFSET) { + action = ACT_STAT_READ; + } else if (offset == DATA_OFFSET) { + if (byte) + action = ACT_DATA_READ_BYTE; + else + action = ACT_DATA_READ_SHORT; + } + + } else { + if (offset != ALTSTAT_OFFSET) + panic("Invalid disk control register offset: %#x\n", offset); + + if (!byte) + panic("Invalid 16-bit read from control block\n"); + + *data = ((uint8_t *)&cmdReg)[STATUS_OFFSET]; + } + + if (action != ACT_NONE) + updateState(action); +} + +void +IdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data) +{ + DevAction_t action = ACT_NONE; + + if (cmdBlk) { + if (offset < 0 || offset > sizeof(CommandReg_t)) + panic("Invalid disk command register offset: %#x\n", offset); + + if (!byte && offset != DATA_OFFSET) + panic("Invalid 16-bit write, only allowed on data reg\n"); + + if (!byte) + *((uint16_t *)&cmdReg.data0) = *(uint16_t *)data; + else + ((uint8_t *)&cmdReg)[offset] = *data; + + // determine if an action needs to be taken on the state machine + if (offset == COMMAND_OFFSET) { + action = ACT_CMD_WRITE; + } else if (offset == DATA_OFFSET) { + if (byte) + action = ACT_DATA_WRITE_BYTE; + else + action = ACT_DATA_WRITE_SHORT; + } + + } else { + if (offset != CONTROL_OFFSET) + panic("Invalid disk control register offset: %#x\n", offset); + + if (!byte) + panic("Invalid 16-bit write to control block\n"); + + if (*data & CONTROL_RST_BIT) + panic("Software reset not supported!\n"); + + nIENBit = (*data & CONTROL_IEN_BIT) ? true : false; + } + + if (action != ACT_NONE) + updateState(action); +} + +//// +// Perform DMA transactions +//// + +void +IdeDisk::doDmaTransfer() +{ + if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma) + panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n", + dmaState, devState); + + // first read the current PRD + if (dmaInterface) { + if (dmaInterface->busy()) { + // reschedule after waiting period + dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } + + dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick, + &dmaPrdReadEvent); + } else { + dmaPrdReadDone(); + } +} + +void +IdeDisk::dmaPrdReadDone() +{ + // actually copy the PRD from physical memory + memcpy((void *)&curPrd.entry, + physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)), + sizeof(PrdEntry_t)); + + curPrdAddr += sizeof(PrdEntry_t); + + if (dmaRead) + doDmaRead(); + else + doDmaWrite(); +} + +void +IdeDisk::doDmaRead() +{ + Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); + + if (dmaInterface) { + if (dmaInterface->busy()) { + // reschedule after waiting period + dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } + + Addr dmaAddr = pciToDma(curPrd.getBaseAddr()); + + uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(), + (uint32_t)curPrd.getByteCount()); + + dmaInterfaceBytes = bytesInPage; + + dmaInterface->doDMA(Read, dmaAddr, bytesInPage, + curTick + totalDiskDelay, &dmaReadEvent); + } else { + // schedule dmaReadEvent with sectorDelay (dmaReadDone) + dmaReadEvent.schedule(curTick + totalDiskDelay); + } +} + +void +IdeDisk::dmaReadDone() +{ + + Addr curAddr = 0, dmaAddr = 0; + uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0; + + // continue to use the DMA interface until all pages are read + if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) { + // see if the interface is busy + if (dmaInterface->busy()) { + // reschedule after waiting period + dmaReadEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } + + uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes; + curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes; + dmaAddr = pciToDma(curAddr); + + bytesInPage = bytesInDmaPage(curAddr, bytesLeft); + dmaInterfaceBytes += bytesInPage; + + dmaInterface->doDMA(Read, dmaAddr, bytesInPage, + curTick, &dmaReadEvent); + + return; + } + + // set initial address + curAddr = curPrd.getBaseAddr(); + + // clear out the data buffer + memset(dataBuffer, 0, MAX_DMA_SIZE); + + // read the data from memory via DMA into a data buffer + while (bytesWritten < curPrd.getByteCount()) { + if (cmdBytesLeft <= 0) + panic("DMA data is larger than # of sectors specified\n"); + + dmaAddr = pciToDma(curAddr); + + // calculate how many bytes are in the current page + bytesLeft = curPrd.getByteCount() - bytesWritten; + bytesInPage = bytesInDmaPage(curAddr, bytesLeft); + + // copy the data from memory into the data buffer + memcpy((void *)(dataBuffer + bytesWritten), + physmem->dma_addr(dmaAddr, bytesInPage), + bytesInPage); + + curAddr += bytesInPage; + bytesWritten += bytesInPage; + cmdBytesLeft -= bytesInPage; + } + + // write the data to the disk image + for (bytesWritten = 0; + bytesWritten < curPrd.getByteCount(); + bytesWritten += SectorSize) { + + writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); + } + +#if 0 + // actually copy the data from memory to data buffer + Addr dmaAddr = + ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr()); + memcpy((void *)dataBuffer, + physmem->dma_addr(dmaAddr, curPrd.getByteCount()), + curPrd.getByteCount()); + + uint32_t bytesWritten = 0; + + while (bytesWritten < curPrd.getByteCount()) { + if (cmdBytesLeft <= 0) + panic("DMA data is larger than # sectors specified\n"); + + writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); + + bytesWritten += SectorSize; + cmdBytesLeft -= SectorSize; + } +#endif + + // check for the EOT + if (curPrd.getEOT()){ + assert(cmdBytesLeft == 0); + dmaState = Dma_Idle; + updateState(ACT_DMA_DONE); + } else { + doDmaTransfer(); + } +} + +void +IdeDisk::doDmaWrite() +{ + Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); + + if (dmaInterface) { + if (dmaInterface->busy()) { + // reschedule after waiting period + dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } + + Addr dmaAddr = pciToDma(curPrd.getBaseAddr()); + + uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(), + (uint32_t)curPrd.getByteCount()); + + dmaInterfaceBytes = bytesInPage; + + dmaInterface->doDMA(WriteInvalidate, dmaAddr, + bytesInPage, curTick + totalDiskDelay, + &dmaWriteEvent); + } else { + // schedule event with disk delay (dmaWriteDone) + dmaWriteEvent.schedule(curTick + totalDiskDelay); + } +} + +void +IdeDisk::dmaWriteDone() +{ + Addr curAddr = 0, pageAddr = 0, dmaAddr = 0; + uint32_t bytesRead = 0, bytesInPage = 0; + + // continue to use the DMA interface until all pages are read + if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) { + // see if the interface is busy + if (dmaInterface->busy()) { + // reschedule after waiting period + dmaWriteEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } + + uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes; + curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes; + dmaAddr = pciToDma(curAddr); + + bytesInPage = bytesInDmaPage(curAddr, bytesLeft); + dmaInterfaceBytes += bytesInPage; + + dmaInterface->doDMA(WriteInvalidate, dmaAddr, + bytesInPage, curTick, + &dmaWriteEvent); + + return; + } + + // setup the initial page and DMA address + curAddr = curPrd.getBaseAddr(); + pageAddr = alpha_trunc_page(curAddr); + dmaAddr = pciToDma(curAddr); + + // clear out the data buffer + memset(dataBuffer, 0, MAX_DMA_SIZE); + + while (bytesRead < curPrd.getByteCount()) { + // see if we have crossed into a new page + if (pageAddr != alpha_trunc_page(curAddr)) { + // write the data to memory + memcpy(physmem->dma_addr(dmaAddr, bytesInPage), + (void *)(dataBuffer + (bytesRead - bytesInPage)), + bytesInPage); + + // update the DMA address and page address + pageAddr = alpha_trunc_page(curAddr); + dmaAddr = pciToDma(curAddr); + + bytesInPage = 0; + } + + if (cmdBytesLeft <= 0) + panic("DMA requested data is larger than # sectors specified\n"); + + readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead)); + + curAddr += SectorSize; + bytesRead += SectorSize; + bytesInPage += SectorSize; + cmdBytesLeft -= SectorSize; + } + + // write the last page worth read to memory + if (bytesInPage != 0) { + memcpy(physmem->dma_addr(dmaAddr, bytesInPage), + (void *)(dataBuffer + (bytesRead - bytesInPage)), + bytesInPage); + } + +#if 0 + Addr dmaAddr = ctrl->tsunami->pchip-> + translatePciToDma(curPrd.getBaseAddr()); + + memcpy(physmem->dma_addr(dmaAddr, curPrd.getByteCount()), + (void *)dataBuffer, curPrd.getByteCount()); +#endif + + // check for the EOT + if (curPrd.getEOT()) { + assert(cmdBytesLeft == 0); + dmaState = Dma_Idle; + updateState(ACT_DMA_DONE); + } else { + doDmaTransfer(); + } +} + +//// +// Disk utility routines +/// + +void +IdeDisk::readDisk(uint32_t sector, uint8_t *data) +{ + uint32_t bytesRead = image->read(data, sector); + + if (bytesRead != SectorSize) + panic("Can't read from %s. Only %d of %d read. errno=%d\n", + name(), bytesRead, SectorSize, errno); +} + +void +IdeDisk::writeDisk(uint32_t sector, uint8_t *data) +{ + uint32_t bytesWritten = image->write(data, sector); + + if (bytesWritten != SectorSize) + panic("Can't write to %s. Only %d of %d written. errno=%d\n", + name(), bytesWritten, SectorSize, errno); +} + +//// +// Setup and handle commands +//// + +void +IdeDisk::startDma(const uint32_t &prdTableBase) +{ + if (dmaState != Dma_Start) + panic("Inconsistent DMA state, should be in Dma_Start!\n"); + + if (devState != Transfer_Data_Dma) + panic("Inconsistent device state for DMA start!\n"); + + curPrdAddr = pciToDma((Addr)prdTableBase); + + dmaState = Dma_Transfer; + + // schedule dma transfer (doDmaTransfer) + dmaTransferEvent.schedule(curTick + 1); +} + +void +IdeDisk::abortDma() +{ + if (dmaState == Dma_Idle) + panic("Inconsistent DMA state, should be in Dma_Start or Dma_Transfer!\n"); + + if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma) + panic("Inconsistent device state, should be in Transfer or Prepare!\n"); + + updateState(ACT_CMD_ERROR); +} + +void +IdeDisk::startCommand() +{ + DevAction_t action = ACT_NONE; + uint32_t size = 0; + dmaRead = false; + + // copy the command to the shadow + curCommand = cmdReg.command; + + // Decode commands + switch (cmdReg.command) { + // Supported non-data commands + case WIN_READ_NATIVE_MAX: + size = image->size() - 1; + cmdReg.sec_num = (size & 0xff); + cmdReg.cyl_low = ((size & 0xff00) >> 8); + cmdReg.cyl_high = ((size & 0xff0000) >> 16); + cmdReg.head = ((size & 0xf000000) >> 24); + + devState = Command_Execution; + action = ACT_CMD_COMPLETE; + break; + + case WIN_RECAL: + case WIN_SPECIFY: + case WIN_STANDBYNOW1: + case WIN_FLUSH_CACHE: + case WIN_VERIFY: + case WIN_SEEK: + case WIN_SETFEATURES: + case WIN_SETMULT: + devState = Command_Execution; + action = ACT_CMD_COMPLETE; + break; + + // Supported PIO data-in commands + case WIN_IDENTIFY: + cmdBytesLeft = sizeof(struct hd_driveid); + devState = Prepare_Data_In; + action = ACT_DATA_READY; + break; + + case WIN_MULTREAD: + case WIN_READ: + if (!(cmdReg.drive & DRIVE_LBA_BIT)) + panic("Attempt to perform CHS access, only supports LBA\n"); + + if (cmdReg.sec_count == 0) + cmdBytesLeft = (256 * SectorSize); + else + cmdBytesLeft = (cmdReg.sec_count * SectorSize); + + curSector = getLBABase(); + + /** @todo make this a scheduled event to simulate disk delay */ + devState = Prepare_Data_In; + action = ACT_DATA_READY; + break; + + // Supported PIO data-out commands + case WIN_MULTWRITE: + case WIN_WRITE: + if (!(cmdReg.drive & DRIVE_LBA_BIT)) + panic("Attempt to perform CHS access, only supports LBA\n"); + + if (cmdReg.sec_count == 0) + cmdBytesLeft = (256 * SectorSize); + else + cmdBytesLeft = (cmdReg.sec_count * SectorSize); + + curSector = getLBABase(); + + devState = Prepare_Data_Out; + action = ACT_DATA_READY; + break; + + // Supported DMA commands + case WIN_WRITEDMA: + dmaRead = true; // a write to the disk is a DMA read from memory + case WIN_READDMA: + if (!(cmdReg.drive & DRIVE_LBA_BIT)) + panic("Attempt to perform CHS access, only supports LBA\n"); + + if (cmdReg.sec_count == 0) + cmdBytesLeft = (256 * SectorSize); + else + cmdBytesLeft = (cmdReg.sec_count * SectorSize); + + curSector = getLBABase(); + + devState = Prepare_Data_Dma; + action = ACT_DMA_READY; + break; + + default: + panic("Unsupported ATA command: %#x\n", cmdReg.command); + } + + if (action != ACT_NONE) { + // set the BSY bit + cmdReg.status |= STATUS_BSY_BIT; + // clear the DRQ bit + cmdReg.status &= ~STATUS_DRQ_BIT; + + updateState(action); + } +} + +//// +// Handle setting and clearing interrupts +//// + +void +IdeDisk::intrPost() +{ + if (intrPending) + panic("Attempt to post an interrupt with one pending\n"); + + intrPending = true; + + // talk to controller to set interrupt + if (ctrl) + ctrl->intrPost(); +} + +void +IdeDisk::intrClear() +{ + if (!intrPending) + panic("Attempt to clear a non-pending interrupt\n"); + + intrPending = false; + + // talk to controller to clear interrupt + if (ctrl) + ctrl->intrClear(); +} + +//// +// Manage the device internal state machine +//// + +void +IdeDisk::updateState(DevAction_t action) +{ + switch (devState) { + case Device_Idle_S: + if (!isDEVSelect()) + devState = Device_Idle_NS; + else if (action == ACT_CMD_WRITE) + startCommand(); + + break; + + case Device_Idle_SI: + if (!isDEVSelect()) { + devState = Device_Idle_NS; + intrClear(); + } else if (action == ACT_STAT_READ || isIENSet()) { + devState = Device_Idle_S; + intrClear(); + } else if (action == ACT_CMD_WRITE) { + intrClear(); + startCommand(); + } + + break; + + case Device_Idle_NS: + if (isDEVSelect()) { + if (!isIENSet() && intrPending) { + devState = Device_Idle_SI; + intrPost(); + } + if (isIENSet() || !intrPending) { + devState = Device_Idle_S; + } + } + break; + + case Command_Execution: + if (action == ACT_CMD_COMPLETE) { + // clear the BSY bit + setComplete(); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } + break; + + case Prepare_Data_In: + if (action == ACT_CMD_ERROR) { + // clear the BSY bit + setComplete(); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } else if (action == ACT_DATA_READY) { + // clear the BSY bit + cmdReg.status &= ~STATUS_BSY_BIT; + // set the DRQ bit + cmdReg.status |= STATUS_DRQ_BIT; + + // copy the data into the data buffer + if (curCommand == WIN_IDENTIFY) { + // Reset the drqBytes for this block + drqBytesLeft = sizeof(struct hd_driveid); + + memcpy((void *)dataBuffer, (void *)&driveID, + sizeof(struct hd_driveid)); + } else { + // Reset the drqBytes for this block + drqBytesLeft = SectorSize; + + readDisk(curSector++, dataBuffer); + } + + // put the first two bytes into the data register + memcpy((void *)&cmdReg.data0, (void *)dataBuffer, + sizeof(uint16_t)); + + if (!isIENSet()) { + devState = Data_Ready_INTRQ_In; + intrPost(); + } else { + devState = Transfer_Data_In; + } + } + break; + + case Data_Ready_INTRQ_In: + if (action == ACT_STAT_READ) { + devState = Transfer_Data_In; + intrClear(); + } + break; + + case Transfer_Data_In: + if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) { + if (action == ACT_DATA_READ_BYTE) { + panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n"); + } else { + drqBytesLeft -= 2; + cmdBytesLeft -= 2; + + // copy next short into data registers + if (drqBytesLeft) + memcpy((void *)&cmdReg.data0, + (void *)&dataBuffer[SectorSize - drqBytesLeft], + sizeof(uint16_t)); + } + + if (drqBytesLeft == 0) { + if (cmdBytesLeft == 0) { + // Clear the BSY bit + setComplete(); + devState = Device_Idle_S; + } else { + devState = Prepare_Data_In; + // set the BSY_BIT + cmdReg.status |= STATUS_BSY_BIT; + // clear the DRQ_BIT + cmdReg.status &= ~STATUS_DRQ_BIT; + + /** @todo change this to a scheduled event to simulate + disk delay */ + updateState(ACT_DATA_READY); + } + } + } + break; + + case Prepare_Data_Out: + if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) { + // clear the BSY bit + setComplete(); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } else if (cmdBytesLeft != 0) { + // clear the BSY bit + cmdReg.status &= ~STATUS_BSY_BIT; + // set the DRQ bit + cmdReg.status |= STATUS_DRQ_BIT; + + // clear the data buffer to get it ready for writes + memset(dataBuffer, 0, MAX_DMA_SIZE); + + if (!isIENSet()) { + devState = Data_Ready_INTRQ_Out; + intrPost(); + } else { + devState = Transfer_Data_Out; + } + } + break; + + case Data_Ready_INTRQ_Out: + if (action == ACT_STAT_READ) { + devState = Transfer_Data_Out; + intrClear(); + } + break; + + case Transfer_Data_Out: + if (action == ACT_DATA_WRITE_BYTE || + action == ACT_DATA_WRITE_SHORT) { + + if (action == ACT_DATA_READ_BYTE) { + panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n"); + } else { + // copy the latest short into the data buffer + memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft], + (void *)&cmdReg.data0, + sizeof(uint16_t)); + + drqBytesLeft -= 2; + cmdBytesLeft -= 2; + } + + if (drqBytesLeft == 0) { + // copy the block to the disk + writeDisk(curSector++, dataBuffer); + + // set the BSY bit + cmdReg.status |= STATUS_BSY_BIT; + // clear the DRQ bit + cmdReg.status &= ~STATUS_DRQ_BIT; + + devState = Prepare_Data_Out; + + /** @todo change this to a scheduled event to simulate + disk delay */ + updateState(ACT_DATA_READY); + } + } + break; + + case Prepare_Data_Dma: + if (action == ACT_CMD_ERROR) { + // clear the BSY bit + setComplete(); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } else if (action == ACT_DMA_READY) { + // clear the BSY bit + cmdReg.status &= ~STATUS_BSY_BIT; + // set the DRQ bit + cmdReg.status |= STATUS_DRQ_BIT; + + devState = Transfer_Data_Dma; + + if (dmaState != Dma_Idle) + panic("Inconsistent DMA state, should be Dma_Idle\n"); + + dmaState = Dma_Start; + // wait for the write to the DMA start bit + } + break; + + case Transfer_Data_Dma: + if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) { + // clear the BSY bit + setComplete(); + // set the seek bit + cmdReg.status |= 0x10; + // clear the controller state for DMA transfer + ctrl->setDmaComplete(this); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } + break; + + default: + panic("Unknown IDE device state: %#x\n", devState); + } +} + +void +IdeDisk::serialize(ostream &os) +{ + // Check all outstanding events to see if they are scheduled + // these are all mutually exclusive + Tick reschedule = 0; + Events_t event = None; + + if (dmaTransferEvent.scheduled()) { + reschedule = dmaTransferEvent.when(); + event = Transfer; + } else if (dmaReadWaitEvent.scheduled()) { + reschedule = dmaReadWaitEvent.when(); + event = ReadWait; + } else if (dmaWriteWaitEvent.scheduled()) { + reschedule = dmaWriteWaitEvent.when(); + event = WriteWait; + } else if (dmaPrdReadEvent.scheduled()) { + reschedule = dmaPrdReadEvent.when(); + event = PrdRead; + } else if (dmaReadEvent.scheduled()) { + reschedule = dmaReadEvent.when(); + event = DmaRead; + } else if (dmaWriteEvent.scheduled()) { + reschedule = dmaWriteEvent.when(); + event = DmaWrite; + } + + SERIALIZE_SCALAR(reschedule); + SERIALIZE_ENUM(event); + + // Serialize device registers + SERIALIZE_SCALAR(cmdReg.data0); + SERIALIZE_SCALAR(cmdReg.data1); + SERIALIZE_SCALAR(cmdReg.sec_count); + SERIALIZE_SCALAR(cmdReg.sec_num); + SERIALIZE_SCALAR(cmdReg.cyl_low); + SERIALIZE_SCALAR(cmdReg.cyl_high); + SERIALIZE_SCALAR(cmdReg.drive); + SERIALIZE_SCALAR(cmdReg.status); + SERIALIZE_SCALAR(nIENBit); + SERIALIZE_SCALAR(devID); + + // Serialize the PRD related information + SERIALIZE_SCALAR(curPrd.entry.baseAddr); + SERIALIZE_SCALAR(curPrd.entry.byteCount); + SERIALIZE_SCALAR(curPrd.entry.endOfTable); + SERIALIZE_SCALAR(curPrdAddr); + + // Serialize current transfer related information + SERIALIZE_SCALAR(cmdBytesLeft); + SERIALIZE_SCALAR(drqBytesLeft); + SERIALIZE_SCALAR(curSector); + SERIALIZE_SCALAR(curCommand); + SERIALIZE_SCALAR(dmaRead); + SERIALIZE_SCALAR(dmaInterfaceBytes); + SERIALIZE_SCALAR(intrPending); + SERIALIZE_ENUM(devState); + SERIALIZE_ENUM(dmaState); + SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); +} + +void +IdeDisk::unserialize(Checkpoint *cp, const string §ion) +{ + // Reschedule events that were outstanding + // these are all mutually exclusive + Tick reschedule = 0; + Events_t event = None; + + UNSERIALIZE_SCALAR(reschedule); + UNSERIALIZE_ENUM(event); + + switch (event) { + case None : break; + case Transfer : dmaTransferEvent.schedule(reschedule); break; + case ReadWait : dmaReadWaitEvent.schedule(reschedule); break; + case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break; + case PrdRead : dmaPrdReadEvent.schedule(reschedule); break; + case DmaRead : dmaReadEvent.schedule(reschedule); break; + case DmaWrite : dmaWriteEvent.schedule(reschedule); break; + } + + // Unserialize device registers + UNSERIALIZE_SCALAR(cmdReg.data0); + UNSERIALIZE_SCALAR(cmdReg.data1); + UNSERIALIZE_SCALAR(cmdReg.sec_count); + UNSERIALIZE_SCALAR(cmdReg.sec_num); + UNSERIALIZE_SCALAR(cmdReg.cyl_low); + UNSERIALIZE_SCALAR(cmdReg.cyl_high); + UNSERIALIZE_SCALAR(cmdReg.drive); + UNSERIALIZE_SCALAR(cmdReg.status); + UNSERIALIZE_SCALAR(nIENBit); + UNSERIALIZE_SCALAR(devID); + + // Unserialize the PRD related information + UNSERIALIZE_SCALAR(curPrd.entry.baseAddr); + UNSERIALIZE_SCALAR(curPrd.entry.byteCount); + UNSERIALIZE_SCALAR(curPrd.entry.endOfTable); + UNSERIALIZE_SCALAR(curPrdAddr); + + // Unserialize current transfer related information + UNSERIALIZE_SCALAR(cmdBytesLeft); + UNSERIALIZE_SCALAR(drqBytesLeft); + UNSERIALIZE_SCALAR(curSector); + UNSERIALIZE_SCALAR(curCommand); + UNSERIALIZE_SCALAR(dmaRead); + UNSERIALIZE_SCALAR(dmaInterfaceBytes); + UNSERIALIZE_SCALAR(intrPending); + UNSERIALIZE_ENUM(devState); + UNSERIALIZE_ENUM(dmaState); + UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) + + SimObjectParam<DiskImage *> image; + SimObjectParam<PhysicalMemory *> physmem; + Param<int> driveID; + Param<int> disk_delay; + +END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) + +BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk) + + INIT_PARAM(image, "Disk image"), + INIT_PARAM(physmem, "Physical memory"), + INIT_PARAM(driveID, "Drive ID (0=master 1=slave)"), + INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in microseconds", 1) + +END_INIT_SIM_OBJECT_PARAMS(IdeDisk) + + +CREATE_SIM_OBJECT(IdeDisk) +{ + return new IdeDisk(getInstanceName(), image, physmem, driveID, + disk_delay); +} + +REGISTER_SIM_OBJECT("IdeDisk", IdeDisk) + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/dev/ide_disk.hh b/dev/ide_disk.hh new file mode 100644 index 000000000..35e7404d5 --- /dev/null +++ b/dev/ide_disk.hh @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2003 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. + */ + +/** @file + * Device model for an IDE disk + */ + +#ifndef __IDE_DISK_HH__ +#define __IDE_DISK_HH__ + +#include "dev/ide.hh" +#include "dev/disk_image.hh" +#include "dev/io_device.hh" +#include "sim/eventq.hh" + +#define DMA_BACKOFF_PERIOD 200 + +#define MAX_DMA_SIZE (131072) // 256 * SectorSize (512) +#define MAX_MULTSECT (128) + +#define PRD_BASE_MASK 0xfffffffe +#define PRD_COUNT_MASK 0xfffe +#define PRD_EOT_MASK 0x8000 + +typedef struct PrdEntry { + uint32_t baseAddr; + uint16_t byteCount; + uint16_t endOfTable; +} PrdEntry_t; + +class PrdTableEntry { + public: + PrdEntry_t entry; + + uint32_t getBaseAddr() + { + return (entry.baseAddr & PRD_BASE_MASK); + } + + uint16_t getByteCount() + { + return ((entry.byteCount == 0) ? MAX_DMA_SIZE : + (entry.byteCount & PRD_COUNT_MASK)); + } + + uint16_t getEOT() + { + return (entry.endOfTable & PRD_EOT_MASK); + } +}; + +#define DATA_OFFSET (0) +#define ERROR_OFFSET (1) +#define FEATURES_OFFSET (1) +#define NSECTOR_OFFSET (2) +#define SECTOR_OFFSET (3) +#define LCYL_OFFSET (4) +#define HCYL_OFFSET (5) +#define SELECT_OFFSET (6) +#define STATUS_OFFSET (7) +#define COMMAND_OFFSET (7) + +#define CONTROL_OFFSET (2) +#define ALTSTAT_OFFSET (2) + +#define SELECT_DEV_BIT 0x10 +#define CONTROL_RST_BIT 0x04 +#define CONTROL_IEN_BIT 0x02 +#define STATUS_BSY_BIT 0x80 +#define STATUS_DRDY_BIT 0x40 +#define STATUS_DRQ_BIT 0x08 +#define DRIVE_LBA_BIT 0x40 + +#define DEV0 (0) +#define DEV1 (1) + +typedef struct CommandReg { + uint8_t data0; + union { + uint8_t data1; + uint8_t error; + uint8_t features; + }; + uint8_t sec_count; + uint8_t sec_num; + uint8_t cyl_low; + uint8_t cyl_high; + union { + uint8_t drive; + uint8_t head; + }; + union { + uint8_t status; + uint8_t command; + }; +} CommandReg_t; + +typedef enum Events { + None = 0, + Transfer, + ReadWait, + WriteWait, + PrdRead, + DmaRead, + DmaWrite +} Events_t; + +typedef enum DevAction { + ACT_NONE = 0, + ACT_CMD_WRITE, + ACT_CMD_COMPLETE, + ACT_CMD_ERROR, + ACT_STAT_READ, + ACT_DATA_READY, + ACT_DATA_READ_BYTE, + ACT_DATA_READ_SHORT, + ACT_DATA_WRITE_BYTE, + ACT_DATA_WRITE_SHORT, + ACT_DMA_READY, + ACT_DMA_DONE +} DevAction_t; + +typedef enum DevState { + // Device idle + Device_Idle_S = 0, + Device_Idle_SI, + Device_Idle_NS, + + // Non-data commands + Command_Execution, + + // PIO data-in (data to host) + Prepare_Data_In, + Data_Ready_INTRQ_In, + Transfer_Data_In, + + // PIO data-out (data from host) + Prepare_Data_Out, + Data_Ready_INTRQ_Out, + Transfer_Data_Out, + + // DMA protocol + Prepare_Data_Dma, + Transfer_Data_Dma +} DevState_t; + +typedef enum DmaState { + Dma_Idle = 0, + Dma_Start, + Dma_Transfer +} DmaState_t; + +class PhysicalMemory; +class IdeController; + +/** + * IDE Disk device model + */ +class IdeDisk : public SimObject +{ + protected: + /** The IDE controller for this disk. */ + IdeController *ctrl; + /** The DMA interface to use for transfers */ + DMAInterface<Bus> *dmaInterface; + /** The image that contains the data of this disk. */ + DiskImage *image; + /** Pointer to physical memory for DMA transfers */ + PhysicalMemory *physmem; + + protected: + /** The disk delay in microseconds. */ + int diskDelay; + + private: + /** Drive identification structure for this disk */ + struct hd_driveid driveID; + /** Data buffer for transfers */ + uint8_t *dataBuffer; + /** Number of bytes left in command data transfer */ + uint32_t cmdBytesLeft; + /** Number of bytes left in DRQ block */ + uint32_t drqBytesLeft; + /** Current sector in access */ + uint32_t curSector; + /** Command block registers */ + CommandReg_t cmdReg; + /** Shadow of the current command code */ + uint8_t curCommand; + /** Interrupt enable bit */ + bool nIENBit; + /** Device state */ + DevState_t devState; + /** Dma state */ + DmaState_t dmaState; + /** Dma transaction is a read */ + bool dmaRead; + /** PRD table base address */ + uint32_t curPrdAddr; + /** PRD entry */ + PrdTableEntry curPrd; + /** Number of bytes transfered by DMA interface for current transfer */ + uint32_t dmaInterfaceBytes; + /** Device ID (master=0/slave=1) */ + int devID; + /** Interrupt pending */ + bool intrPending; + + public: + /** + * Create and initialize this Disk. + * @param name The name of this disk. + * @param img The disk image of this disk. + * @param phys Pointer to physical memory + * @param id The disk ID (master=0/slave=1) + * @param disk_delay The disk delay in milliseconds + */ + IdeDisk(const std::string &name, DiskImage *img, PhysicalMemory *phys, + int id, int disk_delay); + + /** + * Delete the data buffer. + */ + ~IdeDisk(); + + /** + * Set the controller for this device + * @param c The IDE controller + */ + void setController(IdeController *c, DMAInterface<Bus> *dmaIntr) { + if (ctrl) panic("Cannot change the controller once set!\n"); + ctrl = c; + dmaInterface = dmaIntr; + } + + // Device register read/write + void read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data); + void write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data); + + // Start/abort functions + void startDma(const uint32_t &prdTableBase); + void abortDma(); + + private: + void startCommand(); + + // Interrupt management + void intrPost(); + void intrClear(); + + // DMA stuff + void doDmaTransfer(); + friend class EventWrapper<IdeDisk, &IdeDisk::doDmaTransfer>; + EventWrapper<IdeDisk, &IdeDisk::doDmaTransfer> dmaTransferEvent; + + void doDmaRead(); + friend class EventWrapper<IdeDisk, &IdeDisk::doDmaRead>; + EventWrapper<IdeDisk, &IdeDisk::doDmaRead> dmaReadWaitEvent; + + void doDmaWrite(); + friend class EventWrapper<IdeDisk, &IdeDisk::doDmaWrite>; + EventWrapper<IdeDisk, &IdeDisk::doDmaWrite> dmaWriteWaitEvent; + + void dmaPrdReadDone(); + friend class EventWrapper<IdeDisk, &IdeDisk::dmaPrdReadDone>; + EventWrapper<IdeDisk, &IdeDisk::dmaPrdReadDone> dmaPrdReadEvent; + + void dmaReadDone(); + friend class EventWrapper<IdeDisk, &IdeDisk::dmaReadDone>; + EventWrapper<IdeDisk, &IdeDisk::dmaReadDone> dmaReadEvent; + + void dmaWriteDone(); + friend class EventWrapper<IdeDisk, &IdeDisk::dmaWriteDone>; + EventWrapper<IdeDisk, &IdeDisk::dmaWriteDone> dmaWriteEvent; + + // Disk image read/write + void readDisk(uint32_t sector, uint8_t *data); + void writeDisk(uint32_t sector, uint8_t *data); + + // State machine management + void updateState(DevAction_t action); + + // Utility functions + bool isBSYSet() { return (cmdReg.status & STATUS_BSY_BIT); } + bool isIENSet() { return nIENBit; } + bool isDEVSelect() { return ((cmdReg.drive & SELECT_DEV_BIT) == devID); } + + void setComplete() + { + // clear out the status byte + cmdReg.status = 0; + + // set the DRDY bit + cmdReg.status |= STATUS_DRDY_BIT; + } + + uint32_t getLBABase() + { + return (Addr)(((cmdReg.head & 0xf) << 24) | (cmdReg.cyl_high << 16) | + (cmdReg.cyl_low << 8) | (cmdReg.sec_num)); + } + + inline Addr pciToDma(Addr pciAddr); + + uint32_t bytesInDmaPage(Addr curAddr, uint32_t bytesLeft); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint to use. + * @param section The section name describing this object. + */ + void unserialize(Checkpoint *cp, const std::string §ion); +}; + + +#endif // __IDE_DISK_HH__ diff --git a/dev/ns_gige.cc b/dev/ns_gige.cc new file mode 100644 index 000000000..0b54d9210 --- /dev/null +++ b/dev/ns_gige.cc @@ -0,0 +1,2418 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * Device module for modelling the National Semiconductor + * DP83820 ethernet controller. Does not support priority queueing + */ +#include <cstdio> +#include <deque> +#include <string> + +#include "base/inet.hh" +#include "cpu/exec_context.hh" +#include "cpu/intr_control.hh" +#include "dev/dma.hh" +#include "dev/ns_gige.hh" +#include "dev/etherlink.hh" +#include "mem/bus/bus.hh" +#include "mem/bus/dma_interface.hh" +#include "mem/bus/pio_interface.hh" +#include "mem/bus/pio_interface_impl.hh" +#include "mem/functional_mem/memory_control.hh" +#include "mem/functional_mem/physical_memory.hh" +#include "sim/builder.hh" +#include "sim/host.hh" +#include "sim/sim_stats.hh" +#include "targetarch/vtophys.hh" +#include "dev/pciconfigall.hh" +#include "dev/tsunami_cchip.hh" + +const char *NsRxStateStrings[] = +{ + "rxIdle", + "rxDescRefr", + "rxDescRead", + "rxFifoBlock", + "rxFragWrite", + "rxDescWrite", + "rxAdvance" +}; + +const char *NsTxStateStrings[] = +{ + "txIdle", + "txDescRefr", + "txDescRead", + "txFifoBlock", + "txFragRead", + "txDescWrite", + "txAdvance" +}; + +const char *NsDmaState[] = +{ + "dmaIdle", + "dmaReading", + "dmaWriting", + "dmaReadWaiting", + "dmaWriteWaiting" +}; + +using namespace std; + +/////////////////////////////////////////////////////////////////////// +// +// NSGigE PCI Device +// +NSGigE::NSGigE(const std::string &name, IntrControl *i, Tick intr_delay, + PhysicalMemory *pmem, Tick tx_delay, Tick rx_delay, + MemoryController *mmu, HierParams *hier, Bus *header_bus, + Bus *payload_bus, Tick pio_latency, bool dma_desc_free, + bool dma_data_free, Tick dma_read_delay, Tick dma_write_delay, + Tick dma_read_factor, Tick dma_write_factor, PciConfigAll *cf, + PciConfigData *cd, Tsunami *t, uint32_t bus, uint32_t dev, + uint32_t func, bool rx_filter, const int eaddr[6], Addr addr) + : PciDev(name, mmu, cf, cd, bus, dev, func), tsunami(t), + addr(addr), txPacketBufPtr(NULL), rxPacketBufPtr(NULL), + txXferLen(0), rxXferLen(0), txPktXmitted(0), txState(txIdle), CTDD(false), + txFifoCnt(0), txFifoAvail(MAX_TX_FIFO_SIZE), txHalt(false), + txFragPtr(0), txDescCnt(0), txDmaState(dmaIdle), rxState(rxIdle), + CRDD(false), rxPktBytes(0), rxFifoCnt(0), rxHalt(false), + rxFragPtr(0), rxDescCnt(0), rxDmaState(dmaIdle), extstsEnable(false), + rxDmaReadEvent(this), rxDmaWriteEvent(this), + txDmaReadEvent(this), txDmaWriteEvent(this), + dmaDescFree(dma_desc_free), dmaDataFree(dma_data_free), + txDelay(tx_delay), rxDelay(rx_delay), rxKickTick(0), txKickTick(0), + txEvent(this), rxFilterEnable(rx_filter), acceptBroadcast(false), + acceptMulticast(false), acceptUnicast(false), + acceptPerfect(false), acceptArp(false), + physmem(pmem), intctrl(i), intrTick(0), + cpuPendingIntr(false), intrEvent(0), interface(0), pioLatency(pio_latency) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + tsunami->ethernet = this; + + if (header_bus) { + pioInterface = newPioInterface(name, hier, header_bus, this, + &NSGigE::cacheAccess); + pioInterface->addAddrRange(addr, addr + size - 1); + if (payload_bus) + dmaInterface = new DMAInterface<Bus>(name + ".dma", + header_bus, payload_bus, 1); + else + dmaInterface = new DMAInterface<Bus>(name + ".dma", + header_bus, header_bus, 1); + } else if (payload_bus) { + pioInterface = newPioInterface(name, hier, payload_bus, this, + &NSGigE::cacheAccess); + pioInterface->addAddrRange(addr, addr + size - 1); + dmaInterface = new DMAInterface<Bus>(name + ".dma", + payload_bus, payload_bus, 1); + + } + + + intrDelay = US2Ticks(intr_delay); + dmaReadDelay = dma_read_delay; + dmaWriteDelay = dma_write_delay; + dmaReadFactor = dma_read_factor; + dmaWriteFactor = dma_write_factor; + + memset(®s, 0, sizeof(regs)); + regsReset(); + rom.perfectMatch[0] = eaddr[0]; + rom.perfectMatch[1] = eaddr[1]; + rom.perfectMatch[2] = eaddr[2]; + rom.perfectMatch[3] = eaddr[3]; + rom.perfectMatch[4] = eaddr[4]; + rom.perfectMatch[5] = eaddr[5]; +} + +NSGigE::~NSGigE() +{} + +void +NSGigE::regStats() +{ + txBytes + .name(name() + ".txBytes") + .desc("Bytes Transmitted") + .prereq(txBytes) + ; + + rxBytes + .name(name() + ".rxBytes") + .desc("Bytes Received") + .prereq(rxBytes) + ; + + txPackets + .name(name() + ".txPackets") + .desc("Number of Packets Transmitted") + .prereq(txBytes) + ; + + rxPackets + .name(name() + ".rxPackets") + .desc("Number of Packets Received") + .prereq(rxBytes) + ; + + txBandwidth + .name(name() + ".txBandwidth") + .desc("Transmit Bandwidth (bits/s)") + .precision(0) + .prereq(txBytes) + ; + + rxBandwidth + .name(name() + ".rxBandwidth") + .desc("Receive Bandwidth (bits/s)") + .precision(0) + .prereq(rxBytes) + ; + + txPacketRate + .name(name() + ".txPPS") + .desc("Packet Tranmission Rate (packets/s)") + .precision(0) + .prereq(txBytes) + ; + + rxPacketRate + .name(name() + ".rxPPS") + .desc("Packet Reception Rate (packets/s)") + .precision(0) + .prereq(rxBytes) + ; + + txBandwidth = txBytes * Stats::constant(8) / simSeconds; + rxBandwidth = rxBytes * Stats::constant(8) / simSeconds; + txPacketRate = txPackets / simSeconds; + rxPacketRate = rxPackets / simSeconds; +} + +/** + * This is to read the PCI general configuration registers + */ +void +NSGigE::ReadConfig(int offset, int size, uint8_t *data) +{ + if (offset < PCI_DEVICE_SPECIFIC) + PciDev::ReadConfig(offset, size, data); + else { + panic("need to do this\n"); + } +} + +/** + * This is to write to the PCI general configuration registers + */ +void +NSGigE::WriteConfig(int offset, int size, uint32_t data) +{ + if (offset < PCI_DEVICE_SPECIFIC) + PciDev::WriteConfig(offset, size, data); + else + panic("Need to do that\n"); +} + +/** + * This reads the device registers, which are detailed in the NS83820 + * spec sheet + */ +Fault +NSGigE::read(MemReqPtr &req, uint8_t *data) +{ + //The mask is to give you only the offset into the device register file + Addr daddr = req->paddr & 0xfff; + DPRINTF(EthernetPIO, "read da=%#x pa=%#x va=%#x size=%d\n", + daddr, req->paddr, req->vaddr, req->size); + + + //there are some reserved registers, you can see ns_gige_reg.h and + //the spec sheet for details + if (daddr > LAST && daddr <= RESERVED) { + panic("Accessing reserved register"); + } else if (daddr > RESERVED && daddr <= 0x3FC) { + ReadConfig(daddr & 0xff, req->size, data); + return No_Fault; + } else if (daddr >= MIB_START && daddr <= MIB_END) { + // don't implement all the MIB's. hopefully the kernel + // doesn't actually DEPEND upon their values + // MIB are just hardware stats keepers + uint32_t ® = *(uint32_t *) data; + reg = 0; + return No_Fault; + } else if (daddr > 0x3FC) + panic("Something is messed up!\n"); + + switch (req->size) { + case sizeof(uint32_t): + { + uint32_t ® = *(uint32_t *)data; + + switch (daddr) { + case CR: + reg = regs.command; + //these are supposed to be cleared on a read + reg &= ~(CR_RXD | CR_TXD | CR_TXR | CR_RXR); + break; + + case CFG: + reg = regs.config; + break; + + case MEAR: + reg = regs.mear; + break; + + case PTSCR: + reg = regs.ptscr; + break; + + case ISR: + reg = regs.isr; + devIntrClear(ISR_ALL); + break; + + case IMR: + reg = regs.imr; + break; + + case IER: + reg = regs.ier; + break; + + case IHR: + reg = regs.ihr; + break; + + case TXDP: + reg = regs.txdp; + break; + + case TXDP_HI: + reg = regs.txdp_hi; + break; + + case TXCFG: + reg = regs.txcfg; + break; + + case GPIOR: + reg = regs.gpior; + break; + + case RXDP: + reg = regs.rxdp; + break; + + case RXDP_HI: + reg = regs.rxdp_hi; + break; + + case RXCFG: + reg = regs.rxcfg; + break; + + case PQCR: + reg = regs.pqcr; + break; + + case WCSR: + reg = regs.wcsr; + break; + + case PCR: + reg = regs.pcr; + break; + + //see the spec sheet for how RFCR and RFDR work + //basically, you write to RFCR to tell the machine what you want to do next + //then you act upon RFDR, and the device will be prepared b/c + //of what you wrote to RFCR + case RFCR: + reg = regs.rfcr; + break; + + case RFDR: + switch (regs.rfcr & RFCR_RFADDR) { + case 0x000: + reg = rom.perfectMatch[1]; + reg = reg << 8; + reg += rom.perfectMatch[0]; + break; + case 0x002: + reg = rom.perfectMatch[3] << 8; + reg += rom.perfectMatch[2]; + break; + case 0x004: + reg = rom.perfectMatch[5] << 8; + reg += rom.perfectMatch[4]; + break; + default: + panic("reading from RFDR for something for other than PMATCH!\n"); + //didn't implement other RFDR functionality b/c driver didn't use + } + break; + + case SRR: + reg = regs.srr; + break; + + case MIBC: + reg = regs.mibc; + reg &= ~(MIBC_MIBS | MIBC_ACLR); + break; + + case VRCR: + reg = regs.vrcr; + break; + + case VTCR: + reg = regs.vtcr; + break; + + case VDR: + reg = regs.vdr; + break; + + case CCSR: + reg = regs.ccsr; + break; + + case TBICR: + reg = regs.tbicr; + break; + + case TBISR: + reg = regs.tbisr; + break; + + case TANAR: + reg = regs.tanar; + break; + + case TANLPAR: + reg = regs.tanlpar; + break; + + case TANER: + reg = regs.taner; + break; + + case TESR: + reg = regs.tesr; + break; + + default: + panic("reading unimplemented register: addr = %#x", daddr); + } + + DPRINTF(EthernetPIO, "read from %#x: data=%d data=%#x\n", + daddr, reg, reg); + } + break; + + default: + panic("accessing register with invalid size: addr=%#x, size=%d", + daddr, req->size); + } + + return No_Fault; +} + +Fault +NSGigE::write(MemReqPtr &req, const uint8_t *data) +{ + Addr daddr = req->paddr & 0xfff; + DPRINTF(EthernetPIO, "write da=%#x pa=%#x va=%#x size=%d\n", + daddr, req->paddr, req->vaddr, req->size); + + if (daddr > LAST && daddr <= RESERVED) { + panic("Accessing reserved register"); + } else if (daddr > RESERVED && daddr <= 0x3FC) { + WriteConfig(daddr & 0xff, req->size, *(uint32_t *)data); + return No_Fault; + } else if (daddr > 0x3FC) + panic("Something is messed up!\n"); + + if (req->size == sizeof(uint32_t)) { + uint32_t reg = *(uint32_t *)data; + DPRINTF(EthernetPIO, "write data=%d data=%#x\n", reg, reg); + + switch (daddr) { + case CR: + regs.command = reg; + if ((reg & (CR_TXE | CR_TXD)) == (CR_TXE | CR_TXD)) { + txHalt = true; + } else if (reg & CR_TXE) { + //the kernel is enabling the transmit machine + if (txState == txIdle) + txKick(); + } else if (reg & CR_TXD) { + txHalt = true; + } + + if ((reg & (CR_RXE | CR_RXD)) == (CR_RXE | CR_RXD)) { + rxHalt = true; + } else if (reg & CR_RXE) { + if (rxState == rxIdle) { + rxKick(); + } + } else if (reg & CR_RXD) { + rxHalt = true; + } + + if (reg & CR_TXR) + txReset(); + + if (reg & CR_RXR) + rxReset(); + + if (reg & CR_SWI) + devIntrPost(ISR_SWI); + + if (reg & CR_RST) { + txReset(); + rxReset(); + + regsReset(); + } + break; + + case CFG: + if (reg & CFG_LNKSTS || reg & CFG_SPDSTS || reg & CFG_DUPSTS + || reg & CFG_RESERVED || reg & CFG_T64ADDR + || reg & CFG_PCI64_DET) + panic("writing to read-only or reserved CFG bits!\n"); + + regs.config |= reg & ~(CFG_LNKSTS | CFG_SPDSTS | CFG_DUPSTS | CFG_RESERVED | + CFG_T64ADDR | CFG_PCI64_DET); + +// all these #if 0's are because i don't THINK the kernel needs to have these implemented +// if there is a problem relating to one of these, you may need to add functionality in +#if 0 + if (reg & CFG_TBI_EN) ; + if (reg & CFG_MODE_1000) ; +#endif + + if (reg & CFG_AUTO_1000) + panic("CFG_AUTO_1000 not implemented!\n"); + +#if 0 + if (reg & CFG_PINT_DUPSTS || reg & CFG_PINT_LNKSTS || reg & CFG_PINT_SPDSTS) ; + if (reg & CFG_TMRTEST) ; + if (reg & CFG_MRM_DIS) ; + if (reg & CFG_MWI_DIS) ; + + if (reg & CFG_T64ADDR) + panic("CFG_T64ADDR is read only register!\n"); + + if (reg & CFG_PCI64_DET) + panic("CFG_PCI64_DET is read only register!\n"); + + if (reg & CFG_DATA64_EN) ; + if (reg & CFG_M64ADDR) ; + if (reg & CFG_PHY_RST) ; + if (reg & CFG_PHY_DIS) ; +#endif + + if (reg & CFG_EXTSTS_EN) + extstsEnable = true; + else + extstsEnable = false; + +#if 0 + if (reg & CFG_REQALG) ; + if (reg & CFG_SB) ; + if (reg & CFG_POW) ; + if (reg & CFG_EXD) ; + if (reg & CFG_PESEL) ; + if (reg & CFG_BROM_DIS) ; + if (reg & CFG_EXT_125) ; + if (reg & CFG_BEM) ; +#endif + break; + + case MEAR: + regs.mear = reg; + /* since phy is completely faked, MEAR_MD* don't matter + and since the driver never uses MEAR_EE*, they don't matter */ +#if 0 + if (reg & MEAR_EEDI) ; + if (reg & MEAR_EEDO) ; //this one is read only + if (reg & MEAR_EECLK) ; + if (reg & MEAR_EESEL) ; + if (reg & MEAR_MDIO) ; + if (reg & MEAR_MDDIR) ; + if (reg & MEAR_MDC) ; +#endif + break; + + case PTSCR: + regs.ptscr = reg & ~(PTSCR_RBIST_RDONLY); + /* these control BISTs for various parts of chip - we don't care or do + just fake that the BIST is done */ + if (reg & PTSCR_RBIST_EN) + regs.ptscr |= PTSCR_RBIST_DONE; + if (reg & PTSCR_EEBIST_EN) + regs.ptscr &= ~PTSCR_EEBIST_EN; + if (reg & PTSCR_EELOAD_EN) + regs.ptscr &= ~PTSCR_EELOAD_EN; + break; + + case ISR: /* writing to the ISR has no effect */ + panic("ISR is a read only register!\n"); + + case IMR: + regs.imr = reg; + devIntrChangeMask(); + break; + + case IER: + regs.ier = reg; + break; + + case IHR: + regs.ihr = reg; + /* not going to implement real interrupt holdoff */ + break; + + case TXDP: + regs.txdp = (reg & 0xFFFFFFFC); + assert(txState == txIdle); + CTDD = false; + break; + + case TXDP_HI: + regs.txdp_hi = reg; + break; + + case TXCFG: + regs.txcfg = reg; +#if 0 + if (reg & TXCFG_CSI) ; + if (reg & TXCFG_HBI) ; + if (reg & TXCFG_MLB) ; + if (reg & TXCFG_ATP) ; + if (reg & TXCFG_ECRETRY) ; /* this could easily be implemented, but + considering the network is just a fake + pipe, wouldn't make sense to do this */ + + if (reg & TXCFG_BRST_DIS) ; +#endif + + + /* we handle our own DMA, ignore the kernel's exhortations */ + if (reg & TXCFG_MXDMA) ; + + break; + + case GPIOR: + regs.gpior = reg; + /* these just control general purpose i/o pins, don't matter */ + break; + + case RXDP: + regs.rxdp = reg; + break; + + case RXDP_HI: + regs.rxdp_hi = reg; + break; + + case RXCFG: + regs.rxcfg = reg; +#if 0 + if (reg & RXCFG_AEP) ; + if (reg & RXCFG_ARP) ; + if (reg & RXCFG_STRIPCRC) ; + if (reg & RXCFG_RX_RD) ; + if (reg & RXCFG_ALP) ; + if (reg & RXCFG_AIRL) ; +#endif + + /* we handle our own DMA, ignore what kernel says about it */ + if (reg & RXCFG_MXDMA) ; + +#if 0 + if (reg & (RXCFG_DRTH | RXCFG_DRTH0)) ; +#endif + break; + + case PQCR: + /* there is no priority queueing used in the linux 2.6 driver */ + regs.pqcr = reg; + break; + + case WCSR: + /* not going to implement wake on LAN */ + regs.wcsr = reg; + break; + + case PCR: + /* not going to implement pause control */ + regs.pcr = reg; + break; + + case RFCR: + regs.rfcr = reg; + + rxFilterEnable = (reg & RFCR_RFEN) ? true : false; + + acceptBroadcast = (reg & RFCR_AAB) ? true : false; + + acceptMulticast = (reg & RFCR_AAM) ? true : false; + + acceptUnicast = (reg & RFCR_AAU) ? true : false; + + acceptPerfect = (reg & RFCR_APM) ? true : false; + + acceptArp = (reg & RFCR_AARP) ? true : false; + + if (reg & RFCR_APAT) ; +// panic("RFCR_APAT not implemented!\n"); + + if (reg & RFCR_MHEN || reg & RFCR_UHEN) + panic("hash filtering not implemented!\n"); + + if (reg & RFCR_ULM) + panic("RFCR_ULM not implemented!\n"); + + break; + + case RFDR: + panic("the driver never writes to RFDR, something is wrong!\n"); + + case BRAR: + panic("the driver never uses BRAR, something is wrong!\n"); + + case BRDR: + panic("the driver never uses BRDR, something is wrong!\n"); + + case SRR: + panic("SRR is read only register!\n"); + + case MIBC: + panic("the driver never uses MIBC, something is wrong!\n"); + + case VRCR: + regs.vrcr = reg; + break; + + case VTCR: + regs.vtcr = reg; + break; + + case VDR: + panic("the driver never uses VDR, something is wrong!\n"); + break; + + case CCSR: + /* not going to implement clockrun stuff */ + regs.ccsr = reg; + break; + + case TBICR: + regs.tbicr = reg; + if (reg & TBICR_MR_LOOPBACK) + panic("TBICR_MR_LOOPBACK never used, something wrong!\n"); + + if (reg & TBICR_MR_AN_ENABLE) { + regs.tanlpar = regs.tanar; + regs.tbisr |= (TBISR_MR_AN_COMPLETE | TBISR_MR_LINK_STATUS); + } + +#if 0 + if (reg & TBICR_MR_RESTART_AN) ; +#endif + + break; + + case TBISR: + panic("TBISR is read only register!\n"); + + case TANAR: + regs.tanar = reg; + if (reg & TANAR_PS2) + panic("this isn't used in driver, something wrong!\n"); + + if (reg & TANAR_PS1) + panic("this isn't used in driver, something wrong!\n"); + break; + + case TANLPAR: + panic("this should only be written to by the fake phy!\n"); + + case TANER: + panic("TANER is read only register!\n"); + + case TESR: + regs.tesr = reg; + break; + + default: + panic("thought i covered all the register, what is this? addr=%#x", + daddr); + } + } else + panic("Invalid Request Size"); + + return No_Fault; +} + +void +NSGigE::devIntrPost(uint32_t interrupts) +{ + bool delay = false; + + if (interrupts & ISR_RESERVE) + panic("Cannot set a reserved interrupt"); + + if (interrupts & ISR_TXRCMP) + regs.isr |= ISR_TXRCMP; + + if (interrupts & ISR_RXRCMP) + regs.isr |= ISR_RXRCMP; + +//ISR_DPERR not implemented +//ISR_SSERR not implemented +//ISR_RMABT not implemented +//ISR_RXSOVR not implemented +//ISR_HIBINT not implemented +//ISR_PHY not implemented +//ISR_PME not implemented + + if (interrupts & ISR_SWI) + regs.isr |= ISR_SWI; + +//ISR_MIB not implemented +//ISR_TXURN not implemented + + if (interrupts & ISR_TXIDLE) + regs.isr |= ISR_TXIDLE; + + if (interrupts & ISR_TXERR) + regs.isr |= ISR_TXERR; + + if (interrupts & ISR_TXDESC) + regs.isr |= ISR_TXDESC; + + if (interrupts & ISR_TXOK) { + regs.isr |= ISR_TXOK; + delay = true; + } + + if (interrupts & ISR_RXORN) + regs.isr |= ISR_RXORN; + + if (interrupts & ISR_RXIDLE) + regs.isr |= ISR_RXIDLE; + +//ISR_RXEARLY not implemented + + if (interrupts & ISR_RXERR) + regs.isr |= ISR_RXERR; + + if (interrupts & ISR_RXDESC) + regs.isr |= ISR_RXDESC; + + if (interrupts & ISR_RXOK) { + delay = true; + regs.isr |= ISR_RXOK; + } + + if ((regs.isr & regs.imr)) { + Tick when = curTick; + if (delay) + when += intrDelay; + cpuIntrPost(when); + } + + DPRINTF(Ethernet, "interrupt posted intr=%#x isr=%#x imr=%#x\n", + interrupts, regs.isr, regs.imr); +} + +void +NSGigE::devIntrClear(uint32_t interrupts) +{ + if (interrupts & ISR_RESERVE) + panic("Cannot clear a reserved interrupt"); + + if (interrupts & ISR_TXRCMP) + regs.isr &= ~ISR_TXRCMP; + + if (interrupts & ISR_RXRCMP) + regs.isr &= ~ISR_RXRCMP; + +//ISR_DPERR not implemented +//ISR_SSERR not implemented +//ISR_RMABT not implemented +//ISR_RXSOVR not implemented +//ISR_HIBINT not implemented +//ISR_PHY not implemented +//ISR_PME not implemented + + if (interrupts & ISR_SWI) + regs.isr &= ~ISR_SWI; + +//ISR_MIB not implemented +//ISR_TXURN not implemented + + if (interrupts & ISR_TXIDLE) + regs.isr &= ~ISR_TXIDLE; + + if (interrupts & ISR_TXERR) + regs.isr &= ~ISR_TXERR; + + if (interrupts & ISR_TXDESC) + regs.isr &= ~ISR_TXDESC; + + if (interrupts & ISR_TXOK) + regs.isr &= ~ISR_TXOK; + + if (interrupts & ISR_RXORN) + regs.isr &= ~ISR_RXORN; + + if (interrupts & ISR_RXIDLE) + regs.isr &= ~ISR_RXIDLE; + +//ISR_RXEARLY not implemented + + if (interrupts & ISR_RXERR) + regs.isr &= ~ISR_RXERR; + + if (interrupts & ISR_RXDESC) + regs.isr &= ~ISR_RXDESC; + + if (interrupts & ISR_RXOK) + regs.isr &= ~ISR_RXOK; + + if (!(regs.isr & regs.imr)) + cpuIntrClear(); + + DPRINTF(Ethernet, "interrupt cleared intr=%x isr=%x imr=%x\n", + interrupts, regs.isr, regs.imr); +} + +void +NSGigE::devIntrChangeMask() +{ + DPRINTF(Ethernet, "interrupt mask changed\n"); + + if (regs.isr & regs.imr) + cpuIntrPost(curTick); + else + cpuIntrClear(); +} + +void +NSGigE::cpuIntrPost(Tick when) +{ + if (when > intrTick && intrTick != 0) + return; + + intrTick = when; + + if (intrEvent) { + intrEvent->squash(); + intrEvent = 0; + } + + if (when < curTick) { + cpuInterrupt(); + } else { + intrEvent = new IntrEvent(this, true); + intrEvent->schedule(intrTick); + } +} + +void +NSGigE::cpuInterrupt() +{ + // Don't send an interrupt if there's already one + if (cpuPendingIntr) + return; + + // Don't send an interrupt if it's supposed to be delayed + if (intrTick > curTick) + return; + + // Whether or not there's a pending interrupt, we don't care about + // it anymore + intrEvent = 0; + intrTick = 0; + + // Send interrupt + cpuPendingIntr = true; + /** @todo rework the intctrl to be tsunami ok */ + //intctrl->post(TheISA::INTLEVEL_IRQ1, TheISA::INTINDEX_ETHERNET); + tsunami->cchip->postDRIR(configData->config.hdr.pci0.interruptLine); +} + +void +NSGigE::cpuIntrClear() +{ + if (cpuPendingIntr) { + cpuPendingIntr = false; + /** @todo rework the intctrl to be tsunami ok */ + //intctrl->clear(TheISA::INTLEVEL_IRQ1, TheISA::INTINDEX_ETHERNET); + tsunami->cchip->clearDRIR(configData->config.hdr.pci0.interruptLine); + } +} + +bool +NSGigE::cpuIntrPending() const +{ return cpuPendingIntr; } + +void +NSGigE::txReset() +{ + + DPRINTF(Ethernet, "transmit reset\n"); + + CTDD = false; + txFifoCnt = 0; + txFifoAvail = MAX_TX_FIFO_SIZE; + txHalt = false; + txFragPtr = 0; + assert(txDescCnt == 0); + txFifo.clear(); + regs.command &= ~CR_TXE; + txState = txIdle; + assert(txDmaState == dmaIdle); +} + +void +NSGigE::rxReset() +{ + DPRINTF(Ethernet, "receive reset\n"); + + CRDD = false; + assert(rxPktBytes == 0); + rxFifoCnt = 0; + rxHalt = false; + rxFragPtr = 0; + assert(rxDescCnt == 0); + assert(rxDmaState == dmaIdle); + rxFifo.clear(); + regs.command &= ~CR_RXE; + rxState = rxIdle; +} + +void +NSGigE::rxDmaReadCopy() +{ + assert(rxDmaState == dmaReading); + + memcpy(rxDmaData, physmem->dma_addr(rxDmaAddr, rxDmaLen), rxDmaLen); + rxDmaState = dmaIdle; + + DPRINTF(EthernetDMA, "rx dma read paddr=%#x len=%d\n", + rxDmaAddr, rxDmaLen); + DDUMP(EthernetDMA, rxDmaData, rxDmaLen); +} + +bool +NSGigE::doRxDmaRead() +{ + assert(rxDmaState == dmaIdle || rxDmaState == dmaReadWaiting); + rxDmaState = dmaReading; + + if (dmaInterface && !rxDmaFree) { + if (dmaInterface->busy()) + rxDmaState = dmaReadWaiting; + else + dmaInterface->doDMA(Read, rxDmaAddr, rxDmaLen, curTick, + &rxDmaReadEvent); + return true; + } + + if (dmaReadDelay == 0 && dmaReadFactor == 0) { + rxDmaReadCopy(); + return false; + } + + Tick factor = ((rxDmaLen + ULL(63)) >> ULL(6)) * dmaReadFactor; + Tick start = curTick + dmaReadDelay + factor; + rxDmaReadEvent.schedule(start); + return true; +} + +void +NSGigE::rxDmaReadDone() +{ + assert(rxDmaState == dmaReading); + rxDmaReadCopy(); + + // If the transmit state machine has a pending DMA, let it go first + if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting) + txKick(); + + rxKick(); +} + +void +NSGigE::rxDmaWriteCopy() +{ + assert(rxDmaState == dmaWriting); + + memcpy(physmem->dma_addr(rxDmaAddr, rxDmaLen), rxDmaData, rxDmaLen); + rxDmaState = dmaIdle; + + DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n", + rxDmaAddr, rxDmaLen); + DDUMP(EthernetDMA, rxDmaData, rxDmaLen); +} + +bool +NSGigE::doRxDmaWrite() +{ + assert(rxDmaState == dmaIdle || rxDmaState == dmaWriteWaiting); + rxDmaState = dmaWriting; + + if (dmaInterface && !rxDmaFree) { + if (dmaInterface->busy()) + rxDmaState = dmaWriteWaiting; + else + dmaInterface->doDMA(WriteInvalidate, rxDmaAddr, rxDmaLen, curTick, + &rxDmaWriteEvent); + return true; + } + + if (dmaWriteDelay == 0 && dmaWriteFactor == 0) { + rxDmaWriteCopy(); + return false; + } + + Tick factor = ((rxDmaLen + ULL(63)) >> ULL(6)) * dmaWriteFactor; + Tick start = curTick + dmaWriteDelay + factor; + rxDmaWriteEvent.schedule(start); + return true; +} + +void +NSGigE::rxDmaWriteDone() +{ + assert(rxDmaState == dmaWriting); + rxDmaWriteCopy(); + + // If the transmit state machine has a pending DMA, let it go first + if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting) + txKick(); + + rxKick(); +} + +void +NSGigE::rxKick() +{ + DPRINTF(Ethernet, "receive kick state=%s (rxBuf.size=%d)\n", + NsRxStateStrings[rxState], rxFifo.size()); + + if (rxKickTick > curTick) { + DPRINTF(Ethernet, "receive kick exiting, can't run till %d\n", + rxKickTick); + return; + } + + next: + switch(rxDmaState) { + case dmaReadWaiting: + if (doRxDmaRead()) + goto exit; + break; + case dmaWriteWaiting: + if (doRxDmaWrite()) + goto exit; + break; + default: + break; + } + + // see state machine from spec for details + // the way this works is, if you finish work on one state and can go directly to + // another, you do that through jumping to the label "next". however, if you have + // intermediate work, like DMA so that you can't go to the next state yet, you go to + // exit and exit the loop. however, when the DMA is done it will trigger an + // event and come back to this loop. + switch (rxState) { + case rxIdle: + if (!regs.command & CR_RXE) { + DPRINTF(Ethernet, "Receive Disabled! Nothing to do.\n"); + goto exit; + } + + if (CRDD) { + rxState = rxDescRefr; + + rxDmaAddr = regs.rxdp & 0x3fffffff; + rxDmaData = &rxDescCache + offsetof(ns_desc, link); + rxDmaLen = sizeof(rxDescCache.link); + rxDmaFree = dmaDescFree; + + if (doRxDmaRead()) + goto exit; + } else { + rxState = rxDescRead; + + rxDmaAddr = regs.rxdp & 0x3fffffff; + rxDmaData = &rxDescCache; + rxDmaLen = sizeof(ns_desc); + rxDmaFree = dmaDescFree; + + if (doRxDmaRead()) + goto exit; + } + break; + + case rxDescRefr: + if (rxDmaState != dmaIdle) + goto exit; + + rxState = rxAdvance; + break; + + case rxDescRead: + if (rxDmaState != dmaIdle) + goto exit; + + DPRINTF(Ethernet, + "rxDescCache:\n\tlink=%#x\n\tbufptr=%#x\n\tcmdsts=%#x\n\textsts=%#x\n" + ,rxDescCache.link, rxDescCache.bufptr, rxDescCache.cmdsts, + rxDescCache.extsts); + + if (rxDescCache.cmdsts & CMDSTS_OWN) { + rxState = rxIdle; + } else { + rxState = rxFifoBlock; + rxFragPtr = rxDescCache.bufptr; + rxDescCnt = rxDescCache.cmdsts & CMDSTS_LEN_MASK; + } + break; + + case rxFifoBlock: + if (!rxPacket) { + /** + * @todo in reality, we should be able to start processing + * the packet as it arrives, and not have to wait for the + * full packet ot be in the receive fifo. + */ + if (rxFifo.empty()) + goto exit; + + // If we don't have a packet, grab a new one from the fifo. + rxPacket = rxFifo.front(); + rxPktBytes = rxPacket->length; + rxPacketBufPtr = rxPacket->data; + + // sanity check - i think the driver behaves like this + assert(rxDescCnt >= rxPktBytes); + + // Must clear the value before popping to decrement the + // reference count + rxFifo.front() = NULL; + rxFifo.pop_front(); + } + + + // dont' need the && rxDescCnt > 0 if driver sanity check above holds + if (rxPktBytes > 0) { + rxState = rxFragWrite; + // don't need min<>(rxPktBytes,rxDescCnt) if above sanity check holds + rxXferLen = rxPktBytes; + + rxDmaAddr = rxFragPtr & 0x3fffffff; + rxDmaData = rxPacketBufPtr; + rxDmaLen = rxXferLen; + rxDmaFree = dmaDataFree; + + if (doRxDmaWrite()) + goto exit; + + } else { + rxState = rxDescWrite; + + //if (rxPktBytes == 0) { /* packet is done */ + assert(rxPktBytes == 0); + + rxDescCache.cmdsts |= CMDSTS_OWN; + rxDescCache.cmdsts &= ~CMDSTS_MORE; + rxDescCache.cmdsts |= CMDSTS_OK; + rxDescCache.cmdsts &= 0xffff0000; + rxDescCache.cmdsts += rxPacket->length; //i.e. set CMDSTS_SIZE + +#if 0 + /* all the driver uses these are for its own stats keeping + which we don't care about, aren't necessary for functionality + and doing this would just slow us down. if they end up using + this in a later version for functional purposes, just undef + */ + if (rxFilterEnable) { + rxDescCache.cmdsts &= ~CMDSTS_DEST_MASK; + if (rxFifo.front()->IsUnicast()) + rxDescCache.cmdsts |= CMDSTS_DEST_SELF; + if (rxFifo.front()->IsMulticast()) + rxDescCache.cmdsts |= CMDSTS_DEST_MULTI; + if (rxFifo.front()->IsBroadcast()) + rxDescCache.cmdsts |= CMDSTS_DEST_MASK; + } +#endif + + eth_header *eth = (eth_header *) rxPacket->data; + // eth->type 0x800 indicated that it's an ip packet. + if (eth->type == 0x800 && extstsEnable) { + rxDescCache.extsts |= EXTSTS_IPPKT; + if (!ipChecksum(rxPacket, false)) + rxDescCache.extsts |= EXTSTS_IPERR; + ip_header *ip = rxFifo.front()->getIpHdr(); + + if (ip->protocol == 6) { + rxDescCache.extsts |= EXTSTS_TCPPKT; + if (!tcpChecksum(rxPacket, false)) + rxDescCache.extsts |= EXTSTS_TCPERR; + } else if (ip->protocol == 17) { + rxDescCache.extsts |= EXTSTS_UDPPKT; + if (!udpChecksum(rxPacket, false)) + rxDescCache.extsts |= EXTSTS_UDPERR; + } + } + + rxFifoCnt -= rxPacket->length; + rxPacket = 0; + + /* the driver seems to always receive into desc buffers + of size 1514, so you never have a pkt that is split + into multiple descriptors on the receive side, so + i don't implement that case, hence the assert above. + */ + + DPRINTF(Ethernet, "rxDesc writeback:\n\tcmdsts=%#x\n\textsts=%#x\n", + rxDescCache.cmdsts, rxDescCache.extsts); + + rxDmaAddr = (regs.rxdp + offsetof(ns_desc, cmdsts)) & 0x3fffffff; + rxDmaData = &(rxDescCache.cmdsts); + rxDmaLen = sizeof(rxDescCache.cmdsts) + sizeof(rxDescCache.extsts); + rxDmaFree = dmaDescFree; + + if (doRxDmaWrite()) + goto exit; + } + break; + + case rxFragWrite: + if (rxDmaState != dmaIdle) + goto exit; + + rxPacketBufPtr += rxXferLen; + rxFragPtr += rxXferLen; + rxPktBytes -= rxXferLen; + + rxState = rxFifoBlock; + break; + + case rxDescWrite: + if (rxDmaState != dmaIdle) + goto exit; + + assert(rxDescCache.cmdsts & CMDSTS_OWN); + + assert(rxPacket == 0); + devIntrPost(ISR_RXOK); + + if (rxDescCache.cmdsts & CMDSTS_INTR) + devIntrPost(ISR_RXDESC); + + if (rxHalt) { + rxState = rxIdle; + rxHalt = false; + } else + rxState = rxAdvance; + break; + + case rxAdvance: + if (rxDescCache.link == 0) { + rxState = rxIdle; + return; + } else { + rxState = rxDescRead; + regs.rxdp = rxDescCache.link; + CRDD = false; + + rxDmaAddr = regs.rxdp & 0x3fffffff; + rxDmaData = &rxDescCache; + rxDmaLen = sizeof(ns_desc); + rxDmaFree = dmaDescFree; + + if (doRxDmaRead()) + goto exit; + } + break; + + default: + panic("Invalid rxState!"); + } + + + DPRINTF(Ethernet, "entering next rx state = %s\n", + NsRxStateStrings[rxState]); + + if (rxState == rxIdle) { + regs.command &= ~CR_RXE; + devIntrPost(ISR_RXIDLE); + return; + } + + goto next; + + exit: + /** + * @todo do we want to schedule a future kick? + */ + DPRINTF(Ethernet, "rx state machine exited state=%s\n", + NsRxStateStrings[rxState]); +} + +void +NSGigE::transmit() +{ + if (txFifo.empty()) { + DPRINTF(Ethernet, "nothing to transmit\n"); + return; + } + + if (interface->sendPacket(txFifo.front())) { + DPRINTF(Ethernet, "transmit packet\n"); + DDUMP(Ethernet, txFifo.front()->data, txFifo.front()->length); + txBytes += txFifo.front()->length; + txPackets++; + + txFifoCnt -= (txFifo.front()->length - txPktXmitted); + txPktXmitted = 0; + txFifo.front() = NULL; + txFifo.pop_front(); + + /* normally do a writeback of the descriptor here, and ONLY after that is + done, send this interrupt. but since our stuff never actually fails, + just do this interrupt here, otherwise the code has to stray from this + nice format. besides, it's functionally the same. + */ + devIntrPost(ISR_TXOK); + } + + if (!txFifo.empty() && !txEvent.scheduled()) { + DPRINTF(Ethernet, "reschedule transmit\n"); + txEvent.schedule(curTick + 1000); + } +} + +void +NSGigE::txDmaReadCopy() +{ + assert(txDmaState == dmaReading); + + memcpy(txDmaData, physmem->dma_addr(txDmaAddr, txDmaLen), txDmaLen); + txDmaState = dmaIdle; + + DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n", + txDmaAddr, txDmaLen); + DDUMP(EthernetDMA, txDmaData, txDmaLen); +} + +bool +NSGigE::doTxDmaRead() +{ + assert(txDmaState == dmaIdle || txDmaState == dmaReadWaiting); + txDmaState = dmaReading; + + if (dmaInterface && !txDmaFree) { + if (dmaInterface->busy()) + txDmaState = dmaReadWaiting; + else + dmaInterface->doDMA(Read, txDmaAddr, txDmaLen, curTick, + &txDmaReadEvent); + return true; + } + + if (dmaReadDelay == 0 && dmaReadFactor == 0.0) { + txDmaReadCopy(); + return false; + } + + Tick factor = ((txDmaLen + ULL(63)) >> ULL(6)) * dmaReadFactor; + Tick start = curTick + dmaReadDelay + factor; + txDmaReadEvent.schedule(start); + return true; +} + +void +NSGigE::txDmaReadDone() +{ + assert(txDmaState == dmaReading); + txDmaReadCopy(); + + // If the receive state machine has a pending DMA, let it go first + if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting) + rxKick(); + + txKick(); +} + +void +NSGigE::txDmaWriteCopy() +{ + assert(txDmaState == dmaWriting); + + memcpy(physmem->dma_addr(txDmaAddr, txDmaLen), txDmaData, txDmaLen); + txDmaState = dmaIdle; + + DPRINTF(EthernetDMA, "tx dma write paddr=%#x len=%d\n", + txDmaAddr, txDmaLen); + DDUMP(EthernetDMA, txDmaData, txDmaLen); +} + +bool +NSGigE::doTxDmaWrite() +{ + assert(txDmaState == dmaIdle || txDmaState == dmaWriteWaiting); + txDmaState = dmaWriting; + + if (dmaInterface && !txDmaFree) { + if (dmaInterface->busy()) + txDmaState = dmaWriteWaiting; + else + dmaInterface->doDMA(WriteInvalidate, txDmaAddr, txDmaLen, curTick, + &txDmaWriteEvent); + return true; + } + + if (dmaWriteDelay == 0 && dmaWriteFactor == 0.0) { + txDmaWriteCopy(); + return false; + } + + Tick factor = ((txDmaLen + ULL(63)) >> ULL(6)) * dmaWriteFactor; + Tick start = curTick + dmaWriteDelay + factor; + txDmaWriteEvent.schedule(start); + return true; +} + +void +NSGigE::txDmaWriteDone() +{ + assert(txDmaState == dmaWriting); + txDmaWriteCopy(); + + // If the receive state machine has a pending DMA, let it go first + if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting) + rxKick(); + + txKick(); +} + +void +NSGigE::txKick() +{ + DPRINTF(Ethernet, "transmit kick state=%s\n", NsTxStateStrings[txState]); + + if (rxKickTick > curTick) { + DPRINTF(Ethernet, "receive kick exiting, can't run till %d\n", + rxKickTick); + + return; + } + + next: + switch(txDmaState) { + case dmaReadWaiting: + if (doTxDmaRead()) + goto exit; + break; + case dmaWriteWaiting: + if (doTxDmaWrite()) + goto exit; + break; + default: + break; + } + + switch (txState) { + case txIdle: + if (!regs.command & CR_TXE) { + DPRINTF(Ethernet, "Transmit disabled. Nothing to do.\n"); + goto exit; + } + + if (CTDD) { + txState = txDescRefr; + + txDmaAddr = regs.txdp & 0x3fffffff; + txDmaData = &txDescCache + offsetof(ns_desc, link); + txDmaLen = sizeof(txDescCache.link); + txDmaFree = dmaDescFree; + + if (doTxDmaRead()) + goto exit; + + } else { + txState = txDescRead; + + txDmaAddr = regs.txdp & 0x3fffffff; + txDmaData = &txDescCache; + txDmaLen = sizeof(ns_desc); + txDmaFree = dmaDescFree; + + if (doTxDmaRead()) + goto exit; + } + break; + + case txDescRefr: + if (txDmaState != dmaIdle) + goto exit; + + txState = txAdvance; + break; + + case txDescRead: + if (txDmaState != dmaIdle) + goto exit; + + DPRINTF(Ethernet, + "txDescCache data:\n\tlink=%#x\n\tbufptr=%#x\n\tcmdsts=%#x\n\textsts=%#x\n" + ,txDescCache.link, txDescCache.bufptr, txDescCache.cmdsts, + txDescCache.extsts); + + if (txDescCache.cmdsts & CMDSTS_OWN) { + txState = txFifoBlock; + txFragPtr = txDescCache.bufptr; + txDescCnt = txDescCache.cmdsts & CMDSTS_LEN_MASK; + } else { + txState = txIdle; + } + break; + + case txFifoBlock: + if (!txPacket) { + DPRINTF(Ethernet, "starting the tx of a new packet\n"); + txPacket = new EtherPacket; + txPacket->data = new uint8_t[16384]; + txPacketBufPtr = txPacket->data; + } + + if (txDescCnt == 0) { + DPRINTF(Ethernet, "the txDescCnt == 0, done with descriptor\n"); + if (txDescCache.cmdsts & CMDSTS_MORE) { + DPRINTF(Ethernet, "there are more descriptors to come\n"); + txState = txDescWrite; + + txDescCache.cmdsts &= ~CMDSTS_OWN; + + txDmaAddr = (regs.txdp + offsetof(ns_desc, cmdsts)) & 0x3fffffff; + txDmaData = &(txDescCache.cmdsts); + txDmaLen = sizeof(txDescCache.cmdsts); + txDmaFree = dmaDescFree; + + if (doTxDmaWrite()) + goto exit; + + } else { /* this packet is totally done */ + DPRINTF(Ethernet, "This packet is done, let's wrap it up\n"); + /* deal with the the packet that just finished */ + if ((regs.vtcr & VTCR_PPCHK) && extstsEnable) { + if (txDescCache.extsts & EXTSTS_UDPPKT) { + udpChecksum(txPacket, true); + } else if (txDescCache.extsts & EXTSTS_TCPPKT) { + tcpChecksum(txPacket, true); + } else if (txDescCache.extsts & EXTSTS_IPPKT) { + ipChecksum(txPacket, true); + } + } + + txPacket->length = txPacketBufPtr - txPacket->data; + /* this is just because the receive can't handle a packet bigger + want to make sure */ + assert(txPacket->length <= 1514); + txFifo.push_back(txPacket); + + + /* this following section is not to spec, but functionally shouldn't + be any different. normally, the chip will wait til the transmit has + occurred before writing back the descriptor because it has to wait + to see that it was successfully transmitted to decide whether to set + CMDSTS_OK or not. however, in the simulator since it is always + successfully transmitted, and writing it exactly to spec would + complicate the code, we just do it here + */ + txDescCache.cmdsts &= ~CMDSTS_OWN; + txDescCache.cmdsts |= CMDSTS_OK; + + DPRINTF(Ethernet, + "txDesc writeback:\n\tcmdsts=%#x\n\textsts=%#x\n", + txDescCache.cmdsts, txDescCache.extsts); + + txDmaAddr = (regs.txdp + offsetof(ns_desc, cmdsts)) & 0x3fffffff; + txDmaData = &(txDescCache.cmdsts); + txDmaLen = sizeof(txDescCache.cmdsts) + sizeof(txDescCache.extsts); + txDmaFree = dmaDescFree; + + if (doTxDmaWrite()) + goto exit; + + txPacket = 0; + transmit(); + + if (txHalt) { + txState = txIdle; + txHalt = false; + } else + txState = txAdvance; + } + } else { + DPRINTF(Ethernet, "this descriptor isn't done yet\n"); + /* the fill thresh is in units of 32 bytes, shift right by 8 to get the + value, shift left by 5 to get the real number of bytes */ + if (txFifoAvail < ((regs.txcfg & TXCFG_FLTH_MASK) >> 3)) { + DPRINTF(Ethernet, "txFifoAvail=%d, regs.txcfg & TXCFG_FLTH_MASK = %#x\n", + txFifoAvail, regs.txcfg & TXCFG_FLTH_MASK); + goto exit; + } + + txState = txFragRead; + + /* The number of bytes transferred is either whatever is left + in the descriptor (txDescCnt), or if there is not enough + room in the fifo, just whatever room is left in the fifo + */ + txXferLen = min<uint32_t>(txDescCnt, txFifoAvail); + + txDmaAddr = txFragPtr & 0x3fffffff; + txDmaData = txPacketBufPtr; + txDmaLen = txXferLen; + txDmaFree = dmaDataFree; + + if (doTxDmaRead()) + goto exit; + } + break; + + case txFragRead: + if (txDmaState != dmaIdle) + goto exit; + + txPacketBufPtr += txXferLen; + txFragPtr += txXferLen; + txFifoCnt += txXferLen; + txDescCnt -= txXferLen; + + txState = txFifoBlock; + break; + + case txDescWrite: + if (txDmaState != dmaIdle) + goto exit; + + if (txFifoCnt >= ((regs.txcfg & TXCFG_DRTH_MASK) << 5)) { + if (txFifo.empty()) { + uint32_t xmitted = (uint32_t) (txPacketBufPtr - txPacket->data - txPktXmitted); + txFifoCnt -= xmitted; + txPktXmitted += xmitted; + } else { + transmit(); + } + } + + if (txDescCache.cmdsts & CMDSTS_INTR) { + devIntrPost(ISR_TXDESC); + } + + txState = txAdvance; + break; + + case txAdvance: + if (txDescCache.link == 0) { + txState = txIdle; + } else { + txState = txDescRead; + regs.txdp = txDescCache.link; + CTDD = false; + + txDmaAddr = txDescCache.link & 0x3fffffff; + txDmaData = &txDescCache; + txDmaLen = sizeof(ns_desc); + txDmaFree = dmaDescFree; + + if (doTxDmaRead()) + goto exit; + } + break; + + default: + panic("invalid state"); + } + + DPRINTF(Ethernet, "entering next tx state=%s\n", + NsTxStateStrings[txState]); + + if (txState == txIdle) { + regs.command &= ~CR_TXE; + devIntrPost(ISR_TXIDLE); + return; + } + + goto next; + + exit: + /** + * @todo do we want to schedule a future kick? + */ + DPRINTF(Ethernet, "tx state machine exited state=%s\n", + NsTxStateStrings[txState]); +} + +void +NSGigE::transferDone() +{ + if (txFifo.empty()) + return; + + DPRINTF(Ethernet, "schedule transmit\n"); + + if (txEvent.scheduled()) + txEvent.reschedule(curTick + 1); + else + txEvent.schedule(curTick + 1); +} + +bool +NSGigE::rxFilter(PacketPtr packet) +{ + bool drop = true; + string type; + + if (packet->IsUnicast()) { + type = "unicast"; + + // If we're accepting all unicast addresses + if (acceptUnicast) + drop = false; + + // If we make a perfect match + if ((acceptPerfect) + && (memcmp(rom.perfectMatch, packet->data, sizeof(rom.perfectMatch)) == 0)) + drop = false; + + eth_header *eth = (eth_header *) packet->data; + if ((acceptArp) && (eth->type == 0x806)) + drop = false; + + } else if (packet->IsBroadcast()) { + type = "broadcast"; + + // if we're accepting broadcasts + if (acceptBroadcast) + drop = false; + + } else if (packet->IsMulticast()) { + type = "multicast"; + + // if we're accepting all multicasts + if (acceptMulticast) + drop = false; + + } else { + type = "unknown"; + + // oh well, punt on this one + } + + if (drop) { + DPRINTF(Ethernet, "rxFilter drop\n"); + DDUMP(EthernetData, packet->data, packet->length); + } + + return drop; +} + +bool +NSGigE::recvPacket(PacketPtr packet) +{ + rxBytes += packet->length; + rxPackets++; + + if (rxState == rxIdle) { + DPRINTF(Ethernet, "receive disabled...packet dropped\n"); + interface->recvDone(); + return true; + } + + if (rxFilterEnable && rxFilter(packet)) { + DPRINTF(Ethernet, "packet filtered...dropped\n"); + interface->recvDone(); + return true; + } + + if (rxFifoCnt + packet->length >= MAX_RX_FIFO_SIZE) { + DPRINTF(Ethernet, + "packet will not fit in receive buffer...packet dropped\n"); + devIntrPost(ISR_RXORN); + return false; + } + + rxFifo.push_back(packet); + rxFifoCnt += packet->length; + interface->recvDone(); + + rxKick(); + return true; +} + +/** + * does a udp checksum. if gen is true, then it generates it and puts it in the right place + * else, it just checks what it calculates against the value in the header in packet + */ +bool +NSGigE::udpChecksum(PacketPtr packet, bool gen) +{ + udp_header *hdr = (udp_header *) packet->getTransportHdr(); + + ip_header *ip = packet->getIpHdr(); + + pseudo_header *pseudo = new pseudo_header; + + pseudo->src_ip_addr = ip->src_ip_addr; + pseudo->dest_ip_addr = ip->dest_ip_addr; + pseudo->protocol = ip->protocol; + pseudo->len = hdr->len; + + uint16_t cksum = checksumCalc((uint16_t *) pseudo, (uint16_t *) hdr, + (uint32_t) hdr->len); + + delete pseudo; + if (gen) + hdr->chksum = cksum; + else + if (cksum != 0) + return false; + + return true; +} + +bool +NSGigE::tcpChecksum(PacketPtr packet, bool gen) +{ + tcp_header *hdr = (tcp_header *) packet->getTransportHdr(); + + ip_header *ip = packet->getIpHdr(); + + pseudo_header *pseudo = new pseudo_header; + + pseudo->src_ip_addr = ip->src_ip_addr; + pseudo->dest_ip_addr = ip->dest_ip_addr; + pseudo->protocol = ip->protocol; + pseudo->len = ip->dgram_len - (ip->vers_len & 0xf); + + uint16_t cksum = checksumCalc((uint16_t *) pseudo, (uint16_t *) hdr, + (uint32_t) pseudo->len); + + delete pseudo; + if (gen) + hdr->chksum = cksum; + else + if (cksum != 0) + return false; + + return true; +} + +bool +NSGigE::ipChecksum(PacketPtr packet, bool gen) +{ + ip_header *hdr = packet->getIpHdr(); + + uint16_t cksum = checksumCalc(NULL, (uint16_t *) hdr, (hdr->vers_len & 0xf)); + + if (gen) + hdr->hdr_chksum = cksum; + else + if (cksum != 0) + return false; + + return true; +} + +uint16_t +NSGigE::checksumCalc(uint16_t *pseudo, uint16_t *buf, uint32_t len) +{ + uint32_t sum = 0; + + uint16_t last_pad = 0; + if (len & 1) { + last_pad = buf[len/2] & 0xff; + len--; + sum += last_pad; + } + + if (pseudo) { + sum = pseudo[0] + pseudo[1] + pseudo[2] + + pseudo[3] + pseudo[4] + pseudo[5]; + } + + for (int i=0; i < (len/2); ++i) { + sum += buf[i]; + } + + while (sum >> 16) + sum = (sum >> 16) + (sum & 0xffff); + + return ~sum; +} + +//===================================================================== +// +// +void +NSGigE::serialize(ostream &os) +{ + /* + * Finalize any DMA events now. + */ + if (rxDmaReadEvent.scheduled()) + rxDmaReadCopy(); + if (rxDmaWriteEvent.scheduled()) + rxDmaWriteCopy(); + if (txDmaReadEvent.scheduled()) + txDmaReadCopy(); + if (txDmaWriteEvent.scheduled()) + txDmaWriteCopy(); + + /* + * Serialize the device registers + */ + SERIALIZE_SCALAR(regs.command); + SERIALIZE_SCALAR(regs.config); + SERIALIZE_SCALAR(regs.mear); + SERIALIZE_SCALAR(regs.ptscr); + SERIALIZE_SCALAR(regs.isr); + SERIALIZE_SCALAR(regs.imr); + SERIALIZE_SCALAR(regs.ier); + SERIALIZE_SCALAR(regs.ihr); + SERIALIZE_SCALAR(regs.txdp); + SERIALIZE_SCALAR(regs.txdp_hi); + SERIALIZE_SCALAR(regs.txcfg); + SERIALIZE_SCALAR(regs.gpior); + SERIALIZE_SCALAR(regs.rxdp); + SERIALIZE_SCALAR(regs.rxdp_hi); + SERIALIZE_SCALAR(regs.rxcfg); + SERIALIZE_SCALAR(regs.pqcr); + SERIALIZE_SCALAR(regs.wcsr); + SERIALIZE_SCALAR(regs.pcr); + SERIALIZE_SCALAR(regs.rfcr); + SERIALIZE_SCALAR(regs.rfdr); + SERIALIZE_SCALAR(regs.srr); + SERIALIZE_SCALAR(regs.mibc); + SERIALIZE_SCALAR(regs.vrcr); + SERIALIZE_SCALAR(regs.vtcr); + SERIALIZE_SCALAR(regs.vdr); + SERIALIZE_SCALAR(regs.ccsr); + SERIALIZE_SCALAR(regs.tbicr); + SERIALIZE_SCALAR(regs.tbisr); + SERIALIZE_SCALAR(regs.tanar); + SERIALIZE_SCALAR(regs.tanlpar); + SERIALIZE_SCALAR(regs.taner); + SERIALIZE_SCALAR(regs.tesr); + + SERIALIZE_ARRAY(rom.perfectMatch, EADDR_LEN); + + /* + * Serialize the various helper variables + */ + uint32_t txPktBufPtr = (uint32_t) txPacketBufPtr; + SERIALIZE_SCALAR(txPktBufPtr); + uint32_t rxPktBufPtr = (uint32_t) rxPktBufPtr; + SERIALIZE_SCALAR(rxPktBufPtr); + SERIALIZE_SCALAR(txXferLen); + SERIALIZE_SCALAR(rxXferLen); + SERIALIZE_SCALAR(txPktXmitted); + + bool txPacketExists = txPacket; + SERIALIZE_SCALAR(txPacketExists); + bool rxPacketExists = rxPacket; + SERIALIZE_SCALAR(rxPacketExists); + + /* + * Serialize DescCaches + */ + SERIALIZE_SCALAR(txDescCache.link); + SERIALIZE_SCALAR(txDescCache.bufptr); + SERIALIZE_SCALAR(txDescCache.cmdsts); + SERIALIZE_SCALAR(txDescCache.extsts); + SERIALIZE_SCALAR(rxDescCache.link); + SERIALIZE_SCALAR(rxDescCache.bufptr); + SERIALIZE_SCALAR(rxDescCache.cmdsts); + SERIALIZE_SCALAR(rxDescCache.extsts); + + /* + * Serialize tx state machine + */ + int txNumPkts = txFifo.size(); + SERIALIZE_SCALAR(txNumPkts); + int txState = this->txState; + SERIALIZE_SCALAR(txState); + SERIALIZE_SCALAR(CTDD); + SERIALIZE_SCALAR(txFifoCnt); + SERIALIZE_SCALAR(txFifoAvail); + SERIALIZE_SCALAR(txHalt); + SERIALIZE_SCALAR(txFragPtr); + SERIALIZE_SCALAR(txDescCnt); + int txDmaState = this->txDmaState; + SERIALIZE_SCALAR(txDmaState); + + /* + * Serialize rx state machine + */ + int rxNumPkts = rxFifo.size(); + SERIALIZE_SCALAR(rxNumPkts); + int rxState = this->rxState; + SERIALIZE_SCALAR(rxState); + SERIALIZE_SCALAR(CRDD); + SERIALIZE_SCALAR(rxPktBytes); + SERIALIZE_SCALAR(rxFifoCnt); + SERIALIZE_SCALAR(rxHalt); + SERIALIZE_SCALAR(rxDescCnt); + int rxDmaState = this->rxDmaState; + SERIALIZE_SCALAR(rxDmaState); + + SERIALIZE_SCALAR(extstsEnable); + + /* + * If there's a pending transmit, store the time so we can + * reschedule it later + */ + Tick transmitTick = txEvent.scheduled() ? txEvent.when() - curTick : 0; + SERIALIZE_SCALAR(transmitTick); + + /* + * Keep track of pending interrupt status. + */ + SERIALIZE_SCALAR(intrTick); + SERIALIZE_SCALAR(cpuPendingIntr); + Tick intrEventTick = 0; + if (intrEvent) + intrEventTick = intrEvent->when(); + SERIALIZE_SCALAR(intrEventTick); + + int i = 0; + for (pktiter_t p = rxFifo.begin(); p != rxFifo.end(); ++p) { + nameOut(os, csprintf("%s.rxFifo%d", name(), i++)); + (*p)->serialize(os); + } + if (rxPacketExists) { + nameOut(os, csprintf("%s.rxPacket", name())); + rxPacket->serialize(os); + } + i = 0; + for (pktiter_t p = txFifo.begin(); p != txFifo.end(); ++p) { + nameOut(os, csprintf("%s.txFifo%d", name(), i++)); + (*p)->serialize(os); + } + if (txPacketExists) { + nameOut(os, csprintf("%s.txPacket", name())); + txPacket->serialize(os); + } +} + +void +NSGigE::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(regs.command); + UNSERIALIZE_SCALAR(regs.config); + UNSERIALIZE_SCALAR(regs.mear); + UNSERIALIZE_SCALAR(regs.ptscr); + UNSERIALIZE_SCALAR(regs.isr); + UNSERIALIZE_SCALAR(regs.imr); + UNSERIALIZE_SCALAR(regs.ier); + UNSERIALIZE_SCALAR(regs.ihr); + UNSERIALIZE_SCALAR(regs.txdp); + UNSERIALIZE_SCALAR(regs.txdp_hi); + UNSERIALIZE_SCALAR(regs.txcfg); + UNSERIALIZE_SCALAR(regs.gpior); + UNSERIALIZE_SCALAR(regs.rxdp); + UNSERIALIZE_SCALAR(regs.rxdp_hi); + UNSERIALIZE_SCALAR(regs.rxcfg); + UNSERIALIZE_SCALAR(regs.pqcr); + UNSERIALIZE_SCALAR(regs.wcsr); + UNSERIALIZE_SCALAR(regs.pcr); + UNSERIALIZE_SCALAR(regs.rfcr); + UNSERIALIZE_SCALAR(regs.rfdr); + UNSERIALIZE_SCALAR(regs.srr); + UNSERIALIZE_SCALAR(regs.mibc); + UNSERIALIZE_SCALAR(regs.vrcr); + UNSERIALIZE_SCALAR(regs.vtcr); + UNSERIALIZE_SCALAR(regs.vdr); + UNSERIALIZE_SCALAR(regs.ccsr); + UNSERIALIZE_SCALAR(regs.tbicr); + UNSERIALIZE_SCALAR(regs.tbisr); + UNSERIALIZE_SCALAR(regs.tanar); + UNSERIALIZE_SCALAR(regs.tanlpar); + UNSERIALIZE_SCALAR(regs.taner); + UNSERIALIZE_SCALAR(regs.tesr); + + UNSERIALIZE_ARRAY(rom.perfectMatch, EADDR_LEN); + + /* + * unserialize the various helper variables + */ + uint32_t txPktBufPtr; + UNSERIALIZE_SCALAR(txPktBufPtr); + txPacketBufPtr = (uint8_t *) txPktBufPtr; + uint32_t rxPktBufPtr; + UNSERIALIZE_SCALAR(rxPktBufPtr); + rxPacketBufPtr = (uint8_t *) rxPktBufPtr; + UNSERIALIZE_SCALAR(txXferLen); + UNSERIALIZE_SCALAR(rxXferLen); + UNSERIALIZE_SCALAR(txPktXmitted); + + bool txPacketExists; + UNSERIALIZE_SCALAR(txPacketExists); + bool rxPacketExists; + UNSERIALIZE_SCALAR(rxPacketExists); + + /* + * Unserialize DescCaches + */ + UNSERIALIZE_SCALAR(txDescCache.link); + UNSERIALIZE_SCALAR(txDescCache.bufptr); + UNSERIALIZE_SCALAR(txDescCache.cmdsts); + UNSERIALIZE_SCALAR(txDescCache.extsts); + UNSERIALIZE_SCALAR(rxDescCache.link); + UNSERIALIZE_SCALAR(rxDescCache.bufptr); + UNSERIALIZE_SCALAR(rxDescCache.cmdsts); + UNSERIALIZE_SCALAR(rxDescCache.extsts); + + /* + * unserialize tx state machine + */ + int txNumPkts; + UNSERIALIZE_SCALAR(txNumPkts); + int txState; + UNSERIALIZE_SCALAR(txState); + this->txState = (TxState) txState; + UNSERIALIZE_SCALAR(CTDD); + UNSERIALIZE_SCALAR(txFifoCnt); + UNSERIALIZE_SCALAR(txFifoAvail); + UNSERIALIZE_SCALAR(txHalt); + UNSERIALIZE_SCALAR(txFragPtr); + UNSERIALIZE_SCALAR(txDescCnt); + int txDmaState; + UNSERIALIZE_SCALAR(txDmaState); + this->txDmaState = (DmaState) txDmaState; + + /* + * unserialize rx state machine + */ + int rxNumPkts; + UNSERIALIZE_SCALAR(rxNumPkts); + int rxState; + UNSERIALIZE_SCALAR(rxState); + this->rxState = (RxState) rxState; + UNSERIALIZE_SCALAR(CRDD); + UNSERIALIZE_SCALAR(rxPktBytes); + UNSERIALIZE_SCALAR(rxFifoCnt); + UNSERIALIZE_SCALAR(rxHalt); + UNSERIALIZE_SCALAR(rxDescCnt); + int rxDmaState; + UNSERIALIZE_SCALAR(rxDmaState); + this->rxDmaState = (DmaState) rxDmaState; + + UNSERIALIZE_SCALAR(extstsEnable); + + /* + * If there's a pending transmit, store the time so we can + * reschedule it later + */ + Tick transmitTick; + UNSERIALIZE_SCALAR(transmitTick); + if (transmitTick) + txEvent.schedule(curTick + transmitTick); + + /* + * Keep track of pending interrupt status. + */ + UNSERIALIZE_SCALAR(intrTick); + UNSERIALIZE_SCALAR(cpuPendingIntr); + Tick intrEventTick; + UNSERIALIZE_SCALAR(intrEventTick); + if (intrEventTick) { + intrEvent = new IntrEvent(this, true); + intrEvent->schedule(intrEventTick); + } + + for (int i = 0; i < rxNumPkts; ++i) { + PacketPtr p = new EtherPacket; + p->unserialize(cp, csprintf("%s.rxFifo%d", section, i)); + rxFifo.push_back(p); + } + rxPacket = NULL; + if (rxPacketExists) { + rxPacket = new EtherPacket; + rxPacket->unserialize(cp, csprintf("%s.rxPacket", section)); + } + for (int i = 0; i < txNumPkts; ++i) { + PacketPtr p = new EtherPacket; + p->unserialize(cp, csprintf("%s.rxFifo%d", section, i)); + txFifo.push_back(p); + } + if (txPacketExists) { + txPacket = new EtherPacket; + txPacket->unserialize(cp, csprintf("%s.txPacket", section)); + } +} + + +Tick +NSGigE::cacheAccess(MemReqPtr &req) +{ + DPRINTF(EthernetPIO, "timing access to paddr=%#x (daddr=%#x)\n", + req->paddr, req->paddr - addr); + return curTick + pioLatency; +} +//===================================================================== + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(NSGigEInt) + + SimObjectParam<EtherInt *> peer; + SimObjectParam<NSGigE *> device; + +END_DECLARE_SIM_OBJECT_PARAMS(NSGigEInt) + +BEGIN_INIT_SIM_OBJECT_PARAMS(NSGigEInt) + + INIT_PARAM_DFLT(peer, "peer interface", NULL), + INIT_PARAM(device, "Ethernet device of this interface") + +END_INIT_SIM_OBJECT_PARAMS(NSGigEInt) + +CREATE_SIM_OBJECT(NSGigEInt) +{ + NSGigEInt *dev_int = new NSGigEInt(getInstanceName(), device); + + EtherInt *p = (EtherInt *)peer; + if (p) { + dev_int->setPeer(p); + p->setPeer(dev_int); + } + + return dev_int; +} + +REGISTER_SIM_OBJECT("NSGigEInt", NSGigEInt) + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(NSGigE) + + Param<Tick> tx_delay; + Param<Tick> rx_delay; + SimObjectParam<IntrControl *> intr_ctrl; + Param<Tick> intr_delay; + SimObjectParam<MemoryController *> mmu; + SimObjectParam<PhysicalMemory *> physmem; + Param<Addr> addr; + Param<bool> rx_filter; + Param<string> hardware_address; + SimObjectParam<Bus*> header_bus; + SimObjectParam<Bus*> payload_bus; + SimObjectParam<HierParams *> hier; + Param<Tick> pio_latency; + Param<bool> dma_desc_free; + Param<bool> dma_data_free; + Param<Tick> dma_read_delay; + Param<Tick> dma_write_delay; + Param<Tick> dma_read_factor; + Param<Tick> dma_write_factor; + SimObjectParam<PciConfigAll *> configspace; + SimObjectParam<PciConfigData *> configdata; + SimObjectParam<Tsunami *> tsunami; + Param<uint32_t> pci_bus; + Param<uint32_t> pci_dev; + Param<uint32_t> pci_func; + +END_DECLARE_SIM_OBJECT_PARAMS(NSGigE) + +BEGIN_INIT_SIM_OBJECT_PARAMS(NSGigE) + + INIT_PARAM_DFLT(tx_delay, "Transmit Delay", 1000), + INIT_PARAM_DFLT(rx_delay, "Receive Delay", 1000), + INIT_PARAM(intr_ctrl, "Interrupt Controller"), + INIT_PARAM_DFLT(intr_delay, "Interrupt Delay in microseconds", 0), + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(physmem, "Physical Memory"), + INIT_PARAM(addr, "Device Address"), + INIT_PARAM_DFLT(rx_filter, "Enable Receive Filter", true), + INIT_PARAM_DFLT(hardware_address, "Ethernet Hardware Address", + "00:99:00:00:00:01"), + INIT_PARAM_DFLT(header_bus, "The IO Bus to attach to for headers", NULL), + INIT_PARAM_DFLT(payload_bus, "The IO Bus to attach to for payload", NULL), + INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams), + INIT_PARAM_DFLT(pio_latency, "Programmed IO latency", 1000), + INIT_PARAM_DFLT(dma_desc_free, "DMA of Descriptors is free", false), + INIT_PARAM_DFLT(dma_data_free, "DMA of Data is free", false), + INIT_PARAM_DFLT(dma_read_delay, "fixed delay for dma reads", 0), + INIT_PARAM_DFLT(dma_write_delay, "fixed delay for dma writes", 0), + INIT_PARAM_DFLT(dma_read_factor, "multiplier for dma reads", 0), + INIT_PARAM_DFLT(dma_write_factor, "multiplier for dma writes", 0), + INIT_PARAM(configspace, "PCI Configspace"), + INIT_PARAM(configdata, "PCI Config data"), + INIT_PARAM(tsunami, "Tsunami"), + INIT_PARAM(pci_bus, "PCI bus"), + INIT_PARAM(pci_dev, "PCI device number"), + INIT_PARAM(pci_func, "PCI function code") + +END_INIT_SIM_OBJECT_PARAMS(NSGigE) + + +CREATE_SIM_OBJECT(NSGigE) +{ + int eaddr[6]; + sscanf(((string)hardware_address).c_str(), "%x:%x:%x:%x:%x:%x", + &eaddr[0], &eaddr[1], &eaddr[2], &eaddr[3], &eaddr[4], &eaddr[5]); + + return new NSGigE(getInstanceName(), intr_ctrl, intr_delay, + physmem, tx_delay, rx_delay, mmu, hier, header_bus, + payload_bus, pio_latency, dma_desc_free, dma_data_free, + dma_read_delay, dma_write_delay, dma_read_factor, + dma_write_factor, configspace, configdata, + tsunami, pci_bus, pci_dev, pci_func, rx_filter, eaddr, + addr); +} + +REGISTER_SIM_OBJECT("NSGigE", NSGigE) diff --git a/dev/ns_gige.hh b/dev/ns_gige.hh new file mode 100644 index 000000000..ff648adcf --- /dev/null +++ b/dev/ns_gige.hh @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * Device module for modelling the National Semiconductor + * DP83820 ethernet controller + */ + +#ifndef __NS_GIGE_HH__ +#define __NS_GIGE_HH__ + +//#include "base/range.hh" +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "sim/eventq.hh" +#include "dev/ns_gige_reg.h" +#include "base/statistics.hh" +#include "dev/pcidev.hh" +#include "dev/tsunami.hh" +#include "dev/io_device.hh" +#include "mem/bus/bus.hh" + +/** defined by the NS83820 data sheet */ +#define MAX_TX_FIFO_SIZE 8192 +#define MAX_RX_FIFO_SIZE 32768 + +/** length of ethernet address in bytes */ +#define EADDR_LEN 6 + +/** + * Ethernet device registers + */ +struct dp_regs { + uint32_t command; + uint32_t config; + uint32_t mear; + uint32_t ptscr; + uint32_t isr; + uint32_t imr; + uint32_t ier; + uint32_t ihr; + uint32_t txdp; + uint32_t txdp_hi; + uint32_t txcfg; + uint32_t gpior; + uint32_t rxdp; + uint32_t rxdp_hi; + uint32_t rxcfg; + uint32_t pqcr; + uint32_t wcsr; + uint32_t pcr; + uint32_t rfcr; + uint32_t rfdr; + uint32_t srr; + uint32_t mibc; + uint32_t vrcr; + uint32_t vtcr; + uint32_t vdr; + uint32_t ccsr; + uint32_t tbicr; + uint32_t tbisr; + uint32_t tanar; + uint32_t tanlpar; + uint32_t taner; + uint32_t tesr; +}; + +struct dp_rom { + /** for perfect match memory. the linux driver doesn't use any other ROM */ + uint8_t perfectMatch[EADDR_LEN]; +}; + +class IntrControl; +class NSGigEInt; +class PhysicalMemory; +class BaseInterface; +class HierParams; +class Bus; +class PciConfigAll; + +/** + * NS DP82830 Ethernet device model + */ +class NSGigE : public PciDev +{ + public: + /** Transmit State Machine states */ + enum TxState + { + txIdle, + txDescRefr, + txDescRead, + txFifoBlock, + txFragRead, + txDescWrite, + txAdvance + }; + + /** Receive State Machine States */ + enum RxState + { + rxIdle, + rxDescRefr, + rxDescRead, + rxFifoBlock, + rxFragWrite, + rxDescWrite, + rxAdvance + }; + + enum DmaState + { + dmaIdle, + dmaReading, + dmaWriting, + dmaReadWaiting, + dmaWriteWaiting + }; + + private: + /** pointer to the chipset */ + Tsunami *tsunami; + + private: + Addr addr; + static const Addr size = sizeof(dp_regs); + + protected: + typedef std::deque<PacketPtr> pktbuf_t; + typedef pktbuf_t::iterator pktiter_t; + + /** device register file */ + dp_regs regs; + dp_rom rom; + + /*** BASIC STRUCTURES FOR TX/RX ***/ + /* Data FIFOs */ + pktbuf_t txFifo; + pktbuf_t rxFifo; + + /** various helper vars */ + uint8_t *txPacketBufPtr; + uint8_t *rxPacketBufPtr; + uint32_t txXferLen; + uint32_t rxXferLen; + uint32_t txPktXmitted; + bool rxDmaFree; + bool txDmaFree; + PacketPtr txPacket; + PacketPtr rxPacket; + + /** DescCaches */ + ns_desc txDescCache; + ns_desc rxDescCache; + + /* tx State Machine */ + TxState txState; + /** Current Transmit Descriptor Done */ + bool CTDD; + /** amt of data in the txDataFifo in bytes (logical) */ + uint32_t txFifoCnt; + /** current amt of free space in txDataFifo in bytes */ + uint32_t txFifoAvail; + /** halt the tx state machine after next packet */ + bool txHalt; + /** ptr to the next byte in the current fragment */ + Addr txFragPtr; + /** count of bytes remaining in the current descriptor */ + uint32_t txDescCnt; + DmaState txDmaState; + + /** rx State Machine */ + RxState rxState; + /** Current Receive Descriptor Done */ + bool CRDD; + /** num of bytes in the current packet being drained from rxDataFifo */ + uint32_t rxPktBytes; + /** number of bytes in the rxFifo */ + uint32_t rxFifoCnt; + /** halt the rx state machine after current packet */ + bool rxHalt; + /** ptr to the next byte in current fragment */ + Addr rxFragPtr; + /** count of bytes remaining in the current descriptor */ + uint32_t rxDescCnt; + DmaState rxDmaState; + + bool extstsEnable; + + protected: + Tick dmaReadDelay; + Tick dmaWriteDelay; + + Tick dmaReadFactor; + Tick dmaWriteFactor; + + void *rxDmaData; + Addr rxDmaAddr; + int rxDmaLen; + bool doRxDmaRead(); + bool doRxDmaWrite(); + void rxDmaReadCopy(); + void rxDmaWriteCopy(); + + void *txDmaData; + Addr txDmaAddr; + int txDmaLen; + bool doTxDmaRead(); + bool doTxDmaWrite(); + void txDmaReadCopy(); + void txDmaWriteCopy(); + + void rxDmaReadDone(); + friend class EventWrapper<NSGigE, &NSGigE::rxDmaReadDone>; + EventWrapper<NSGigE, &NSGigE::rxDmaReadDone> rxDmaReadEvent; + + void rxDmaWriteDone(); + friend class EventWrapper<NSGigE, &NSGigE::rxDmaWriteDone>; + EventWrapper<NSGigE, &NSGigE::rxDmaWriteDone> rxDmaWriteEvent; + + void txDmaReadDone(); + friend class EventWrapper<NSGigE, &NSGigE::txDmaReadDone>; + EventWrapper<NSGigE, &NSGigE::txDmaReadDone> txDmaReadEvent; + + void txDmaWriteDone(); + friend class EventWrapper<NSGigE, &NSGigE::txDmaWriteDone>; + EventWrapper<NSGigE, &NSGigE::txDmaWriteDone> txDmaWriteEvent; + + bool dmaDescFree; + bool dmaDataFree; + + + protected: + Tick txDelay; + Tick rxDelay; + + void txReset(); + void rxReset(); + void regsReset() { + memset(®s, 0, sizeof(regs)); + regs.config = 0x80000000; + regs.mear = 0x12; + regs.isr = 0x00608000; + regs.txcfg = 0x120; + regs.rxcfg = 0x4; + regs.srr = 0x0103; + regs.mibc = 0x2; + regs.vdr = 0x81; + regs.tesr = 0xc000; + } + + void rxKick(); + Tick rxKickTick; + typedef EventWrapper<NSGigE, &NSGigE::rxKick> RxKickEvent; + friend class RxKickEvent; + + void txKick(); + Tick txKickTick; + typedef EventWrapper<NSGigE, &NSGigE::txKick> TxKickEvent; + friend class TxKickEvent; + + /** + * Retransmit event + */ + void transmit(); + typedef EventWrapper<NSGigE, &NSGigE::transmit> TxEvent; + friend class TxEvent; + TxEvent txEvent; + + void txDump() const; + void rxDump() const; + + /** + * receive address filter + */ + bool rxFilterEnable; + bool rxFilter(PacketPtr packet); + bool acceptBroadcast; + bool acceptMulticast; + bool acceptUnicast; + bool acceptPerfect; + bool acceptArp; + + PhysicalMemory *physmem; + + /** + * Interrupt management + */ + IntrControl *intctrl; + void devIntrPost(uint32_t interrupts); + void devIntrClear(uint32_t interrupts); + void devIntrChangeMask(); + + Tick intrDelay; + Tick intrTick; + bool cpuPendingIntr; + void cpuIntrPost(Tick when); + void cpuInterrupt(); + void cpuIntrClear(); + + typedef EventWrapper<NSGigE, &NSGigE::cpuInterrupt> IntrEvent; + friend class IntrEvent; + IntrEvent *intrEvent; + + /** + * Hardware checksum support + */ + bool udpChecksum(PacketPtr packet, bool gen); + bool tcpChecksum(PacketPtr packet, bool gen); + bool ipChecksum(PacketPtr packet, bool gen); + uint16_t checksumCalc(uint16_t *pseudo, uint16_t *buf, uint32_t len); + + NSGigEInt *interface; + + public: + NSGigE(const std::string &name, IntrControl *i, Tick intr_delay, + PhysicalMemory *pmem, Tick tx_delay, Tick rx_delay, + MemoryController *mmu, HierParams *hier, Bus *header_bus, + Bus *payload_bus, Tick pio_latency, bool dma_desc_free, + bool dma_data_free, Tick dma_read_delay, Tick dma_write_delay, + Tick dma_read_factor, Tick dma_write_factor, PciConfigAll *cf, + PciConfigData *cd, Tsunami *t, uint32_t bus, uint32_t dev, + uint32_t func, bool rx_filter, const int eaddr[6], Addr addr); + ~NSGigE(); + + virtual void WriteConfig(int offset, int size, uint32_t data); + virtual void ReadConfig(int offset, int size, uint8_t *data); + + virtual Fault read(MemReqPtr &req, uint8_t *data); + virtual Fault write(MemReqPtr &req, const uint8_t *data); + + bool cpuIntrPending() const; + void cpuIntrAck() { cpuIntrClear(); } + + bool recvPacket(PacketPtr packet); + void transferDone(); + + void setInterface(NSGigEInt *i) { assert(!interface); interface = i; } + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + public: + void regStats(); + + private: + Stats::Scalar<> txBytes; + Stats::Scalar<> rxBytes; + Stats::Scalar<> txPackets; + Stats::Scalar<> rxPackets; + Stats::Formula txBandwidth; + Stats::Formula rxBandwidth; + Stats::Formula txPacketRate; + Stats::Formula rxPacketRate; + + private: + Tick pioLatency; + + public: + Tick cacheAccess(MemReqPtr &req); +}; + +/* + * Ethernet Interface for an Ethernet Device + */ +class NSGigEInt : public EtherInt +{ + private: + NSGigE *dev; + + public: + NSGigEInt(const std::string &name, NSGigE *d) + : EtherInt(name), dev(d) { dev->setInterface(this); } + + virtual bool recvPacket(PacketPtr &pkt) { return dev->recvPacket(pkt); } + virtual void sendDone() { dev->transferDone(); } +}; + +#endif // __NS_GIGE_HH__ diff --git a/dev/ns_gige_reg.h b/dev/ns_gige_reg.h new file mode 100644 index 000000000..774fec435 --- /dev/null +++ b/dev/ns_gige_reg.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2003 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. + */ + +/* Portions of code taken from: */ + +/* ns83820.c by Benjamin LaHaise with contributions. + * + * Questions/comments/discussion to linux-ns83820@kvack.org. + * + * $Revision: 1.34.2.23 $ + * + * Copyright 2001 Benjamin LaHaise. + * Copyright 2001, 2002 Red Hat. + * + * Mmmm, chocolate vanilla mocha... + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + + + +/* @file + * Ethernet device register definitions for the National + * Semiconductor DP83820 Ethernet controller + */ + +#ifndef _NS_GIGE_H +#define _NS_GIGE_H_ + +/* + * Configuration Register Map + */ +#define NS_ID 0x00 /* identification register */ +#define NS_CS 0x04 /* command and status register */ +#define NS_RID 0x08 /* revision ID register */ +#define NS_LAT 0x0C /* latency timer register */ +#define NS_IOA 0x10 /* IO base address register */ +#define NS_MA 0x14 /* memory address register */ +#define NS_MA1 0x18 /* memory address high dword register */ +#define NS_SID 0x2C /* subsystem identification register */ +#define NS_ROM 0x30 /* boot ROM configuration register */ +#define NS_CAPPTR 0x34 /* number of tx descriptors */ +#define NS_INT 0x3C /* interrupt select register */ +#define NS_PMCAP 0x40 /* power mgmt capabilities register */ +#define NS_PMCS 0x44 /* power mgmt control and status + register */ +/* Operational Register Map */ +#define CR 0x00 +#define CFG 0x04 +#define MEAR 0x08 +#define PTSCR 0x0c +#define ISR 0x10 +#define IMR 0x14 +#define IER 0x18 +#define IHR 0x1c +#define TXDP 0x20 +#define TXDP_HI 0x24 +#define TXCFG 0x28 +#define GPIOR 0x2c +#define RXDP 0x30 +#define RXDP_HI 0x34 +#define RXCFG 0x38 +#define PQCR 0x3c +#define WCSR 0x40 +#define PCR 0x44 +#define RFCR 0x48 +#define RFDR 0x4c +#define BRAR 0x50 +#define BRDR 0x54 +#define SRR 0x58 +#define MIBC 0x5c +#define MIB_START 0x60 +#define MIB_END 0x88 +#define VRCR 0xbc +#define VTCR 0xc0 +#define VDR 0xc4 +#define CCSR 0xcc +#define TBICR 0xe0 +#define TBISR 0xe4 +#define TANAR 0xe8 +#define TANLPAR 0xec +#define TANER 0xf0 +#define TESR 0xf4 +#define LAST 0xf4 +#define RESERVED 0xfc + +/* chip command register */ +#define CR_TXE 0x00000001 +#define CR_TXD 0x00000002 +#define CR_RXE 0x00000004 +#define CR_RXD 0x00000008 +#define CR_TXR 0x00000010 +#define CR_RXR 0x00000020 +#define CR_SWI 0x00000080 +#define CR_RST 0x00000100 + +/* configuration register */ +#define CFG_LNKSTS 0x80000000 +#define CFG_SPDSTS 0x60000000 +#define CFG_SPDSTS1 0x40000000 +#define CFG_SPDSTS0 0x20000000 +#define CFG_DUPSTS 0x10000000 +#define CFG_TBI_EN 0x01000000 +#define CFG_RESERVED 0x0e000000 +#define CFG_MODE_1000 0x00400000 +#define CFG_AUTO_1000 0x00200000 +#define CFG_PINT_CTL 0x001c0000 +#define CFG_PINT_DUPSTS 0x00100000 +#define CFG_PINT_LNKSTS 0x00080000 +#define CFG_PINT_SPDSTS 0x00040000 +#define CFG_TMRTEST 0x00020000 +#define CFG_MRM_DIS 0x00010000 +#define CFG_MWI_DIS 0x00008000 +#define CFG_T64ADDR 0x00004000 +#define CFG_PCI64_DET 0x00002000 +#define CFG_DATA64_EN 0x00001000 +#define CFG_M64ADDR 0x00000800 +#define CFG_PHY_RST 0x00000400 +#define CFG_PHY_DIS 0x00000200 +#define CFG_EXTSTS_EN 0x00000100 +#define CFG_REQALG 0x00000080 +#define CFG_SB 0x00000040 +#define CFG_POW 0x00000020 +#define CFG_EXD 0x00000010 +#define CFG_PESEL 0x00000008 +#define CFG_BROM_DIS 0x00000004 +#define CFG_EXT_125 0x00000002 +#define CFG_BEM 0x00000001 + +/* EEPROM access register */ +#define MEAR_EEDI 0x00000001 +#define MEAR_EEDO 0x00000002 +#define MEAR_EECLK 0x00000004 +#define MEAR_EESEL 0x00000008 +#define MEAR_MDIO 0x00000010 +#define MEAR_MDDIR 0x00000020 +#define MEAR_MDC 0x00000040 + +/* PCI test control register */ +#define PTSCR_EEBIST_FAIL 0x00000001 +#define PTSCR_EEBIST_EN 0x00000002 +#define PTSCR_EELOAD_EN 0x00000004 +#define PTSCR_RBIST_FAIL 0x000001b8 +#define PTSCR_RBIST_DONE 0x00000200 +#define PTSCR_RBIST_EN 0x00000400 +#define PTSCR_RBIST_RST 0x00002000 +#define PTSCR_RBIST_RDONLY 0x000003f9 + +/* interrupt status register */ +#define ISR_RESERVE 0x80000000 +#define ISR_TXDESC3 0x40000000 +#define ISR_TXDESC2 0x20000000 +#define ISR_TXDESC1 0x10000000 +#define ISR_TXDESC0 0x08000000 +#define ISR_RXDESC3 0x04000000 +#define ISR_RXDESC2 0x02000000 +#define ISR_RXDESC1 0x01000000 +#define ISR_RXDESC0 0x00800000 +#define ISR_TXRCMP 0x00400000 +#define ISR_RXRCMP 0x00200000 +#define ISR_DPERR 0x00100000 +#define ISR_SSERR 0x00080000 +#define ISR_RMABT 0x00040000 +#define ISR_RTABT 0x00020000 +#define ISR_RXSOVR 0x00010000 +#define ISR_HIBINT 0x00008000 +#define ISR_PHY 0x00004000 +#define ISR_PME 0x00002000 +#define ISR_SWI 0x00001000 +#define ISR_MIB 0x00000800 +#define ISR_TXURN 0x00000400 +#define ISR_TXIDLE 0x00000200 +#define ISR_TXERR 0x00000100 +#define ISR_TXDESC 0x00000080 +#define ISR_TXOK 0x00000040 +#define ISR_RXORN 0x00000020 +#define ISR_RXIDLE 0x00000010 +#define ISR_RXEARLY 0x00000008 +#define ISR_RXERR 0x00000004 +#define ISR_RXDESC 0x00000002 +#define ISR_RXOK 0x00000001 +#define ISR_ALL 0x7FFFFFFF + +/* transmit configuration register */ +#define TXCFG_CSI 0x80000000 +#define TXCFG_HBI 0x40000000 +#define TXCFG_MLB 0x20000000 +#define TXCFG_ATP 0x10000000 +#define TXCFG_ECRETRY 0x00800000 +#define TXCFG_BRST_DIS 0x00080000 +#define TXCFG_MXDMA1024 0x00000000 +#define TXCFG_MXDMA512 0x00700000 +#define TXCFG_MXDMA256 0x00600000 +#define TXCFG_MXDMA128 0x00500000 +#define TXCFG_MXDMA64 0x00400000 +#define TXCFG_MXDMA32 0x00300000 +#define TXCFG_MXDMA16 0x00200000 +#define TXCFG_MXDMA8 0x00100000 +#define TXCFG_MXDMA 0x00700000 + +#define TXCFG_FLTH_MASK 0x0000ff00 +#define TXCFG_DRTH_MASK 0x000000ff + +/*general purpose I/O control register */ +#define GPIOR_GP5_OE 0x00000200 +#define GPIOR_GP4_OE 0x00000100 +#define GPIOR_GP3_OE 0x00000080 +#define GPIOR_GP2_OE 0x00000040 +#define GPIOR_GP1_OE 0x00000020 +#define GPIOR_GP3_OUT 0x00000004 +#define GPIOR_GP1_OUT 0x00000001 + +/* receive configuration register */ +#define RXCFG_AEP 0x80000000 +#define RXCFG_ARP 0x40000000 +#define RXCFG_STRIPCRC 0x20000000 +#define RXCFG_RX_FD 0x10000000 +#define RXCFG_ALP 0x08000000 +#define RXCFG_AIRL 0x04000000 +#define RXCFG_MXDMA512 0x00700000 +#define RXCFG_MXDMA 0x00700000 +#define RXCFG_DRTH 0x0000003e +#define RXCFG_DRTH0 0x00000002 + +/* pause control status register */ +#define PCR_PSEN (1 << 31) +#define PCR_PS_MCAST (1 << 30) +#define PCR_PS_DA (1 << 29) +#define PCR_STHI_8 (3 << 23) +#define PCR_STLO_4 (1 << 23) +#define PCR_FFHI_8K (3 << 21) +#define PCR_FFLO_4K (1 << 21) +#define PCR_PAUSE_CNT 0xFFFE + +/*receive filter/match control register */ +#define RFCR_RFEN 0x80000000 +#define RFCR_AAB 0x40000000 +#define RFCR_AAM 0x20000000 +#define RFCR_AAU 0x10000000 +#define RFCR_APM 0x08000000 +#define RFCR_APAT 0x07800000 +#define RFCR_APAT3 0x04000000 +#define RFCR_APAT2 0x02000000 +#define RFCR_APAT1 0x01000000 +#define RFCR_APAT0 0x00800000 +#define RFCR_AARP 0x00400000 +#define RFCR_MHEN 0x00200000 +#define RFCR_UHEN 0x00100000 +#define RFCR_ULM 0x00080000 +#define RFCR_RFADDR 0x000003ff + +/* receive filter/match data register */ +#define RFDR_BMASK 0x00030000 +#define RFDR_RFDATA0 0x000000ff +#define RFDR_RFDATA1 0x0000ff00 + +/* management information base control register */ +#define MIBC_MIBS 0x00000008 +#define MIBC_ACLR 0x00000004 +#define MIBC_FRZ 0x00000002 +#define MIBC_WRN 0x00000001 + +/* VLAN/IP receive control register */ +#define VRCR_RUDPE 0x00000080 +#define VRCR_RTCPE 0x00000040 +#define VRCR_RIPE 0x00000020 +#define VRCR_IPEN 0x00000010 +#define VRCR_DUTF 0x00000008 +#define VRCR_DVTF 0x00000004 +#define VRCR_VTREN 0x00000002 +#define VRCR_VTDEN 0x00000001 + +/* VLAN/IP transmit control register */ +#define VTCR_PPCHK 0x00000008 +#define VTCR_GCHK 0x00000004 +#define VTCR_VPPTI 0x00000002 +#define VTCR_VGTI 0x00000001 + +/* Clockrun Control/Status Register */ +#define CCSR_CLKRUN_EN 0x00000001 + +/* TBI control register */ +#define TBICR_MR_LOOPBACK 0x00004000 +#define TBICR_MR_AN_ENABLE 0x00001000 +#define TBICR_MR_RESTART_AN 0x00000200 + +/* TBI status register */ +#define TBISR_MR_LINK_STATUS 0x00000020 +#define TBISR_MR_AN_COMPLETE 0x00000004 + +/* TBI auto-negotiation advertisement register */ +#define TANAR_PS2 0x00000100 +#define TANAR_PS1 0x00000080 +#define TANAR_HALF_DUP 0x00000040 +#define TANAR_FULL_DUP 0x00000020 + +/* + * descriptor format currently assuming link and bufptr + * are set for 32 bits,( may be wrong ) ASSUME32 + */ +struct ns_desc { + uint32_t link; /* link field to next descriptor in linked list */ + uint32_t bufptr; /* pointer to the first fragment or buffer */ + uint32_t cmdsts; /* command/status field */ + uint32_t extsts; /* extended status field for VLAN and IP info */ +}; + +/* cmdsts flags for descriptors */ +#define CMDSTS_OWN 0x80000000 +#define CMDSTS_MORE 0x40000000 +#define CMDSTS_INTR 0x20000000 +#define CMDSTS_ERR 0x10000000 +#define CMDSTS_OK 0x08000000 +#define CMDSTS_LEN_MASK 0x0000ffff + +#define CMDSTS_DEST_MASK 0x01800000 +#define CMDSTS_DEST_SELF 0x00800000 +#define CMDSTS_DEST_MULTI 0x01000000 + +/* extended flags for descriptors */ +#define EXTSTS_UDPERR 0x00400000 +#define EXTSTS_UDPPKT 0x00200000 +#define EXTSTS_TCPERR 0x00100000 +#define EXTSTS_TCPPKT 0x00080000 +#define EXTSTS_IPERR 0x00040000 +#define EXTSTS_IPPKT 0x00020000 + + +/* speed status */ +#define SPDSTS_POLARITY (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0)) + +#endif /* _NS_GIGE_H_ */ diff --git a/dev/pciconfigall.cc b/dev/pciconfigall.cc new file mode 100644 index 000000000..226fd2749 --- /dev/null +++ b/dev/pciconfigall.cc @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * PCI Configspace implementation + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "cpu/exec_context.hh" +#include "dev/scsi_ctrl.hh" +#include "dev/pciconfigall.hh" +#include "dev/pcidev.hh" +#include "mem/functional_mem/memory_control.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; + +PciConfigAll::PciConfigAll(const string &name, Addr a, + MemoryController *mmu) + : FunctionalMemory(name), addr(a) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + + // Make all the pointers to devices null + for(int x=0; x < MAX_PCI_DEV; x++) + for(int y=0; y < MAX_PCI_FUNC; y++) + devices[x][y] = NULL; +} + +Fault +PciConfigAll::read(MemReqPtr &req, uint8_t *data) +{ + DPRINTF(PciConfigAll, "read va=%#x size=%d\n", + req->vaddr, req->size); + + Addr daddr = (req->paddr - (addr & PA_IMPL_MASK)); + + int device = (daddr >> 11) & 0x1F; + int func = (daddr >> 8) & 0x7; + int reg = daddr & 0xFF; + + if (devices[device][func] == NULL) { + switch (req->size) { + // case sizeof(uint64_t): + // *(uint64_t*)data = 0xFFFFFFFFFFFFFFFF; + // return No_Fault; + case sizeof(uint32_t): + *(uint32_t*)data = 0xFFFFFFFF; + return No_Fault; + case sizeof(uint16_t): + *(uint16_t*)data = 0xFFFF; + return No_Fault; + case sizeof(uint8_t): + *(uint8_t*)data = 0xFF; + return No_Fault; + default: + panic("invalid access size(?) for PCI configspace!\n"); + } + } else { + switch (req->size) { + case sizeof(uint32_t): + case sizeof(uint16_t): + case sizeof(uint8_t): + devices[device][func]->ReadConfig(reg, req->size, data); + return No_Fault; + default: + panic("invalid access size(?) for PCI configspace!\n"); + } + } + + DPRINTFN("PCI Configspace ERROR: read daddr=%#x size=%d\n", + daddr, req->size); + + return No_Fault; +} + +Fault +PciConfigAll::write(MemReqPtr &req, const uint8_t *data) +{ + Addr daddr = (req->paddr - (addr & PA_IMPL_MASK)); + + int device = (daddr >> 11) & 0x1F; + int func = (daddr >> 8) & 0x7; + int reg = daddr & 0xFF; + + union { + uint8_t byte_value; + uint16_t half_value; + uint32_t word_value; + }; + + if (devices[device][func] == NULL) + panic("Attempting to write to config space on non-existant device\n"); + else { + switch (req->size) { + case sizeof(uint8_t): + byte_value = *(uint8_t*)data; + break; + case sizeof(uint16_t): + half_value = *(uint16_t*)data; + break; + case sizeof(uint32_t): + word_value = *(uint32_t*)data; + break; + default: + panic("invalid access size(?) for PCI configspace!\n"); + } + } + + DPRINTF(PciConfigAll, "write - va=%#x size=%d data=%#x\n", + req->vaddr, req->size, word_value); + + devices[device][func]->WriteConfig(reg, req->size, word_value); + + return No_Fault; +} + +void +PciConfigAll::serialize(std::ostream &os) +{ + // code should be written +} + +void +PciConfigAll::unserialize(Checkpoint *cp, const std::string §ion) +{ + //code should be written +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(PciConfigAll) + + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + Param<Addr> mask; + +END_DECLARE_SIM_OBJECT_PARAMS(PciConfigAll) + +BEGIN_INIT_SIM_OBJECT_PARAMS(PciConfigAll) + + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(addr, "Device Address"), + INIT_PARAM(mask, "Address Mask") + +END_INIT_SIM_OBJECT_PARAMS(PciConfigAll) + +CREATE_SIM_OBJECT(PciConfigAll) +{ + return new PciConfigAll(getInstanceName(), addr, mmu); +} + +REGISTER_SIM_OBJECT("PciConfigAll", PciConfigAll) + +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/dev/pciconfigall.hh b/dev/pciconfigall.hh new file mode 100644 index 000000000..6a31a9e73 --- /dev/null +++ b/dev/pciconfigall.hh @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2003 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. + */ + +/* + * @file + * PCI Config space implementation. + */ + +#ifndef __PCICONFIGALL_HH__ +#define __PCICONFIGALL_HH__ + +#include "mem/functional_mem/functional_memory.hh" +#include "dev/pcireg.h" + +static const uint32_t MAX_PCI_DEV = 32; +static const uint32_t MAX_PCI_FUNC = 8; + +class PciDev; + +/** + * PCI Config Space + * All of PCI config space needs to return -1 on Tsunami, except + * the devices that exist. This device maps the entire bus config + * space and passes the requests on to TsunamiPCIDev devices as + * appropriate. + */ +class PciConfigAll : public FunctionalMemory +{ + private: + Addr addr; + static const Addr size = 0xffffff; + + /** + * Pointers to all the devices that are registered with this + * particular config space. + */ + PciDev* devices[MAX_PCI_DEV][MAX_PCI_FUNC]; + + public: + /** + * Constructor for PCIConfigAll + * @param name name of the object + * @param a base address of the write + * @param mmu the memory controller + */ + PciConfigAll(const std::string &name, Addr a, MemoryController *mmu); + + + /** + * Check if a device exists. + * @param pcidev PCI device to check + * @param pcifunc PCI function to check + * @return true if device exists, false otherwise + */ + bool deviceExists(uint32_t pcidev, uint32_t pcifunc) + { return devices[pcidev][pcifunc] != NULL ? true : false; } + + /** + * Registers a device with the config space object. + * @param pcidev PCI device to register + * @param pcifunc PCI function to register + * @param device device to register + */ + void registerDevice(uint8_t pcidev, uint8_t pcifunc, PciDev *device) + { devices[pcidev][pcifunc] = device; } + + /** + * Read something in PCI config space. If the device does not exist + * -1 is returned, if the device does exist its PciDev::ReadConfig (or the + * virtual function that overrides) it is called. + * @param req Contains the address of the field to read. + * @param data Return the field read. + * @return The fault condition of the access. + */ + virtual Fault read(MemReqPtr &req, uint8_t *data); + + /** + * Write to PCI config spcae. If the device does not exit the simulator + * panics. If it does it is passed on the PciDev::WriteConfig (or the virtual + * function that overrides it). + * @param req Contains the address to write to. + * @param data The data to write. + * @return The fault condition of the access. + */ + + virtual Fault write(MemReqPtr &req, const uint8_t *data); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +}; + +#endif // __PCICONFIGALL_HH__ diff --git a/dev/pcidev.cc b/dev/pcidev.cc new file mode 100644 index 000000000..8d2828809 --- /dev/null +++ b/dev/pcidev.cc @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * A single PCI device configuration space entry. + */ + +#include <list> +#include <sstream> +#include <string> +#include <vector> + +#include "base/inifile.hh" +#include "base/misc.hh" +#include "base/str.hh" // for to_number +#include "base/trace.hh" +#include "dev/pciareg.h" +#include "dev/pcidev.hh" +#include "dev/pciconfigall.hh" +#include "mem/functional_mem/memory_control.hh" +#include "sim/builder.hh" +#include "sim/param.hh" +#include "sim/universe.hh" +#include "dev/tsunamireg.h" + +using namespace std; + +PciDev::PciDev(const string &name, MemoryController *mmu, PciConfigAll *cf, + PciConfigData *cd, uint32_t bus, uint32_t dev, uint32_t func) + : DmaDevice(name), mmu(mmu), configSpace(cf), configData(cd), busNum(bus), + deviceNum(dev), functionNum(func) +{ + // copy the config data from the PciConfigData object + if (cd) { + memcpy(config.data, cd->config.data, sizeof(config.data)); + memcpy(BARSize, cd->BARSize, sizeof(BARSize)); + memcpy(BARAddrs, cd->BARAddrs, sizeof(BARAddrs)); + } else + panic("NULL pointer to configuration data"); + + // Setup pointer in config space to point to this entry + if (cf->deviceExists(dev,func)) + panic("Two PCI devices occuping same dev: %#x func: %#x", dev, func); + else + cf->registerDevice(dev, func, this); +} + +void +PciDev::ReadConfig(int offset, int size, uint8_t *data) +{ + switch(size) { + case sizeof(uint32_t): + memcpy((uint32_t*)data, config.data + offset, sizeof(uint32_t)); + DPRINTF(PCIDEV, + "read device: %#x function: %#x register: %#x %d bytes: data: %#x\n", + deviceNum, functionNum, offset, size, + *(uint32_t*)(config.data + offset)); + break; + + case sizeof(uint16_t): + memcpy((uint16_t*)data, config.data + offset, sizeof(uint16_t)); + DPRINTF(PCIDEV, + "read device: %#x function: %#x register: %#x %d bytes: data: %#x\n", + deviceNum, functionNum, offset, size, + *(uint16_t*)(config.data + offset)); + break; + + case sizeof(uint8_t): + memcpy((uint8_t*)data, config.data + offset, sizeof(uint8_t)); + DPRINTF(PCIDEV, + "read device: %#x function: %#x register: %#x %d bytes: data: %#x\n", + deviceNum, functionNum, offset, size, + (uint16_t)(*(uint8_t*)(config.data + offset))); + break; + + default: + panic("Invalid Read Size"); + } +} + +void +PciDev::WriteConfig(int offset, int size, uint32_t data) +{ + uint32_t barnum; + + union { + uint8_t byte_value; + uint16_t half_value; + uint32_t word_value; + }; + word_value = data; + + DPRINTF(PCIDEV, + "write device: %#x function: %#x reg: %#x size: %d data: %#x\n", + deviceNum, functionNum, offset, size, word_value); + + barnum = (offset - PCI0_BASE_ADDR0) >> 2; + + switch (size) { + case sizeof(uint8_t): // 1-byte access + switch (offset) { + case PCI0_INTERRUPT_LINE: + case PCI_CACHE_LINE_SIZE: + case PCI_LATENCY_TIMER: + *(uint8_t *)&config.data[offset] = byte_value; + break; + + default: + panic("writing to a read only register"); + } + break; + + case sizeof(uint16_t): // 2-byte access + switch (offset) { + case PCI_COMMAND: + case PCI_STATUS: + case PCI_CACHE_LINE_SIZE: + *(uint16_t *)&config.data[offset] = half_value; + break; + + default: + panic("writing to a read only register"); + } + break; + + case sizeof(uint16_t)+1: // 3-byte access + panic("invalid access size"); + + case sizeof(uint32_t): // 4-byte access + switch (offset) { + case PCI0_BASE_ADDR0: + case PCI0_BASE_ADDR1: + case PCI0_BASE_ADDR2: + case PCI0_BASE_ADDR3: + case PCI0_BASE_ADDR4: + case PCI0_BASE_ADDR5: + // Writing 0xffffffff to a BAR tells the card to set the + // value of the bar + // to size of memory it needs + if (word_value == 0xffffffff) { + // This is I/O Space, bottom two bits are read only + if (config.data[offset] & 0x1) { + *(uint32_t *)&config.data[offset] = + ~(BARSize[barnum] - 1) | + (config.data[offset] & 0x3); + } else { + // This is memory space, bottom four bits are read only + *(uint32_t *)&config.data[offset] = + ~(BARSize[barnum] - 1) | + (config.data[offset] & 0xF); + } + } else { + // This is I/O Space, bottom two bits are read only + if(config.data[offset] & 0x1) { + *(uint32_t *)&config.data[offset] = (word_value & ~0x3) | + (config.data[offset] & 0x3); + + if (word_value & ~0x1) { + Addr base_addr = (word_value & ~0x1) + TSUNAMI_PCI0_IO; + Addr base_size = BARSize[barnum]-1; + + // It's never been set + if (BARAddrs[barnum] == 0) + mmu->add_child((FunctionalMemory *)this, + Range<Addr>(base_addr, + base_addr + base_size)); + else + mmu->update_child((FunctionalMemory *)this, + Range<Addr>(BARAddrs[barnum], + BARAddrs[barnum] + + base_size), + Range<Addr>(base_addr, + base_addr + + base_size)); + + BARAddrs[barnum] = base_addr; + } + + } else { + // This is memory space, bottom four bits are read only + *(uint32_t *)&config.data[offset] = (word_value & ~0xF) | + (config.data[offset] & 0xF); + + if (word_value & ~0x3) { + Addr base_addr = (word_value & ~0x3) + + TSUNAMI_PCI0_MEMORY; + + Addr base_size = BARSize[barnum]-1; + + // It's never been set + if (BARAddrs[barnum] == 0) + mmu->add_child((FunctionalMemory *)this, + Range<Addr>(base_addr, + base_addr + base_size)); + else + mmu->update_child((FunctionalMemory *)this, + Range<Addr>(BARAddrs[barnum], + BARAddrs[barnum] + + base_size), + Range<Addr>(base_addr, + base_addr + + base_size)); + + BARAddrs[barnum] = base_addr; + } + } + } + break; + + case PCI0_ROM_BASE_ADDR: + if (word_value == 0xfffffffe) + *(uint32_t *)&config.data[offset] = 0xffffffff; + else + *(uint32_t *)&config.data[offset] = word_value; + break; + + case PCI_COMMAND: + // This could also clear some of the error bits in the Status + // register. However they should never get set, so lets ignore + // it for now + *(uint16_t *)&config.data[offset] = half_value; + break; + + default: + DPRINTF(PCIDEV, "Writing to a read only register"); + } + break; + } +} + +void +PciDev::serialize(ostream &os) +{ + SERIALIZE_ARRAY(config.data, 64); +} + +void +PciDev::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(config.data, 64); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(PciConfigData) + + Param<int> VendorID; + Param<int> DeviceID; + Param<int> Command; + Param<int> Status; + Param<int> Revision; + Param<int> ProgIF; + Param<int> SubClassCode; + Param<int> ClassCode; + Param<int> CacheLineSize; + Param<int> LatencyTimer; + Param<int> HeaderType; + Param<int> BIST; + Param<uint32_t> BAR0; + Param<uint32_t> BAR1; + Param<uint32_t> BAR2; + Param<uint32_t> BAR3; + Param<uint32_t> BAR4; + Param<uint32_t> BAR5; + Param<uint32_t> CardbusCIS; + Param<int> SubsystemVendorID; + Param<int> SubsystemID; + Param<uint32_t> ExpansionROM; + Param<int> InterruptLine; + Param<int> InterruptPin; + Param<int> MinimumGrant; + Param<int> MaximumLatency; + Param<uint32_t> BAR0Size; + Param<uint32_t> BAR1Size; + Param<uint32_t> BAR2Size; + Param<uint32_t> BAR3Size; + Param<uint32_t> BAR4Size; + Param<uint32_t> BAR5Size; + +END_DECLARE_SIM_OBJECT_PARAMS(PciConfigData) + +BEGIN_INIT_SIM_OBJECT_PARAMS(PciConfigData) + + INIT_PARAM(VendorID, "Vendor ID"), + INIT_PARAM(DeviceID, "Device ID"), + INIT_PARAM_DFLT(Command, "Command Register", 0x00), + INIT_PARAM_DFLT(Status, "Status Register", 0x00), + INIT_PARAM_DFLT(Revision, "Device Revision", 0x00), + INIT_PARAM_DFLT(ProgIF, "Programming Interface", 0x00), + INIT_PARAM(SubClassCode, "Sub-Class Code"), + INIT_PARAM(ClassCode, "Class Code"), + INIT_PARAM_DFLT(CacheLineSize, "System Cacheline Size", 0x00), + INIT_PARAM_DFLT(LatencyTimer, "PCI Latency Timer", 0x00), + INIT_PARAM_DFLT(HeaderType, "PCI Header Type", 0x00), + INIT_PARAM_DFLT(BIST, "Built In Self Test", 0x00), + INIT_PARAM_DFLT(BAR0, "Base Address Register 0", 0x00), + INIT_PARAM_DFLT(BAR1, "Base Address Register 1", 0x00), + INIT_PARAM_DFLT(BAR2, "Base Address Register 2", 0x00), + INIT_PARAM_DFLT(BAR3, "Base Address Register 3", 0x00), + INIT_PARAM_DFLT(BAR4, "Base Address Register 4", 0x00), + INIT_PARAM_DFLT(BAR5, "Base Address Register 5", 0x00), + INIT_PARAM_DFLT(CardbusCIS, "Cardbus Card Information Structure", 0x00), + INIT_PARAM_DFLT(SubsystemVendorID, "Subsystem Vendor ID", 0x00), + INIT_PARAM_DFLT(SubsystemID, "Subsystem ID", 0x00), + INIT_PARAM_DFLT(ExpansionROM, "Expansion ROM Base Address Register", 0x00), + INIT_PARAM(InterruptLine, "Interrupt Line Register"), + INIT_PARAM(InterruptPin, "Interrupt Pin Register"), + INIT_PARAM_DFLT(MinimumGrant, "Minimum Grant", 0x00), + INIT_PARAM_DFLT(MaximumLatency, "Maximum Latency", 0x00), + INIT_PARAM_DFLT(BAR0Size, "Base Address Register 0 Size", 0x00), + INIT_PARAM_DFLT(BAR1Size, "Base Address Register 1 Size", 0x00), + INIT_PARAM_DFLT(BAR2Size, "Base Address Register 2 Size", 0x00), + INIT_PARAM_DFLT(BAR3Size, "Base Address Register 3 Size", 0x00), + INIT_PARAM_DFLT(BAR4Size, "Base Address Register 4 Size", 0x00), + INIT_PARAM_DFLT(BAR5Size, "Base Address Register 5 Size", 0x00) + +END_INIT_SIM_OBJECT_PARAMS(PciConfigData) + +CREATE_SIM_OBJECT(PciConfigData) +{ + PciConfigData *data = new PciConfigData(getInstanceName()); + + data->config.hdr.vendor = VendorID; + data->config.hdr.device = DeviceID; + data->config.hdr.command = Command; + data->config.hdr.status = Status; + data->config.hdr.revision = Revision; + data->config.hdr.progIF = ProgIF; + data->config.hdr.subClassCode = SubClassCode; + data->config.hdr.classCode = ClassCode; + data->config.hdr.cacheLineSize = CacheLineSize; + data->config.hdr.latencyTimer = LatencyTimer; + data->config.hdr.headerType = HeaderType; + data->config.hdr.bist = BIST; + + data->config.hdr.pci0.baseAddr0 = BAR0; + data->config.hdr.pci0.baseAddr1 = BAR1; + data->config.hdr.pci0.baseAddr2 = BAR2; + data->config.hdr.pci0.baseAddr3 = BAR3; + data->config.hdr.pci0.baseAddr4 = BAR4; + data->config.hdr.pci0.baseAddr5 = BAR5; + data->config.hdr.pci0.cardbusCIS = CardbusCIS; + data->config.hdr.pci0.subsystemVendorID = SubsystemVendorID; + data->config.hdr.pci0.subsystemID = SubsystemVendorID; + data->config.hdr.pci0.expansionROM = ExpansionROM; + data->config.hdr.pci0.interruptLine = InterruptLine; + data->config.hdr.pci0.interruptPin = InterruptPin; + data->config.hdr.pci0.minimumGrant = MinimumGrant; + data->config.hdr.pci0.maximumLatency = MaximumLatency; + + data->BARSize[0] = BAR0Size; + data->BARSize[1] = BAR1Size; + data->BARSize[2] = BAR2Size; + data->BARSize[3] = BAR3Size; + data->BARSize[4] = BAR4Size; + data->BARSize[5] = BAR5Size; + + return data; +} + +REGISTER_SIM_OBJECT("PciConfigData", PciConfigData) + +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/dev/pcidev.hh b/dev/pcidev.hh new file mode 100644 index 000000000..1c4de18ad --- /dev/null +++ b/dev/pcidev.hh @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * Interface for devices using PCI configuration + */ + +#ifndef __PCI_DEV_HH__ +#define __PCI_DEV_HH__ + +#include "dev/pcireg.h" +#include "dev/io_device.hh" + +class PciConfigAll; +class MemoryController; + + +/** + * This class encapulates the first 64 bytes of a singles PCI + * devices config space that in configured by the configuration file. + */ +class PciConfigData : public SimObject +{ + public: + /** + * Constructor to initialize the devices config space to 0. + */ + PciConfigData(const std::string &name) + : SimObject(name) + { + memset(config.data, 0, sizeof(config.data)); + memset(BARAddrs, 0, sizeof(BARAddrs)); + memset(BARSize, 0, sizeof(BARSize)); + } + + /** The first 64 bytes */ + PCIConfig config; + + /** The size of the BARs */ + uint32_t BARSize[6]; + + /** The addresses of the BARs */ + Addr BARAddrs[6]; +}; + +/** + * PCI device, base implemnation is only config space. + * Each device is connected to a PCIConfigSpace device + * which returns -1 for everything but the pcidevs that + * register with it. This object registers with the PCIConfig space + * object. + */ +class PciDev : public DmaDevice +{ + protected: + MemoryController *mmu; + /** A pointer to the configspace all object that calls + * us when a read comes to this particular device/function. + */ + PciConfigAll *configSpace; + + /** + * A pointer to the object that contains the first 64 bytes of + * config space + */ + PciConfigData *configData; + + /** The bus number we are on */ + uint32_t busNum; + + /** The device number we have */ + uint32_t deviceNum; + + /** The function number */ + uint32_t functionNum; + + /** The current config space. Unlike the PciConfigData this is updated + * during simulation while continues to refelect what was in the config file. + */ + PCIConfig config; + + /** The size of the BARs */ + uint32_t BARSize[6]; + + /** The current address mapping of the BARs */ + Addr BARAddrs[6]; + + public: + /** + * Constructor for PCI Dev. This function copies data from the config file + * object PCIConfigData and registers the device with a PciConfigAll object. + * @param name name of the object + * @param mmu a pointer to the memory controller + * @param cf a pointer to the config space object that this device need to + * register with + * @param cd A pointer to the config space values specified in the conig file + * @param bus the bus this device is on + * @param dev the device id of this device + * @param func the function number of this device + */ + PciDev(const std::string &name, MemoryController *mmu, PciConfigAll *cf, + PciConfigData *cd, uint32_t bus, uint32_t dev, uint32_t func); + + virtual Fault read(MemReqPtr &req, uint8_t *data) { + return No_Fault; + } + virtual Fault write(MemReqPtr &req, const uint8_t *data) { + return No_Fault; + } + + /** + * Write to the PCI config space data that is stored locally. This may be + * overridden by the device but at some point it will eventually call this + * for normal operations that it does not need to override. + * @param offset the offset into config space + * @param size the size of the write + * @param data the data to write + */ + virtual void WriteConfig(int offset, int size, uint32_t data); + + + /** + * Read from the PCI config space data that is stored locally. This may be + * overridden by the device but at some point it will eventually call this + * for normal operations that it does not need to override. + * @param offset the offset into config space + * @param size the size of the read + * @param data pointer to the location where the read value should be stored + */ + virtual void ReadConfig(int offset, int size, uint8_t *data); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +#endif // __PCI_DEV_HH__ diff --git a/dev/platform.cc b/dev/platform.cc new file mode 100644 index 000000000..c39849162 --- /dev/null +++ b/dev/platform.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2003 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. + */ + +#include "dev/platform.hh" +#include "sim/builder.hh" +#include "sim/sim_exit.hh" + +using namespace std; + +DEFINE_SIM_OBJECT_CLASS_NAME("Platform", Platform) + diff --git a/dev/platform.hh b/dev/platform.hh new file mode 100644 index 000000000..407f58406 --- /dev/null +++ b/dev/platform.hh @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2003 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. + */ + +/** + * @file + * Generic interface for platforms + */ + +#ifndef __PLATFORM_HH_ +#define __PLATFORM_HH_ + +#include "sim/sim_object.hh" + +class PciConfigAll; +class IntrControl; +class SimConsole; + +class Platform : public SimObject +{ + public: + /** Pointer to the interrupt controller */ + IntrControl *intrctrl; + /** Pointer to the simulation console */ + SimConsole *cons; + /** Pointer to the PCI configuration space */ + PciConfigAll *pciconfig; + + int interrupt_frequency; + + public: + Platform(const std::string &name, IntrControl *intctrl, + PciConfigAll *pci, int intrFreq) + : SimObject(name), intrctrl(intctrl), pciconfig(pci), + interrupt_frequency(intrFreq) {} + virtual ~Platform() {} + virtual void postConsoleInt() = 0; + virtual void clearConsoleInt() = 0; +}; + +#endif // __PLATFORM_HH_ diff --git a/dev/tsunami.cc b/dev/tsunami.cc new file mode 100644 index 000000000..8956ee557 --- /dev/null +++ b/dev/tsunami.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2003 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. + */ + +#include <deque> +#include <string> +#include <vector> + +#include "cpu/intr_control.hh" +#include "dev/console.hh" +#include "dev/etherdev.hh" +#include "dev/ide_ctrl.hh" +#include "dev/tlaser_clock.hh" +#include "dev/tsunami_cchip.hh" +#include "dev/tsunami_pchip.hh" +#include "dev/tsunami_io.hh" +#include "dev/tsunami.hh" +#include "dev/pciconfigall.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; + +Tsunami::Tsunami(const string &name, System *s, + IntrControl *ic, PciConfigAll *pci, int intr_freq) + : Platform(name, ic, pci, intr_freq), system(s) +{ + // set the back pointer from the system to myself + system->platform = this; + + for (int i = 0; i < Tsunami::Max_CPUs; i++) + intr_sum_type[i] = 0; +} + +void +Tsunami::postConsoleInt() +{ + io->postPIC(0x10); +} + +void +Tsunami::clearConsoleInt() +{ + io->clearPIC(0x10); +} + +void +Tsunami::serialize(std::ostream &os) +{ + SERIALIZE_ARRAY(intr_sum_type, Tsunami::Max_CPUs); +} + +void +Tsunami::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(intr_sum_type, Tsunami::Max_CPUs); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(Tsunami) + + SimObjectParam<System *> system; + SimObjectParam<SimConsole *> cons; + SimObjectParam<IntrControl *> intrctrl; + SimObjectParam<PciConfigAll *> pciconfig; + Param<int> interrupt_frequency; + +END_DECLARE_SIM_OBJECT_PARAMS(Tsunami) + +BEGIN_INIT_SIM_OBJECT_PARAMS(Tsunami) + + INIT_PARAM(system, "system"), + INIT_PARAM(cons, "system console"), + INIT_PARAM(intrctrl, "interrupt controller"), + INIT_PARAM(pciconfig, "PCI configuration"), + INIT_PARAM_DFLT(interrupt_frequency, "frequency of interrupts", 1024) + +END_INIT_SIM_OBJECT_PARAMS(Tsunami) + +CREATE_SIM_OBJECT(Tsunami) +{ + return new Tsunami(getInstanceName(), system, intrctrl, pciconfig, + interrupt_frequency); +} + +REGISTER_SIM_OBJECT("Tsunami", Tsunami) diff --git a/dev/tsunami.hh b/dev/tsunami.hh new file mode 100644 index 000000000..c5dbe797b --- /dev/null +++ b/dev/tsunami.hh @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2003 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. + */ + +/** + * @file + * Declaration of top level class for the Tsunami chipset. This class just + * retains pointers to all its children so the children can communicate. + */ + +#ifndef __TSUNAMI_HH__ +#define __TSUNAMI_HH__ + +#include "dev/platform.hh" + +class IdeController; +class TlaserClock; +class NSGigE; +class TsunamiCChip; +class TsunamiPChip; +class TsunamiIO; +class PciConfigAll; +class System; + +/** + * Top level class for Tsunami Chipset emulation. + * This structure just contains pointers to all the + * children so the children can commnicate to do the + * read work + */ + +class Tsunami : public Platform +{ + public: + + /** Max number of CPUs in a Tsunami */ + static const int Max_CPUs = 4; + + /** Pointer to the system */ + System *system; + /** Pointer to the TsunamiIO device which has the RTC */ + TsunamiIO *io; + /** Pointer to the disk controller device */ + IdeController *disk_controller; + /** Pointer to the ethernet controller device */ + NSGigE *ethernet; + + /** Pointer to the Tsunami CChip. + * The chip contains some configuration information and + * all the interrupt mask and status registers + */ + TsunamiCChip *cchip; + + /** Pointer to the Tsunami PChip. + * The pchip is the interface to the PCI bus, in our case + * it does not have to do much. + */ + TsunamiPChip *pchip; + + int intr_sum_type[Tsunami::Max_CPUs]; + int ipi_pending[Tsunami::Max_CPUs]; + + public: + /** + * Constructor for the Tsunami Class. + * @param name name of the object + * @param con pointer to the console + * @param intrcontrol pointer to the interrupt controller + * @param intrFreq frequency that interrupts happen + */ + Tsunami(const std::string &name, System *s, IntrControl *intctrl, + PciConfigAll *pci, int intrFreq); + + /** + * Cause the cpu to post a serial interrupt to the CPU. + */ + virtual void postConsoleInt(); + + /** + * Clear a posted CPU interrupt (id=55) + */ + virtual void clearConsoleInt(); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +#endif // __TSUNAMI_HH__ diff --git a/dev/tsunami_cchip.cc b/dev/tsunami_cchip.cc new file mode 100644 index 000000000..ba49c361b --- /dev/null +++ b/dev/tsunami_cchip.cc @@ -0,0 +1,390 @@ +/* $Id$ */ + +/* @file + * Emulation of the Tsunami CChip CSRs + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "cpu/exec_context.hh" +#include "dev/console.hh" +#include "dev/tsunami_cchip.hh" +#include "dev/tsunamireg.h" +#include "dev/tsunami.hh" +#include "cpu/intr_control.hh" +#include "mem/functional_mem/memory_control.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; + +TsunamiCChip::TsunamiCChip(const string &name, Tsunami *t, Addr a, + MemoryController *mmu) + : FunctionalMemory(name), addr(a), tsunami(t) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + + for(int i=0; i < Tsunami::Max_CPUs; i++) { + dim[i] = 0; + dir[i] = 0; + dirInterrupting[i] = false; + ipiInterrupting[i] = false; + RTCInterrupting[i] = false; + } + + drir = 0; + misc = 0; + + //Put back pointer in tsunami + tsunami->cchip = this; +} + +Fault +TsunamiCChip::read(MemReqPtr &req, uint8_t *data) +{ + DPRINTF(Tsunami, "read va=%#x size=%d\n", + req->vaddr, req->size); + + Addr daddr = (req->paddr - (addr & PA_IMPL_MASK)) >> 6; + ExecContext *xc = req->xc; + + switch (req->size) { + + case sizeof(uint64_t): + switch(daddr) { + case TSDEV_CC_CSR: + *(uint64_t*)data = 0x0; + return No_Fault; + case TSDEV_CC_MTR: + panic("TSDEV_CC_MTR not implemeted\n"); + return No_Fault; + case TSDEV_CC_MISC: + *(uint64_t*)data = misc | (xc->cpu_id & 0x3); + return No_Fault; + case TSDEV_CC_AAR0: + case TSDEV_CC_AAR1: + case TSDEV_CC_AAR2: + case TSDEV_CC_AAR3: + panic("TSDEV_CC_AARx not implemeted\n"); + return No_Fault; + case TSDEV_CC_DIM0: + *(uint64_t*)data = dim[0]; + return No_Fault; + case TSDEV_CC_DIM1: + *(uint64_t*)data = dim[1]; + return No_Fault; + case TSDEV_CC_DIM2: + *(uint64_t*)data = dim[2]; + return No_Fault; + case TSDEV_CC_DIM3: + *(uint64_t*)data = dim[3]; + return No_Fault; + case TSDEV_CC_DIR0: + *(uint64_t*)data = dir[0]; + return No_Fault; + case TSDEV_CC_DIR1: + *(uint64_t*)data = dir[1]; + return No_Fault; + case TSDEV_CC_DIR2: + *(uint64_t*)data = dir[2]; + return No_Fault; + case TSDEV_CC_DIR3: + *(uint64_t*)data = dir[3]; + return No_Fault; + case TSDEV_CC_DRIR: + *(uint64_t*)data = drir; + return No_Fault; + case TSDEV_CC_PRBEN: + panic("TSDEV_CC_PRBEN not implemented\n"); + return No_Fault; + case TSDEV_CC_IIC0: + case TSDEV_CC_IIC1: + case TSDEV_CC_IIC2: + case TSDEV_CC_IIC3: + panic("TSDEV_CC_IICx not implemented\n"); + return No_Fault; + case TSDEV_CC_MPR0: + case TSDEV_CC_MPR1: + case TSDEV_CC_MPR2: + case TSDEV_CC_MPR3: + panic("TSDEV_CC_MPRx not implemented\n"); + return No_Fault; + default: + panic("default in cchip read reached, accessing 0x%x\n"); + } // uint64_t + + break; + case sizeof(uint32_t): + case sizeof(uint16_t): + case sizeof(uint8_t): + default: + panic("invalid access size(?) for tsunami register!\n"); + } + DPRINTFN("Tsunami CChip ERROR: read daddr=%#x size=%d\n", daddr, req->size); + + return No_Fault; +} + +Fault +TsunamiCChip::write(MemReqPtr &req, const uint8_t *data) +{ + DPRINTF(Tsunami, "write - va=%#x size=%d \n", + req->vaddr, req->size); + + Addr daddr = (req->paddr - (addr & PA_IMPL_MASK)) >> 6; + + bool supportedWrite = false; + uint64_t size = tsunami->intrctrl->cpu->system->execContexts.size(); + + switch (req->size) { + + case sizeof(uint64_t): + switch(daddr) { + case TSDEV_CC_CSR: + panic("TSDEV_CC_CSR write\n"); + return No_Fault; + case TSDEV_CC_MTR: + panic("TSDEV_CC_MTR write not implemented\n"); + return No_Fault; + case TSDEV_CC_MISC: + //If it is the 4-7th bit, clear the RTC interrupt + uint64_t itintr; + if ((itintr = (*(uint64_t*) data) & (0xf<<4))) { + //Clear the bits in ITINTR + misc &= ~(itintr); + for (int i=0; i < size; i++) { + if ((itintr & (1 << (i+4))) && RTCInterrupting[i]) { + tsunami->intrctrl->clear(i, TheISA::INTLEVEL_IRQ2, 0); + RTCInterrupting[i] = false; + DPRINTF(Tsunami, "clearing rtc interrupt to cpu=%d\n", i); + } + } + supportedWrite = true; + } + //If it is 12th-15th bit, IPI sent to Processor 1 + uint64_t ipreq; + if ((ipreq = (*(uint64_t*) data) & (0xf << 12))) { + //Set the bits in IPINTR + misc |= (ipreq >> 4); + for (int i=0; i < size; i++) { + if ((ipreq & (1 << (i + 12)))) { + if (!ipiInterrupting[i]) + tsunami->intrctrl->post(i, TheISA::INTLEVEL_IRQ3, 0); + ipiInterrupting[i]++; + DPRINTF(IPI, "send cpu=%d pending=%d from=%d\n", i, + ipiInterrupting[i], req->cpu_num); + } + } + supportedWrite = true; + } + //If it is bits 8-11, then clearing IPI's + uint64_t ipintr; + if ((ipintr = (*(uint64_t*) data) & (0xf << 8))) { + //Clear the bits in IPINTR + misc &= ~(ipintr); + for (int i=0; i < size; i++) { + if ((ipintr & (1 << (i + 8))) && ipiInterrupting[i]) { + if (!(--ipiInterrupting[i])) + tsunami->intrctrl->clear(i, TheISA::INTLEVEL_IRQ3, 0); + DPRINTF(IPI, "clearing cpu=%d pending=%d from=%d\n", i, + ipiInterrupting[i] + 1, req->cpu_num); + } + } + supportedWrite = true; + } + if(!supportedWrite) panic("TSDEV_CC_MISC write not implemented\n"); + return No_Fault; + case TSDEV_CC_AAR0: + case TSDEV_CC_AAR1: + case TSDEV_CC_AAR2: + case TSDEV_CC_AAR3: + panic("TSDEV_CC_AARx write not implemeted\n"); + return No_Fault; + case TSDEV_CC_DIM0: + case TSDEV_CC_DIM1: + case TSDEV_CC_DIM2: + case TSDEV_CC_DIM3: + int number; + if(daddr == TSDEV_CC_DIM0) + number = 0; + else if(daddr == TSDEV_CC_DIM1) + number = 1; + else if(daddr == TSDEV_CC_DIM2) + number = 2; + else + number = 3; + + uint64_t bitvector; + uint64_t olddim; + uint64_t olddir; + + olddim = dim[number]; + olddir = dir[number]; + dim[number] = *(uint64_t*)data; + dir[number] = dim[number] & drir; + for(int x = 0; x < 64; x++) + { + bitvector = (uint64_t)1 << x; + // Figure out which bits have changed + if ((dim[number] & bitvector) != (olddim & bitvector)) + { + // The bit is now set and it wasn't before (set) + if((dim[number] & bitvector) && (dir[number] & bitvector)) + { + tsunami->intrctrl->post(number, TheISA::INTLEVEL_IRQ1, x); + DPRINTF(Tsunami, "posting dir interrupt to cpu 0\n"); + } + else if ((olddir & bitvector) && + !(dir[number] & bitvector)) + { + // The bit was set and now its now clear and + // we were interrupting on that bit before + tsunami->intrctrl->clear(number, TheISA::INTLEVEL_IRQ1, x); + DPRINTF(Tsunami, "dim write resulting in clear" + "dir interrupt to cpu 0\n"); + + } + + + } + } + return No_Fault; + case TSDEV_CC_DIR0: + case TSDEV_CC_DIR1: + case TSDEV_CC_DIR2: + case TSDEV_CC_DIR3: + panic("TSDEV_CC_DIR write not implemented\n"); + case TSDEV_CC_DRIR: + panic("TSDEV_CC_DRIR write not implemented\n"); + case TSDEV_CC_PRBEN: + panic("TSDEV_CC_PRBEN write not implemented\n"); + case TSDEV_CC_IIC0: + case TSDEV_CC_IIC1: + case TSDEV_CC_IIC2: + case TSDEV_CC_IIC3: + panic("TSDEV_CC_IICx write not implemented\n"); + case TSDEV_CC_MPR0: + case TSDEV_CC_MPR1: + case TSDEV_CC_MPR2: + case TSDEV_CC_MPR3: + panic("TSDEV_CC_MPRx write not implemented\n"); + default: + panic("default in cchip read reached, accessing 0x%x\n"); + } + + break; + case sizeof(uint32_t): + case sizeof(uint16_t): + case sizeof(uint8_t): + default: + panic("invalid access size(?) for tsunami register!\n"); + } + + DPRINTFN("Tsunami ERROR: write daddr=%#x size=%d\n", daddr, req->size); + + return No_Fault; +} + +void +TsunamiCChip::postRTC() +{ + int size = tsunami->intrctrl->cpu->system->execContexts.size(); + + for (int i = 0; i < size; i++) { + if (!RTCInterrupting[i]) { + misc |= 16 << i; + RTCInterrupting[i] = true; + tsunami->intrctrl->post(i, TheISA::INTLEVEL_IRQ2, 0); + DPRINTF(Tsunami, "Posting RTC interrupt to cpu=%d", i); + } + } + +} + +void +TsunamiCChip::postDRIR(uint32_t interrupt) +{ + uint64_t bitvector = (uint64_t)0x1 << interrupt; + drir |= bitvector; + uint64_t size = tsunami->intrctrl->cpu->system->execContexts.size(); + for(int i=0; i < size; i++) { + dir[i] = dim[i] & drir; + if (dim[i] & bitvector) { + tsunami->intrctrl->post(i, TheISA::INTLEVEL_IRQ1, interrupt); + DPRINTF(Tsunami, "posting dir interrupt to cpu %d," + "interrupt %d\n",i, interrupt); + } + } +} + +void +TsunamiCChip::clearDRIR(uint32_t interrupt) +{ + uint64_t bitvector = (uint64_t)0x1 << interrupt; + uint64_t size = tsunami->intrctrl->cpu->system->execContexts.size(); + if (drir & bitvector) + { + drir &= ~bitvector; + for(int i=0; i < size; i++) { + if (dir[i] & bitvector) { + tsunami->intrctrl->clear(i, TheISA::INTLEVEL_IRQ1, interrupt); + DPRINTF(Tsunami, "clearing dir interrupt to cpu %d," + "interrupt %d\n",i, interrupt); + + } + dir[i] = dim[i] & drir; + } + } + else + DPRINTF(Tsunami, "Spurrious clear? interrupt %d\n", interrupt); +} + +void +TsunamiCChip::serialize(std::ostream &os) +{ + SERIALIZE_ARRAY(dim, Tsunami::Max_CPUs); + SERIALIZE_ARRAY(dir, Tsunami::Max_CPUs); + SERIALIZE_ARRAY(dirInterrupting, Tsunami::Max_CPUs); + SERIALIZE_ARRAY(ipiInterrupting, Tsunami::Max_CPUs); + SERIALIZE_SCALAR(drir); + SERIALIZE_SCALAR(misc); + SERIALIZE_ARRAY(RTCInterrupting, Tsunami::Max_CPUs); +} + +void +TsunamiCChip::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(dim, Tsunami::Max_CPUs); + UNSERIALIZE_ARRAY(dir, Tsunami::Max_CPUs); + UNSERIALIZE_ARRAY(dirInterrupting, Tsunami::Max_CPUs); + UNSERIALIZE_ARRAY(ipiInterrupting, Tsunami::Max_CPUs); + UNSERIALIZE_SCALAR(drir); + UNSERIALIZE_SCALAR(misc); + UNSERIALIZE_ARRAY(RTCInterrupting, Tsunami::Max_CPUs); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiCChip) + + SimObjectParam<Tsunami *> tsunami; + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + +END_DECLARE_SIM_OBJECT_PARAMS(TsunamiCChip) + +BEGIN_INIT_SIM_OBJECT_PARAMS(TsunamiCChip) + + INIT_PARAM(tsunami, "Tsunami"), + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(addr, "Device Address") + +END_INIT_SIM_OBJECT_PARAMS(TsunamiCChip) + +CREATE_SIM_OBJECT(TsunamiCChip) +{ + return new TsunamiCChip(getInstanceName(), tsunami, addr, mmu); +} + +REGISTER_SIM_OBJECT("TsunamiCChip", TsunamiCChip) diff --git a/dev/tsunami_cchip.hh b/dev/tsunami_cchip.hh new file mode 100644 index 000000000..79fadf06a --- /dev/null +++ b/dev/tsunami_cchip.hh @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * Emulation of the Tsunami CChip CSRs + */ + +#ifndef __TSUNAMI_CCHIP_HH__ +#define __TSUNAMI_CCHIP_HH__ + +#include "mem/functional_mem/functional_memory.hh" +#include "dev/tsunami.hh" + +/* + * Tsunami CChip + */ +class TsunamiCChip : public FunctionalMemory +{ + private: + /** The base address of this device */ + Addr addr; + + /** The size of mappad from the above address */ + static const Addr size = 0xfff; + + protected: + /** + * pointer to the tsunami object. + * This is our access to all the other tsunami + * devices. + */ + Tsunami *tsunami; + + /** + * The dims are device interrupt mask registers. + * One exists for each CPU, the DRIR X DIM = DIR + */ + uint64_t dim[Tsunami::Max_CPUs]; + + /** + * The dirs are device interrupt registers. + * One exists for each CPU, the DRIR X DIM = DIR + */ + uint64_t dir[Tsunami::Max_CPUs]; + bool dirInterrupting[Tsunami::Max_CPUs]; + + /** + * This register contains bits for each PCI interrupt + * that can occur. + */ + uint64_t drir; + + /** + * The MISC register contains the CPU we are currently on + * as well as bits to ack RTC and IPI interrupts. + */ + uint64_t misc; + + /** Count of the number of pending IPIs on a CPU */ + uint64_t ipiInterrupting[Tsunami::Max_CPUs]; + + /** Indicator of which CPUs have had an RTC interrupt */ + bool RTCInterrupting[Tsunami::Max_CPUs]; + + public: + /** + * Initialize the Tsunami CChip by setting all of the + * device register to 0. + * @param name name of this device. + * @param t pointer back to the Tsunami object that we belong to. + * @param a address we are mapped at. + * @param mmu pointer to the memory controller that sends us events. + */ + TsunamiCChip(const std::string &name, Tsunami *t, Addr a, + MemoryController *mmu); + + /** + * Process a read to the CChip. + * @param req Contains the address to read from. + * @param data A pointer to write the read data to. + * @return The fault condition of the access. + */ + virtual Fault read(MemReqPtr &req, uint8_t *data); + + + /** + * Process a write to the CChip. + * @param req Contains the address to write to. + * @param data The data to write. + * @return The fault condition of the access. + */ + virtual Fault write(MemReqPtr &req, const uint8_t *data); + + /** + * post an RTC interrupt to the CPU + */ + void postRTC(); + + /** + * post an interrupt to the CPU. + * @param interrupt the interrupt number to post (0-64) + */ + void postDRIR(uint32_t interrupt); + + /** + * clear an interrupt previously posted to the CPU. + * @param interrupt the interrupt number to post (0-64) + */ + void clearDRIR(uint32_t interrupt); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +#endif // __TSUNAMI_CCHIP_HH__ diff --git a/dev/tsunami_io.cc b/dev/tsunami_io.cc new file mode 100644 index 000000000..2dda86fbc --- /dev/null +++ b/dev/tsunami_io.cc @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * Tsunami I/O including PIC, PIT, RTC, DMA + */ + +#include <sys/time.h> + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "cpu/exec_context.hh" +#include "dev/console.hh" +#include "dev/tlaser_clock.hh" +#include "dev/tsunami_io.hh" +#include "dev/tsunamireg.h" +#include "dev/tsunami.hh" +#include "mem/functional_mem/memory_control.hh" +#include "sim/builder.hh" +#include "dev/tsunami_cchip.hh" + +using namespace std; + +#define UNIX_YEAR_OFFSET 52 + +// Timer Event for Periodic interrupt of RTC +TsunamiIO::RTCEvent::RTCEvent(Tsunami* t) + : Event(&mainEventQueue), tsunami(t) +{ + DPRINTF(MC146818, "RTC Event Initilizing\n"); + schedule(curTick + ticksPerSecond/RTC_RATE); +} + +void +TsunamiIO::RTCEvent::process() +{ + DPRINTF(MC146818, "RTC Timer Interrupt\n"); + schedule(curTick + ticksPerSecond/RTC_RATE); + //Actually interrupt the processor here + tsunami->cchip->postRTC(); + +} + +const char * +TsunamiIO::RTCEvent::description() +{ + return "tsunami RTC 1024Hz interrupt"; +} + +// Timer Event for PIT Timers +TsunamiIO::ClockEvent::ClockEvent() + : Event(&mainEventQueue) +{ + DPRINTF(Tsunami, "Clock Event Initilizing\n"); + mode = 0; +} + +void +TsunamiIO::ClockEvent::process() +{ + DPRINTF(Tsunami, "Timer Interrupt\n"); + if (mode == 0) + status = 0x20; // set bit that linux is looking for + else + schedule(curTick + interval); +} + +void +TsunamiIO::ClockEvent::Program(int count) +{ + DPRINTF(Tsunami, "Timer set to curTick + %d\n", count); + // should be count * (cpufreq/pitfreq) + interval = count * ticksPerSecond/1193180UL; + schedule(curTick + interval); + status = 0; +} + +const char * +TsunamiIO::ClockEvent::description() +{ + return "tsunami 8254 Interval timer"; +} + +void +TsunamiIO::ClockEvent::ChangeMode(uint8_t md) +{ + mode = md; +} + +uint8_t +TsunamiIO::ClockEvent::Status() +{ + return status; +} + +TsunamiIO::TsunamiIO(const string &name, Tsunami *t, time_t init_time, + Addr a, MemoryController *mmu) + : FunctionalMemory(name), addr(a), tsunami(t), rtc(t) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + + // set the back pointer from tsunami to myself + tsunami->io = this; + + timerData = 0; + set_time(init_time == 0 ? time(NULL) : init_time); + uip = 1; + picr = 0; + picInterrupting = false; +} + +void +TsunamiIO::set_time(time_t t) +{ + gmtime_r(&t, &tm); + DPRINTFN("Real-time clock set to %s", asctime(&tm)); +} + +Fault +TsunamiIO::read(MemReqPtr &req, uint8_t *data) +{ + DPRINTF(Tsunami, "io read va=%#x size=%d IOPorrt=%#x\n", + req->vaddr, req->size, req->vaddr & 0xfff); + + Addr daddr = (req->paddr - (addr & PA_IMPL_MASK)); + + + switch(req->size) { + case sizeof(uint8_t): + switch(daddr) { + case TSDEV_PIC1_ISR: + // !!! If this is modified 64bit case needs to be too + // Pal code has to do a 64 bit physical read because there is + // no load physical byte instruction + *(uint8_t*)data = picr; + return No_Fault; + case TSDEV_PIC2_ISR: + // PIC2 not implemnted... just return 0 + *(uint8_t*)data = 0x00; + return No_Fault; + case TSDEV_TMR_CTL: + *(uint8_t*)data = timer2.Status(); + return No_Fault; + case TSDEV_RTC_DATA: + switch(RTCAddress) { + case RTC_CONTROL_REGISTERA: + *(uint8_t*)data = uip << 7 | 0x26; + uip = !uip; + return No_Fault; + case RTC_CONTROL_REGISTERB: + // DM and 24/12 and UIE + *(uint8_t*)data = 0x46; + return No_Fault; + case RTC_CONTROL_REGISTERC: + // If we want to support RTC user access in linux + // This won't work, but for now it's fine + *(uint8_t*)data = 0x00; + return No_Fault; + case RTC_CONTROL_REGISTERD: + panic("RTC Control Register D not implemented"); + case RTC_SECOND: + *(uint8_t *)data = tm.tm_sec; + return No_Fault; + case RTC_MINUTE: + *(uint8_t *)data = tm.tm_min; + return No_Fault; + case RTC_HOUR: + *(uint8_t *)data = tm.tm_hour; + return No_Fault; + case RTC_DAY_OF_WEEK: + *(uint8_t *)data = tm.tm_wday; + return No_Fault; + case RTC_DAY_OF_MONTH: + *(uint8_t *)data = tm.tm_mday; + case RTC_MONTH: + *(uint8_t *)data = tm.tm_mon + 1; + return No_Fault; + case RTC_YEAR: + *(uint8_t *)data = tm.tm_year - UNIX_YEAR_OFFSET; + return No_Fault; + default: + panic("Unknown RTC Address\n"); + } + + default: + panic("I/O Read - va%#x size %d\n", req->vaddr, req->size); + } + case sizeof(uint16_t): + case sizeof(uint32_t): + panic("I/O Read - invalid size - va %#x size %d\n", + req->vaddr, req->size); + + case sizeof(uint64_t): + switch(daddr) { + case TSDEV_PIC1_ISR: + // !!! If this is modified 8bit case needs to be too + // Pal code has to do a 64 bit physical read because there is + // no load physical byte instruction + *(uint64_t*)data = (uint64_t)picr; + return No_Fault; + default: + panic("I/O Read - invalid size - va %#x size %d\n", + req->vaddr, req->size); + } + + default: + panic("I/O Read - invalid size - va %#x size %d\n", + req->vaddr, req->size); + } + panic("I/O Read - va%#x size %d\n", req->vaddr, req->size); + + return No_Fault; +} + +Fault +TsunamiIO::write(MemReqPtr &req, const uint8_t *data) +{ + uint8_t dt = *(uint8_t*)data; + uint64_t dt64 = dt; + + DPRINTF(Tsunami, "io write - va=%#x size=%d IOPort=%#x Data=%#x\n", + req->vaddr, req->size, req->vaddr & 0xfff, dt64); + + Addr daddr = (req->paddr - (addr & PA_IMPL_MASK)); + + switch(req->size) { + case sizeof(uint8_t): + switch(daddr) { + case TSDEV_PIC1_MASK: + mask1 = ~(*(uint8_t*)data); + if ((picr & mask1) && !picInterrupting) { + picInterrupting = true; + tsunami->cchip->postDRIR(55); + DPRINTF(Tsunami, "posting pic interrupt to cchip\n"); + } + if ((!(picr & mask1)) && picInterrupting) { + picInterrupting = false; + tsunami->cchip->clearDRIR(55); + DPRINTF(Tsunami, "clearing pic interrupt\n"); + } + return No_Fault; + case TSDEV_PIC2_MASK: + mask2 = *(uint8_t*)data; + //PIC2 Not implemented to interrupt + return No_Fault; + case TSDEV_PIC1_ACK: + // clear the interrupt on the PIC + picr &= ~(1 << (*(uint8_t*)data & 0xF)); + if (!(picr & mask1)) + tsunami->cchip->clearDRIR(55); + return No_Fault; + case TSDEV_PIC2_ACK: + return No_Fault; + case TSDEV_DMA1_RESET: + return No_Fault; + case TSDEV_DMA2_RESET: + return No_Fault; + case TSDEV_DMA1_MODE: + mode1 = *(uint8_t*)data; + return No_Fault; + case TSDEV_DMA2_MODE: + mode2 = *(uint8_t*)data; + return No_Fault; + case TSDEV_DMA1_MASK: + case TSDEV_DMA2_MASK: + return No_Fault; + case TSDEV_TMR_CTL: + return No_Fault; + case TSDEV_TMR2_CTL: + if ((*(uint8_t*)data & 0x30) != 0x30) + panic("Only L/M write supported\n"); + + switch(*(uint8_t*)data >> 6) { + case 0: + timer0.ChangeMode((*(uint8_t*)data & 0xF) >> 1); + break; + case 2: + timer2.ChangeMode((*(uint8_t*)data & 0xF) >> 1); + break; + default: + panic("Read Back Command not implemented\n"); + } + return No_Fault; + case TSDEV_TMR2_DATA: + /* two writes before we actually start the Timer + so I set a flag in the timerData */ + if(timerData & 0x1000) { + timerData &= 0x1000; + timerData += *(uint8_t*)data << 8; + timer2.Program(timerData); + } else { + timerData = *(uint8_t*)data; + timerData |= 0x1000; + } + return No_Fault; + case TSDEV_TMR0_DATA: + /* two writes before we actually start the Timer + so I set a flag in the timerData */ + if(timerData & 0x1000) { + timerData &= 0x1000; + timerData += *(uint8_t*)data << 8; + timer0.Program(timerData); + } else { + timerData = *(uint8_t*)data; + timerData |= 0x1000; + } + return No_Fault; + case TSDEV_RTC_ADDR: + RTCAddress = *(uint8_t*)data; + return No_Fault; + case TSDEV_RTC_DATA: + panic("RTC Write not implmented (rtc.o won't work)\n"); + default: + panic("I/O Write - va%#x size %d\n", req->vaddr, req->size); + } + case sizeof(uint16_t): + case sizeof(uint32_t): + case sizeof(uint64_t): + default: + panic("I/O Write - invalid size - va %#x size %d\n", + req->vaddr, req->size); + } + + + return No_Fault; +} + +void +TsunamiIO::postPIC(uint8_t bitvector) +{ + //PIC2 Is not implemented, because nothing of interest there + picr |= bitvector; + if (picr & mask1) { + tsunami->cchip->postDRIR(55); + DPRINTF(Tsunami, "posting pic interrupt to cchip\n"); + } +} + +void +TsunamiIO::clearPIC(uint8_t bitvector) +{ + //PIC2 Is not implemented, because nothing of interest there + picr &= ~bitvector; + if (!(picr & mask1)) { + tsunami->cchip->clearDRIR(55); + DPRINTF(Tsunami, "clearing pic interrupt to cchip\n"); + } +} + +void +TsunamiIO::serialize(std::ostream &os) +{ + SERIALIZE_SCALAR(timerData); + SERIALIZE_SCALAR(uip); + SERIALIZE_SCALAR(picr); + SERIALIZE_SCALAR(picInterrupting); + Tick time0when = timer0.when(); + Tick time2when = timer2.when(); + Tick rtcwhen = rtc.when(); + SERIALIZE_SCALAR(time0when); + SERIALIZE_SCALAR(time2when); + SERIALIZE_SCALAR(rtcwhen); + +} + +void +TsunamiIO::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(timerData); + UNSERIALIZE_SCALAR(uip); + UNSERIALIZE_SCALAR(picr); + UNSERIALIZE_SCALAR(picInterrupting); + Tick time0when; + Tick time2when; + Tick rtcwhen; + UNSERIALIZE_SCALAR(time0when); + UNSERIALIZE_SCALAR(time2when); + UNSERIALIZE_SCALAR(rtcwhen); + timer0.reschedule(time0when); + timer2.reschedule(time2when); + rtc.reschedule(rtcwhen); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiIO) + + SimObjectParam<Tsunami *> tsunami; + Param<time_t> time; + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + +END_DECLARE_SIM_OBJECT_PARAMS(TsunamiIO) + +BEGIN_INIT_SIM_OBJECT_PARAMS(TsunamiIO) + + INIT_PARAM(tsunami, "Tsunami"), + INIT_PARAM_DFLT(time, "System time to use " + "(0 for actual time, default is 1/1/06", ULL(1136073600)), + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(addr, "Device Address") + +END_INIT_SIM_OBJECT_PARAMS(TsunamiIO) + +CREATE_SIM_OBJECT(TsunamiIO) +{ + return new TsunamiIO(getInstanceName(), tsunami, time, addr, mmu); +} + +REGISTER_SIM_OBJECT("TsunamiIO", TsunamiIO) diff --git a/dev/tsunami_io.hh b/dev/tsunami_io.hh new file mode 100644 index 000000000..1dd7762e3 --- /dev/null +++ b/dev/tsunami_io.hh @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * Tsunami Fake I/O Space mapping including RTC/timer interrupts + */ + +#ifndef __TSUNAMI_DMA_HH__ +#define __TSUNAMI_DMA_HH__ + +#include "mem/functional_mem/functional_memory.hh" +#include "dev/tsunami.hh" + +/** How often the RTC interrupts */ +static const int RTC_RATE = 1024; + +/* + * Tsunami I/O device is a catch all for all the south bridge stuff we care + * to implement. + */ +class TsunamiIO : public FunctionalMemory +{ + private: + /** The base address of this device */ + Addr addr; + + /** The size of mappad from the above address */ + static const Addr size = 0xff; + + struct tm tm; + + /** In Tsunami RTC only has two i/o ports one for data and one for address, + * so you write the address and then read/write the data. This store the + * address you are going to be reading from on a read. + */ + uint8_t RTCAddress; + + protected: + + /** + * The ClockEvent is handles the PIT interrupts + */ + class ClockEvent : public Event + { + protected: + /** how often the PIT fires */ + Tick interval; + /** The mode of the PIT */ + uint8_t mode; + /** The status of the PIT */ + uint8_t status; + + public: + /** + * Just set the mode to 0 + */ + ClockEvent(); + + /** + * processs the timer event + */ + virtual void process(); + + /** + * Returns a description of this event + * @return the description + */ + virtual const char *description(); + + /** + * Schedule a timer interrupt to occur sometime in the future. + */ + void Program(int count); + + /** + * Write the mode bits of the PIT. + * @param mode the new mode + */ + void ChangeMode(uint8_t mode); + + /** + * The current PIT status. + * @return the status of the PIT + */ + uint8_t Status(); + + }; + + /** + * Process RTC timer events and generate interrupts appropriately. + */ + class RTCEvent : public Event + { + protected: + /** A pointer back to tsunami to create interrupt the processor. */ + Tsunami* tsunami; + public: + /** RTC Event initializes the RTC event by scheduling an event + * RTC_RATE times pre second. */ + RTCEvent(Tsunami* t); + + /** + * Interrupth the processor and reschedule the event. + * */ + virtual void process(); + + /** + * Return a description of this event. + * @return a description + */ + virtual const char *description(); + }; + + /** uip UpdateInProgess says that the rtc is updating, but we just fake it + * by alternating it on every read of the bit since we are going to + * override the loop_per_jiffy time that it is trying to use the UIP to + * calculate. + */ + uint8_t uip; + + /** Mask of the PIC1 */ + uint8_t mask1; + + /** Mask of the PIC2 */ + uint8_t mask2; + + /** Mode of PIC1. Not used for anything */ + uint8_t mode1; + + /** Mode of PIC2. Not used for anything */ + uint8_t mode2; + + /** Raw PIC interrupt register before masking */ + uint8_t picr; //Raw PIC interrput register + + /** Is the pic interrupting right now or not. */ + bool picInterrupting; + + /** A pointer to the Tsunami device which be belong to */ + Tsunami *tsunami; + + /** + * This timer is initilized, but after I wrote the code + * it doesn't seem to be used again, and best I can tell + * it too is not connected to any interrupt port + */ + ClockEvent timer0; + + /** + * This timer is used to control the speaker, which + * we normally could care less about, however it is + * also used to calculated the clockspeed and hense + * bogomips which is kinda important to the scheduler + * so we need to implemnt it although after boot I can't + * imagine we would be playing with the PC speaker much + */ + ClockEvent timer2; + + /** This is the event used to interrupt the cpu like an RTC. */ + RTCEvent rtc; + + /** The interval is set via two writes to the PIT. + * This variable contains a flag as to how many writes have happened, and + * the time so far. + */ + uint32_t timerData; + + + public: + /** Return the freqency of the RTC */ + uint32_t frequency() const { return RTC_RATE; } + + + /** + * Initialize all the data for devices supported by Tsunami I/O. + * @param name name of this device. + * @param t pointer back to the Tsunami object that we belong to. + * @param init_time Time (as in seconds since 1970) to set RTC to. + * @param a address we are mapped at. + * @param mmu pointer to the memory controller that sends us events. + */ + TsunamiIO(const std::string &name, Tsunami *t, time_t init_time, + Addr a, MemoryController *mmu); + + /** + * Create the tm struct from seconds since 1970 + */ + void set_time(time_t t); + + /** + * Process a read to one of the devices we are emulating. + * @param req Contains the address to read from. + * @param data A pointer to write the read data to. + * @return The fault condition of the access. + */ + virtual Fault read(MemReqPtr &req, uint8_t *data); + + /** + * Process a write to one of the devices we emulate. + * @param req Contains the address to write to. + * @param data The data to write. + * @return The fault condition of the access. + */ + virtual Fault write(MemReqPtr &req, const uint8_t *data); + + /** + * Post an PIC interrupt to the CPU via the CChip + * @param bitvector interrupt to post. + */ + void postPIC(uint8_t bitvector); + + /** + * Clear a posted interrupt + * @param bitvector interrupt to clear + */ + void clearPIC(uint8_t bitvector); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +#endif // __TSUNAMI_IO_HH__ diff --git a/dev/tsunami_pchip.cc b/dev/tsunami_pchip.cc new file mode 100644 index 000000000..2ac3cae86 --- /dev/null +++ b/dev/tsunami_pchip.cc @@ -0,0 +1,317 @@ +/* $Id$ */ + +/* @file + * Tsunami PChip (pci) + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "cpu/exec_context.hh" +#include "dev/console.hh" +#include "dev/etherdev.hh" +#include "dev/scsi_ctrl.hh" +#include "dev/tlaser_clock.hh" +#include "dev/tsunami_pchip.hh" +#include "dev/tsunamireg.h" +#include "dev/tsunami.hh" +#include "mem/functional_mem/memory_control.hh" +#include "mem/functional_mem/physical_memory.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; + +TsunamiPChip::TsunamiPChip(const string &name, Tsunami *t, Addr a, + MemoryController *mmu) + : FunctionalMemory(name), addr(a), tsunami(t) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + + for (int i = 0; i < 4; i++) { + wsba[i] = 0; + wsm[i] = 0; + tba[i] = 0; + } + + //Set back pointer in tsunami + tsunami->pchip = this; +} + +Fault +TsunamiPChip::read(MemReqPtr &req, uint8_t *data) +{ + DPRINTF(Tsunami, "read va=%#x size=%d\n", + req->vaddr, req->size); + + Addr daddr = (req->paddr - (addr & PA_IMPL_MASK)) >> 6; + + switch (req->size) { + + case sizeof(uint64_t): + switch(daddr) { + case TSDEV_PC_WSBA0: + *(uint64_t*)data = wsba[0]; + return No_Fault; + case TSDEV_PC_WSBA1: + *(uint64_t*)data = wsba[1]; + return No_Fault; + case TSDEV_PC_WSBA2: + *(uint64_t*)data = wsba[2]; + return No_Fault; + case TSDEV_PC_WSBA3: + *(uint64_t*)data = wsba[3]; + return No_Fault; + case TSDEV_PC_WSM0: + *(uint64_t*)data = wsm[0]; + return No_Fault; + case TSDEV_PC_WSM1: + *(uint64_t*)data = wsm[1]; + return No_Fault; + case TSDEV_PC_WSM2: + *(uint64_t*)data = wsm[2]; + return No_Fault; + case TSDEV_PC_WSM3: + *(uint64_t*)data = wsm[3]; + return No_Fault; + case TSDEV_PC_TBA0: + *(uint64_t*)data = tba[0]; + return No_Fault; + case TSDEV_PC_TBA1: + *(uint64_t*)data = tba[1]; + return No_Fault; + case TSDEV_PC_TBA2: + *(uint64_t*)data = tba[2]; + return No_Fault; + case TSDEV_PC_TBA3: + *(uint64_t*)data = tba[3]; + return No_Fault; + case TSDEV_PC_PCTL: + // might want to change the clock?? + *(uint64_t*)data = 0x00; // try this + return No_Fault; + case TSDEV_PC_PLAT: + panic("PC_PLAT not implemented\n"); + case TSDEV_PC_RES: + panic("PC_RES not implemented\n"); + case TSDEV_PC_PERROR: + panic("PC_PERROR not implemented\n"); + case TSDEV_PC_PERRMASK: + panic("PC_PERRMASK not implemented\n"); + case TSDEV_PC_PERRSET: + panic("PC_PERRSET not implemented\n"); + case TSDEV_PC_TLBIV: + panic("PC_TLBIV not implemented\n"); + case TSDEV_PC_TLBIA: + *(uint64_t*)data = 0x00; // shouldn't be readable, but linux + return No_Fault; + case TSDEV_PC_PMONCTL: + panic("PC_PMONCTL not implemented\n"); + case TSDEV_PC_PMONCNT: + panic("PC_PMONCTN not implemented\n"); + default: + panic("Default in PChip Read reached reading 0x%x\n", daddr); + + } // uint64_t + + break; + case sizeof(uint32_t): + case sizeof(uint16_t): + case sizeof(uint8_t): + default: + panic("invalid access size(?) for tsunami register!\n\n"); + } + DPRINTFN("Tsunami PChip ERROR: read daddr=%#x size=%d\n", daddr, req->size); + + return No_Fault; +} + +Fault +TsunamiPChip::write(MemReqPtr &req, const uint8_t *data) +{ + DPRINTF(Tsunami, "write - va=%#x size=%d \n", + req->vaddr, req->size); + + Addr daddr = (req->paddr - (addr & PA_IMPL_MASK)) >> 6; + + switch (req->size) { + + case sizeof(uint64_t): + switch(daddr) { + case TSDEV_PC_WSBA0: + wsba[0] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_WSBA1: + wsba[1] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_WSBA2: + wsba[2] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_WSBA3: + wsba[3] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_WSM0: + wsm[0] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_WSM1: + wsm[1] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_WSM2: + wsm[2] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_WSM3: + wsm[3] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_TBA0: + tba[0] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_TBA1: + tba[1] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_TBA2: + tba[2] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_TBA3: + tba[3] = *(uint64_t*)data; + return No_Fault; + case TSDEV_PC_PCTL: + // might want to change the clock?? + //*(uint64_t*)data; // try this + return No_Fault; + case TSDEV_PC_PLAT: + panic("PC_PLAT not implemented\n"); + case TSDEV_PC_RES: + panic("PC_RES not implemented\n"); + case TSDEV_PC_PERROR: + panic("PC_PERROR not implemented\n"); + case TSDEV_PC_PERRMASK: + panic("PC_PERRMASK not implemented\n"); + case TSDEV_PC_PERRSET: + panic("PC_PERRSET not implemented\n"); + case TSDEV_PC_TLBIV: + panic("PC_TLBIV not implemented\n"); + case TSDEV_PC_TLBIA: + return No_Fault; // value ignored, supposted to invalidate SG TLB + case TSDEV_PC_PMONCTL: + panic("PC_PMONCTL not implemented\n"); + case TSDEV_PC_PMONCNT: + panic("PC_PMONCTN not implemented\n"); + default: + panic("Default in PChip Read reached reading 0x%x\n", daddr); + + } // uint64_t + + break; + case sizeof(uint32_t): + case sizeof(uint16_t): + case sizeof(uint8_t): + default: + panic("invalid access size(?) for tsunami register!\n\n"); + } + + DPRINTFN("Tsunami ERROR: write daddr=%#x size=%d\n", daddr, req->size); + + return No_Fault; +} + +#define DMA_ADDR_MASK ULL(0x3ffffffff) + +Addr +TsunamiPChip::translatePciToDma(Addr busAddr) +{ + // compare the address to the window base registers + uint64_t tbaMask = 0; + uint64_t baMask = 0; + + uint64_t windowMask = 0; + uint64_t windowBase = 0; + + uint64_t pteEntry = 0; + + Addr pteAddr; + Addr dmaAddr; + + for (int i = 0; i < 4; i++) { + windowBase = wsba[i]; + windowMask = ~wsm[i] & (0x7ff << 20); + + if ((busAddr & windowMask) == (windowBase & windowMask)) { + + + if (wsba[i] & 0x1) { // see if enabled + if (wsba[i] & 0x2) { // see if SG bit is set + /** @todo + This currently is faked by just doing a direct + read from memory, however, to be realistic, this + needs to actually do a bus transaction. The process + is explained in the tsunami documentation on page + 10-12 and basically munges the address to look up a + PTE from a table in memory and then uses that mapping + to create an address for the SG page + */ + + tbaMask = ~(((wsm[i] & (0x7ff << 20)) >> 10) | 0x3ff); + baMask = (wsm[i] & (0x7ff << 20)) | (0x7f << 13); + pteAddr = (tba[i] & tbaMask) | ((busAddr & baMask) >> 10); + + memcpy((void *)&pteEntry, + tsunami->system-> + physmem->dma_addr(pteAddr, sizeof(uint64_t)), + sizeof(uint64_t)); + + dmaAddr = ((pteEntry & ~0x1) << 12) | (busAddr & 0x1fff); + + } else { + baMask = (wsm[i] & (0x7ff << 20)) | 0xfffff; + tbaMask = ~baMask; + dmaAddr = (tba[i] & tbaMask) | (busAddr & baMask); + } + + return (dmaAddr & DMA_ADDR_MASK); + } + } + } + + return 0; +} + +void +TsunamiPChip::serialize(std::ostream &os) +{ + SERIALIZE_ARRAY(wsba, 4); + SERIALIZE_ARRAY(wsm, 4); + SERIALIZE_ARRAY(tba, 4); +} + +void +TsunamiPChip::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(wsba, 4); + UNSERIALIZE_ARRAY(wsm, 4); + UNSERIALIZE_ARRAY(tba, 4); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiPChip) + + SimObjectParam<Tsunami *> tsunami; + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + +END_DECLARE_SIM_OBJECT_PARAMS(TsunamiPChip) + +BEGIN_INIT_SIM_OBJECT_PARAMS(TsunamiPChip) + + INIT_PARAM(tsunami, "Tsunami"), + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(addr, "Device Address") + +END_INIT_SIM_OBJECT_PARAMS(TsunamiPChip) + +CREATE_SIM_OBJECT(TsunamiPChip) +{ + return new TsunamiPChip(getInstanceName(), tsunami, addr, mmu); +} + +REGISTER_SIM_OBJECT("TsunamiPChip", TsunamiPChip) diff --git a/dev/tsunami_pchip.hh b/dev/tsunami_pchip.hh new file mode 100644 index 000000000..44f145a60 --- /dev/null +++ b/dev/tsunami_pchip.hh @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * Tsunami PChip + */ + +#ifndef __TSUNAMI_PCHIP_HH__ +#define __TSUNAMI_PCHIP_HH__ + +#include "mem/functional_mem/functional_memory.hh" +#include "dev/tsunami.hh" + +/* + * Tsunami PChip + */ +class TsunamiPChip : public FunctionalMemory +{ + private: + /** The base address of this device */ + Addr addr; + + /** The size of mappad from the above address */ + static const Addr size = 0xfff; + + protected: + /** + * pointer to the tsunami object. + * This is our access to all the other tsunami + * devices. + */ + Tsunami *tsunami; + + /** Window Base addresses */ + uint64_t wsba[4]; + + /** Window masks */ + uint64_t wsm[4]; + + /** Translated Base Addresses */ + uint64_t tba[4]; + + public: + /** + * Register the PChip with the mmu and init all wsba, wsm, and tba to 0 + * @param name the name of thes device + * @param t a pointer to the tsunami device + * @param a the address which we respond to + * @param mmu the mmu we are to register with + */ + TsunamiPChip(const std::string &name, Tsunami *t, Addr a, + MemoryController *mmu); + + /** + * Translate a PCI bus address to a memory address for DMA. + * @todo Andrew says this needs to be fixed. What's wrong with it? + * @param busAddr PCI address to translate. + * @return memory system address + */ + Addr translatePciToDma(Addr busAddr); + + /** + * Process a read to the PChip. + * @param req Contains the address to read from. + * @param data A pointer to write the read data to. + * @return The fault condition of the access. + */ + virtual Fault read(MemReqPtr &req, uint8_t *data); + + /** + * Process a write to the PChip. + * @param req Contains the address to write to. + * @param data The data to write. + * @return The fault condition of the access. + */ + virtual Fault write(MemReqPtr &req, const uint8_t *data); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +#endif // __TSUNAMI_PCHIP_HH__ diff --git a/dev/tsunami_uart.cc b/dev/tsunami_uart.cc new file mode 100644 index 000000000..1eef24926 --- /dev/null +++ b/dev/tsunami_uart.cc @@ -0,0 +1,295 @@ +/* $Id$ */ + +/* @file + * Tsunami UART + */ + +/* + * Copyright (C) 1998 by the Board of Trustees + * of Leland Stanford Junior University. + * Copyright (C) 1998 Digital Equipment Corporation + * + * This file is part of the SimOS distribution. + * See LICENSE file for terms of the license. + * + */ + +#include <string> +#include <vector> + +#include "base/inifile.hh" +#include "base/str.hh" // for to_number +#include "base/trace.hh" +#include "dev/console.hh" +#include "dev/tsunami_uart.hh" +#include "mem/bus/bus.hh" +#include "mem/bus/pio_interface.hh" +#include "mem/bus/pio_interface_impl.hh" +#include "mem/functional_mem/memory_control.hh" +#include "sim/builder.hh" +#include "targetarch/ev5.hh" + +using namespace std; + +#define CONS_INT_TX 0x01 // interrupt enable / state bits +#define CONS_INT_RX 0x02 + + +TsunamiUart::IntrEvent::IntrEvent(TsunamiUart *u) + : Event(&mainEventQueue), uart(u) +{ + DPRINTF(TsunamiUart, "UART Interrupt Event Initilizing\n"); +} + +const char * +TsunamiUart::IntrEvent::description() +{ + return "tsunami uart interrupt delay event"; +} + +void +TsunamiUart::IntrEvent::process() +{ + if (UART_IER_THRI & uart->IER) { + DPRINTF(TsunamiUart, "UART InterEvent, interrupting\n"); + uart->cons->raiseInt(CONS_INT_TX); + } + else + DPRINTF(TsunamiUart, "UART InterEvent, not interrupting\n"); + +} + +void +TsunamiUart::IntrEvent::scheduleIntr() +{ + DPRINTF(TsunamiUart, "Scheduling IER interrupt\n"); + if (!scheduled()) + schedule(curTick + 300); + else + reschedule(curTick + 300); +} + + + +TsunamiUart::TsunamiUart(const string &name, SimConsole *c, + MemoryController *mmu, Addr a, + HierParams *hier, Bus *bus) + : PioDevice(name), addr(a), cons(c), status_store(0), valid_char(false), + intrEvent(this) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + + if (bus) { + pioInterface = newPioInterface(name, hier, bus, this, + &TsunamiUart::cacheAccess); + pioInterface->addAddrRange(addr, addr + size - 1); + } + + IER = 0; +} + +Fault +TsunamiUart::read(MemReqPtr &req, uint8_t *data) +{ + Addr daddr = req->paddr - (addr & PA_IMPL_MASK); + DPRINTF(TsunamiUart, " read register %#x\n", daddr); + + switch (req->size) { + case sizeof(uint64_t): + *(uint64_t *)data = 0; + break; + case sizeof(uint32_t): + *(uint32_t *)data = 0; + break; + case sizeof(uint16_t): + *(uint16_t *)data = 0; + break; + case sizeof(uint8_t): + *(uint8_t *)data = 0; + break; + } + + + switch(daddr) { + case 0x5: // Status Register + { + int status = cons->intStatus(); + if (!valid_char) { + valid_char = cons->in(next_char); + if (!valid_char) + status &= ~CONS_INT_RX; + } else { + status |= CONS_INT_RX; + } + + if (status_store == 3) { + // RR3 stuff? Don't really understand it, btw + status_store = 0; + if (status & CONS_INT_TX) { + *data = (1 << 4); + return No_Fault; + } else if (status & CONS_INT_RX) { + *data = (1 << 5); + return No_Fault; + } else { + DPRINTF(TsunamiUart, "spurious read\n"); + return No_Fault; + } + } else { + int reg = (1 << 2) | (1 << 5) | (1 << 6); + if (status & CONS_INT_RX) + reg |= (1 << 0); + *data = reg; + return No_Fault; + } + break; + } + + case 0x0: // Data register (RX) + DPRINTF(TsunamiUart, "read data register \'%c\' %#02x\n", + isprint(next_char) ? next_char : ' ', next_char); + + *data = next_char; + valid_char = false; + return No_Fault; + + case 0x1: // Interrupt Enable Register + // This is the lovely way linux checks there is actually a serial + // port at the desired address + if (IER == 0) + *data = 0; + else if (IER == 0x0F) + *data = 0x0F; + else + *data = 0; + return No_Fault; + case 0x2: + // High two bits need to be clear for an 8250 (simple) serial port + // Low bit of IIR is 0 for a pending interrupt, 1 otherwise. + int status = cons->intStatus(); + status = (status & 0x1) | (status >> 1); + *data = (~status) & 0x1 ; + return No_Fault; + } + *data = 0; + // panic("%s: read daddr=%#x type=read *data=%#x\n", name(), daddr, *data); + + return No_Fault; +} + +Fault +TsunamiUart::write(MemReqPtr &req, const uint8_t *data) +{ + Addr daddr = req->paddr - (addr & PA_IMPL_MASK); + + DPRINTF(TsunamiUart, " write register %#x value %#x\n", daddr, *(uint8_t*)data); + + switch (daddr) { + case 0x3: + status_store = *data; + switch (*data) { + case 0x03: // going to read RR3 + return No_Fault; + + case 0x28: // Ack of TX + { + if ((cons->intStatus() & CONS_INT_TX) == 0) + panic("Ack of transmit, though there was no interrupt"); + + cons->clearInt(CONS_INT_TX); + return No_Fault; + } + + case 0x00: + case 0x01: + case 0x12: + // going to write data??? + return No_Fault; + + default: + DPRINTF(TsunamiUart, "writing status register %#x \n", + *(uint8_t *)data); + return No_Fault; + } + + case 0x0: // Data register (TX) + char ourchar; + ourchar = *(uint64_t *)data; + if ((isprint(ourchar) || iscntrl(ourchar)) && (ourchar != 0x0C)) + cons->out(ourchar); + cons->clearInt(CONS_INT_TX); + intrEvent.scheduleIntr(); + return No_Fault; + break; + case 0x1: // IER + IER = *(uint8_t*)data; + DPRINTF(TsunamiUart, "writing to IER [%#x]\n", IER); + if (UART_IER_THRI & IER) + cons->raiseInt(CONS_INT_TX); + else { + cons->clearInt(CONS_INT_TX); + if (intrEvent.scheduled()) + intrEvent.deschedule(); + } + return No_Fault; + break; + case 0x4: // MCR + DPRINTF(TsunamiUart, "writing to MCR %#x\n", *(uint8_t*)data); + return No_Fault; + + } + + return No_Fault; +} + +Tick +TsunamiUart::cacheAccess(MemReqPtr &req) +{ + return curTick + 1000; +} + +void +TsunamiUart::serialize(ostream &os) +{ + SERIALIZE_SCALAR(status_store); + SERIALIZE_SCALAR(next_char); + SERIALIZE_SCALAR(valid_char); + SERIALIZE_SCALAR(IER); +} + +void +TsunamiUart::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(status_store); + UNSERIALIZE_SCALAR(next_char); + UNSERIALIZE_SCALAR(valid_char); + UNSERIALIZE_SCALAR(IER); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiUart) + + SimObjectParam<SimConsole *> console; + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + SimObjectParam<Bus*> io_bus; + SimObjectParam<HierParams *> hier; + + +END_DECLARE_SIM_OBJECT_PARAMS(TsunamiUart) + +BEGIN_INIT_SIM_OBJECT_PARAMS(TsunamiUart) + + INIT_PARAM(console, "The console"), + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(addr, "Device Address"), + INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to", NULL), + INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams) + +END_INIT_SIM_OBJECT_PARAMS(TsunamiUart) + +CREATE_SIM_OBJECT(TsunamiUart) +{ + return new TsunamiUart(getInstanceName(), console, mmu, addr, hier, io_bus); +} + +REGISTER_SIM_OBJECT("TsunamiUart", TsunamiUart) diff --git a/dev/tsunami_uart.hh b/dev/tsunami_uart.hh new file mode 100644 index 000000000..84e415067 --- /dev/null +++ b/dev/tsunami_uart.hh @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * Tsunami UART + */ + +#ifndef __TSUNAMI_UART_HH__ +#define __TSUNAMI_UART_HH__ + +#include "dev/tsunamireg.h" +#include "base/range.hh" +#include "dev/io_device.hh" + +class SimConsole; + +/* + * Tsunami UART + */ +class TsunamiUart : public PioDevice +{ + private: + Addr addr; + static const Addr size = 0x8; + + + protected: + SimConsole *cons; + int status_store; + uint8_t next_char; + bool valid_char; + uint8_t IER; + + class IntrEvent : public Event + { + protected: + TsunamiUart *uart; + public: + IntrEvent(TsunamiUart *u); + virtual void process(); + virtual const char *description(); + void scheduleIntr(); + }; + + IntrEvent intrEvent; + + public: + TsunamiUart(const string &name, SimConsole *c, MemoryController *mmu, + Addr a, HierParams *hier, Bus *bus); + + Fault read(MemReqPtr &req, uint8_t *data); + Fault write(MemReqPtr &req, const uint8_t *data); + + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + public: + Tick cacheAccess(MemReqPtr &req); +}; + +#endif // __TSUNAMI_UART_HH__ diff --git a/dev/tsunamireg.h b/dev/tsunamireg.h new file mode 100644 index 000000000..927dd60c9 --- /dev/null +++ b/dev/tsunamireg.h @@ -0,0 +1,120 @@ + +#ifndef __TSUNAMIREG_H__ +#define __TSUNAMIREG_H__ + +#define ALPHA_K0SEG_BASE 0xfffffc0000000000ULL + +// CChip Registers +#define TSDEV_CC_CSR 0x00 +#define TSDEV_CC_MTR 0x01 +#define TSDEV_CC_MISC 0x02 + +#define TSDEV_CC_AAR0 0x04 +#define TSDEV_CC_AAR1 0x05 +#define TSDEV_CC_AAR2 0x06 +#define TSDEV_CC_AAR3 0x07 +#define TSDEV_CC_DIM0 0x08 +#define TSDEV_CC_DIM1 0x09 +#define TSDEV_CC_DIR0 0x0A +#define TSDEV_CC_DIR1 0x0B +#define TSDEV_CC_DRIR 0x0C +#define TSDEV_CC_PRBEN 0x0D +#define TSDEV_CC_IIC0 0x0E +#define TSDEV_CC_IIC1 0x0F +#define TSDEV_CC_MPR0 0x10 +#define TSDEV_CC_MPR1 0x11 +#define TSDEV_CC_MPR2 0x12 +#define TSDEV_CC_MPR3 0x13 + +#define TSDEV_CC_DIM2 0x18 +#define TSDEV_CC_DIM3 0x19 +#define TSDEV_CC_DIR2 0x1A +#define TSDEV_CC_DIR3 0x1B +#define TSDEV_CC_IIC2 0x1C +#define TSDEV_CC_IIC3 0x1D + + +// PChip Registers +#define TSDEV_PC_WSBA0 0x00 +#define TSDEV_PC_WSBA1 0x01 +#define TSDEV_PC_WSBA2 0x02 +#define TSDEV_PC_WSBA3 0x03 +#define TSDEV_PC_WSM0 0x04 +#define TSDEV_PC_WSM1 0x05 +#define TSDEV_PC_WSM2 0x06 +#define TSDEV_PC_WSM3 0x07 +#define TSDEV_PC_TBA0 0x08 +#define TSDEV_PC_TBA1 0x09 +#define TSDEV_PC_TBA2 0x0A +#define TSDEV_PC_TBA3 0x0B +#define TSDEV_PC_PCTL 0x0C +#define TSDEV_PC_PLAT 0x0D +#define TSDEV_PC_RES 0x0E +#define TSDEV_PC_PERROR 0x0F +#define TSDEV_PC_PERRMASK 0x10 +#define TSDEV_PC_PERRSET 0x11 +#define TSDEV_PC_TLBIV 0x12 +#define TSDEV_PC_TLBIA 0x13 +#define TSDEV_PC_PMONCTL 0x14 +#define TSDEV_PC_PMONCNT 0x15 + +#define TSDEV_PC_SPST 0x20 + + +// DChip Registers +#define TSDEV_DC_DSC 0x20 +#define TSDEV_DC_STR 0x21 +#define TSDEV_DC_DREV 0x22 +#define TSDEV_DC_DSC2 0x23 + +// I/O Ports +#define TSDEV_PIC1_MASK 0x21 +#define TSDEV_PIC2_MASK 0xA1 +#define TSDEV_PIC1_ISR 0x20 +#define TSDEV_PIC2_ISR 0xA0 +#define TSDEV_PIC1_ACK 0x20 +#define TSDEV_PIC2_ACK 0xA0 +#define TSDEV_DMA1_RESET 0x0D +#define TSDEV_DMA2_RESET 0xDA +#define TSDEV_DMA1_MODE 0x0B +#define TSDEV_DMA2_MODE 0xD6 +#define TSDEV_DMA1_MASK 0x0A +#define TSDEV_DMA2_MASK 0xD4 +#define TSDEV_TMR_CTL 0x61 +#define TSDEV_TMR2_CTL 0x43 +#define TSDEV_TMR2_DATA 0x42 +#define TSDEV_TMR0_DATA 0x40 + +#define TSDEV_RTC_ADDR 0x70 +#define TSDEV_RTC_DATA 0x71 + +// RTC defines +#define RTC_SECOND 0 // second of minute [0..59] +#define RTC_SECOND_ALARM 1 // seconds to alarm +#define RTC_MINUTE 2 // minute of hour [0..59] +#define RTC_MINUTE_ALARM 3 // minutes to alarm +#define RTC_HOUR 4 // hour of day [0..23] +#define RTC_HOUR_ALARM 5 // hours to alarm +#define RTC_DAY_OF_WEEK 6 // day of week [1..7] +#define RTC_DAY_OF_MONTH 7 // day of month [1..31] +#define RTC_MONTH 8 // month of year [1..12] +#define RTC_YEAR 9 // year [00..99] +#define RTC_CONTROL_REGISTERA 10 // control register A +#define RTC_CONTROL_REGISTERB 11 // control register B +#define RTC_CONTROL_REGISTERC 12 // control register C +#define RTC_CONTROL_REGISTERD 13 // control register D +#define RTC_REGNUMBER_RTC_CR1 0x6A // control register 1 + +#define PCHIP_PCI0_MEMORY ULL(0x10000000000) +#define PCHIP_PCI0_IO ULL(0x101FC000000) +#define TSUNAMI_PCI0_MEMORY ALPHA_K0SEG_BASE + PCHIP_PCI0_MEMORY +#define TSUNAMI_PCI0_IO ALPHA_K0SEG_BASE + PCHIP_PCI0_IO + + +// UART Defines + + +#define UART_IER_THRI 0x02 +#define UART_IER_RLSI 0x04 + +#endif // __TSUNAMIREG_H__ diff --git a/kern/linux/linux.hh b/kern/linux/linux.hh new file mode 100644 index 000000000..a3cb94e91 --- /dev/null +++ b/kern/linux/linux.hh @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2003 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. + */ + +#ifndef __LINUX_HH__ +#define __LINUX_HH__ + +class Linux {}; + +#endif // __LINUX_HH__ diff --git a/kern/linux/linux_syscalls.cc b/kern/linux/linux_syscalls.cc new file mode 100644 index 000000000..d67725050 --- /dev/null +++ b/kern/linux/linux_syscalls.cc @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2003 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. + */ + +#include "kern/linux/linux_syscalls.hh" + +namespace { + const char * + standard_strings[SystemCalls<Linux>::Number] = { + + + "llseek", //0 + "newselect", //1 + "sysctl", //2 + "access", //3 + "acct", //4 + "adjtimex", //5 + "afs_syscall", //6 + "alarm", //7 + "bdflush", //8 + "break", //9 + + + "brk", //10 + "capget", //11 + "capset", //12 + "chdir", //13 + "chmod", //14 + "chown", //15 + "chown32", //16 + "chroot", //17 + "clock_getres", //18 + "clock_gettime", //19 + + + "clock_nanosleep", //20 + "clock_settime", //21 + "clone", //22 + "close", //23 + "creat", //24 + "create_module", //25 + "delete_module", //26 + "dup", //27 + "dup2", //28 + "epoll_create", //29 + + + "epoll_ctl", //30 + "epoll_wait", //31 + "execve", //32 + "exit", //33 + "exit_group", //34 + "fadvise64", //35 + "fadvise64_64", //36 + "fchdir", //37 + "fchmod", //38 + "fchown", //39 + + + "fchown32", //40 + "fcntl", //41 + "fcntl64", //42 + "fdatasync", //43 + "fgetxattr", //44 + "flistxattr", //45 + "flock", //46 + "fork", //47 + "fremovexattr", //48 + "fsetxattr", //49 + + + "fstat", //50 + "fstat64", //51 + "fstatfs", //52 + "fstatfs64", //53 + "fsync", //54 + "ftime", //55 + "ftruncate", //56 + "ftruncate64", //57 + "futex", //58 + "get_kernel_syms", //59 + + + "get_thread_area", //60 + "getcwd", //61 + "getdents", //62 + "getdents64", //63 + "getegid", //64 + "getegid32", //65 + "geteuid", //66 + "geteuid32", //67 + "getgid", //68 + "getgid32", //69 + + + "getgroups", //70 + "getgroups32", //71 + "getitimer", //72 + "getpgid", //73 + "getpgrp", //74 + "getpid", //75 + "getpmsg", //76 + "getppid", //77 + "getpriority", //78 + "getresgid", //79 + + + "getresgid32", //80 + "getresuid", //81 + "getresuid32", //82 + "getrlimit", //83 + "getrusage", //84 + "getsid", //85 + "gettid", //86 + "gettimeofday", //87 + "getuid", //88 + "getuid32", //89 + + + "getxattr", //90 + "gtty", //91 + "idle", //92 + "init_module", //93 + "io_cancel", //94 + "io_destroy", //95 + "io_getevents", //96 + "io_setup", //97 + "io_submit", //98 + "ioctl", //99 + + + "ioperm", //100 + "iopl", //101 + "ipc", //102 + "kill", //103 + "lchown", //104 + "lchown32", //105 + "lgetxattr", //106 + "link", //107 + "listxattr", //108 + "llistxattr", //109 + + + "lock", //110 + "lookup_dcookie", //111 + "lremovexattr", //112 + "lseek", //113 + "lsetxattr", //114 + "lstat", //115 + "lstat64", //116 + "madvise", //117 + "madvise1", //118 + "mincore", //119 + + + "mkdir", //120 + "mknod", //121 + "mlock", //122 + "mlockall", //123 + "mmap", //124 + "mmap2", //125 + "modify_ldt", //126 + "mount", //127 + "mprotect", //128 + "mpx", //129 + + + "mremap", //130 + "msync", //131 + "munlock", //132 + "munlockall", //133 + "munmap", //134 + "nanosleep", //135 + "nfsservctl", //136 + "nice", //137 + "oldfstat", //138 + "oldlstat", //139 + + + "oldolduname", //140 + "oldstat", //141 + "olduname", //142 + "open", //143 + "pause", //144 + "personality", //145 + "pipe", //146 + "pivot_root", //147 + "poll", //148 + "prctl", //149 + + + "pread64", //150 + "prof", //151 + "profil", //152 + "ptrace", //153 + "putpmsg", //154 + "pwrite64", //155 + "query_module", //156 + "quotactl", //157 + "read", //158 + "readahead", //159 + + + "readdir", //160 + "readlink", //161 + "readv", //162 + "reboot", //163 + "remap_file_pages", //164 + "removexattr", //165 + "rename", //166 + "restart_syscall", //167 + "rmdir", //168 + "rt_sigaction", //169 + + + "rt_sigpending", //170 + "rt_sigprocmask", //171 + "rt_sigqueueinfo", //172 + "rt_sigreturn", //173 + "rt_sigsuspend", //174 + "rt_sigtimedwait", //175 + "sched_get_priority_max", //176 + "sched_get_priority_min", //177 + "sched_getaffinity", //178 + "sched_getparam", //179 + + + "sched_getscheduler", //180 + "sched_rr_get_interval", //181 + "sched_setaffinity", //182 + "sched_setparam", //183 + "sched_setscheduler", //184 + "sched_yield", //185 + "select", //186 + "sendfile", //187 + "sendfile64", //188 + "set_thread_area", //189 + + + "set_tid_address", //190 + "setdomainname", //191 + "setfsgid", //192 + "setfsgid32", //193 + "setfsuid", //194 + "setfsuid32", //195 + "setgid", //196 + "setgid32", //197 + "setgroups", //198 + "setgroups32", //199 + + + "sethostname", //200 + "setitimer", //201 + "setpgid", //202 + "setpriority", //203 + "setregid", //204 + "setregid32", //205 + "setresgid", //206 + "setresgid32", //207 + "setresuid", //208 + "setresuid32", //209 + + + "setreuid", //210 + "setreuid32", //211 + "setrlimit", //212 + "setsid", //213 + "settimeofday", //214 + "setuid", //215 + "setuid32", //216 + "setxattr", //217 + "sgetmask", //218 + "sigaction", //219 + + + "sigaltstack", //220 + "signal", //221 + "sigpending", //222 + "sigprocmask", //223 + "sigreturn", //224 + "sigsuspend", //225 + "socketcall", //226 + "ssetmask", //227 + "stat", //228 + "stat64", //229 + + + "statfs", //230 + "statfs64", //231 + "stime", //232 + "stty", //233 + "swapoff", //234 + "swapon", //235 + "symlink", //236 + "sync", //237 + "sysfs", //238 + "sysinfo", //239 + + + "syslog", //240 + "tgkill", //241 + "time", //242 + "timer_create", //243 + "timer_delete", //244 + "timer_getoverrun", //245 + "timer_gettime", //246 + "timer_settime", //247 + "times", //248 + "tkill", //249 + + + "truncate", //250 + "truncate64", //251 + "ugetrlimit", //252 + "ulimit", //253 + "umask", //254 + "umount", //255 + "umount2", //256 + "uname", //257 + "unlink", //258 + "uselib", //259 + + + "ustat", //260 + "utime", //261 + "utimes", //262 + "vfork", //263 + "vhangup", //264 + "vm86", //265 + "vm86old", //266 + "vserver", //267 + "wait4", //268 + "waitpid", //269 + + + "write", //270 + "writev", //271 + }; + + +} + +const char * +SystemCalls<Linux>::name(int num) +{ + if ((num >= 0) && (num < Number)) + return standard_strings[num]; + else + return 0; +} diff --git a/kern/linux/linux_syscalls.hh b/kern/linux/linux_syscalls.hh new file mode 100644 index 000000000..86ccdd12d --- /dev/null +++ b/kern/linux/linux_syscalls.hh @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2003 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. + */ + +#ifndef __LINUX_SYSCALLS_HH__ +#define __LINUX_SYSCALLS_HH__ + +#include "targetarch/syscalls.hh" +#include "kern/linux/linux.hh" + +struct SystemCalls<Linux> +{ + enum { + syscall = 0, + llseek = 1, + newselect = 2, + sysctl = 3, + access = 4, + acct = 5, + adjtimex = 6, + afs_syscall = 7, + alarm = 8, + bdflush = 9, + _break = 10, /*renamed from break*/ + brk = 11, + capget = 12, + capset = 13, + chdir = 14, + chmod = 15, + chown = 16, + chown32 = 17, + chroot = 18, + clock_getres = 19, + clock_gettime = 20, + clock_nanosleep = 21, + clock_settime = 22, + clone = 23, + close = 24, + creat = 25, + create_module = 26, + delete_module = 27, + dup = 28, + dup2 = 29, + epoll_create = 30, + epoll_ctl = 31, + epoll_wait = 32, + execve = 33, + exit = 34, + exit_group = 35, + fadvise64 = 36, + fadvise64_64 = 37, + fchdir = 38, + fchmod = 39, + fchown = 40, + fchown32 = 41, + fcntl = 42, + fcntl64 = 43, + fdatasync = 44, + fgetxattr = 45, + flistxattr = 46, + flock = 47, + fork = 48, + fremovexattr = 49, + fsetxattr = 50, + fstat = 51, + fstat64 = 52, + fstatfs = 53, + fstatfs64 = 54, + fsync = 55, + ftime = 56, + ftruncate = 57, + ftruncate64 = 58, + futex = 59, + get_kernel_syms = 60, + get_thread_area = 61, + getcwd = 62, + getdents = 63, + getdents64 = 64, + getegid = 65, + getegid32 = 66, + geteuid = 67, + geteuid32 = 68, + getgid = 69, + getgid32 = 70, + getgroups = 71, + getgroups32 = 72, + getitimer = 73, + getpgid = 74, + getpgrp = 75, + getpid = 76, + getpmsg = 77, + getppid = 78, + getpriority = 79, + getresgid = 80, + getresgid32 = 81, + getresuid = 82, + getresuid32 = 83, + getrlimit = 84, + getrusage = 85, + getsid = 86, + gettid = 87, + gettimeofday = 88, + getuid = 89, + getuid32 = 90, + getxattr = 91, + gtty = 92, + idle = 93, + init_module = 94, + io_cancel = 95, + io_destroy = 96, + io_getevents = 97, + io_setup = 98, + io_submit = 99, + ioctl = 100, + ioperm = 101, + iopl = 102, + ipc = 103, + kill = 104, + lchown = 105, + lchown32 = 106, + lgetxattr = 107, + link = 108, + listxattr = 109, + llistxattr = 110, + lock = 111, + lookup_dcookie = 112, + lremovexattr = 113, + lseek = 114, + lsetxattr = 115, + lstat = 116, + lstat64 = 117, + madvise = 118, + madvise1 = 119, + mincore = 120, + mkdir = 121, + mknod = 122, + mlock = 123, + mlockall = 124, + mmap = 125, + mmap2 = 126, + modify_ldt = 127, + mount = 128, + mprotect = 129, + mpx = 130, + mremap = 131, + msync = 132, + munlock = 133, + munlockall = 134, + munmap = 135, + nanosleep = 136, + nfsservctl = 137, + nice = 138, + oldfstat = 139, + oldlstat = 140, + oldolduname = 141, + oldstat = 142, + olduname = 143, + open = 144, + pause = 145, + personality = 146, + pipe = 147, + pivot_root = 148, + poll = 149, + prctl = 150, + pread64 = 151, + prof = 152, + profil = 153, + ptrace = 154, + putpmsg = 155, + pwrite64 = 156, + query_module = 157, + quotactl = 158, + read = 159, + readahead = 160, + readdir = 161, + readlink = 162, + readv = 163, + reboot = 164, + remap_file_pages = 165, + removexattr = 166, + rename = 167, + restart_syscall = 168, + rmdir = 169, + rt_sigaction = 170, + rt_sigpending = 171, + rt_sigprocmask = 172, + rt_sigqueueinfo = 173, + rt_sigreturn = 174, + rt_sigsuspend = 175, + rt_sigtimedwait = 176, + sched_get_priority_max = 177, + sched_get_priority_min = 178, + sched_getaffinity = 179, + sched_getparam = 180, + sched_getscheduler = 181, + sched_rr_get_interval = 182, + sched_setaffinity = 183, + sched_setparam = 184, + sched_setscheduler = 185, + sched_yield = 186, + select = 187, + sendfile = 188, + sendfile64 = 189, + set_thread_area = 190, + set_tid_address = 191, + setdomainname = 192, + setfsgid = 193, + setfsgid32 = 194, + setfsuid = 195, + setfsuid32 = 196, + setgid = 197, + setgid32 = 198, + setgroups = 199, + setgroups32 = 200, + sethostname = 201, + setitimer = 202, + setpgid = 203, + setpriority = 204, + setregid = 205, + setregid32 = 206, + setresgid = 207, + setresgid32 = 208, + setresuid = 209, + setresuid32 = 210, + setreuid = 211, + setreuid32 = 212, + setrlimit = 213, + setsid = 214, + settimeofday = 215, + setuid = 216, + setuid32 = 217, + setxattr = 218, + sgetmask = 219, + sigaction = 220, + sigaltstack = 221, + signal = 222, + sigpending = 223, + sigprocmask = 224, + sigreturn = 225, + sigsuspend = 226, + socketcall = 227, + ssetmask = 228, + stat = 229, + stat64 = 230, + statfs = 231, + statfs64 = 232, + stime = 233, + stty = 234, + swapoff = 235, + swapon = 236, + symlink = 237, + sync = 238, + sysfs = 239, + sysinfo = 240, + syslog = 241, + tgkill = 242, + time = 243, + timer_create = 244, + timer_delete = 245, + timer_getoverrun = 246, + timer_gettime = 247, + timer_settime = 248, + times = 249, + tkill = 250, + truncate = 251, + truncate64 = 252, + ugetrlimit = 253, + ulimit = 254, + umask = 255, + umount = 256, + umount2 = 257, + uname = 258, + unlink = 259, + uselib = 260, + ustat = 261, + utime = 262, + utimes = 263, + vfork = 264, + vhangup = 265, + vm86 = 266, + vm86old = 267, + vserver = 268, + wait4 = 269, + waitpid = 270, + write = 271, + writev = 272, + Number + }; + + static const char *name(int num); + + static bool validSyscallNumber(int num) { + return num < Number; + } + +}; + +#endif // __LINUX_SYSCALLS_HH__ diff --git a/kern/linux/linux_system.cc b/kern/linux/linux_system.cc new file mode 100644 index 000000000..89688772c --- /dev/null +++ b/kern/linux/linux_system.cc @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2003 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. + */ + +/** + * @file + * linux_system.cc loads the linux kernel, console, pal and patches certain functions. + * The symbol tables are loaded so that traces can show the executing function and we can + * skip functions. Various delay loops are skipped and their final values manually computed to + * speed up boot time. + */ + +#include "base/loader/aout_object.hh" +#include "base/loader/elf_object.hh" +#include "base/loader/object_file.hh" +#include "base/loader/symtab.hh" +#include "base/remote_gdb.hh" +#include "base/trace.hh" +#include "cpu/exec_context.hh" +#include "cpu/base_cpu.hh" +#include "kern/linux/linux_events.hh" +#include "kern/linux/linux_system.hh" +#include "kern/system_events.hh" +#include "mem/functional_mem/memory_control.hh" +#include "mem/functional_mem/physical_memory.hh" +#include "sim/builder.hh" +#include "dev/platform.hh" +#include "targetarch/isa_traits.hh" +#include "targetarch/vtophys.hh" + +extern SymbolTable *debugSymbolTable; + +using namespace std; + +LinuxSystem::LinuxSystem(const string _name, const uint64_t _init_param, + MemoryController *_memCtrl, PhysicalMemory *_physmem, + const string &kernel_path, const string &console_path, + const string &palcode, const string &boot_osflags, + const bool _bin, const vector<string> &_binned_fns) + : System(_name, _init_param, _memCtrl, _physmem, _bin, _binned_fns), + bin(_bin), binned_fns(_binned_fns) +{ + kernelSymtab = new SymbolTable; + consoleSymtab = new SymbolTable; + + /** + * Load the kernel, pal, and console code into memory + */ + // Load kernel code + ObjectFile *kernel = createObjectFile(kernel_path); + if (kernel == NULL) + fatal("Could not load kernel file %s", kernel_path); + + // Load Console Code + ObjectFile *console = createObjectFile(console_path); + if (console == NULL) + fatal("Could not load console file %s", console_path); + + // Load pal file + ObjectFile *pal = createObjectFile(palcode); + if (pal == NULL) + fatal("Could not load PALcode file %s", palcode); + pal->loadSections(physmem, true); + + // Load console file + console->loadSections(physmem, true); + + // Load kernel file + kernel->loadSections(physmem, true); + kernelStart = kernel->textBase(); + kernelEnd = kernel->bssBase() + kernel->bssSize(); + kernelEntry = kernel->entryPoint(); + + // load symbols + if (!kernel->loadGlobalSymbols(kernelSymtab)) + panic("could not load kernel symbols\n"); + debugSymbolTable = kernelSymtab; + + if (!kernel->loadLocalSymbols(kernelSymtab)) + panic("could not load kernel local symbols\n"); + + if (!console->loadGlobalSymbols(consoleSymtab)) + panic("could not load console symbols\n"); + + DPRINTF(Loader, "Kernel start = %#x\n" + "Kernel end = %#x\n" + "Kernel entry = %#x\n", + kernelStart, kernelEnd, kernelEntry); + + DPRINTF(Loader, "Kernel loaded...\n"); + + +#ifdef DEBUG + kernelPanicEvent = new BreakPCEvent(&pcEventQueue, "kernel panic"); + consolePanicEvent = new BreakPCEvent(&pcEventQueue, "console panic"); +#endif + + skipIdeDelay50msEvent = new SkipFuncEvent(&pcEventQueue, + "ide_delay_50ms"); + + skipDelayLoopEvent = new LinuxSkipDelayLoopEvent(&pcEventQueue, + "calibrate_delay"); + + skipCacheProbeEvent = new SkipFuncEvent(&pcEventQueue, + "determine_cpu_caches"); + + Addr addr = 0; + + /** + * find the address of the est_cycle_freq variable and insert it so we don't + * through the lengthly process of trying to calculated it by using the PIT, + * RTC, etc. + */ + if (kernelSymtab->findAddress("est_cycle_freq", addr)) { + Addr paddr = vtophys(physmem, addr); + uint8_t *est_cycle_frequency = + physmem->dma_addr(paddr, sizeof(uint64_t)); + + if (est_cycle_frequency) + *(uint64_t *)est_cycle_frequency = ticksPerSecond; + } + + + /** + * Copy the osflags (kernel arguments) into the consoles memory. Presently + * Linux does use the console service routine to get these command line + * arguments, but we might as well make them available just in case. + */ + if (consoleSymtab->findAddress("env_booted_osflags", addr)) { + Addr paddr = vtophys(physmem, addr); + char *osflags = (char *)physmem->dma_addr(paddr, sizeof(uint32_t)); + + if (osflags) + strcpy(osflags, boot_osflags.c_str()); + } + + /** + * Since we aren't using a bootloader, we have to copy the kernel arguments + * directly into the kernels memory. + */ + { + Addr paddr = vtophys(physmem, PARAM_ADDR); + char *commandline = (char*)physmem->dma_addr(paddr, sizeof(uint64_t)); + if (commandline) + strcpy(commandline, boot_osflags.c_str()); + } + + + /** + * Set the hardware reset parameter block system type and revision information + * to Tsunami. + */ + if (consoleSymtab->findAddress("xxm_rpb", addr)) { + Addr paddr = vtophys(physmem, addr); + char *hwprb = (char *)physmem->dma_addr(paddr, sizeof(uint64_t)); + + if (hwprb) { + *(uint64_t*)(hwprb+0x50) = 34; // Tsunami + *(uint64_t*)(hwprb+0x58) = (1<<10); // Plain DP264 + } + else + panic("could not translate hwprb addr to set system type/variation\n"); + + } else + panic("could not find hwprb to set system type/variation\n"); + +#ifdef DEBUG + if (kernelSymtab->findAddress("panic", addr)) + kernelPanicEvent->schedule(addr); + else + panic("could not find kernel symbol \'panic\'"); + + if (consoleSymtab->findAddress("panic", addr)) + consolePanicEvent->schedule(addr); +#endif + + /** + * Any time ide_delay_50ms, calibarte_delay or determine_cpu_caches is called + * just skip the function. Currently determine_cpu_caches only is used put + * information in proc, however if that changes in the future we will have to + * fill in the cache size variables appropriately. + */ + if (kernelSymtab->findAddress("ide_delay_50ms", addr)) + skipIdeDelay50msEvent->schedule(addr+sizeof(MachInst)); + + if (kernelSymtab->findAddress("calibrate_delay", addr)) + skipDelayLoopEvent->schedule(addr+sizeof(MachInst)); + + if (kernelSymtab->findAddress("determine_cpu_caches", addr)) + skipCacheProbeEvent->schedule(addr+sizeof(MachInst)); +} + +LinuxSystem::~LinuxSystem() +{ + delete kernel; + delete console; + + delete kernelSymtab; + delete consoleSymtab; + + delete kernelPanicEvent; + delete consolePanicEvent; + delete skipIdeDelay50msEvent; + delete skipDelayLoopEvent; + delete skipCacheProbeEvent; +} + + +void +LinuxSystem::setDelayLoop(ExecContext *xc) +{ + Addr addr = 0; + if (kernelSymtab->findAddress("loops_per_jiffy", addr)) { + Addr paddr = vtophys(physmem, addr); + + uint8_t *loops_per_jiffy = + physmem->dma_addr(paddr, sizeof(uint32_t)); + + Tick cpuFreq = xc->cpu->getFreq(); + Tick intrFreq = platform->interrupt_frequency; + *(uint32_t *)loops_per_jiffy = + (uint32_t)((cpuFreq / intrFreq) * 0.9988); + } +} + +int +LinuxSystem::registerExecContext(ExecContext *xc) +{ + int xcIndex = System::registerExecContext(xc); + + if (xcIndex == 0) { + // activate with zero delay so that we start ticking right + // away on cycle 0 + xc->activate(0); + } + + RemoteGDB *rgdb = new RemoteGDB(this, xc); + GDBListener *gdbl = new GDBListener(rgdb, 7000 + xcIndex); + gdbl->listen(); + /** + * Uncommenting this line waits for a remote debugger to connect + * to the simulator before continuing. + */ + //gdbl->accept(); + + if (remoteGDB.size() <= xcIndex) { + remoteGDB.resize(xcIndex+1); + } + + remoteGDB[xcIndex] = rgdb; + + return xcIndex; +} + + +void +LinuxSystem::replaceExecContext(ExecContext *xc, int xcIndex) +{ + System::replaceExecContext(xcIndex, xc); + remoteGDB[xcIndex]->replaceExecContext(xc); +} + +bool +LinuxSystem::breakpoint() +{ + return remoteGDB[0]->trap(ALPHA_KENTRY_IF); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(LinuxSystem) + + Param<bool> bin; + SimObjectParam<MemoryController *> mem_ctl; + SimObjectParam<PhysicalMemory *> physmem; + Param<uint64_t> init_param; + + Param<string> kernel_code; + Param<string> console_code; + Param<string> pal_code; + Param<string> boot_osflags; + VectorParam<string> binned_fns; + +END_DECLARE_SIM_OBJECT_PARAMS(LinuxSystem) + +BEGIN_INIT_SIM_OBJECT_PARAMS(LinuxSystem) + + + INIT_PARAM_DFLT(bin, "is this system to be binned", false), + INIT_PARAM(mem_ctl, "memory controller"), + INIT_PARAM(physmem, "phsyical memory"), + INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), + INIT_PARAM(kernel_code, "file that contains the code"), + INIT_PARAM(console_code, "file that contains the console code"), + INIT_PARAM(pal_code, "file that contains palcode"), + INIT_PARAM_DFLT(boot_osflags, "flags to pass to the kernel during boot", + "a"), + INIT_PARAM(binned_fns, "functions to be broken down and binned") + + +END_INIT_SIM_OBJECT_PARAMS(LinuxSystem) + +CREATE_SIM_OBJECT(LinuxSystem) +{ + LinuxSystem *sys = new LinuxSystem(getInstanceName(), init_param, mem_ctl, + physmem, kernel_code, console_code, + pal_code, boot_osflags, bin, binned_fns); + + return sys; +} + +REGISTER_SIM_OBJECT("LinuxSystem", LinuxSystem) diff --git a/kern/linux/linux_system.hh b/kern/linux/linux_system.hh new file mode 100644 index 000000000..83775cb92 --- /dev/null +++ b/kern/linux/linux_system.hh @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2003 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. + */ + +#ifndef __LINUX_SYSTEM_HH__ +#define __LINUX_SYSTEM_HH__ + +#include <vector> + +#include "sim/system.hh" +#include "sim/host.hh" +#include "targetarch/isa_traits.hh" + +#include <map> + +/** + * MAGIC address where the kernel arguments should go. Defined as + * PARAM in linux kernel alpha-asm. + */ +const Addr PARAM_ADDR = ULL(0xfffffc000030a000); + +class ExecContext; +class ElfObject; +class SymbolTable; + +class BreakPCEvent; +class LinuxSkipDelayLoopEvent; +class SkipFuncEvent; +class FnEvent; +class AlphaArguments; + +/** + * This class contains linux specific system code (Loading, Events, Binning). + * It points to objects that are the system binaries to load and patches them + * appropriately to work in simulator. + */ +class LinuxSystem : public System +{ + private: + /** Object pointer for the kernel code */ + ElfObject *kernel; + + /** Object pointer for the console code */ + ElfObject *console; + + /** kernel Symbol table */ + SymbolTable *kernelSymtab; + + /** console symbol table */ + SymbolTable *consoleSymtab; + + /** Event to halt the simulator if the kernel calls panic() */ + BreakPCEvent *kernelPanicEvent; + + /** Event to halt the simulator if the console calls panic() */ + BreakPCEvent *consolePanicEvent; + + /** Event to skip determine_cpu_caches() because we don't support the + * IPRs that the code can access to figure out cache sizes + */ + SkipFuncEvent *skipCacheProbeEvent; + + /** PC based event to skip the ide_delay_50ms() call */ + SkipFuncEvent *skipIdeDelay50msEvent; + + /** Skip calculate_delay_loop() rather than waiting for this to be + * calculated + */ + LinuxSkipDelayLoopEvent *skipDelayLoopEvent; + + /** Begining of kernel code */ + Addr kernelStart; + + /** End of kernel code */ + Addr kernelEnd; + + /** Entry point in the kernel to start at */ + Addr kernelEntry; + + bool bin; + std::vector<string> binned_fns; + + public: + std::vector<RemoteGDB *> remoteGDB; + std::vector<GDBListener *> gdbListen; + + LinuxSystem(const std::string _name, + const uint64_t _init_param, + MemoryController *_memCtrl, + PhysicalMemory *_physmem, + const std::string &kernel_path, + const std::string &console_path, + const std::string &palcode, + const std::string &boot_osflags, + const bool _bin, + const std::vector<std::string> &_binned_fns); + + ~LinuxSystem(); + + void setDelayLoop(ExecContext *xc); + + int registerExecContext(ExecContext *xc); + void replaceExecContext(ExecContext *xc, int xcIndex); + + /** + * Returns the addess the kernel starts at. + * @return address the kernel starts at + */ + Addr getKernelStart() const { return kernelStart; } + + /** + * Returns the addess the kernel ends at. + * @return address the kernel ends at + */ + Addr getKernelEnd() const { return kernelEnd; } + + /** + * Returns the addess the entry point to the kernel code. + * @return entry point of the kernel code + */ + Addr getKernelEntry() const { return kernelEntry; } + + + bool breakpoint(); +}; + +#endif // __LINUX_SYSTEM_HH__ diff --git a/kern/tru64/tru64_system.cc b/kern/tru64/tru64_system.cc index 0717bcbbe..c02b4f684 100644 --- a/kern/tru64/tru64_system.cc +++ b/kern/tru64/tru64_system.cc @@ -42,18 +42,22 @@ #include "targetarch/isa_traits.hh" #include "targetarch/vtophys.hh" +extern SymbolTable *debugSymbolTable; + using namespace std; Tru64System::Tru64System(const string _name, const uint64_t _init_param, MemoryController *_memCtrl, PhysicalMemory *_physmem, const string &kernel_path, const string &console_path, const string &palcode, const string &boot_osflags, - const bool _bin, const vector<string> &binned_fns) - : System(_name, _init_param, _memCtrl, _physmem, _bin, binned_fns), - bin(_bin), binned_fns(binned_fns) + const bool _bin, const vector<string> &_binned_fns, + const uint64_t system_type, const uint64_t system_rev) + : System(_name, _init_param, _memCtrl, _physmem, _bin,_binned_fns), + bin(_bin), binned_fns(_binned_fns) { kernelSymtab = new SymbolTable; consoleSymtab = new SymbolTable; + debugSymbolTable = kernelSymtab; ObjectFile *kernel = createObjectFile(kernel_path); if (kernel == NULL) @@ -125,6 +129,20 @@ Tru64System::Tru64System(const string _name, const uint64_t _init_param, strcpy(osflags, boot_osflags.c_str()); } + if (consoleSymtab->findAddress("xxm_rpb", addr)) { + Addr paddr = vtophys(physmem, addr); + char *hwprb = (char *)physmem->dma_addr(paddr, sizeof(uint64_t)); + + if (hwprb) { + *(uint64_t*)(hwprb+0x50) = system_type; + *(uint64_t*)(hwprb+0x58) = system_rev; + } + else + panic("could not translate hwprb addr to set system type/variation\n"); + } else + panic("could not find hwprb to set system type/variation\n"); + + #ifdef DEBUG if (kernelSymtab->findAddress("panic", addr)) kernelPanicEvent->schedule(addr); @@ -246,6 +264,8 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Tru64System) Param<string> pal_code; Param<string> boot_osflags; VectorParam<string> binned_fns; + Param<uint64_t> system_type; + Param<uint64_t> system_rev; END_DECLARE_SIM_OBJECT_PARAMS(Tru64System) @@ -260,7 +280,10 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(Tru64System) INIT_PARAM(pal_code, "file that contains palcode"), INIT_PARAM_DFLT(boot_osflags, "flags to pass to the kernel during boot", "a"), - INIT_PARAM(binned_fns, "functions to be broken down and binned") + INIT_PARAM(binned_fns, "functions to be broken down and binned"), + INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 12), + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 2<<1) + END_INIT_SIM_OBJECT_PARAMS(Tru64System) @@ -269,7 +292,7 @@ CREATE_SIM_OBJECT(Tru64System) Tru64System *sys = new Tru64System(getInstanceName(), init_param, mem_ctl, physmem, kernel_code, console_code, pal_code, boot_osflags, bin, - binned_fns); + binned_fns, system_type, system_rev); return sys; } diff --git a/kern/tru64/tru64_system.hh b/kern/tru64/tru64_system.hh index 144febbf9..b97f7760c 100644 --- a/kern/tru64/tru64_system.hh +++ b/kern/tru64/tru64_system.hh @@ -89,7 +89,9 @@ class Tru64System : public System const std::string &palcode, const std::string &boot_osflags, const bool _bin, - const std::vector<string> &binned_fns); + const std::vector<string> &binned_fns, + const uint64_t system_type, + const uint64_t system_rev); ~Tru64System(); int registerExecContext(ExecContext *xc); diff --git a/sim/sim_object.cc b/sim/sim_object.cc index 7f756858c..96c0b197e 100644 --- a/sim/sim_object.cc +++ b/sim/sim_object.cc @@ -36,6 +36,7 @@ #include "sim/host.hh" #include "sim/sim_object.hh" #include "sim/stats.hh" +#include "sim/param.hh" using namespace std; @@ -168,3 +169,5 @@ SimObject::serializeAll(ostream &os) obj->serialize(os); } } + +DEFINE_SIM_OBJECT_CLASS_NAME("SimObject", SimObject) diff --git a/sim/system.cc b/sim/system.cc index 619593abd..b801cb254 100644 --- a/sim/system.cc +++ b/sim/system.cc @@ -52,6 +52,9 @@ System::System(const std::string _name, bin(_bin), binned_fns(binned_fns) { + // increment the number of running systems + numSystemsRunning++; + // add self to global system list systemList.push_back(this); if (bin == true) { diff --git a/sim/system.hh b/sim/system.hh index b83945884..f9909019b 100644 --- a/sim/system.hh +++ b/sim/system.hh @@ -41,6 +41,7 @@ class MemoryController; class PhysicalMemory; +class Platform; class RemoteGDB; class GDBListener; @@ -87,6 +88,7 @@ class System : public SimObject const uint64_t init_param; MemoryController *memCtrl; PhysicalMemory *physmem; + Platform *platform; bool bin; std::vector<string> binned_fns; |