diff options
Diffstat (limited to 'dev')
49 files changed, 10171 insertions, 202 deletions
diff --git a/dev/alpha_console.cc b/dev/alpha_console.cc index 04046557a..680704b30 100644 --- a/dev/alpha_console.cc +++ b/dev/alpha_console.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2001-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,7 +40,7 @@ #include "cpu/base_cpu.hh" #include "cpu/exec_context.hh" #include "dev/alpha_console.hh" -#include "dev/console.hh" +#include "dev/simconsole.hh" #include "dev/simple_disk.hh" #include "dev/tlaser_clock.hh" #include "mem/bus/bus.hh" @@ -49,11 +49,14 @@ #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" +#include "targetarch/byte_swap.hh" using namespace std; AlphaConsole::AlphaConsole(const string &name, SimConsole *cons, SimpleDisk *d, - System *system, BaseCPU *cpu, TlaserClock *clock, + System *system, BaseCPU *cpu, Platform *platform, int num_cpus, MemoryController *mmu, Addr a, HierParams *hier, Bus *bus) : PioDevice(name), disk(d), console(cons), addr(a) @@ -66,9 +69,7 @@ AlphaConsole::AlphaConsole(const string &name, SimConsole *cons, SimpleDisk *d, pioInterface->addAddrRange(addr, addr + size); } - consoleData = new uint8_t[size]; - memset(consoleData, 0, size); - + alphaAccess = new AlphaAccess; alphaAccess->last_offset = size - 1; alphaAccess->kernStart = system->getKernelStart(); alphaAccess->kernEnd = system->getKernelEnd(); @@ -78,45 +79,105 @@ 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(); - + alphaAccess->intrClockFrequency = platform->intrFrequency(); alphaAccess->diskUnit = 1; + + alphaAccess->diskCount = 0; + alphaAccess->diskPAddr = 0; + alphaAccess->diskBlock = 0; + alphaAccess->diskOperation = 0; + alphaAccess->outputChar = 0; + alphaAccess->inputChar = 0; + alphaAccess->bootStrapImpure = 0; + alphaAccess->bootStrapCPU = 0; + alphaAccess->align2 = 0; } Fault AlphaConsole::read(MemReqPtr &req, uint8_t *data) { memset(data, 0, req->size); - uint64_t val; - - Addr daddr = req->paddr - addr; - - switch (daddr) { - case offsetof(AlphaAccess, inputChar): - val = console->console_in(); - break; - default: - val = *(uint64_t *)(consoleData + daddr); - break; + Addr daddr = req->paddr - (addr & PA_IMPL_MASK); + + switch (req->size) + { + case sizeof(uint32_t): + DPRINTF(AlphaConsole, "read: offset=%#x val=%#x\n", daddr, *(uint32_t*)data); + switch (daddr) + { + case offsetof(AlphaAccess, last_offset): + *(uint32_t*)data = alphaAccess->last_offset; + break; + case offsetof(AlphaAccess, version): + *(uint32_t*)data = alphaAccess->version; + break; + case offsetof(AlphaAccess, numCPUs): + *(uint32_t*)data = alphaAccess->numCPUs; + break; + case offsetof(AlphaAccess, bootStrapCPU): + *(uint32_t*)data = alphaAccess->bootStrapCPU; + break; + case offsetof(AlphaAccess, intrClockFrequency): + *(uint32_t*)data = alphaAccess->intrClockFrequency; + break; + default: + // Old console code read in everyting as a 32bit int + *(uint32_t*)data = *(uint32_t*)(consoleData + daddr); + + } + break; + case sizeof(uint64_t): + DPRINTF(AlphaConsole, "read: offset=%#x val=%#x\n", daddr, *(uint64_t*)data); + switch (daddr) + { + case offsetof(AlphaAccess, inputChar): + *(uint64_t*)data = console->console_in(); + break; + case offsetof(AlphaAccess, cpuClock): + *(uint64_t*)data = alphaAccess->cpuClock; + break; + case offsetof(AlphaAccess, mem_size): + *(uint64_t*)data = alphaAccess->mem_size; + break; + case offsetof(AlphaAccess, kernStart): + *(uint64_t*)data = alphaAccess->kernStart; + break; + case offsetof(AlphaAccess, kernEnd): + *(uint64_t*)data = alphaAccess->kernEnd; + break; + case offsetof(AlphaAccess, entryPoint): + *(uint64_t*)data = alphaAccess->entryPoint; + break; + case offsetof(AlphaAccess, diskUnit): + *(uint64_t*)data = alphaAccess->diskUnit; + break; + case offsetof(AlphaAccess, diskCount): + *(uint64_t*)data = alphaAccess->diskCount; + break; + case offsetof(AlphaAccess, diskPAddr): + *(uint64_t*)data = alphaAccess->diskPAddr; + break; + case offsetof(AlphaAccess, diskBlock): + *(uint64_t*)data = alphaAccess->diskBlock; + break; + case offsetof(AlphaAccess, diskOperation): + *(uint64_t*)data = alphaAccess->diskOperation; + break; + case offsetof(AlphaAccess, outputChar): + *(uint64_t*)data = alphaAccess->outputChar; + break; + case offsetof(AlphaAccess, bootStrapImpure): + *(uint64_t*)data = alphaAccess->bootStrapImpure; + break; + default: + panic("Unknown 64bit access, %#x\n", daddr); + } + break; + default: + return Machine_Check_Fault; } - DPRINTF(AlphaConsole, "read: offset=%#x val=%#x\n", daddr, val); - - switch (req->size) { - case sizeof(uint32_t): - *(uint32_t *)data = (uint32_t)val; - break; - - case sizeof(uint64_t): - *(uint64_t *)data = val; - break; - - default: - return Machine_Check_Fault; - } - - return No_Fault; } @@ -137,7 +198,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) { @@ -167,7 +228,7 @@ AlphaConsole::write(MemReqPtr &req, const uint8_t *data) break; case offsetof(AlphaAccess, outputChar): - console->out((char)(val & 0xff), false); + console->out((char)(val & 0xff)); break; case offsetof(AlphaAccess, bootStrapImpure): @@ -266,7 +327,7 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaConsole) Param<Addr> addr; SimObjectParam<System *> system; SimObjectParam<BaseCPU *> cpu; - SimObjectParam<TlaserClock *> clock; + SimObjectParam<Platform *> platform; SimObjectParam<Bus*> io_bus; SimObjectParam<HierParams *> hier; @@ -281,7 +342,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(platform, "platform"), INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to", NULL), INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams) @@ -290,7 +351,7 @@ END_INIT_SIM_OBJECT_PARAMS(AlphaConsole) CREATE_SIM_OBJECT(AlphaConsole) { return new AlphaConsole(getInstanceName(), sim_console, disk, - system, cpu, clock, num_cpus, mmu, + system, cpu, platform, num_cpus, mmu, addr, hier, io_bus); } diff --git a/dev/alpha_console.hh b/dev/alpha_console.hh index b617b64e7..49c3a9f78 100644 --- a/dev/alpha_console.hh +++ b/dev/alpha_console.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2001-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,11 +37,12 @@ #include "dev/alpha_access.h" #include "dev/io_device.hh" #include "sim/host.hh" +#include "sim/sim_object.hh" class BaseCPU; class SimConsole; class System; -class TlaserClock; +class Platform; class SimpleDisk; /* @@ -74,7 +75,7 @@ class AlphaConsole : public PioDevice protected: union { AlphaAccess *alphaAccess; - uint8_t *consoleData; + uint8_t *consoleData; }; /** the disk must be accessed from the console */ @@ -89,7 +90,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, Platform *platform, 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..8b552e989 --- /dev/null +++ b/dev/baddev.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2004 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 + * BadDevice implemenation + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "cpu/exec_context.hh" +#include "dev/baddev.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 "sim/system.hh" + +using namespace std; + +BadDevice::BadDevice(const string &name, Addr a, MemoryController *mmu, + HierParams *hier, Bus *bus, const string &devicename) + : PioDevice(name), addr(a), devname(devicename) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + + if (bus) { + pioInterface = newPioInterface(name, hier, bus, this, + &BadDevice::cacheAccess); + pioInterface->addAddrRange(addr, addr + size - 1); + } + +} + +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; +} + +Tick +BadDevice::cacheAccess(MemReqPtr &req) +{ + return curTick + 1000; +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(BadDevice) + + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + SimObjectParam<HierParams *> hier; + SimObjectParam<Bus*> io_bus; + 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_DFLT(hier, "Hierarchy global variables", &defaultHierParams), + INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to", NULL), + 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, hier, io_bus, devicename); +} + +REGISTER_SIM_OBJECT("BadDevice", BadDevice) diff --git a/dev/baddev.hh b/dev/baddev.hh new file mode 100644 index 000000000..8680f6b0a --- /dev/null +++ b/dev/baddev.hh @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2004 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 "base/range.hh" +#include "dev/io_device.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 PioDevice +{ + 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 hier object to store parameters universal the device hierarchy + * @param bus The bus that this device is attached to + * @param devicename device that is not implemented + */ + BadDevice(const std::string &name, Addr a, MemoryController *mmu, + HierParams *hier, Bus *bus, 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); + + /** + * Return how long this access will take. + * @param req the memory request to calcuate + * @return Tick when the request is done + */ + Tick cacheAccess(MemReqPtr &req); +}; + +#endif // __BADDEV_HH__ diff --git a/dev/disk_image.cc b/dev/disk_image.cc index 142fb60a5..d990d7078 100644 --- a/dev/disk_image.cc +++ b/dev/disk_image.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2001-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,6 +46,7 @@ #include "dev/disk_image.hh" #include "sim/builder.hh" #include "sim/sim_exit.hh" +#include "targetarch/byte_swap.hh" using namespace std; @@ -227,7 +228,17 @@ SafeRead(ifstream &stream, void *data, int count) template<class T> void SafeRead(ifstream &stream, T &data) -{ SafeRead(stream, &data, sizeof(data)); } +{ + SafeRead(stream, &data, sizeof(data)); +} + +template<class T> +void +SafeReadSwap(ifstream &stream, T &data) +{ + SafeRead(stream, &data, sizeof(data)); + data = htoa(data); +} bool CowDiskImage::open(const string &file) @@ -246,21 +257,21 @@ CowDiskImage::open(const string &file) panic("Could not open %s: Invalid magic", file); uint32_t major, minor; - SafeRead(stream, major); - SafeRead(stream, minor); + SafeReadSwap(stream, major); + SafeReadSwap(stream, minor); if (major != VersionMajor && minor != VersionMinor) panic("Could not open %s: invalid version %d.%d != %d.%d", file, major, minor, VersionMajor, VersionMinor); uint64_t sector_count; - SafeRead(stream, sector_count); + SafeReadSwap(stream, sector_count); table = new SectorTable(sector_count); for (uint64_t i = 0; i < sector_count; i++) { uint64_t offset; - SafeRead(stream, offset); + SafeReadSwap(stream, offset); Sector *sector = new Sector; SafeRead(stream, sector, sizeof(Sector)); @@ -300,8 +311,17 @@ SafeWrite(ofstream &stream, const void *data, int count) template<class T> void SafeWrite(ofstream &stream, const T &data) -{ SafeWrite(stream, &data, sizeof(data)); } +{ + SafeWrite(stream, &data, sizeof(data)); +} +template<class T> +void +SafeWriteSwap(ofstream &stream, const T &data) +{ + T swappeddata = htoa(data); + SafeWrite(stream, &swappeddata, sizeof(data)); +} void CowDiskImage::save() { @@ -322,9 +342,9 @@ CowDiskImage::save(const string &file) memcpy(&magic, "COWDISK!", sizeof(magic)); SafeWrite(stream, magic); - SafeWrite(stream, (uint32_t)VersionMajor); - SafeWrite(stream, (uint32_t)VersionMinor); - SafeWrite(stream, (uint64_t)table->size()); + SafeWriteSwap(stream, (uint32_t)VersionMajor); + SafeWriteSwap(stream, (uint32_t)VersionMinor); + SafeWriteSwap(stream, (uint64_t)table->size()); uint64_t size = table->size(); SectorTable::iterator iter = table->begin(); @@ -334,7 +354,7 @@ CowDiskImage::save(const string &file) if (iter == end) panic("Incorrect Table Size during save of COW disk image"); - SafeWrite(stream, (uint64_t)(*iter).first); + SafeWriteSwap(stream, (uint64_t)(*iter).first); SafeWrite(stream, (*iter).second->data, sizeof(Sector)); ++iter; } diff --git a/dev/disk_image.hh b/dev/disk_image.hh index 7b2c12ef7..b21978058 100644 --- a/dev/disk_image.hh +++ b/dev/disk_image.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2001-2003 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/etherbus.cc b/dev/etherbus.cc index 76697dd3e..82722383c 100644 --- a/dev/etherbus.cc +++ b/dev/etherbus.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2002-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/etherbus.hh b/dev/etherbus.hh index 9ef477808..716636c79 100644 --- a/dev/etherbus.hh +++ b/dev/etherbus.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2002-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/etherdump.cc b/dev/etherdump.cc index b6d6bbe30..54e573be4 100644 --- a/dev/etherdump.cc +++ b/dev/etherdump.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2002-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/etherdump.hh b/dev/etherdump.hh index e22b66166..ef4399e1a 100644 --- a/dev/etherdump.hh +++ b/dev/etherdump.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2002-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/etherint.cc b/dev/etherint.cc index cfffb3a87..c78848674 100644 --- a/dev/etherint.cc +++ b/dev/etherint.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2002-2003 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/etherint.hh b/dev/etherint.hh index 70e29eb7c..ddfe16d88 100644 --- a/dev/etherint.hh +++ b/dev/etherint.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2002-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/etherlink.cc b/dev/etherlink.cc index 25991f1a7..3cc4f75ea 100644 --- a/dev/etherlink.cc +++ b/dev/etherlink.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2002-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/etherlink.hh b/dev/etherlink.hh index 3b1dd21bc..4f227fac5 100644 --- a/dev/etherlink.hh +++ b/dev/etherlink.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2002-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without 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..abdf30166 100644 --- a/dev/etherpkt.hh +++ b/dev/etherpkt.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2002-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,12 +35,13 @@ #include <iosfwd> #include <memory> +#include <assert.h> #include "sim/host.hh" #include "base/refcnt.hh" +#include "base/inet_hdrs.hh" class Checkpoint; - /* * Reference counted class containing ethernet packet data */ @@ -61,6 +62,41 @@ class EtherPacket : public RefCounted bool IsMulticast() { return data[0] == 0x01; } bool IsBroadcast() { return data[0] == 0xff; } + bool isIpPkt() { + eth_header *eth = (eth_header *) data; + return (eth->type == 0x8); + } + bool isTcpPkt(ip_header *ip) { + return (ip->protocol == 0x6); + } + bool isTcpPkt() { + ip_header *ip = getIpHdr(); + return (ip->protocol == 0x6); + } + bool isUdpPkt(ip_header *ip) { + return (ip->protocol == 17); + } + bool isUdpPkt() { + ip_header *ip = getIpHdr(); + return (ip->protocol == 17); + } + + ip_header *getIpHdr() { + assert(isIpPkt()); + return (ip_header *) (data + sizeof(eth_header)); + } + + tcp_header *getTcpHdr(ip_header *ip) { + assert(isTcpPkt(ip)); + return (tcp_header *) ((uint8_t *) ip + (ip->vers_len & 0xf)*4); + } + + udp_header *getUdpHdr(ip_header *ip) { + assert(isUdpPkt(ip)); + return (udp_header *) ((uint8_t *) ip + (ip->vers_len & 0xf)*4); + } + typedef RefCountingPtr<EtherPacket> PacketPtr; + void serialize(std::ostream &os); void unserialize(Checkpoint *cp, const std::string §ion); }; diff --git a/dev/ethertap.cc b/dev/ethertap.cc index db1b3660f..edc400760 100644 --- a/dev/ethertap.cc +++ b/dev/ethertap.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2003-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,7 +30,7 @@ * Interface to connect a simulated ethernet device to the real world */ -#if defined(__OpenBSD__) +#if defined(__OpenBSD__) || defined(__APPLE__) #include <sys/param.h> #endif #include <netinet/in.h> diff --git a/dev/ethertap.hh b/dev/ethertap.hh index 0b853a11d..1fe368085 100644 --- a/dev/ethertap.hh +++ b/dev/ethertap.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2003-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/ide_ctrl.cc b/dev/ide_ctrl.cc new file mode 100644 index 000000000..4805570d2 --- /dev/null +++ b/dev/ide_ctrl.cc @@ -0,0 +1,732 @@ +/* + * Copyright (c) 2004 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; +} + +bool +IdeController::isDiskSelected(IdeDisk *diskPtr) +{ + for (int i = 0; i < 4; i++) { + if ((long)diskPtr == (long)disks[i]) { + // is disk is on primary or secondary channel + int channel = i/2; + // is disk the master or slave + int devID = i%2; + + return (dev[channel] == devID); + } + } + panic("Unable to find disk by pointer!!\n"); +} + +//// +// 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) +{ + +#if TRACING_ON + Addr origOffset = offset; +#endif + + 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); + } + + // Catch the writes to specific PCI registers that have side affects + // (like updating the PIO ranges) + switch (offset) { + case PCI_COMMAND: + if (config.data[offset] & PCI_CMD_IOSE) + io_enabled = true; + else + io_enabled = false; + + if (config.data[offset] & PCI_CMD_BME) + bm_enabled = true; + else + bm_enabled = false; + break; + + case PCI0_BASE_ADDR0: + if (BARAddrs[0] != 0) { + pri_cmd_addr = BARAddrs[0]; + if (pioInterface) + pioInterface->addAddrRange(pri_cmd_addr, + pri_cmd_addr + pri_cmd_size - 1); + + pri_cmd_addr &= PA_UNCACHED_MASK; + } + break; + + case PCI0_BASE_ADDR1: + if (BARAddrs[1] != 0) { + pri_ctrl_addr = BARAddrs[1]; + if (pioInterface) + pioInterface->addAddrRange(pri_ctrl_addr, + pri_ctrl_addr + pri_ctrl_size - 1); + + pri_ctrl_addr &= PA_UNCACHED_MASK; + } + break; + + case PCI0_BASE_ADDR2: + if (BARAddrs[2] != 0) { + sec_cmd_addr = BARAddrs[2]; + if (pioInterface) + pioInterface->addAddrRange(sec_cmd_addr, + sec_cmd_addr + sec_cmd_size - 1); + + sec_cmd_addr &= PA_UNCACHED_MASK; + } + break; + + case PCI0_BASE_ADDR3: + if (BARAddrs[3] != 0) { + sec_ctrl_addr = BARAddrs[3]; + if (pioInterface) + pioInterface->addAddrRange(sec_ctrl_addr, + sec_ctrl_addr + sec_ctrl_size - 1); + + sec_ctrl_addr &= PA_UNCACHED_MASK; + } + break; + + case PCI0_BASE_ADDR4: + if (BARAddrs[4] != 0) { + bmi_addr = BARAddrs[4]; + if (pioInterface) + pioInterface->addAddrRange(bmi_addr, bmi_addr + bmi_size - 1); + + 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 the PciDev base class + PciDev::serialize(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 the PciDev base class + PciDev::unserialize(cp, section); + + // 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); + + if (pioInterface) { + pioInterface->addAddrRange(pri_cmd_addr, pri_cmd_addr + + pri_cmd_size - 1); + pioInterface->addAddrRange(pri_ctrl_addr, pri_ctrl_addr + + pri_ctrl_size - 1); + pioInterface->addAddrRange(sec_cmd_addr, sec_cmd_addr + + sec_cmd_size - 1); + pioInterface->addAddrRange(sec_ctrl_addr, sec_ctrl_addr + + sec_ctrl_size - 1); + pioInterface->addAddrRange(bmi_addr, bmi_addr + bmi_size - 1); + } +} + +#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 *> io_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(io_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, io_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..39c64eb30 --- /dev/null +++ b/dev/ide_ctrl.hh @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2004 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) + +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: + /** See if a disk is selected based on its pointer */ + bool isDiskSelected(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); + + /** + * 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); + + /** + * Return how long this access will take. + * @param req the memory request to calcuate + * @return Tick when the request is done + */ + Tick cacheAccess(MemReqPtr &req); +}; +#endif // __IDE_CTRL_HH_ diff --git a/dev/ide_disk.cc b/dev/ide_disk.cc new file mode 100644 index 000000000..ee21feaea --- /dev/null +++ b/dev/ide_disk.cc @@ -0,0 +1,1199 @@ +/* + * Copyright (c) 2004 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) +{ + // Reset the device state + reset(id); + + // calculate disk delay in microseconds + diskDelay = (delay * ticksPerSecond / 100000); + + // 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; +} + +IdeDisk::~IdeDisk() +{ + // destroy the data buffer + delete [] dataBuffer; +} + +void +IdeDisk::reset(int id) +{ + // 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; + cmdBytes = 0; + cmdBytesLeft = 0; + drqBytesLeft = 0; + dmaRead = false; + intrPending = false; + + // 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 + status = STATUS_DRDY_BIT; +} + +//// +// Utility functions +//// + +bool +IdeDisk::isDEVSelect() +{ + return ctrl->isDiskSelected(this); +} + +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; + *data = status; // status is in a shadow, explicity copy + } 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 = status; + } + + 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 == SELECT_OFFSET) { + action = ACT_SELECT_WRITE; + } + + } 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) { + // force the device into the reset state + devState = Device_Srst; + action = ACT_SRST_SET; + } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) { + action = ACT_SRST_CLEAR; + } + + 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)); + + DPRINTF(IdeDisk, "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n", + curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()), + curPrd.getByteCount(), (cmdBytesLeft/SectorSize), + curPrd.getEOT(), curSector); + + // make sure the new curPrdAddr is properly translated from PCI to system + curPrdAddr = pciToDma(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)); + } + + // 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); + } + + // 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"); + + // PRD base address is given by bits 31:2 + curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3))); + + 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; + + // 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: + cmdBytes = 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) + cmdBytes = cmdBytesLeft = (256 * SectorSize); + else + cmdBytes = 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) + cmdBytes = cmdBytesLeft = (256 * SectorSize); + else + cmdBytes = 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) + cmdBytes = cmdBytesLeft = (256 * SectorSize); + else + cmdBytes = 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 + status |= STATUS_BSY_BIT; + // clear the DRQ bit + status &= ~STATUS_DRQ_BIT; + // clear the DF bit + status &= ~STATUS_DF_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_Srst: + if (action == ACT_SRST_SET) { + // set the BSY bit + status |= STATUS_BSY_BIT; + } else if (action == ACT_SRST_CLEAR) { + // clear the BSY bit + status &= ~STATUS_BSY_BIT; + + // reset the device state + reset(devID); + } + break; + + case Device_Idle_S: + if (action == ACT_SELECT_WRITE && !isDEVSelect()) { + devState = Device_Idle_NS; + } else if (action == ACT_CMD_WRITE) { + startCommand(); + } + + break; + + case Device_Idle_SI: + if (action == ACT_SELECT_WRITE && !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 (action == ACT_SELECT_WRITE && 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 + status &= ~STATUS_BSY_BIT; + // set the DRQ bit + status |= STATUS_DRQ_BIT; + + // copy the data into the data buffer + if (cmdReg.command == 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 + status |= STATUS_BSY_BIT; + // clear the DRQ_BIT + 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 (action == ACT_DATA_READY && cmdBytesLeft != 0) { + // clear the BSY bit + status &= ~STATUS_BSY_BIT; + // set the DRQ bit + status |= STATUS_DRQ_BIT; + + // clear the data buffer to get it ready for writes + memset(dataBuffer, 0, MAX_DMA_SIZE); + + // reset the drqBytes for this block + drqBytesLeft = SectorSize; + + if (cmdBytesLeft == cmdBytes || isIENSet()) { + devState = Transfer_Data_Out; + } else { + devState = Data_Ready_INTRQ_Out; + intrPost(); + } + } + 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 + status |= STATUS_BSY_BIT; + // set the seek bit + status |= STATUS_SEEK_BIT; + // clear the DRQ bit + 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 + status &= ~STATUS_BSY_BIT; + // set the DRQ bit + 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 + status |= STATUS_SEEK_BIT; + // 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; + + int eventCount = 0; + + if (dmaTransferEvent.scheduled()) { + reschedule = dmaTransferEvent.when(); + event = Transfer; + eventCount++; + } + if (dmaReadWaitEvent.scheduled()) { + reschedule = dmaReadWaitEvent.when(); + event = ReadWait; + eventCount++; + } + if (dmaWriteWaitEvent.scheduled()) { + reschedule = dmaWriteWaitEvent.when(); + event = WriteWait; + eventCount++; + } + if (dmaPrdReadEvent.scheduled()) { + reschedule = dmaPrdReadEvent.when(); + event = PrdRead; + eventCount++; + } + if (dmaReadEvent.scheduled()) { + reschedule = dmaReadEvent.when(); + event = DmaRead; + eventCount++; + } + if (dmaWriteEvent.scheduled()) { + reschedule = dmaWriteEvent.when(); + event = DmaWrite; + eventCount++; + } + + assert(eventCount <= 1); + + 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.command); + SERIALIZE_SCALAR(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(cmdBytes); + SERIALIZE_SCALAR(drqBytesLeft); + SERIALIZE_SCALAR(curSector); + 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.command); + UNSERIALIZE_SCALAR(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(cmdBytes); + UNSERIALIZE_SCALAR(cmdBytesLeft); + UNSERIALIZE_SCALAR(drqBytesLeft); + UNSERIALIZE_SCALAR(curSector); + 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..9c6eea623 --- /dev/null +++ b/dev/ide_disk.hh @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2004 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 (65536) // 64K +#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); + } + + uint32_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 STATUS_SEEK_BIT 0x10 +#define STATUS_DF_BIT 0x20 +#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; + }; + 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_SELECT_WRITE, + 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, + ACT_SRST_SET, + ACT_SRST_CLEAR +} DevAction_t; + +typedef enum DevState { + // Device idle + Device_Idle_S = 0, + Device_Idle_SI, + Device_Idle_NS, + + // Software reset + Device_Srst, + + // 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 in command data transfer */ + uint32_t cmdBytes; + /** 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; + /** Status register */ + uint8_t status; + /** 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(); + + /** + * Reset the device state + */ + void reset(int id); + + /** + * 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 (status & STATUS_BSY_BIT); } + bool isIENSet() { return nIENBit; } + bool isDEVSelect(); + + void setComplete() + { + // clear out the status byte + status = 0; + // set the DRDY bit + status |= STATUS_DRDY_BIT; + // set the SEEK bit + status |= STATUS_SEEK_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/io_device.cc b/dev/io_device.cc index 910b889d8..b39efa1f5 100644 --- a/dev/io_device.cc +++ b/dev/io_device.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/io_device.hh b/dev/io_device.hh index 9300d87e7..e6014e73d 100644 --- a/dev/io_device.hh +++ b/dev/io_device.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/ns_gige.cc b/dev/ns_gige.cc new file mode 100644 index 000000000..f3f2f10b3 --- /dev/null +++ b/dev/ns_gige.cc @@ -0,0 +1,2595 @@ +/* + * Copyright (c) 2004 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; + +//helper function declarations +//These functions reverse Endianness so we can evaluate network data correctly +uint16_t reverseEnd16(uint16_t); +uint32_t reverseEnd32(uint32_t); + +/////////////////////////////////////////////////////////////////////// +// +// 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]) + : PciDev(name, mmu, cf, cd, bus, dev, func), tsunami(t), ioEnable(false), + txPacket(0), rxPacket(0), txPacketBufPtr(NULL), rxPacketBufPtr(NULL), + txXferLen(0), rxXferLen(0), txState(txIdle), CTDD(false), + 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) +{ + tsunami->ethernet = this; + + if (header_bus) { + pioInterface = newPioInterface(name, hier, header_bus, this, + &NSGigE::cacheAccess); + + 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); + + 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; + + 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("Device specific PCI config space not implemented!\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("Device specific PCI config space not implemented!\n"); + + // Need to catch writes to BARs to update the PIO interface + switch (offset) { + //seems to work fine without all these PCI settings, but i put in the IO + //to double check, an assertion will fail if we need to properly + // implement it + case PCI_COMMAND: + if (config.data[offset] & PCI_CMD_IOSE) + ioEnable = true; + else + ioEnable = false; + +#if 0 + if (config.data[offset] & PCI_CMD_BME) { + bmEnabled = true; + } + else { + bmEnabled = false; + } + + if (config.data[offset] & PCI_CMD_MSE) { + memEnable = true; + } + else { + memEnable = false; + } +#endif + break; + + case PCI0_BASE_ADDR0: + if (BARAddrs[0] != 0) { + + if (pioInterface) + pioInterface->addAddrRange(BARAddrs[0], BARAddrs[0] + BARSize[0] - 1); + + BARAddrs[0] &= PA_UNCACHED_MASK; + + } + break; + case PCI0_BASE_ADDR1: + if (BARAddrs[1] != 0) { + + if (pioInterface) + pioInterface->addAddrRange(BARAddrs[1], BARAddrs[1] + BARSize[1] - 1); + + BARAddrs[1] &= PA_UNCACHED_MASK; + + } + break; + } +} + +/** + * This reads the device registers, which are detailed in the NS83820 + * spec sheet + */ +Fault +NSGigE::read(MemReqPtr &req, uint8_t *data) +{ + assert(ioEnable); + + //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) +{ + assert(ioEnable); + + 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) ; + + //also, we currently don't care about fill/drain thresholds + //though this may change in the future with more realistic + //networks or a driver which changes it according to feedback + + 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 + //also, we currently don't care about fill/drain thresholds + //though this may change in the future with more realistic + //networks or a driver which changes it according to feedback + 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(EthernetIntr, "**interrupt written to ISR: 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(EthernetIntr, "**interrupt cleared from ISR: intr=%x isr=%x imr=%x\n", + interrupts, regs.isr, regs.imr); +} + +void +NSGigE::devIntrChangeMask() +{ + DPRINTF(EthernetIntr, "interrupt mask changed\n"); + + if (regs.isr & regs.imr) + cpuIntrPost(curTick); + else + cpuIntrClear(); +} + +void +NSGigE::cpuIntrPost(Tick when) +{ + //If the interrupt you want to post is later than an + //interrupt already scheduled, just let it post in the coming one and + //don't schedule another. + //HOWEVER, must be sure that the scheduled intrTick is in the future + //(this was formerly the source of a bug) + assert((intrTick >= curTick) || (intrTick == 0)); + if (when > intrTick && intrTick != 0) + return; + + intrTick = when; + + if (intrEvent) { + intrEvent->squash(); + intrEvent = 0; + } + + if (when < curTick) { + cpuInterrupt(); + } else { + DPRINTF(EthernetIntr, "going to schedule an interrupt for intrTick=%d\n", + intrTick); + intrEvent = new IntrEvent(this, true); + intrEvent->schedule(intrTick); + } +} + +void +NSGigE::cpuInterrupt() +{ + // Don't send an interrupt if there's already one + if (cpuPendingIntr) { + DPRINTF(EthernetIntr, + "would send an interrupt now, but there's already pending\n"); + intrTick = 0; + return; + } + // Don't send an interrupt if it's supposed to be delayed + if (intrTick > curTick) { + DPRINTF(EthernetIntr, "an interrupt is scheduled for %d, wait til then\n", + intrTick); + 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); + DPRINTF(EthernetIntr, "Posting interrupts to cchip!\n"); + 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); + DPRINTF(EthernetIntr, "clearing all interrupts from cchip\n"); + tsunami->cchip->clearDRIR(configData->config.hdr.pci0.interruptLine); + } +} + +bool +NSGigE::cpuIntrPending() const +{ return cpuPendingIntr; } + +void +NSGigE::txReset() +{ + + DPRINTF(Ethernet, "transmit reset\n"); + + CTDD = false; + 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::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; + + extstsEnable = false; + acceptBroadcast = false; + acceptMulticast = false; + acceptUnicast = false; + acceptPerfect = false; + acceptArp = false; +} + +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(EthernetSM, "receive kick state=%s (rxBuf.size=%d)\n", + NsRxStateStrings[rxState], rxFifo.size()); + + if (rxKickTick > curTick) { + DPRINTF(EthernetSM, "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(EthernetSM, "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(EthernetDesc, + "rxDescCache:\n\tlink=%08x\n\tbufptr=%08x\n\tcmdsts=%08x\n\textsts=%08x\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; + + DPRINTF(EthernetSM, "\n\n*****processing receive of new packet\n"); + + // If we don't have a packet, grab a new one from the fifo. + rxPacket = rxFifo.front(); + rxPktBytes = rxPacket->length; + rxPacketBufPtr = rxPacket->data; + + if (DTRACE(Ethernet)) { + if (rxPacket->isIpPkt()) { + ip_header *ip = rxPacket->getIpHdr(); + DPRINTF(Ethernet, "ID is %d\n", reverseEnd16(ip->ID)); + if (rxPacket->isTcpPkt()) { + tcp_header *tcp = rxPacket->getTcpHdr(ip); + DPRINTF(Ethernet, "Src Port = %d, Dest Port = %d\n", + reverseEnd16(tcp->src_port_num), + reverseEnd16(tcp->dest_port_num)); + } + } + } + + // 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(); + rxFifoCnt -= rxPacket->length; + } + + + // 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); + DPRINTF(EthernetSM, "done with receiving packet\n"); + + 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 + + if (rxPacket->isIpPkt() && extstsEnable) { + rxDescCache.extsts |= EXTSTS_IPPKT; + if (!ipChecksum(rxPacket, false)) { + DPRINTF(EthernetCksum, "Rx IP Checksum Error\n"); + rxDescCache.extsts |= EXTSTS_IPERR; + } + if (rxPacket->isTcpPkt()) { + rxDescCache.extsts |= EXTSTS_TCPPKT; + if (!tcpChecksum(rxPacket, false)) { + DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n"); + rxDescCache.extsts |= EXTSTS_TCPERR; + } + } else if (rxPacket->isUdpPkt()) { + rxDescCache.extsts |= EXTSTS_UDPPKT; + if (!udpChecksum(rxPacket, false)) { + DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n"); + rxDescCache.extsts |= EXTSTS_UDPERR; + } + } + } + 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(EthernetDesc, "rxDesc writeback:\n\tcmdsts=%08x\n\textsts=%08x\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) { + DPRINTF(EthernetSM, "Halting the RX state machine\n"); + 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(EthernetSM, "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(EthernetSM, "rx state machine exited state=%s\n", + NsRxStateStrings[rxState]); +} + +void +NSGigE::transmit() +{ + if (txFifo.empty()) { + DPRINTF(Ethernet, "nothing to transmit\n"); + return; + } + + DPRINTF(Ethernet, "\n\nAttempt Pkt Transmit: txFifo length = %d\n", + MAX_TX_FIFO_SIZE - txFifoAvail); + if (interface->sendPacket(txFifo.front())) { + if (DTRACE(Ethernet)) { + if (txFifo.front()->isIpPkt()) { + ip_header *ip = txFifo.front()->getIpHdr(); + DPRINTF(Ethernet, "ID is %d\n", reverseEnd16(ip->ID)); + if (txFifo.front()->isTcpPkt()) { + tcp_header *tcp = txFifo.front()->getTcpHdr(ip); + DPRINTF(Ethernet, "Src Port = %d, Dest Port = %d\n", + reverseEnd16(tcp->src_port_num), + reverseEnd16(tcp->dest_port_num)); + } + } + } + + DDUMP(Ethernet, txFifo.front()->data, txFifo.front()->length); + txBytes += txFifo.front()->length; + txPackets++; + + txFifoAvail += txFifo.front()->length; + + DPRINTF(Ethernet, "Successful Xmit! now txFifoAvail is %d\n", txFifoAvail); + 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); + } else + DPRINTF(Ethernet, "May need to rethink always sending the descriptors back?\n"); + + 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(EthernetSM, "transmit kick state=%s\n", NsTxStateStrings[txState]); + + if (txKickTick > curTick) { + DPRINTF(EthernetSM, "transmit kick exiting, can't run till %d\n", + txKickTick); + + 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(EthernetSM, "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(EthernetDesc, + "txDescCache data:\n\tlink=%08x\n\tbufptr=%08x\n\tcmdsts=%08x\n\textsts=%08x\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(EthernetSM, "\n\n*****starting the tx of a new packet\n"); + txPacket = new EtherPacket; + txPacket->data = new uint8_t[16384]; + txPacketBufPtr = txPacket->data; + } + + if (txDescCnt == 0) { + DPRINTF(EthernetSM, "the txDescCnt == 0, done with descriptor\n"); + if (txDescCache.cmdsts & CMDSTS_MORE) { + DPRINTF(EthernetSM, "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(EthernetSM, "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(EthernetDesc, + "txDesc writeback:\n\tcmdsts=%08x\n\textsts=%08x\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; + + transmit(); + + txPacket = 0; + + if (txHalt) { + DPRINTF(EthernetSM, "halting TX state machine\n"); + txState = txIdle; + txHalt = false; + } else + txState = txAdvance; + } + } else { + DPRINTF(EthernetSM, "this descriptor isn't done yet\n"); + 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; + txDescCnt -= txXferLen; + txFifoAvail -= txXferLen; + + txState = txFifoBlock; + break; + + case txDescWrite: + if (txDmaState != dmaIdle) + goto exit; + + 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(EthernetSM, "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(EthernetSM, "tx state machine exited state=%s\n", + NsTxStateStrings[txState]); +} + +void +NSGigE::transferDone() +{ + if (txFifo.empty()) + return; + + 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 == 0x608)) + 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++; + + DPRINTF(Ethernet, "\n\nReceiving packet from wire, rxFifoAvail = %d\n", MAX_RX_FIFO_SIZE - rxFifoCnt); + + 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) +{ + ip_header *ip = packet->getIpHdr(); + udp_header *hdr = packet->getUdpHdr(ip); + + 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) +{ + ip_header *ip = packet->getIpHdr(); + tcp_header *hdr = packet->getTcpHdr(ip); + + pseudo_header *pseudo = new pseudo_header; + + pseudo->src_ip_addr = ip->src_ip_addr; + pseudo->dest_ip_addr = ip->dest_ip_addr; + pseudo->protocol = reverseEnd16(ip->protocol); + pseudo->len = reverseEnd16(reverseEnd16(ip->dgram_len) - (ip->vers_len & 0xf)*4); + + uint16_t cksum = checksumCalc((uint16_t *) pseudo, (uint16_t *) hdr, + (uint32_t) reverseEnd16(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)*4); + + if (gen) { + DPRINTF(Ethernet, "generated checksum: %#x\n", cksum); + 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) +{ + // Serialize the PciDev base class + PciDev::serialize(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_SCALAR(ioEnable); + + /* + * Serialize the data Fifos + */ + int txNumPkts = txFifo.size(); + SERIALIZE_SCALAR(txNumPkts); + int i = 0; + pktiter_t end = txFifo.end(); + for (pktiter_t p = txFifo.begin(); p != end; ++p) { + nameOut(os, csprintf("%s.txFifo%d", name(), i++)); + (*p)->serialize(os); + } + + int rxNumPkts = rxFifo.size(); + SERIALIZE_SCALAR(rxNumPkts); + i = 0; + end = rxFifo.end(); + for (pktiter_t p = rxFifo.begin(); p != end; ++p) { + nameOut(os, csprintf("%s.rxFifo%d", name(), i++)); + (*p)->serialize(os); + } + + /* + * Serialize the various helper variables + */ + bool txPacketExists = txPacket; + SERIALIZE_SCALAR(txPacketExists); + if (txPacketExists) { + nameOut(os, csprintf("%s.txPacket", name())); + txPacket->serialize(os); + uint32_t txPktBufPtr = (uint32_t) (txPacketBufPtr - txPacket->data); + SERIALIZE_SCALAR(txPktBufPtr); + } + + bool rxPacketExists = rxPacket; + SERIALIZE_SCALAR(rxPacketExists); + if (rxPacketExists) { + nameOut(os, csprintf("%s.rxPacket", name())); + rxPacket->serialize(os); + uint32_t rxPktBufPtr = (uint32_t) (rxPacketBufPtr - rxPacket->data); + SERIALIZE_SCALAR(rxPktBufPtr); + } + + SERIALIZE_SCALAR(txXferLen); + SERIALIZE_SCALAR(rxXferLen); + + /* + * 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 txState = this->txState; + SERIALIZE_SCALAR(txState); + SERIALIZE_SCALAR(CTDD); + SERIALIZE_SCALAR(txFifoAvail); + SERIALIZE_SCALAR(txHalt); + SERIALIZE_SCALAR(txFragPtr); + SERIALIZE_SCALAR(txDescCnt); + int txDmaState = this->txDmaState; + SERIALIZE_SCALAR(txDmaState); + + /* + * Serialize rx state machine + */ + 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); + + /* + * receive address filter settings + */ + SERIALIZE_SCALAR(rxFilterEnable); + SERIALIZE_SCALAR(acceptBroadcast); + SERIALIZE_SCALAR(acceptMulticast); + SERIALIZE_SCALAR(acceptUnicast); + SERIALIZE_SCALAR(acceptPerfect); + SERIALIZE_SCALAR(acceptArp); + + /* + * Keep track of pending interrupt status. + */ + SERIALIZE_SCALAR(intrTick); + SERIALIZE_SCALAR(cpuPendingIntr); + Tick intrEventTick = 0; + if (intrEvent) + intrEventTick = intrEvent->when(); + SERIALIZE_SCALAR(intrEventTick); + +} + +void +NSGigE::unserialize(Checkpoint *cp, const std::string §ion) +{ + // Unserialize the PciDev base class + PciDev::unserialize(cp, section); + + 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_SCALAR(ioEnable); + + /* + * unserialize the data fifos + */ + int txNumPkts; + UNSERIALIZE_SCALAR(txNumPkts); + int i; + for (i = 0; i < txNumPkts; ++i) { + PacketPtr p = new EtherPacket; + p->unserialize(cp, csprintf("%s.rxFifo%d", section, i)); + txFifo.push_back(p); + } + + int rxNumPkts; + UNSERIALIZE_SCALAR(rxNumPkts); + for (i = 0; i < rxNumPkts; ++i) { + PacketPtr p = new EtherPacket; + p->unserialize(cp, csprintf("%s.rxFifo%d", section, i)); + rxFifo.push_back(p); + } + + /* + * unserialize the various helper variables + */ + bool txPacketExists; + UNSERIALIZE_SCALAR(txPacketExists); + if (txPacketExists) { + txPacket = new EtherPacket; + txPacket->unserialize(cp, csprintf("%s.txPacket", section)); + uint32_t txPktBufPtr; + UNSERIALIZE_SCALAR(txPktBufPtr); + txPacketBufPtr = (uint8_t *) txPacket->data + txPktBufPtr; + } else + txPacket = 0; + + bool rxPacketExists; + UNSERIALIZE_SCALAR(rxPacketExists); + rxPacket = 0; + if (rxPacketExists) { + rxPacket = new EtherPacket; + rxPacket->unserialize(cp, csprintf("%s.rxPacket", section)); + uint32_t rxPktBufPtr; + UNSERIALIZE_SCALAR(rxPktBufPtr); + rxPacketBufPtr = (uint8_t *) rxPacket->data + rxPktBufPtr; + } else + rxPacket = 0; + + UNSERIALIZE_SCALAR(txXferLen); + UNSERIALIZE_SCALAR(rxXferLen); + + /* + * 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 txState; + UNSERIALIZE_SCALAR(txState); + this->txState = (TxState) txState; + UNSERIALIZE_SCALAR(CTDD); + 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 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, reschedule it now + */ + Tick transmitTick; + UNSERIALIZE_SCALAR(transmitTick); + if (transmitTick) + txEvent.schedule(curTick + transmitTick); + + /* + * unserialize receive address filter settings + */ + UNSERIALIZE_SCALAR(rxFilterEnable); + UNSERIALIZE_SCALAR(acceptBroadcast); + UNSERIALIZE_SCALAR(acceptMulticast); + UNSERIALIZE_SCALAR(acceptUnicast); + UNSERIALIZE_SCALAR(acceptPerfect); + UNSERIALIZE_SCALAR(acceptArp); + + /* + * 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); + } + + /* + * re-add addrRanges to bus bridges + */ + if (pioInterface) { + pioInterface->addAddrRange(BARAddrs[0], BARAddrs[0] + BARSize[0] - 1); + pioInterface->addAddrRange(BARAddrs[1], BARAddrs[1] + BARSize[1] - 1); + } +} + +Tick +NSGigE::cacheAccess(MemReqPtr &req) +{ + DPRINTF(EthernetPIO, "timing access to paddr=%#x (daddr=%#x)\n", + req->paddr, req->paddr - addr); + return curTick + pioLatency; +} +//===================================================================== + + +//********** helper functions****************************************** + +uint16_t reverseEnd16(uint16_t num) +{ + uint16_t reverse = (num & 0xff)<<8; + reverse += ((num & 0xff00) >> 8); + return reverse; +} + +uint32_t reverseEnd32(uint32_t num) +{ + uint32_t reverse = (reverseEnd16(num & 0xffff)) << 16; + reverse += reverseEnd16((uint16_t) ((num & 0xffff0000) >> 8)); + return reverse; +} + + + +//===================================================================== + +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<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_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); +} + +REGISTER_SIM_OBJECT("NSGigE", NSGigE) diff --git a/dev/ns_gige.hh b/dev/ns_gige.hh new file mode 100644 index 000000000..191c867ce --- /dev/null +++ b/dev/ns_gige.hh @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2004 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; + + /** pci settings */ + bool ioEnable; +#if 0 + bool memEnable; + bool bmEnable; +#endif + + /*** BASIC STRUCTURES FOR TX/RX ***/ + /* Data FIFOs */ + pktbuf_t txFifo; + pktbuf_t rxFifo; + + /** various helper vars */ + PacketPtr txPacket; + PacketPtr rxPacket; + uint8_t *txPacketBufPtr; + uint8_t *rxPacketBufPtr; + uint32_t txXferLen; + uint32_t rxXferLen; + bool rxDmaFree; + bool txDmaFree; + + /** DescCaches */ + ns_desc txDescCache; + ns_desc rxDescCache; + + /* tx State Machine */ + TxState txState; + /** Current Transmit Descriptor Done */ + bool CTDD; + /** 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(); + + 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]); + ~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..c87dfe960 --- /dev/null +++ b/dev/ns_gige_reg.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2004 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..8937b8e67 --- /dev/null +++ b/dev/pciconfigall.cc @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2004 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 "dev/pciconfigall.hh" +#include "dev/pcidev.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 "sim/system.hh" + +using namespace std; + +PciConfigAll::PciConfigAll(const string &name, Addr a, MemoryController *mmu, + HierParams *hier, Bus *bus) + : PioDevice(name), addr(a) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + + if (bus) { + pioInterface = newPioInterface(name, hier, bus, this, + &PciConfigAll::cacheAccess); + pioInterface->addAddrRange(addr, addr + size - 1); + } + + // 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) +{ + /* + * There is no state associated with this object that requires + * serialization. The only real state are the device pointers + * which are all setup by the constructor of the PciDev class + */ +} + +void +PciConfigAll::unserialize(Checkpoint *cp, const std::string §ion) +{ + /* + * There is no state associated with this object that requires + * serialization. The only real state are the device pointers + * which are all setup by the constructor of the PciDev class + */ +} + +Tick +PciConfigAll::cacheAccess(MemReqPtr &req) +{ + return curTick + 1000; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(PciConfigAll) + + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + Param<Addr> mask; + SimObjectParam<Bus*> io_bus; + SimObjectParam<HierParams *> hier; + +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"), + 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(PciConfigAll) + +CREATE_SIM_OBJECT(PciConfigAll) +{ + return new PciConfigAll(getInstanceName(), addr, mmu, hier, io_bus); +} + +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..356e62a3c --- /dev/null +++ b/dev/pciconfigall.hh @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2004 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 "dev/pcireg.h" +#include "base/range.hh" +#include "dev/io_device.hh" + + +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 PioDevice +{ + 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 + * @param hier object to store parameters universal the device hierarchy + * @param bus The bus that this device is attached to + */ + PciConfigAll(const std::string &name, Addr a, MemoryController *mmu, + HierParams *hier, Bus *bus); + + + /** + * 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); + + /** + * Return how long this access will take. + * @param req the memory request to calcuate + * @return Tick when the request is done + */ + Tick cacheAccess(MemReqPtr &req); +}; + +#endif // __PCICONFIGALL_HH__ diff --git a/dev/pcidev.cc b/dev/pcidev.cc new file mode 100644 index 000000000..7b13aac80 --- /dev/null +++ b/dev/pcidev.cc @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2004 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((uint8_t*)data, config.data + offset, sizeof(uint32_t)); + *(uint32_t*)data = htoa(*(uint32_t*)data); + 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((uint8_t*)data, config.data + offset, sizeof(uint16_t)); + *(uint16_t*)data = htoa(*(uint16_t*)data); + 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(BARSize, 6); + SERIALIZE_ARRAY(BARAddrs, 6); + SERIALIZE_ARRAY(config.data, 64); +} + +void +PciDev::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(BARSize, 6); + UNSERIALIZE_ARRAY(BARAddrs, 6); + UNSERIALIZE_ARRAY(config.data, 64); + + // Add the MMU mappings for the BARs + for (int i=0; i < 6; i++) { + if (BARAddrs[i] != 0) + mmu->add_child((FunctionalMemory *)this, + Range<Addr>(BARAddrs[i], + BARAddrs[i] + + BARSize[i] - 1)); + } +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(PciConfigData) + + Param<uint16_t> VendorID; + Param<uint16_t> DeviceID; + Param<uint16_t> Command; + Param<uint16_t> Status; + Param<uint8_t> Revision; + Param<uint8_t> ProgIF; + Param<uint8_t> SubClassCode; + Param<uint8_t> ClassCode; + Param<uint8_t> CacheLineSize; + Param<uint8_t> LatencyTimer; + Param<uint8_t> HeaderType; + Param<uint8_t> 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<uint16_t> SubsystemVendorID; + Param<uint16_t> SubsystemID; + Param<uint32_t> ExpansionROM; + Param<uint8_t> InterruptLine; + Param<uint8_t> InterruptPin; + Param<uint8_t> MinimumGrant; + Param<uint8_t> 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 = htoa(VendorID); + data->config.hdr.device = htoa(DeviceID); + data->config.hdr.command = htoa(Command); + data->config.hdr.status = htoa(Status); + data->config.hdr.revision = htoa(Revision); + data->config.hdr.progIF = htoa(ProgIF); + data->config.hdr.subClassCode = htoa(SubClassCode); + data->config.hdr.classCode = htoa(ClassCode); + data->config.hdr.cacheLineSize = htoa(CacheLineSize); + data->config.hdr.latencyTimer = htoa(LatencyTimer); + data->config.hdr.headerType = htoa(HeaderType); + data->config.hdr.bist = htoa(BIST); + + data->config.hdr.pci0.baseAddr0 = htoa(BAR0); + data->config.hdr.pci0.baseAddr1 = htoa(BAR1); + data->config.hdr.pci0.baseAddr2 = htoa(BAR2); + data->config.hdr.pci0.baseAddr3 = htoa(BAR3); + data->config.hdr.pci0.baseAddr4 = htoa(BAR4); + data->config.hdr.pci0.baseAddr5 = htoa(BAR5); + data->config.hdr.pci0.cardbusCIS = htoa(CardbusCIS); + data->config.hdr.pci0.subsystemVendorID = htoa(SubsystemVendorID); + data->config.hdr.pci0.subsystemID = htoa(SubsystemVendorID); + data->config.hdr.pci0.expansionROM = htoa(ExpansionROM); + data->config.hdr.pci0.interruptLine = htoa(InterruptLine); + data->config.hdr.pci0.interruptPin = htoa(InterruptPin); + data->config.hdr.pci0.minimumGrant = htoa(MinimumGrant); + data->config.hdr.pci0.maximumLatency = htoa(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..c0fe47ac4 --- /dev/null +++ b/dev/pcidev.hh @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2004 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/pcireg.h b/dev/pcireg.h index 2921c30be..8e8b7b451 100644 --- a/dev/pcireg.h +++ b/dev/pcireg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2001-2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -114,6 +114,11 @@ union PCIConfig { #define PCI_HEADER_TYPE 0x0E // Header Type ro #define PCI_BIST 0x0F // Built in self test rw +// some pci command reg bitfields +#define PCI_CMD_BME 0x04 // Bus master function enable +#define PCI_CMD_MSE 0x02 // Memory Space Access enable +#define PCI_CMD_IOSE 0x01 // I/O space enable + // Type 0 PCI offsets #define PCI0_BASE_ADDR0 0x10 // Base Address 0 rw #define PCI0_BASE_ADDR1 0x14 // Base Address 1 rw diff --git a/dev/platform.cc b/dev/platform.cc new file mode 100644 index 000000000..8515d543a --- /dev/null +++ b/dev/platform.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004 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..7920480bc --- /dev/null +++ b/dev/platform.hh @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004 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 Uart; + +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; + + /** Pointer to the UART, set by the uart */ + Uart *uart; + + 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; + virtual Tick intrFrequency() = 0; +}; + +#endif // __PLATFORM_HH_ diff --git a/dev/console.cc b/dev/simconsole.cc index 5e7b0abf6..a15057402 100644 --- a/dev/console.cc +++ b/dev/simconsole.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,7 +27,7 @@ */ /* @file - * User Console Definitions + * Implements the user interface to a serial console */ #include <sys/ioctl.h> @@ -45,10 +45,12 @@ #include "base/misc.hh" #include "base/socket.hh" #include "base/trace.hh" -#include "dev/console.hh" +#include "dev/simconsole.hh" #include "mem/functional_mem/memory_control.hh" #include "sim/builder.hh" #include "targetarch/ev5.hh" +#include "dev/uart.hh" +#include "dev/platform.hh" using namespace std; @@ -72,17 +74,17 @@ SimConsole::Event::process(int revent) SimConsole::SimConsole(const string &name, const string &file, int num) : SimObject(name), event(NULL), number(num), in_fd(-1), out_fd(-1), - listener(NULL), txbuf(16384), rxbuf(16384), outfile(NULL), + listener(NULL), txbuf(16384), rxbuf(16384), outfile(NULL) #if TRACING_ON == 1 - linebuf(16384), + , linebuf(16384) #endif - _status(0), _enable(0), intr(NULL) { if (!file.empty()) outfile = new ofstream(file.c_str()); if (outfile) outfile->setf(ios::unitbuf); + } SimConsole::~SimConsole() @@ -154,7 +156,8 @@ SimConsole::data() len = read(buf, sizeof(buf)); if (len) { rxbuf.write((char *)buf, len); - raiseInt(ReceiveInterrupt); + // Inform the UART there is data available + uart->dataAvailable(); } } @@ -162,7 +165,7 @@ size_t SimConsole::read(uint8_t *buf, size_t len) { if (in_fd < 0) - panic("SimConsole(read): Console not properly attached.\n"); + panic("Console not properly attached.\n"); size_t ret; do { @@ -171,7 +174,7 @@ SimConsole::read(uint8_t *buf, size_t len) if (ret < 0) - DPRINTFN("SimConsole(read): Read failed.\n"); + DPRINTFN("Read failed.\n"); if (ret <= 0) { detach(); @@ -186,7 +189,7 @@ size_t SimConsole::write(const uint8_t *buf, size_t len) { if (out_fd < 0) - panic("SimConsole(write): Console not properly attached.\n"); + panic("Console not properly attached.\n"); size_t ret; for (;;) { @@ -196,33 +199,12 @@ SimConsole::write(const uint8_t *buf, size_t len) break; if (errno != EINTR) - detach(); + detach(); } return ret; } -void -SimConsole::configTerm() -{ - struct termios ios; - - if (isatty(out_fd)) { - if (tcgetattr(out_fd, &ios) < 0) { - panic( "tcgetattr\n"); - } - ios.c_iflag &= ~(ISTRIP|ICRNL|IGNCR|ICRNL|IXOFF|IXON); - ios.c_oflag &= ~(OPOST); - ios.c_oflag &= (ONLCR); - ios.c_lflag &= ~(ISIG|ICANON|ECHO); - ios.c_cc[VMIN] = 1; - ios.c_cc[VTIME] = 0; - if (tcsetattr(out_fd, TCSANOW, &ios) < 0) { - panic( "tcsetattr\n"); - } - } -} - #define MORE_PENDING (ULL(1) << 61) #define RECEIVE_SUCCESS (ULL(0) << 62) #define RECEIVE_NONE (ULL(2) << 62) @@ -240,9 +222,6 @@ SimConsole::in(uint8_t &c) empty = rxbuf.empty(); } - if (empty) - clearInt(ReceiveInterrupt); - DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d, return: %d\n", isprint(c) ? c : ' ', c, !empty, ret); @@ -269,7 +248,7 @@ SimConsole::console_in() } void -SimConsole::out(char c, bool raise_int) +SimConsole::out(char c) { #if TRACING_ON == 1 if (DTRACE(Console)) { @@ -301,68 +280,9 @@ SimConsole::out(char c, bool raise_int) if (outfile) outfile->write(&c, 1); - if (raise_int) - raiseInt(TransmitInterrupt); - - DPRINTF(ConsoleVerbose, "out: \'%c\' %#02x", + DPRINTF(ConsoleVerbose, "out: \'%c\' %#02x\n", isprint(c) ? c : ' ', (int)c); - if (raise_int) - DPRINTF(ConsoleVerbose, "status: %#x\n", _status); - else - DPRINTF(ConsoleVerbose, "\n"); -} - -inline bool -MaskStatus(int status, int mask) -{ return (status & mask) != 0; } - -int -SimConsole::clearInt(int i) -{ - int old = _status; - _status &= ~i; - if (MaskStatus(old, _enable) != MaskStatus(_status, _enable) && intr) - intr->clear(TheISA::INTLEVEL_IRQ0); - - return old; -} - -void -SimConsole::raiseInt(int i) -{ - int old = _status; - _status |= i; - if (MaskStatus(old, _enable) != MaskStatus(_status, _enable) && intr) - intr->post(TheISA::INTLEVEL_IRQ0); -} - -void -SimConsole::initInt(IntrControl *i) -{ - if (intr) - panic("Console has already been initialized."); - - intr = i; -} - -void -SimConsole::setInt(int bits) -{ - int old; - - if (bits & ~(TransmitInterrupt | ReceiveInterrupt)) - panic("An interrupt was not set!"); - - old = _enable; - _enable |= bits; - - if (MaskStatus(_status, old) != MaskStatus(_status, _enable) && intr) { - if (MaskStatus(_status, _enable)) - intr->post(TheISA::INTLEVEL_IRQ0); - else - intr->clear(TheISA::INTLEVEL_IRQ0); - } } @@ -412,9 +332,6 @@ 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); return console; } diff --git a/dev/console.hh b/dev/simconsole.hh index d2bba4612..138e2e36a 100644 --- a/dev/console.hh +++ b/dev/simconsole.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,8 +42,13 @@ #include "sim/sim_object.hh" class ConsoleListener; +class Uart; + class SimConsole : public SimObject { + public: + Uart *uart; + protected: class Event : public PollEvent { @@ -62,8 +67,6 @@ class SimConsole : public SimObject int number; int in_fd; int out_fd; - - protected: ConsoleListener *listener; public: @@ -94,16 +97,6 @@ class SimConsole : public SimObject void write(uint8_t c) { write(&c, 1); } size_t write(const uint8_t *buf, size_t len); - void configTerm(); - - protected: - // interrupt status/enable - int _status; - int _enable; - - // interrupt handle - IntrControl *intr; - public: ///////////////// // OS interface @@ -126,22 +119,10 @@ class SimConsole : public SimObject uint64_t console_in(); // Send a character to the console - void out(char c, bool raise_int = true); - - enum { - TransmitInterrupt = 1, - ReceiveInterrupt = 2 - }; - - // Read the current interrupt status of this console. - int intStatus() { return _status; } - - // Set the interrupt enable bits. - int clearInt(int i); - void raiseInt(int i); + void out(char c); - void initInt(IntrControl *i); - void setInt(int bits); + //Ask the console if data is available + bool dataAvailable() { return !rxbuf.empty(); } virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); diff --git a/dev/simple_disk.cc b/dev/simple_disk.cc index aa6ff5b38..19e8683df 100644 --- a/dev/simple_disk.cc +++ b/dev/simple_disk.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2001-2003 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/simple_disk.hh b/dev/simple_disk.hh index 935865ba4..3031cdb8b 100644 --- a/dev/simple_disk.hh +++ b/dev/simple_disk.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2001-2003 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/dev/tsunami.cc b/dev/tsunami.cc new file mode 100644 index 000000000..c44da69b7 --- /dev/null +++ b/dev/tsunami.cc @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2004 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/simconsole.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; +} + +Tick +Tsunami::intrFrequency() +{ + return io->frequency(); +} + +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..db266d62d --- /dev/null +++ b/dev/tsunami.hh @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2004 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); + + /** + * Return the interrupting frequency to AlphaAccess + * @return frequency of RTC interrupts + */ + virtual Tick intrFrequency(); + + /** + * 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..0fbfcb56f --- /dev/null +++ b/dev/tsunami_cchip.cc @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2004 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 + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "dev/tsunami_cchip.hh" +#include "dev/tsunamireg.h" +#include "dev/tsunami.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 "cpu/intr_control.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; + +TsunamiCChip::TsunamiCChip(const string &name, Tsunami *t, Addr a, + MemoryController *mmu, HierParams *hier, Bus* bus) + : PioDevice(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; + } + + if (bus) { + pioInterface = newPioInterface(name, hier, bus, this, + &TsunamiCChip::cacheAccess); + pioInterface->addAddrRange(addr, addr + size - 1); + } + + 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: + *(uint64_t*)data = 0; + 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 value=%#x size=%d \n", + req->vaddr, *(uint64_t*)data, 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; + } + + // ignore NXMs + if (*(uint64_t*)data & 0x10000000) + 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); +} + +Tick +TsunamiCChip::cacheAccess(MemReqPtr &req) +{ + return curTick + 1000; +} + + +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; + SimObjectParam<Bus*> io_bus; + SimObjectParam<HierParams *> hier; + +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"), + 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(TsunamiCChip) + +CREATE_SIM_OBJECT(TsunamiCChip) +{ + return new TsunamiCChip(getInstanceName(), tsunami, addr, mmu, hier, io_bus); +} + +REGISTER_SIM_OBJECT("TsunamiCChip", TsunamiCChip) diff --git a/dev/tsunami_cchip.hh b/dev/tsunami_cchip.hh new file mode 100644 index 000000000..a358c98ba --- /dev/null +++ b/dev/tsunami_cchip.hh @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2004 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 "dev/tsunami.hh" +#include "base/range.hh" +#include "dev/io_device.hh" + +/* + * Tsunami CChip + */ +class TsunamiCChip : public PioDevice +{ + 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. + * @param hier object to store parameters universal the device hierarchy + * @param bus The bus that this device is attached to + */ + TsunamiCChip(const std::string &name, Tsunami *t, Addr a, + MemoryController *mmu, HierParams *hier, Bus *bus); + + /** + * 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); + + /** + * Return how long this access will take. + * @param req the memory request to calcuate + * @return Tick when the request is done + */ + Tick cacheAccess(MemReqPtr &req); +}; + +#endif // __TSUNAMI_CCHIP_HH__ diff --git a/dev/tsunami_io.cc b/dev/tsunami_io.cc new file mode 100644 index 000000000..4c798a852 --- /dev/null +++ b/dev/tsunami_io.cc @@ -0,0 +1,501 @@ +/* + * Copyright (c) 2004 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 "dev/tsunami_io.hh" +#include "dev/tsunami.hh" +#include "mem/bus/bus.hh" +#include "mem/bus/pio_interface.hh" +#include "mem/bus/pio_interface_impl.hh" +#include "sim/builder.hh" +#include "dev/tsunami_cchip.hh" +#include "dev/tsunamireg.h" +#include "mem/functional_mem/memory_control.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"; +} + +void +TsunamiIO::RTCEvent::serialize(std::ostream &os) +{ + Tick time = when(); + SERIALIZE_SCALAR(time); +} + +void +TsunamiIO::RTCEvent::unserialize(Checkpoint *cp, const std::string §ion) +{ + Tick time; + UNSERIALIZE_SCALAR(time); + reschedule(time); +} + + +// 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; +} + +void +TsunamiIO::ClockEvent::serialize(std::ostream &os) +{ + Tick time = scheduled() ? when() : 0; + SERIALIZE_SCALAR(time); + SERIALIZE_SCALAR(status); + SERIALIZE_SCALAR(mode); + SERIALIZE_SCALAR(interval); +} + +void +TsunamiIO::ClockEvent::unserialize(Checkpoint *cp, const std::string §ion) +{ + Tick time; + UNSERIALIZE_SCALAR(time); + UNSERIALIZE_SCALAR(status); + UNSERIALIZE_SCALAR(mode); + UNSERIALIZE_SCALAR(interval); + if (time) + schedule(time); +} + +TsunamiIO::TsunamiIO(const string &name, Tsunami *t, time_t init_time, + Addr a, MemoryController *mmu, HierParams *hier, Bus *bus) + : PioDevice(name), addr(a), tsunami(t), rtc(t) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + + if (bus) { + pioInterface = newPioInterface(name, hier, bus, this, + &TsunamiIO::cacheAccess); + pioInterface->addAddrRange(addr, addr + size - 1); + } + + // 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) +{ + +#if TRACING_ON + uint8_t dt = *(uint8_t*)data; + uint64_t dt64 = dt; +#endif + + 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"); + } +} + +Tick +TsunamiIO::cacheAccess(MemReqPtr &req) +{ + return curTick + 1000; +} + +void +TsunamiIO::serialize(std::ostream &os) +{ + SERIALIZE_SCALAR(timerData); + SERIALIZE_SCALAR(uip); + SERIALIZE_SCALAR(mask1); + SERIALIZE_SCALAR(mask2); + SERIALIZE_SCALAR(mode1); + SERIALIZE_SCALAR(mode2); + SERIALIZE_SCALAR(picr); + SERIALIZE_SCALAR(picInterrupting); + SERIALIZE_SCALAR(RTCAddress); + + // Serialize the timers + nameOut(os, csprintf("%s.timer0", name())); + timer0.serialize(os); + nameOut(os, csprintf("%s.timer2", name())); + timer2.serialize(os); + nameOut(os, csprintf("%s.rtc", name())); + rtc.serialize(os); +} + +void +TsunamiIO::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(timerData); + UNSERIALIZE_SCALAR(uip); + UNSERIALIZE_SCALAR(mask1); + UNSERIALIZE_SCALAR(mask2); + UNSERIALIZE_SCALAR(mode1); + UNSERIALIZE_SCALAR(mode2); + UNSERIALIZE_SCALAR(picr); + UNSERIALIZE_SCALAR(picInterrupting); + UNSERIALIZE_SCALAR(RTCAddress); + + // Unserialize the timers + timer0.unserialize(cp, csprintf("%s.timer0", section)); + timer2.unserialize(cp, csprintf("%s.timer2", section)); + rtc.unserialize(cp, csprintf("%s.rtc", section)); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiIO) + + SimObjectParam<Tsunami *> tsunami; + Param<time_t> time; + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + SimObjectParam<Bus*> io_bus; + SimObjectParam<HierParams *> hier; + +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"), + 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(TsunamiIO) + +CREATE_SIM_OBJECT(TsunamiIO) +{ + return new TsunamiIO(getInstanceName(), tsunami, time, addr, mmu, hier, + io_bus); +} + +REGISTER_SIM_OBJECT("TsunamiIO", TsunamiIO) diff --git a/dev/tsunami_io.hh b/dev/tsunami_io.hh new file mode 100644 index 000000000..75e5d764c --- /dev/null +++ b/dev/tsunami_io.hh @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2004 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 "dev/io_device.hh" +#include "base/range.hh" +#include "dev/tsunami.hh" +#include "sim/eventq.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 PioDevice +{ + 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(); + + /** + * 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); + }; + + /** + * 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(); + + /** + * 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); + }; + + /** 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 + * @return interrupt rate of the RTC + */ + Tick 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, HierParams *hier, Bus *bus); + + /** + * 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); + + + Tick cacheAccess(MemReqPtr &req); +}; + +#endif // __TSUNAMI_IO_HH__ diff --git a/dev/tsunami_pchip.cc b/dev/tsunami_pchip.cc new file mode 100644 index 000000000..b1346bb1a --- /dev/null +++ b/dev/tsunami_pchip.cc @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2004 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 (pci) + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "dev/tsunami_pchip.hh" +#include "dev/tsunamireg.h" +#include "dev/tsunami.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 "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, HierParams *hier, + Bus *bus) + : PioDevice(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; + } + + if (bus) { + pioInterface = newPioInterface(name, hier, bus, this, + &TsunamiPChip::cacheAccess); + pioInterface->addAddrRange(addr, addr + size - 1); + } + + + // initialize pchip control register + pctl = (ULL(0x1) << 20) | (ULL(0x1) << 32) | (ULL(0x2) << 36); + + //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: + *(uint64_t*)data = pctl; + 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: + *(uint64_t*)data = 0x00; + return No_Fault; + case TSDEV_PC_PERRMASK: + *(uint64_t*)data = 0x00; + return No_Fault; + 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: + pctl = *(uint64_t*)data; + 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: + return No_Fault; + 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; + +#if 0 + DPRINTF(IdeDisk, "Translation for bus address: %#x\n", busAddr); + for (int i = 0; i < 4; i++) { + DPRINTF(IdeDisk, "(%d) base:%#x mask:%#x\n", + i, wsba[i], wsm[i]); + + windowBase = wsba[i]; + windowMask = ~wsm[i] & (ULL(0xfff) << 20); + + if ((busAddr & windowMask) == (windowBase & windowMask)) { + DPRINTF(IdeDisk, "Would have matched %d (wb:%#x wm:%#x --> ba&wm:%#x wb&wm:%#x)\n", + i, windowBase, windowMask, (busAddr & windowMask), + (windowBase & windowMask)); + } + } +#endif + + for (int i = 0; i < 4; i++) { + + windowBase = wsba[i]; + windowMask = ~wsm[i] & (ULL(0xfff) << 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] & (ULL(0xfff) << 20)) >> 10) | ULL(0x3ff)); + baMask = (wsm[i] & (ULL(0xfff) << 20)) | (ULL(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 & ~ULL(0x1)) << 12) | (busAddr & ULL(0x1fff)); + + } else { + baMask = (wsm[i] & (ULL(0xfff) << 20)) | ULL(0xfffff); + tbaMask = ~baMask; + dmaAddr = (tba[i] & tbaMask) | (busAddr & baMask); + } + + return (dmaAddr & DMA_ADDR_MASK); + } + } + } + + // if no match was found, then return the original address + return busAddr; +} + +void +TsunamiPChip::serialize(std::ostream &os) +{ + SERIALIZE_SCALAR(pctl); + SERIALIZE_ARRAY(wsba, 4); + SERIALIZE_ARRAY(wsm, 4); + SERIALIZE_ARRAY(tba, 4); +} + +void +TsunamiPChip::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(pctl); + UNSERIALIZE_ARRAY(wsba, 4); + UNSERIALIZE_ARRAY(wsm, 4); + UNSERIALIZE_ARRAY(tba, 4); +} + +Tick +TsunamiPChip::cacheAccess(MemReqPtr &req) +{ + return curTick + 1000; +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiPChip) + + SimObjectParam<Tsunami *> tsunami; + SimObjectParam<MemoryController *> mmu; + Param<Addr> addr; + SimObjectParam<Bus*> io_bus; + SimObjectParam<HierParams *> hier; + +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"), + 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(TsunamiPChip) + +CREATE_SIM_OBJECT(TsunamiPChip) +{ + return new TsunamiPChip(getInstanceName(), tsunami, addr, mmu, hier, io_bus); +} + +REGISTER_SIM_OBJECT("TsunamiPChip", TsunamiPChip) diff --git a/dev/tsunami_pchip.hh b/dev/tsunami_pchip.hh new file mode 100644 index 000000000..af50872a0 --- /dev/null +++ b/dev/tsunami_pchip.hh @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2004 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 "dev/tsunami.hh" +#include "base/range.hh" +#include "dev/io_device.hh" + +/* + * Tsunami PChip + */ +class TsunamiPChip : public PioDevice +{ + 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; + + /** Pchip control register */ + uint64_t pctl; + + /** 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 + * @param hier object to store parameters universal the device hierarchy + * @param bus The bus that this device is attached to + */ + TsunamiPChip(const std::string &name, Tsunami *t, Addr a, + MemoryController *mmu, HierParams *hier, Bus *bus); + + /** + * 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); + + /** + * Return how long this access will take. + * @param req the memory request to calcuate + * @return Tick when the request is done + */ + Tick cacheAccess(MemReqPtr &req); +}; + +#endif // __TSUNAMI_PCHIP_HH__ diff --git a/dev/tsunamireg.h b/dev/tsunamireg.h new file mode 100644 index 000000000..876c6bf18 --- /dev/null +++ b/dev/tsunamireg.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2004 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 __TSUNAMIREG_H__ +#define __TSUNAMIREG_H__ + +#define ALPHA_K0SEG_BASE ULL(0xfffffc0000000000) + +// 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(0x00000000000) +#define PCHIP_PCI0_IO ULL(0x001FC000000) +#define TSUNAMI_UNCACHABLE_BIT ULL(0x80000000000) +#define TSUNAMI_PCI0_MEMORY TSUNAMI_UNCACHABLE_BIT + PCHIP_PCI0_MEMORY +#define TSUNAMI_PCI0_IO TSUNAMI_UNCACHABLE_BIT + PCHIP_PCI0_IO + + +// UART Defines +#define UART_IER_RDI 0x01 +#define UART_IER_THRI 0x02 +#define UART_IER_RLSI 0x04 + + +#define UART_LSR_TEMT 0x40 +#define UART_LSR_THRE 0x20 +#define UART_LSR_DR 0x01 + +#define UART_MCR_LOOP 0x10 + +#endif // __TSUNAMIREG_H__ diff --git a/dev/uart.cc b/dev/uart.cc new file mode 100644 index 000000000..30dde1984 --- /dev/null +++ b/dev/uart.cc @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2004 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 + * Implements a 8250 UART + */ + +#include <string> +#include <vector> + +#include "base/inifile.hh" +#include "base/str.hh" // for to_number +#include "base/trace.hh" +#include "dev/simconsole.hh" +#include "dev/uart.hh" +#include "dev/platform.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; + +Uart::IntrEvent::IntrEvent(Uart *u) + : Event(&mainEventQueue), uart(u) +{ + DPRINTF(Uart, "UART Interrupt Event Initilizing\n"); +} + +const char * +Uart::IntrEvent::description() +{ + return "uart interrupt delay event"; +} + +void +Uart::IntrEvent::process() +{ + if (UART_IER_THRI & uart->IER) { + DPRINTF(Uart, "UART InterEvent, interrupting\n"); + uart->platform->postConsoleInt(); + uart->status |= TX_INT; + } + else + DPRINTF(Uart, "UART InterEvent, not interrupting\n"); + +} + +void +Uart::IntrEvent::scheduleIntr() +{ + DPRINTF(Uart, "Scheduling IER interrupt\n"); + if (!scheduled()) + schedule(curTick + 300); + else + reschedule(curTick + 300); +} + +Uart::Uart(const string &name, SimConsole *c, MemoryController *mmu, Addr a, + Addr s, HierParams *hier, Bus *bus, Platform *p) + : PioDevice(name), addr(a), size(s), cons(c), intrEvent(this), platform(p) +{ + mmu->add_child(this, Range<Addr>(addr, addr + size)); + + + if (bus) { + pioInterface = newPioInterface(name, hier, bus, this, + &Uart::cacheAccess); + pioInterface->addAddrRange(addr, addr + size - 1); + } + + readAddr = 0; + IER = 0; + DLAB = 0; + LCR = 0; + MCR = 0; + status = 0; + + // set back pointers + cons->uart = this; + platform->uart = this; + +} + +Fault +Uart::read(MemReqPtr &req, uint8_t *data) +{ + Addr daddr = req->paddr - (addr & PA_IMPL_MASK); + DPRINTF(Uart, " read register %#x\n", daddr); + + + +#ifdef ALPHA_TLASER + + 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 0x80: // Status Register + if (readAddr == 3) { + readAddr = 0; + if (status & TX_INT) + *data = (1 << 4); + else if (status & RX_INT) + *data = (1 << 5); + else + DPRINTF(Uart, "spurious read\n"); + + } else { + *data = (1 << 2); + if (status & RX_INT) + *data |= (1 << 0); + } + break; + + case 0xc0: // Data register (RX) + if (!cons->dataAvailable()) + panic("No data to read"); + + cons->in(*data); + + if (!cons->dataAvailable()) { + platform->clearConsoleInt(); + status &= ~RX_INT; + } + + DPRINTF(Uart, "read data register \'%c\' %2x\n", + isprint(*data) ? *data : ' ', *data); + break; + } + + +#else + + + assert(req->size == 1); + + switch (daddr) { + case 0x0: + if (!(LCR & 0x80)) { // read byte + //assert(cons->dataAvailable()); + if (cons->dataAvailable()) + cons->in(*data); + else { + *(uint8_t*)data = 0; + // A limited amount of these are ok. + DPRINTF(Uart, "empty read of RX register\n"); + } + + if (cons->dataAvailable()) + platform->postConsoleInt(); + else + { + status &= ~RX_INT; + platform->clearConsoleInt(); + } + } else { // dll divisor latch + ; + } + break; + case 0x1: + if (!(LCR & 0x80)) { // Intr Enable Register(IER) + *(uint8_t*)data = IER; + } else { // DLM divisor latch MSB + ; + } + break; + case 0x2: // Intr Identification Register (IIR) + if (status) + *(uint8_t*)data = 1; + else + *(uint8_t*)data = 0; + break; + case 0x3: // Line Control Register (LCR) + *(uint8_t*)data = LCR; + break; + case 0x4: // Modem Control Register (MCR) + break; + case 0x5: // Line Status Register (LSR) + uint8_t lsr; + lsr = 0; + // check if there are any bytes to be read + if (cons->dataAvailable()) + lsr = UART_LSR_DR; + lsr |= UART_LSR_TEMT | UART_LSR_THRE; + *(uint8_t*)data = lsr; + break; + case 0x6: // Modem Status Register (MSR) + *(uint8_t*)data = 0; + break; + case 0x7: // Scratch Register (SCR) + *(uint8_t*)data = 0; // doesn't exist with at 8250. + break; + default: + panic("Tried to access a UART port that doesn't exist\n"); + break; + } + +#endif + return No_Fault; + +} + +Fault +Uart::write(MemReqPtr &req, const uint8_t *data) +{ + Addr daddr = req->paddr - (addr & PA_IMPL_MASK); + + DPRINTF(Uart, " write register %#x value %#x\n", daddr, *(uint8_t*)data); + +#ifdef ALPHA_TLASER + + switch (daddr) { + case 0x80: + readAddr = *data; + switch (*data) { + case 0x28: // Ack of TX + if ((status & TX_INT) == 0) + panic("Ack of transmit, though there was no interrupt"); + + status &= ~TX_INT; + platform->clearConsoleInt(); + break; + case 0x00: + case 0x01: + case 0x03: // going to read RR3 + case 0x12: + break; + default: + DPRINTF(Uart, "writing status register %#x \n", + *(uint64_t *)data); + break; + } + break; + + case 0xc0: // Data register (TX) + cons->out(*(uint64_t *)data); + platform->postConsoleInt(); + status |= TX_INT; + break; + } + + +#else + switch (daddr) { + case 0x0: + if (!(LCR & 0x80)) { // write byte + cons->out(*(uint64_t *)data); + platform->clearConsoleInt(); + status &= ~TX_INT; + if (UART_IER_THRI & IER) + intrEvent.scheduleIntr(); + } else { // dll divisor latch + ; + } + break; + case 0x1: + if (!(LCR & 0x80)) { // Intr Enable Register(IER) + IER = *(uint8_t*)data; + if ((UART_IER_THRI & IER) || ((UART_IER_RDI & IER) && cons->dataAvailable())) + platform->postConsoleInt(); + else + { + platform->clearConsoleInt(); + if (intrEvent.scheduled()) + intrEvent.deschedule(); + + } + if (!(UART_IER_THRI & IER)) + status &= ~TX_INT; + if (!((UART_IER_RDI & IER) && cons->dataAvailable())) + status &= ~RX_INT; + } else { // DLM divisor latch MSB + ; + } + break; + case 0x2: // FIFO Control Register (FCR) + break; + case 0x3: // Line Control Register (LCR) + LCR = *(uint8_t*)data; + break; + case 0x4: // Modem Control Register (MCR) + if (*(uint8_t*)data == (UART_MCR_LOOP | 0x0A)) + MCR = 0x9A; + break; + case 0x7: // Scratch Register (SCR) + // We are emulating a 8250 so we don't have a scratch reg + break; + default: + panic("Tried to access a UART port that doesn't exist\n"); + break; + } +#endif + + return No_Fault; +} + +void +Uart::dataAvailable() +{ +#ifdef ALPHA_TLASER + platform->postConsoleInt(); + status |= RX_INT; +#else + + // if the kernel wants an interrupt when we have data + if (IER & UART_IER_RDI) + { + platform->postConsoleInt(); + status |= RX_INT; + } + +#endif +} + +Tick +Uart::cacheAccess(MemReqPtr &req) +{ + return curTick + 1000; +} + +void +Uart::serialize(ostream &os) +{ +#ifdef ALPHA_TLASER + SERIALIZE_SCALAR(readAddr); + SERIALIZE_SCALAR(status); +#else + SERIALIZE_SCALAR(status); + SERIALIZE_SCALAR(IER); + SERIALIZE_SCALAR(DLAB); + SERIALIZE_SCALAR(LCR); + SERIALIZE_SCALAR(MCR); + Tick intrwhen; + if (intrEvent.scheduled()) + intrwhen = intrEvent.when(); + else + intrwhen = 0; + SERIALIZE_SCALAR(intrwhen); +#endif +} + +void +Uart::unserialize(Checkpoint *cp, const std::string §ion) +{ +#ifdef ALPHA_TLASER + UNSERIALIZE_SCALAR(readAddr); + UNSERIALIZE_SCALAR(status); +#else + UNSERIALIZE_SCALAR(status); + UNSERIALIZE_SCALAR(IER); + UNSERIALIZE_SCALAR(DLAB); + UNSERIALIZE_SCALAR(LCR); + UNSERIALIZE_SCALAR(MCR); + Tick intrwhen; + UNSERIALIZE_SCALAR(intrwhen); + if (intrwhen != 0) + intrEvent.schedule(intrwhen); +#endif + +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(Uart) + + SimObjectParam<SimConsole *> console; + SimObjectParam<MemoryController *> mmu; + SimObjectParam<Platform *> platform; + Param<Addr> addr; + Param<Addr> size; + SimObjectParam<Bus*> io_bus; + SimObjectParam<HierParams *> hier; + + +END_DECLARE_SIM_OBJECT_PARAMS(Uart) + +BEGIN_INIT_SIM_OBJECT_PARAMS(Uart) + + INIT_PARAM(console, "The console"), + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(platform, "Pointer to platfrom"), + INIT_PARAM(addr, "Device Address"), + INIT_PARAM_DFLT(size, "Device size", 0x8), + 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(Uart) + +CREATE_SIM_OBJECT(Uart) +{ + return new Uart(getInstanceName(), console, mmu, addr, size, hier, io_bus, + platform); +} + +REGISTER_SIM_OBJECT("Uart", Uart) diff --git a/dev/uart.hh b/dev/uart.hh new file mode 100644 index 000000000..83e1a758c --- /dev/null +++ b/dev/uart.hh @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2004 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 + * Defines a 8250 UART + */ + +#ifndef __TSUNAMI_UART_HH__ +#define __TSUNAMI_UART_HH__ + +#include "dev/tsunamireg.h" +#include "base/range.hh" +#include "dev/io_device.hh" + +class SimConsole; +class Platform; + +const int RX_INT = 0x1; +const int TX_INT = 0x2; + + +class Uart : public PioDevice +{ + + private: + Addr addr; + Addr size; + SimConsole *cons; + + + protected: + int readAddr; // tlaser only + uint8_t IER, DLAB, LCR, MCR; + int status; + + class IntrEvent : public Event + { + protected: + Uart *uart; + public: + IntrEvent(Uart *u); + virtual void process(); + virtual const char *description(); + void scheduleIntr(); + }; + + IntrEvent intrEvent; + Platform *platform; + + public: + Uart(const string &name, SimConsole *c, MemoryController *mmu, + Addr a, Addr s, HierParams *hier, Bus *bus, Platform *p); + + Fault read(MemReqPtr &req, uint8_t *data); + Fault write(MemReqPtr &req, const uint8_t *data); + + + /** + * Inform the uart that there is data available. + */ + void dataAvailable(); + + + /** + * Return if we have an interrupt pending + * @return interrupt status + */ + bool intStatus() { return status ? true : false; } + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + /** + * Return how long this access will take. + * @param req the memory request to calcuate + * @return Tick when the request is done + */ + Tick cacheAccess(MemReqPtr &req); +}; + +#endif // __TSUNAMI_UART_HH__ |