diff options
Diffstat (limited to 'src/dev')
110 files changed, 8597 insertions, 2763 deletions
diff --git a/src/dev/CopyEngine.py b/src/dev/CopyEngine.py new file mode 100644 index 000000000..29d9a23dd --- /dev/null +++ b/src/dev/CopyEngine.py @@ -0,0 +1,59 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Ali Saidi + +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +from Pci import PciDevice + +class CopyEngine(PciDevice): + type = 'CopyEngine' + VendorID = 0x8086 + DeviceID = 0x1a38 + Revision = 0xA2 # CM2 stepping (newest listed) + SubsystemID = 0 + SubsystemVendorID = 0 + Status = 0x0000 + SubClassCode = 0x08 + ClassCode = 0x80 + ProgIF = 0x00 + MaximumLatency = 0x00 + MinimumGrant = 0xff + InterruptLine = 0x20 + InterruptPin = 0x01 + BAR0Size = '1kB' + + ChanCnt = Param.UInt8(4, "Number of DMA channels that exist on device") + XferCap = Param.MemorySize('4kB', "Number of bits of transfer size that are supported") + + + clock = Param.Clock('500MHz', "Clock speed of the device") + latBeforeBegin = Param.Latency('20ns', "Latency after a DMA command is seen before it's proccessed") + latAfterCompletion = Param.Latency('20ns', "Latency after a DMA command is complete before it's reported as such") + + diff --git a/src/dev/DiskImage.py b/src/dev/DiskImage.py index af2407458..92eb0553c 100644 --- a/src/dev/DiskImage.py +++ b/src/dev/DiskImage.py @@ -42,3 +42,4 @@ class CowDiskImage(DiskImage): child = Param.DiskImage(RawDiskImage(read_only=True), "child image") table_size = Param.Int(65536, "initial table size") + image_file = "" diff --git a/src/dev/Ethernet.py b/src/dev/Ethernet.py index 2beb0d537..d73d56d03 100644 --- a/src/dev/Ethernet.py +++ b/src/dev/Ethernet.py @@ -67,6 +67,7 @@ class EtherDevice(PciDevice): interface = Port("Ethernet Interrface") class IGbE(EtherDevice): + # Base class for two IGbE adapters listed above type = 'IGbE' hardware_address = Param.EthernetAddr(NextEthernetAddr, "Ethernet Hardware Address") @@ -80,7 +81,6 @@ class IGbE(EtherDevice): "Number of enteries in the rx descriptor cache") clock = Param.Clock('500MHz', "Clock speed of the device") VendorID = 0x8086 - DeviceID = 0x1075 SubsystemID = 0x1008 SubsystemVendorID = 0x8086 Status = 0x0000 @@ -98,6 +98,28 @@ class IGbE(EtherDevice): InterruptLine = 0x1e InterruptPin = 0x01 BAR0Size = '128kB' + wb_delay = Param.Latency('10ns', "delay before desc writeback occurs") + fetch_delay = Param.Latency('10ns', "delay before desc fetch occurs") + fetch_comp_delay = Param.Latency('10ns', "delay after desc fetch occurs") + wb_comp_delay = Param.Latency('10ns', "delay after desc wb occurs") + tx_read_delay = Param.Latency('0ns', "delay after tx dma read") + rx_write_delay = Param.Latency('0ns', "delay after rx dma read") + phy_pid = Param.UInt16("Phy PID that corresponds to device ID") + phy_epid = Param.UInt16("Phy EPID that corresponds to device ID") + +class IGbE_e1000(IGbE): + # Older Intel 8254x based gigabit ethernet adapter + # Uses Intel e1000 driver + DeviceID = 0x1075 + phy_pid = 0x02A8 + phy_epid = 0x0380 + +class IGbE_igb(IGbE): + # Newer Intel 8257x based gigabit ethernet adapter + # Uses Intel igb driver and in theory supports packet splitting and LRO + DeviceID = 0x10C9 + phy_pid = 0x0141 + phy_epid = 0x0CC0 class EtherDevBase(EtherDevice): type = 'EtherDevBase' @@ -153,8 +175,7 @@ class NSGigE(EtherDevBase): class Sinic(EtherDevBase): type = 'Sinic' - cxx_namespace = 'Sinic' - cxx_class = 'Device' + cxx_class = 'Sinic::Device' rx_max_copy = Param.MemorySize('1514B', "rx max copy") tx_max_copy = Param.MemorySize('16kB', "tx max copy") @@ -164,6 +185,9 @@ class Sinic(EtherDevBase): tx_fifo_high_mark = Param.MemorySize('384kB', "tx fifo high threshold") tx_fifo_threshold = Param.MemorySize('128kB', "tx fifo low threshold") virtual_count = Param.UInt32(1, "Virtualized SINIC") + zero_copy_size = Param.UInt32(64, "Bytes to copy if below threshold") + zero_copy_threshold = Param.UInt32(256, + "Only zero copy above this threshold") zero_copy = Param.Bool(False, "Zero copy receive") delay_copy = Param.Bool(False, "Delayed copy transmit") virtual_addr = Param.Bool(False, "Virtual addressing") diff --git a/src/dev/Pci.py b/src/dev/Pci.py index b50e1b15c..bd67d82fb 100644 --- a/src/dev/Pci.py +++ b/src/dev/Pci.py @@ -73,6 +73,12 @@ class PciDevice(DmaDevice): BAR3Size = Param.MemorySize32('0B', "Base Address Register 3 Size") BAR4Size = Param.MemorySize32('0B', "Base Address Register 4 Size") BAR5Size = Param.MemorySize32('0B', "Base Address Register 5 Size") + BAR0LegacyIO = Param.Bool(False, "Whether BAR0 is hardwired legacy IO") + BAR1LegacyIO = Param.Bool(False, "Whether BAR1 is hardwired legacy IO") + BAR2LegacyIO = Param.Bool(False, "Whether BAR2 is hardwired legacy IO") + BAR3LegacyIO = Param.Bool(False, "Whether BAR3 is hardwired legacy IO") + BAR4LegacyIO = Param.Bool(False, "Whether BAR4 is hardwired legacy IO") + BAR5LegacyIO = Param.Bool(False, "Whether BAR5 is hardwired legacy IO") CardbusCIS = Param.UInt32(0x00, "Cardbus Card Information Structure") SubsystemID = Param.UInt16(0x00, "Subsystem ID") diff --git a/src/dev/SConscript b/src/dev/SConscript index 0aba8ac35..c09ec3dcd 100644 --- a/src/dev/SConscript +++ b/src/dev/SConscript @@ -33,19 +33,22 @@ Import('*') if env['FULL_SYSTEM']: SimObject('BadDevice.py') + SimObject('CopyEngine.py') SimObject('Device.py') SimObject('DiskImage.py') SimObject('Ethernet.py') SimObject('Ide.py') SimObject('Pci.py') SimObject('Platform.py') - SimObject('SimConsole.py') SimObject('SimpleDisk.py') + SimObject('Terminal.py') SimObject('Uart.py') Source('baddev.cc') + Source('copy_engine.cc') Source('disk_image.cc') Source('etherbus.cc') + Source('etherdevice.cc') Source('etherdump.cc') Source('etherint.cc') Source('etherlink.cc') @@ -54,24 +57,25 @@ if env['FULL_SYSTEM']: Source('i8254xGBe.cc') Source('ide_ctrl.cc') Source('ide_disk.cc') + Source('intel_8254_timer.cc') Source('io_device.cc') Source('isa_fake.cc') + Source('mc146818.cc') Source('ns_gige.cc') Source('pciconfigall.cc') Source('pcidev.cc') Source('pktfifo.cc') Source('platform.cc') - Source('simconsole.cc') Source('simple_disk.cc') Source('sinic.cc') + Source('terminal.cc') Source('uart.cc') Source('uart8250.cc') - TraceFlag('Console') - TraceFlag('ConsoleVerbose') TraceFlag('DiskImageRead') TraceFlag('DiskImageWrite') TraceFlag('DMA') + TraceFlag('DMACopyEngine') TraceFlag('Ethernet') TraceFlag('EthernetCksum') TraceFlag('EthernetDMA') @@ -83,17 +87,21 @@ if env['FULL_SYSTEM']: TraceFlag('EthernetSM') TraceFlag('IdeCtrl') TraceFlag('IdeDisk') + TraceFlag('Intel8254Timer') TraceFlag('IsaFake') + TraceFlag('MC146818') TraceFlag('PCIDEV') TraceFlag('PciConfigAll') TraceFlag('SimpleDisk') TraceFlag('SimpleDiskData') + TraceFlag('Terminal') + TraceFlag('TerminalVerbose') TraceFlag('Uart') CompoundFlag('DiskImageAll', [ 'DiskImageRead', 'DiskImageWrite' ]) CompoundFlag('EthernetAll', [ 'Ethernet', 'EthernetPIO', 'EthernetDMA', 'EthernetData' , 'EthernetDesc', 'EthernetIntr', 'EthernetSM', - 'EthernetCksum' ]) + 'EthernetCksum', 'EthernetEEPROM' ]) CompoundFlag('EthernetNoData', [ 'Ethernet', 'EthernetPIO', 'EthernetDesc', 'EthernetIntr', 'EthernetSM', 'EthernetCksum' ]) CompoundFlag('IdeAll', [ 'IdeCtrl', 'IdeDisk' ]) diff --git a/src/dev/SimConsole.py b/src/dev/Terminal.py index bb8420527..d67019198 100644 --- a/src/dev/SimConsole.py +++ b/src/dev/Terminal.py @@ -30,10 +30,9 @@ from m5.SimObject import SimObject from m5.params import * from m5.proxy import * -class SimConsole(SimObject): - type = 'SimConsole' - append_name = Param.Bool(True, "append name() to filename") +class Terminal(SimObject): + type = 'Terminal' intr_control = Param.IntrControl(Parent.any, "interrupt controller") port = Param.TcpPort(3456, "listen port") - number = Param.Int(0, "console number") - output = Param.String('console', "file to dump output to") + number = Param.Int(0, "terminal number") + output = Param.Bool(True, "Enable output dump to file") diff --git a/src/dev/Uart.py b/src/dev/Uart.py index e32517a4c..c5db3c42f 100644 --- a/src/dev/Uart.py +++ b/src/dev/Uart.py @@ -34,7 +34,7 @@ from Device import BasicPioDevice class Uart(BasicPioDevice): type = 'Uart' abstract = True - sim_console = Param.SimConsole(Parent.any, "The console") + terminal = Param.Terminal(Parent.any, "The terminal") class Uart8250(Uart): type = 'Uart8250' diff --git a/src/dev/alpha/AlphaConsole.py b/src/dev/alpha/AlphaBackdoor.py index 43c7ef954..fa9627164 100644 --- a/src/dev/alpha/AlphaConsole.py +++ b/src/dev/alpha/AlphaBackdoor.py @@ -30,9 +30,9 @@ from m5.params import * from m5.proxy import * from Device import BasicPioDevice -class AlphaConsole(BasicPioDevice): - type = 'AlphaConsole' +class AlphaBackdoor(BasicPioDevice): + type = 'AlphaBackdoor' cpu = Param.BaseCPU(Parent.cpu[0], "Processor") disk = Param.SimpleDisk("Simple Disk") - sim_console = Param.SimConsole(Parent.any, "The Simulator Console") + terminal = Param.Terminal(Parent.any, "The console terminal") system = Param.AlphaSystem(Parent.any, "system object") diff --git a/src/dev/alpha/SConscript b/src/dev/alpha/SConscript index 2292c3c57..4dbb73903 100644 --- a/src/dev/alpha/SConscript +++ b/src/dev/alpha/SConscript @@ -32,15 +32,14 @@ Import('*') if env['FULL_SYSTEM'] and env['TARGET_ISA'] == 'alpha': - SimObject('AlphaConsole.py') + SimObject('AlphaBackdoor.py') SimObject('Tsunami.py') - Source('console.cc') + Source('backdoor.cc') Source('tsunami.cc') Source('tsunami_cchip.cc') Source('tsunami_io.cc') Source('tsunami_pchip.cc') - TraceFlag('AlphaConsole') - TraceFlag('MC146818') + TraceFlag('AlphaBackdoor') TraceFlag('Tsunami') diff --git a/src/dev/alpha/Tsunami.py b/src/dev/alpha/Tsunami.py index 484976c09..5440486b6 100644 --- a/src/dev/alpha/Tsunami.py +++ b/src/dev/alpha/Tsunami.py @@ -28,12 +28,12 @@ from m5.params import * from m5.proxy import * +from BadDevice import BadDevice +from AlphaBackdoor import AlphaBackdoor from Device import BasicPioDevice, IsaFake, BadAddr +from Pci import PciConfigAll from Platform import Platform -from AlphaConsole import AlphaConsole from Uart import Uart8250 -from Pci import PciConfigAll -from BadDevice import BadDevice class TsunamiCChip(BasicPioDevice): type = 'TsunamiCChip' @@ -87,7 +87,7 @@ class Tsunami(Platform): fb = BadDevice(pio_addr=0x801fc0003d0, devicename='FrameBuffer') io = TsunamiIO(pio_addr=0x801fc000000) uart = Uart8250(pio_addr=0x801fc0003f8) - console = AlphaConsole(pio_addr=0x80200000000, disk=Parent.simple_disk) + backdoor = AlphaBackdoor(pio_addr=0x80200000000, disk=Parent.simple_disk) # Attach I/O devices to specified bus object. Can't do this # earlier, since the bus object itself is typically defined at the @@ -120,4 +120,4 @@ class Tsunami(Platform): self.fb.pio = bus.port self.io.pio = bus.port self.uart.pio = bus.port - self.console.pio = bus.port + self.backdoor.pio = bus.port diff --git a/src/dev/alpha/access.h b/src/dev/alpha/access.h index 4adeaf84b..72eb4950a 100644 --- a/src/dev/alpha/access.h +++ b/src/dev/alpha/access.h @@ -45,31 +45,31 @@ typedef unsigned long uint64_t; // This structure hacked up from simos struct AlphaAccess { - uint32_t last_offset; // 00: must be first field - uint32_t version; // 04: - uint32_t numCPUs; // 08: - uint32_t intrClockFrequency; // 0C: Hz - uint64_t cpuClock; // 10: MHz - uint64_t mem_size; // 18: + uint32_t last_offset; // 00: must be first field + uint32_t version; // 04: + uint32_t numCPUs; // 08: + uint32_t intrClockFrequency; // 0C: Hz + uint64_t cpuClock; // 10: MHz + uint64_t mem_size; // 18: // Loaded kernel - uint64_t kernStart; // 20: - uint64_t kernEnd; // 28: - uint64_t entryPoint; // 30: + uint64_t kernStart; // 20: + uint64_t kernEnd; // 28: + uint64_t entryPoint; // 30: // console disk stuff - uint64_t diskUnit; // 38: - uint64_t diskCount; // 40: - uint64_t diskPAddr; // 48: - uint64_t diskBlock; // 50: - uint64_t diskOperation; // 58: + uint64_t diskUnit; // 38: + uint64_t diskCount; // 40: + uint64_t diskPAddr; // 48: + uint64_t diskBlock; // 50: + uint64_t diskOperation; // 58: // console simple output stuff - uint64_t outputChar; // 60: Placeholder for output - uint64_t inputChar; // 68: Placeholder for input + uint64_t outputChar; // 60: Placeholder for output + uint64_t inputChar; // 68: Placeholder for input // MP boot - uint64_t cpuStack[64]; // 70: + uint64_t cpuStack[64]; // 70: }; #endif // __ALPHA_ACCESS_H__ diff --git a/src/dev/alpha/console.cc b/src/dev/alpha/backdoor.cc index 493a21f99..66f682e66 100644 --- a/src/dev/alpha/console.cc +++ b/src/dev/alpha/backdoor.cc @@ -32,7 +32,7 @@ */ /** @file - * Alpha Console Definition + * Alpha Console Backdoor Definition */ #include <cstddef> @@ -44,21 +44,21 @@ #include "base/trace.hh" #include "cpu/base.hh" #include "cpu/thread_context.hh" -#include "dev/alpha/console.hh" +#include "dev/alpha/backdoor.hh" #include "dev/platform.hh" -#include "dev/simconsole.hh" #include "dev/simple_disk.hh" +#include "dev/terminal.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" #include "mem/physical.hh" -#include "params/AlphaConsole.hh" +#include "params/AlphaBackdoor.hh" #include "sim/sim_object.hh" using namespace std; using namespace AlphaISA; -AlphaConsole::AlphaConsole(const Params *p) - : BasicPioDevice(p), disk(p->disk), console(p->sim_console), +AlphaBackdoor::AlphaBackdoor(const Params *p) + : BasicPioDevice(p), disk(p->disk), terminal(p->terminal), system(p->system), cpu(p->cpu) { @@ -81,10 +81,10 @@ AlphaConsole::AlphaConsole(const Params *p) } void -AlphaConsole::startup() +AlphaBackdoor::startup() { system->setAlphaAccess(pioAddr); - alphaAccess->numCPUs = system->getNumCPUs(); + alphaAccess->numCPUs = system->numContexts(); alphaAccess->kernStart = system->getKernelStart(); alphaAccess->kernEnd = system->getKernelEnd(); alphaAccess->entryPoint = system->getKernelEntry(); @@ -94,7 +94,7 @@ AlphaConsole::startup() } Tick -AlphaConsole::read(PacketPtr pkt) +AlphaBackdoor::read(PacketPtr pkt) { /** XXX Do we want to push the addr munging to a bus brige or something? So @@ -132,14 +132,14 @@ AlphaConsole::read(PacketPtr pkt) */ pkt->setBadAddress(); } - DPRINTF(AlphaConsole, "read: offset=%#x val=%#x\n", daddr, + DPRINTF(AlphaBackdoor, "read: offset=%#x val=%#x\n", daddr, pkt->get<uint32_t>()); break; case sizeof(uint64_t): switch (daddr) { case offsetof(AlphaAccess, inputChar): - pkt->set(console->console_in()); + pkt->set(terminal->console_in()); break; case offsetof(AlphaAccess, cpuClock): pkt->set(alphaAccess->cpuClock); @@ -183,7 +183,7 @@ AlphaConsole::read(PacketPtr pkt) else panic("Unknown 64bit access, %#x\n", daddr); } - DPRINTF(AlphaConsole, "read: offset=%#x val=%#x\n", daddr, + DPRINTF(AlphaBackdoor, "read: offset=%#x val=%#x\n", daddr, pkt->get<uint64_t>()); break; default: @@ -193,7 +193,7 @@ AlphaConsole::read(PacketPtr pkt) } Tick -AlphaConsole::write(PacketPtr pkt) +AlphaBackdoor::write(PacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; @@ -228,7 +228,7 @@ AlphaConsole::write(PacketPtr pkt) break; case offsetof(AlphaAccess, outputChar): - console->out((char)(val & 0xff)); + terminal->out((char)(val & 0xff)); break; default: @@ -248,7 +248,7 @@ AlphaConsole::write(PacketPtr pkt) } void -AlphaConsole::Access::serialize(ostream &os) +AlphaBackdoor::Access::serialize(ostream &os) { SERIALIZE_SCALAR(last_offset); SERIALIZE_SCALAR(version); @@ -270,7 +270,7 @@ AlphaConsole::Access::serialize(ostream &os) } void -AlphaConsole::Access::unserialize(Checkpoint *cp, const std::string §ion) +AlphaBackdoor::Access::unserialize(Checkpoint *cp, const std::string §ion) { UNSERIALIZE_SCALAR(last_offset); UNSERIALIZE_SCALAR(version); @@ -292,19 +292,19 @@ AlphaConsole::Access::unserialize(Checkpoint *cp, const std::string §ion) } void -AlphaConsole::serialize(ostream &os) +AlphaBackdoor::serialize(ostream &os) { alphaAccess->serialize(os); } void -AlphaConsole::unserialize(Checkpoint *cp, const std::string §ion) +AlphaBackdoor::unserialize(Checkpoint *cp, const std::string §ion) { alphaAccess->unserialize(cp, section); } -AlphaConsole * -AlphaConsoleParams::create() +AlphaBackdoor * +AlphaBackdoorParams::create() { - return new AlphaConsole(this); + return new AlphaBackdoor(this); } diff --git a/src/dev/alpha/console.hh b/src/dev/alpha/backdoor.hh index e77a7fad6..ad3c79823 100644 --- a/src/dev/alpha/console.hh +++ b/src/dev/alpha/backdoor.hh @@ -29,28 +29,28 @@ */ /** @file - * System Console Interface + * System Console Backdoor Interface */ -#ifndef __ALPHA_CONSOLE_HH__ -#define __ALPHA_CONSOLE_HH__ +#ifndef __DEV_ALPHA_BACKDOOR_HH__ +#define __DEV_ALPHA_BACKDOOR_HH__ #include "base/range.hh" #include "dev/alpha/access.h" #include "dev/io_device.hh" -#include "params/AlphaConsole.hh" +#include "params/AlphaBackdoor.hh" #include "sim/host.hh" #include "sim/sim_object.hh" class BaseCPU; -class SimConsole; +class Terminal; class AlphaSystem; class SimpleDisk; /** * Memory mapped interface to the system console. This device * represents a shared data region between the OS Kernel and the - * System Console. + * System Console Backdoor. * * The system console is a small standalone program that is initially * run when the system boots. It contains the necessary code to @@ -72,7 +72,7 @@ class SimpleDisk; * primarily used doing boot before the kernel has loaded its device * drivers. */ -class AlphaConsole : public BasicPioDevice +class AlphaBackdoor : public BasicPioDevice { protected: struct Access : public AlphaAccess @@ -90,7 +90,7 @@ class AlphaConsole : public BasicPioDevice SimpleDisk *disk; /** the system console (the terminal) is accessable from the console */ - SimConsole *console; + Terminal *terminal; /** a pointer to the system we are running in */ AlphaSystem *system; @@ -99,8 +99,8 @@ class AlphaConsole : public BasicPioDevice BaseCPU *cpu; public: - typedef AlphaConsoleParams Params; - AlphaConsole(const Params *p); + typedef AlphaBackdoorParams Params; + AlphaBackdoor(const Params *p); const Params * params() const @@ -123,4 +123,4 @@ class AlphaConsole : public BasicPioDevice virtual void unserialize(Checkpoint *cp, const std::string §ion); }; -#endif // __ALPHA_CONSOLE_HH__ +#endif // __DEV_ALPHA_BACKDOOR_HH__ diff --git a/src/dev/alpha/tsunami.cc b/src/dev/alpha/tsunami.cc index 5bc0de5da..b6478fe22 100644 --- a/src/dev/alpha/tsunami.cc +++ b/src/dev/alpha/tsunami.cc @@ -37,11 +37,11 @@ #include <vector> #include "cpu/intr_control.hh" -#include "dev/simconsole.hh" #include "dev/alpha/tsunami_cchip.hh" #include "dev/alpha/tsunami_pchip.hh" #include "dev/alpha/tsunami_io.hh" #include "dev/alpha/tsunami.hh" +#include "dev/terminal.hh" #include "sim/system.hh" using namespace std; @@ -96,11 +96,23 @@ Tsunami::pciToDma(Addr pciAddr) const Addr -Tsunami::calcConfigAddr(int bus, int dev, int func) +Tsunami::calcPciConfigAddr(int bus, int dev, int func) { return pchip->calcConfigAddr(bus, dev, func); } +Addr +Tsunami::calcPciIOAddr(Addr addr) +{ + return pchip->calcIOAddr(addr); +} + +Addr +Tsunami::calcPciMemAddr(Addr addr) +{ + return pchip->calcMemAddr(addr); +} + void Tsunami::serialize(std::ostream &os) { diff --git a/src/dev/alpha/tsunami.hh b/src/dev/alpha/tsunami.hh index 44c5d41a4..64aafe533 100644 --- a/src/dev/alpha/tsunami.hh +++ b/src/dev/alpha/tsunami.hh @@ -116,7 +116,17 @@ class Tsunami : public Platform /** * Calculate the configuration address given a bus/dev/func. */ - virtual Addr calcConfigAddr(int bus, int dev, int func); + virtual Addr calcPciConfigAddr(int bus, int dev, int func); + + /** + * Calculate the address for an IO location on the PCI bus. + */ + virtual Addr calcPciIOAddr(Addr addr); + + /** + * Calculate the address for a memory location on the PCI bus. + */ + virtual Addr calcPciMemAddr(Addr addr); /** * Serialize this object to the given output stream. diff --git a/src/dev/alpha/tsunami_cchip.cc b/src/dev/alpha/tsunami_cchip.cc index 891fe17da..52a2aea14 100644 --- a/src/dev/alpha/tsunami_cchip.cc +++ b/src/dev/alpha/tsunami_cchip.cc @@ -109,8 +109,14 @@ TsunamiCChip::read(PacketPtr pkt) panic("TSDEV_CC_MTR not implemeted\n"); break; case TSDEV_CC_MISC: - pkt->set((ipint << 8) & 0xF | (itint << 4) & 0xF | - (pkt->req->getCpuNum() & 0x3)); + pkt->set(((ipint << 8) & 0xF) | ((itint << 4) & 0xF) | + (pkt->req->contextId() & 0x3)); + // currently, FS cannot handle MT so contextId and + // cpuId are effectively the same, don't know if it will + // matter if FS becomes MT enabled. I suspect no because + // we are currently able to boot up to 64 procs anyway + // which would render the CPUID of this register useless + // anyway break; case TSDEV_CC_AAR0: case TSDEV_CC_AAR1: diff --git a/src/dev/alpha/tsunami_io.cc b/src/dev/alpha/tsunami_io.cc index 710aca48d..9c88904e3 100644 --- a/src/dev/alpha/tsunami_io.cc +++ b/src/dev/alpha/tsunami_io.cc @@ -42,7 +42,6 @@ #include "base/time.hh" #include "base/trace.hh" -#include "dev/pitreg.h" #include "dev/rtcreg.h" #include "dev/alpha/tsunami_cchip.hh" #include "dev/alpha/tsunami.hh" @@ -57,386 +56,15 @@ using namespace std; //Should this be AlphaISA? using namespace TheISA; -TsunamiIO::RTC::RTC(const string &n, Tsunami* tsunami, - const TsunamiIO::Params *p) - : _name(n), event(tsunami, p->frequency), addr(0) +TsunamiIO::RTC::RTC(const string &n, const TsunamiIOParams *p) + : MC146818(p->tsunami, n, p->time, p->year_is_bcd, p->frequency), + tsunami(p->tsunami) { - memset(clock_data, 0, sizeof(clock_data)); - stat_regA = RTCA_32768HZ | RTCA_1024HZ; - stat_regB = RTCB_PRDC_IE |RTCB_BIN | RTCB_24HR; - - year = p->time.tm_year; - - if (p->year_is_bcd) { - // The datasheet says that the year field can be either BCD or - // years since 1900. Linux seems to be happy with years since - // 1900. - year = year % 100; - int tens = year / 10; - int ones = year % 10; - year = (tens << 4) + ones; - } - - // Unix is 0-11 for month, data seet says start at 1 - mon = p->time.tm_mon + 1; - mday = p->time.tm_mday; - hour = p->time.tm_hour; - min = p->time.tm_min; - sec = p->time.tm_sec; - - // Datasheet says 1 is sunday - wday = p->time.tm_wday + 1; - - DPRINTFN("Real-time clock set to %s", asctime(&p->time)); -} - -void -TsunamiIO::RTC::writeAddr(const uint8_t data) -{ - if (data <= RTC_STAT_REGD) - addr = data; - else - panic("RTC addresses over 0xD are not implemented.\n"); -} - -void -TsunamiIO::RTC::writeData(const uint8_t data) -{ - if (addr < RTC_STAT_REGA) - clock_data[addr] = data; - else { - switch (addr) { - case RTC_STAT_REGA: - if (data != (RTCA_32768HZ | RTCA_1024HZ)) - panic("Unimplemented RTC register A value write!\n"); - stat_regA = data; - break; - case RTC_STAT_REGB: - if ((data & ~(RTCB_PRDC_IE | RTCB_SQWE)) != (RTCB_BIN | RTCB_24HR)) - panic("Write to RTC reg B bits that are not implemented!\n"); - - if (data & RTCB_PRDC_IE) { - if (!event.scheduled()) - event.scheduleIntr(); - } else { - if (event.scheduled()) - event.deschedule(); - } - stat_regB = data; - break; - case RTC_STAT_REGC: - case RTC_STAT_REGD: - panic("RTC status registers C and D are not implemented.\n"); - break; - } - } -} - -uint8_t -TsunamiIO::RTC::readData() -{ - if (addr < RTC_STAT_REGA) - return clock_data[addr]; - else { - switch (addr) { - case RTC_STAT_REGA: - // toggle UIP bit for linux - stat_regA ^= RTCA_UIP; - return stat_regA; - break; - case RTC_STAT_REGB: - return stat_regB; - break; - case RTC_STAT_REGC: - case RTC_STAT_REGD: - return 0x00; - break; - default: - panic("Shouldn't be here"); - } - } -} - -void -TsunamiIO::RTC::serialize(const string &base, ostream &os) -{ - paramOut(os, base + ".addr", addr); - arrayParamOut(os, base + ".clock_data", clock_data, sizeof(clock_data)); - paramOut(os, base + ".stat_regA", stat_regA); - paramOut(os, base + ".stat_regB", stat_regB); -} - -void -TsunamiIO::RTC::unserialize(const string &base, Checkpoint *cp, - const string §ion) -{ - paramIn(cp, section, base + ".addr", addr); - arrayParamIn(cp, section, base + ".clock_data", clock_data, - sizeof(clock_data)); - paramIn(cp, section, base + ".stat_regA", stat_regA); - paramIn(cp, section, base + ".stat_regB", stat_regB); - - // We're not unserializing the event here, but we need to - // rescehedule the event since curTick was moved forward by the - // checkpoint - event.reschedule(curTick + event.interval); -} - -TsunamiIO::RTC::RTCEvent::RTCEvent(Tsunami*t, Tick i) - : Event(&mainEventQueue), tsunami(t), interval(i) -{ - DPRINTF(MC146818, "RTC Event Initilizing\n"); - schedule(curTick + interval); -} - -void -TsunamiIO::RTC::RTCEvent::scheduleIntr() -{ - schedule(curTick + interval); -} - -void -TsunamiIO::RTC::RTCEvent::process() -{ - DPRINTF(MC146818, "RTC Timer Interrupt\n"); - schedule(curTick + interval); - //Actually interrupt the processor here - tsunami->cchip->postRTC(); -} - -const char * -TsunamiIO::RTC::RTCEvent::description() const -{ - return "tsunami RTC interrupt"; -} - -TsunamiIO::PITimer::PITimer(const string &name) - : _name(name), counter0(name + ".counter0"), counter1(name + ".counter1"), - counter2(name + ".counter2") -{ - counter[0] = &counter0; - counter[1] = &counter0; - counter[2] = &counter0; -} - -void -TsunamiIO::PITimer::writeControl(const uint8_t data) -{ - int rw; - int sel; - - sel = GET_CTRL_SEL(data); - - if (sel == PIT_READ_BACK) - panic("PITimer Read-Back Command is not implemented.\n"); - - rw = GET_CTRL_RW(data); - - if (rw == PIT_RW_LATCH_COMMAND) - counter[sel]->latchCount(); - else { - counter[sel]->setRW(rw); - counter[sel]->setMode(GET_CTRL_MODE(data)); - counter[sel]->setBCD(GET_CTRL_BCD(data)); - } -} - -void -TsunamiIO::PITimer::serialize(const string &base, ostream &os) -{ - // serialize the counters - counter0.serialize(base + ".counter0", os); - counter1.serialize(base + ".counter1", os); - counter2.serialize(base + ".counter2", os); -} - -void -TsunamiIO::PITimer::unserialize(const string &base, Checkpoint *cp, - const string §ion) -{ - // unserialze the counters - counter0.unserialize(base + ".counter0", cp, section); - counter1.unserialize(base + ".counter1", cp, section); - counter2.unserialize(base + ".counter2", cp, section); -} - -TsunamiIO::PITimer::Counter::Counter(const string &name) - : _name(name), event(this), count(0), latched_count(0), period(0), - mode(0), output_high(false), latch_on(false), read_byte(LSB), - write_byte(LSB) -{ - -} - -void -TsunamiIO::PITimer::Counter::latchCount() -{ - // behave like a real latch - if(!latch_on) { - latch_on = true; - read_byte = LSB; - latched_count = count; - } -} - -uint8_t -TsunamiIO::PITimer::Counter::read() -{ - if (latch_on) { - switch (read_byte) { - case LSB: - read_byte = MSB; - return (uint8_t)latched_count; - break; - case MSB: - read_byte = LSB; - latch_on = false; - return latched_count >> 8; - break; - default: - panic("Shouldn't be here"); - } - } else { - switch (read_byte) { - case LSB: - read_byte = MSB; - return (uint8_t)count; - break; - case MSB: - read_byte = LSB; - return count >> 8; - break; - default: - panic("Shouldn't be here"); - } - } -} - -void -TsunamiIO::PITimer::Counter::write(const uint8_t data) -{ - switch (write_byte) { - case LSB: - count = (count & 0xFF00) | data; - - if (event.scheduled()) - event.deschedule(); - output_high = false; - write_byte = MSB; - break; - - case MSB: - count = (count & 0x00FF) | (data << 8); - period = count; - - if (period > 0) { - DPRINTF(Tsunami, "Timer set to curTick + %d\n", - count * event.interval); - event.schedule(curTick + count * event.interval); - } - write_byte = LSB; - break; - } -} - -void -TsunamiIO::PITimer::Counter::setRW(int rw_val) -{ - if (rw_val != PIT_RW_16BIT) - panic("Only LSB/MSB read/write is implemented.\n"); -} - -void -TsunamiIO::PITimer::Counter::setMode(int mode_val) -{ - if(mode_val != PIT_MODE_INTTC && mode_val != PIT_MODE_RATEGEN && - mode_val != PIT_MODE_SQWAVE) - panic("PIT mode %#x is not implemented: \n", mode_val); - - mode = mode_val; -} - -void -TsunamiIO::PITimer::Counter::setBCD(int bcd_val) -{ - if (bcd_val != PIT_BCD_FALSE) - panic("PITimer does not implement BCD counts.\n"); -} - -bool -TsunamiIO::PITimer::Counter::outputHigh() -{ - return output_high; -} - -void -TsunamiIO::PITimer::Counter::serialize(const string &base, ostream &os) -{ - paramOut(os, base + ".count", count); - paramOut(os, base + ".latched_count", latched_count); - paramOut(os, base + ".period", period); - paramOut(os, base + ".mode", mode); - paramOut(os, base + ".output_high", output_high); - paramOut(os, base + ".latch_on", latch_on); - paramOut(os, base + ".read_byte", read_byte); - paramOut(os, base + ".write_byte", write_byte); - - Tick event_tick = 0; - if (event.scheduled()) - event_tick = event.when(); - paramOut(os, base + ".event_tick", event_tick); -} - -void -TsunamiIO::PITimer::Counter::unserialize(const string &base, Checkpoint *cp, - const string §ion) -{ - paramIn(cp, section, base + ".count", count); - paramIn(cp, section, base + ".latched_count", latched_count); - paramIn(cp, section, base + ".period", period); - paramIn(cp, section, base + ".mode", mode); - paramIn(cp, section, base + ".output_high", output_high); - paramIn(cp, section, base + ".latch_on", latch_on); - paramIn(cp, section, base + ".read_byte", read_byte); - paramIn(cp, section, base + ".write_byte", write_byte); - - Tick event_tick; - paramIn(cp, section, base + ".event_tick", event_tick); - if (event_tick) - event.schedule(event_tick); -} - -TsunamiIO::PITimer::Counter::CounterEvent::CounterEvent(Counter* c_ptr) - : Event(&mainEventQueue) -{ - interval = (Tick)(Clock::Float::s / 1193180.0); - counter = c_ptr; -} - -void -TsunamiIO::PITimer::Counter::CounterEvent::process() -{ - DPRINTF(Tsunami, "Timer Interrupt\n"); - switch (counter->mode) { - case PIT_MODE_INTTC: - counter->output_high = true; - case PIT_MODE_RATEGEN: - case PIT_MODE_SQWAVE: - break; - default: - panic("Unimplemented PITimer mode.\n"); - } -} - -const char * -TsunamiIO::PITimer::Counter::CounterEvent::description() const -{ - return "tsunami 8254 Interval timer"; } TsunamiIO::TsunamiIO(const Params *p) - : BasicPioDevice(p), tsunami(p->tsunami), pitimer(p->name + "pitimer"), - rtc(p->name + ".rtc", p->tsunami, p) + : BasicPioDevice(p), tsunami(p->tsunami), + pitimer(this, p->name + "pitimer"), rtc(p->name + ".rtc", p) { pioSize = 0x100; @@ -486,19 +114,19 @@ TsunamiIO::read(PacketPtr pkt) pkt->set(0x00); break; case TSDEV_TMR0_DATA: - pkt->set(pitimer.counter0.read()); + pkt->set(pitimer.readCounter(0)); break; case TSDEV_TMR1_DATA: - pkt->set(pitimer.counter1.read()); + pkt->set(pitimer.readCounter(1)); break; case TSDEV_TMR2_DATA: - pkt->set(pitimer.counter2.read()); + pkt->set(pitimer.readCounter(2)); break; case TSDEV_RTC_DATA: - pkt->set(rtc.readData()); + pkt->set(rtc.readData(rtcAddr)); break; case TSDEV_CTRL_PORTB: - if (pitimer.counter2.outputHigh()) + if (pitimer.outputHigh(2)) pkt->set(PORTB_SPKR_HIGH); else pkt->set(0x00); @@ -561,22 +189,22 @@ TsunamiIO::write(PacketPtr pkt) mode2 = pkt->get<uint8_t>(); break; case TSDEV_TMR0_DATA: - pitimer.counter0.write(pkt->get<uint8_t>()); + pitimer.writeCounter(0, pkt->get<uint8_t>()); break; case TSDEV_TMR1_DATA: - pitimer.counter1.write(pkt->get<uint8_t>()); + pitimer.writeCounter(1, pkt->get<uint8_t>()); break; case TSDEV_TMR2_DATA: - pitimer.counter2.write(pkt->get<uint8_t>()); + pitimer.writeCounter(2, pkt->get<uint8_t>()); break; case TSDEV_TMR_CTRL: pitimer.writeControl(pkt->get<uint8_t>()); break; case TSDEV_RTC_ADDR: - rtc.writeAddr(pkt->get<uint8_t>()); + rtcAddr = pkt->get<uint8_t>(); break; case TSDEV_RTC_DATA: - rtc.writeData(pkt->get<uint8_t>()); + rtc.writeData(rtcAddr, pkt->get<uint8_t>()); break; case TSDEV_KBD: case TSDEV_DMA1_CMND: @@ -623,6 +251,7 @@ TsunamiIO::clearPIC(uint8_t bitvector) void TsunamiIO::serialize(ostream &os) { + SERIALIZE_SCALAR(rtcAddr); SERIALIZE_SCALAR(timerData); SERIALIZE_SCALAR(mask1); SERIALIZE_SCALAR(mask2); @@ -639,6 +268,7 @@ TsunamiIO::serialize(ostream &os) void TsunamiIO::unserialize(Checkpoint *cp, const string §ion) { + UNSERIALIZE_SCALAR(rtcAddr); UNSERIALIZE_SCALAR(timerData); UNSERIALIZE_SCALAR(mask1); UNSERIALIZE_SCALAR(mask2); diff --git a/src/dev/alpha/tsunami_io.hh b/src/dev/alpha/tsunami_io.hh index 05c4ee910..b6d63322b 100644 --- a/src/dev/alpha/tsunami_io.hh +++ b/src/dev/alpha/tsunami_io.hh @@ -39,6 +39,8 @@ #include "base/range.hh" #include "dev/alpha/tsunami.hh" +#include "dev/intel_8254_timer.hh" +#include "dev/mc146818.hh" #include "dev/io_device.hh" #include "params/TsunamiIO.hh" #include "sim/eventq.hh" @@ -53,223 +55,19 @@ class TsunamiIO : public BasicPioDevice struct tm tm; protected: - /** Real-Time Clock (MC146818) */ - class RTC - { - private: - /** Event for RTC periodic interrupt */ - struct RTCEvent : public Event - { - /** A pointer back to tsunami to create interrupt the processor. */ - Tsunami* tsunami; - Tick interval; - - RTCEvent(Tsunami* t, Tick i); - - /** Schedule the RTC periodic interrupt */ - void scheduleIntr(); - - /** Event process to occur at interrupt*/ - virtual void process(); - - /** Event description */ - virtual const char *description() const; - }; - - private: - std::string _name; - const std::string &name() const { return _name; } - /** RTC periodic interrupt event */ - RTCEvent event; - - /** Current RTC register address/index */ - int addr; - - /** Data for real-time clock function */ - union { - uint8_t clock_data[10]; - - struct { - uint8_t sec; - uint8_t sec_alrm; - uint8_t min; - uint8_t min_alrm; - uint8_t hour; - uint8_t hour_alrm; - uint8_t wday; - uint8_t mday; - uint8_t mon; - uint8_t year; - }; - }; - - /** RTC status register A */ - uint8_t stat_regA; - - /** RTC status register B */ - uint8_t stat_regB; - - public: - RTC(const std::string &name, Tsunami* tsunami, - const TsunamiIOParams *params); - - /** RTC address port: write address of RTC RAM data to access */ - void writeAddr(const uint8_t data); - - /** RTC write data */ - void writeData(const uint8_t data); - - /** RTC read data */ - uint8_t readData(); - - /** - * Serialize this object to the given output stream. - * @param base The base name of the counter object. - * @param os The stream to serialize to. - */ - void serialize(const std::string &base, std::ostream &os); - - /** - * Reconstruct the state of this object from a checkpoint. - * @param base The base name of the counter object. - * @param cp The checkpoint use. - * @param section The section name of this object - */ - void unserialize(const std::string &base, Checkpoint *cp, - const std::string §ion); - }; - - /** Programmable Interval Timer (Intel 8254) */ - class PITimer + class RTC : public MC146818 { - /** Counter element for PIT */ - class Counter - { - /** Event for counter interrupt */ - class CounterEvent : public Event - { - private: - /** Pointer back to Counter */ - Counter* counter; - Tick interval; - - public: - CounterEvent(Counter*); - - /** Event process */ - virtual void process(); - - /** Event description */ - virtual const char *description() const; - - friend class Counter; - }; - - private: - std::string _name; - const std::string &name() const { return _name; } - - CounterEvent event; - - /** Current count value */ - uint16_t count; - - /** Latched count */ - uint16_t latched_count; - - /** Interrupt period */ - uint16_t period; - - /** Current mode of operation */ - uint8_t mode; - - /** Output goes high when the counter reaches zero */ - bool output_high; - - /** State of the count latch */ - bool latch_on; - - /** Set of values for read_byte and write_byte */ - enum {LSB, MSB}; - - /** Determine which byte of a 16-bit count value to read/write */ - uint8_t read_byte, write_byte; - - public: - Counter(const std::string &name); - - /** Latch the current count (if one is not already latched) */ - void latchCount(); - - /** Set the read/write mode */ - void setRW(int rw_val); - - /** Set operational mode */ - void setMode(int mode_val); - - /** Set count encoding */ - void setBCD(int bcd_val); - - /** Read a count byte */ - uint8_t read(); - - /** Write a count byte */ - void write(const uint8_t data); - - /** Is the output high? */ - bool outputHigh(); - - /** - * Serialize this object to the given output stream. - * @param base The base name of the counter object. - * @param os The stream to serialize to. - */ - void serialize(const std::string &base, std::ostream &os); - - /** - * Reconstruct the state of this object from a checkpoint. - * @param base The base name of the counter object. - * @param cp The checkpoint use. - * @param section The section name of this object - */ - void unserialize(const std::string &base, Checkpoint *cp, - const std::string §ion); - }; - - private: - std::string _name; - const std::string &name() const { return _name; } - - /** PIT has three seperate counters */ - Counter *counter[3]; - public: - /** Public way to access individual counters (avoid array accesses) */ - Counter counter0; - Counter counter1; - Counter counter2; - - PITimer(const std::string &name); - - /** Write control word */ - void writeControl(const uint8_t data); + Tsunami *tsunami; + RTC(const std::string &n, const TsunamiIOParams *p); - /** - * Serialize this object to the given output stream. - * @param base The base name of the counter object. - * @param os The stream to serialize to. - */ - void serialize(const std::string &base, std::ostream &os); - - /** - * Reconstruct the state of this object from a checkpoint. - * @param base The base name of the counter object. - * @param cp The checkpoint use. - * @param section The section name of this object - */ - void unserialize(const std::string &base, Checkpoint *cp, - const std::string §ion); + protected: + void handleEvent() + { + //Actually interrupt the processor here + tsunami->cchip->postRTC(); + } }; /** Mask of the PIC1 */ @@ -294,10 +92,12 @@ class TsunamiIO : public BasicPioDevice Tsunami *tsunami; /** Intel 8253 Periodic Interval Timer */ - PITimer pitimer; + Intel8254Timer pitimer; RTC rtc; + uint8_t rtcAddr; + /** 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. diff --git a/src/dev/alpha/tsunami_pchip.cc b/src/dev/alpha/tsunami_pchip.cc index 83bcf8e65..4df7d1150 100644 --- a/src/dev/alpha/tsunami_pchip.cc +++ b/src/dev/alpha/tsunami_pchip.cc @@ -300,6 +300,7 @@ TsunamiPChip::translatePciToDma(Addr busAddr) // if no match was found, then return the original address return busAddr; } + Addr TsunamiPChip::calcConfigAddr(int bus, int dev, int func) { @@ -310,7 +311,17 @@ TsunamiPChip::calcConfigAddr(int bus, int dev, int func) return TsunamiPciBus0Config | (func << 8) | (dev << 11); } +Addr +TsunamiPChip::calcIOAddr(Addr addr) +{ + return TSUNAMI_PCI0_IO + addr; +} +Addr +TsunamiPChip::calcMemAddr(Addr addr) +{ + return TSUNAMI_PCI0_MEMORY + addr; +} void TsunamiPChip::serialize(std::ostream &os) diff --git a/src/dev/alpha/tsunami_pchip.hh b/src/dev/alpha/tsunami_pchip.hh index 53050565f..d31a28dbe 100644 --- a/src/dev/alpha/tsunami_pchip.hh +++ b/src/dev/alpha/tsunami_pchip.hh @@ -84,6 +84,8 @@ class TsunamiPChip : public BasicPioDevice Addr translatePciToDma(Addr busAddr); Addr calcConfigAddr(int bus, int dev, int func); + Addr calcIOAddr(Addr addr); + Addr calcMemAddr(Addr addr); virtual Tick read(PacketPtr pkt); virtual Tick write(PacketPtr pkt); diff --git a/src/dev/copy_engine.cc b/src/dev/copy_engine.cc new file mode 100644 index 000000000..3c759ac1d --- /dev/null +++ b/src/dev/copy_engine.cc @@ -0,0 +1,764 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + */ + +/* @file + * Device model for Intel's I/O AT DMA copy engine. + */ + +#include <algorithm> + +#include "base/cp_annotate.hh" +#include "base/trace.hh" +#include "dev/copy_engine.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" +#include "params/CopyEngine.hh" +#include "sim/stats.hh" +#include "sim/system.hh" + +using namespace CopyEngineReg; +using namespace std; + +CopyEngine::CopyEngine(const Params *p) + : PciDev(p) +{ + // All Reg regs are initialized to 0 by default + regs.chanCount = p->ChanCnt; + regs.xferCap = findMsbSet(p->XferCap); + regs.attnStatus = 0; + + if (regs.chanCount > 64) + fatal("CopyEngine interface doesn't support more than 64 DMA engines\n"); + + for (int x = 0; x < regs.chanCount; x++) { + CopyEngineChannel *ch = new CopyEngineChannel(this, x); + chan.push_back(ch); + } +} + + +CopyEngine::CopyEngineChannel::CopyEngineChannel(CopyEngine *_ce, int cid) + : ce(_ce), channelId(cid), busy(false), underReset(false), + refreshNext(false), latBeforeBegin(ce->params()->latBeforeBegin), + latAfterCompletion(ce->params()->latAfterCompletion), + completionDataReg(0), nextState(Idle), drainEvent(NULL), + fetchCompleteEvent(this), addrCompleteEvent(this), + readCompleteEvent(this), writeCompleteEvent(this), + statusCompleteEvent(this) + +{ + cr.status.dma_transfer_status(3); + cr.descChainAddr = 0; + cr.completionAddr = 0; + + curDmaDesc = new DmaDesc; + memset(curDmaDesc, 0, sizeof(DmaDesc)); + copyBuffer = new uint8_t[ce->params()->XferCap]; +} + +CopyEngine::~CopyEngine() +{ + for (int x = 0; x < chan.size(); x++) { + delete chan[x]; + } +} + +CopyEngine::CopyEngineChannel::~CopyEngineChannel() +{ + delete curDmaDesc; + delete [] copyBuffer; + delete cePort; +} + +void +CopyEngine::init() +{ + PciDev::init(); + for (int x = 0; x < chan.size(); x++) + chan[x]->init(); +} + +void +CopyEngine::CopyEngineChannel::init() +{ + Port *peer; + + cePort = new DmaPort(ce, ce->sys); + peer = ce->dmaPort->getPeer()->getOwner()->getPort(""); + peer->setPeer(cePort); + cePort->setPeer(peer); +} + +void +CopyEngine::CopyEngineChannel::recvCommand() +{ + if (cr.command.start_dma()) { + assert(!busy); + cr.status.dma_transfer_status(0); + nextState = DescriptorFetch; + fetchAddress = cr.descChainAddr; + if (ce->getState() == SimObject::Running) + fetchDescriptor(cr.descChainAddr); + } else if (cr.command.append_dma()) { + if (!busy) { + nextState = AddressFetch; + if (ce->getState() == SimObject::Running) + fetchNextAddr(lastDescriptorAddr); + } else + refreshNext = true; + } else if (cr.command.reset_dma()) { + if (busy) + underReset = true; + else { + cr.status.dma_transfer_status(3); + nextState = Idle; + } + } else if (cr.command.resume_dma() || cr.command.abort_dma() || + cr.command.suspend_dma()) + panic("Resume, Abort, and Suspend are not supported\n"); + cr.command(0); +} + +Tick +CopyEngine::read(PacketPtr pkt) +{ + int bar; + Addr daddr; + + if (!getBAR(pkt->getAddr(), bar, daddr)) + panic("Invalid PCI memory access to unmapped memory.\n"); + + // Only Memory register BAR is allowed + assert(bar == 0); + + int size = pkt->getSize(); + if (size != sizeof(uint64_t) && size != sizeof(uint32_t) && + size != sizeof(uint16_t) && size != sizeof(uint8_t)) { + panic("Unknown size for MMIO access: %d\n", pkt->getSize()); + } + + DPRINTF(DMACopyEngine, "Read device register %#X size: %d\n", daddr, size); + + pkt->allocate(); + + /// + /// Handle read of register here + /// + + if (daddr < 0x80) { + switch (daddr) { + case GEN_CHANCOUNT: + assert(size == sizeof(regs.chanCount)); + pkt->set<uint8_t>(regs.chanCount); + break; + case GEN_XFERCAP: + assert(size == sizeof(regs.xferCap)); + pkt->set<uint8_t>(regs.xferCap); + break; + case GEN_INTRCTRL: + assert(size == sizeof(uint8_t)); + pkt->set<uint8_t>(regs.intrctrl()); + regs.intrctrl.master_int_enable(0); + break; + case GEN_ATTNSTATUS: + assert(size == sizeof(regs.attnStatus)); + pkt->set<uint32_t>(regs.attnStatus); + regs.attnStatus = 0; + break; + default: + panic("Read request to unknown register number: %#x\n", daddr); + } + pkt->makeAtomicResponse(); + return pioDelay; + } + + + // Find which channel we're accessing + int chanid = 0; + daddr -= 0x80; + while (daddr >= 0x80) { + chanid++; + daddr -= 0x80; + } + + if (chanid >= regs.chanCount) + panic("Access to channel %d (device only configured for %d channels)", + chanid, regs.chanCount); + + /// + /// Channel registers are handled here + /// + chan[chanid]->channelRead(pkt, daddr, size); + + pkt->makeAtomicResponse(); + return pioDelay; +} + +void +CopyEngine::CopyEngineChannel::channelRead(Packet *pkt, Addr daddr, int size) +{ + switch (daddr) { + case CHAN_CONTROL: + assert(size == sizeof(uint16_t)); + pkt->set<uint16_t>(cr.ctrl()); + cr.ctrl.in_use(1); + break; + case CHAN_STATUS: + assert(size == sizeof(uint64_t)); + pkt->set<uint64_t>(cr.status() | ~busy); + break; + case CHAN_CHAINADDR: + assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); + if (size == sizeof(uint64_t)) + pkt->set<uint64_t>(cr.descChainAddr); + else + pkt->set<uint32_t>(bits(cr.descChainAddr,0,31)); + break; + case CHAN_CHAINADDR_HIGH: + assert(size == sizeof(uint32_t)); + pkt->set<uint32_t>(bits(cr.descChainAddr,32,63)); + break; + case CHAN_COMMAND: + assert(size == sizeof(uint8_t)); + pkt->set<uint32_t>(cr.command()); + break; + case CHAN_CMPLNADDR: + assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); + if (size == sizeof(uint64_t)) + pkt->set<uint64_t>(cr.completionAddr); + else + pkt->set<uint32_t>(bits(cr.completionAddr,0,31)); + break; + case CHAN_CMPLNADDR_HIGH: + assert(size == sizeof(uint32_t)); + pkt->set<uint32_t>(bits(cr.completionAddr,32,63)); + break; + case CHAN_ERROR: + assert(size == sizeof(uint32_t)); + pkt->set<uint32_t>(cr.error()); + break; + default: + panic("Read request to unknown channel register number: (%d)%#x\n", + channelId, daddr); + } +} + + +Tick +CopyEngine::write(PacketPtr pkt) +{ + int bar; + Addr daddr; + + + if (!getBAR(pkt->getAddr(), bar, daddr)) + panic("Invalid PCI memory access to unmapped memory.\n"); + + // Only Memory register BAR is allowed + assert(bar == 0); + + int size = pkt->getSize(); + + /// + /// Handle write of register here + /// + + if (size == sizeof(uint64_t)) { + uint64_t val M5_VAR_USED = pkt->get<uint64_t>(); + DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val); + } else if (size == sizeof(uint32_t)) { + uint32_t val M5_VAR_USED = pkt->get<uint32_t>(); + DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val); + } else if (size == sizeof(uint16_t)) { + uint16_t val M5_VAR_USED = pkt->get<uint16_t>(); + DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val); + } else if (size == sizeof(uint8_t)) { + uint8_t val M5_VAR_USED = pkt->get<uint8_t>(); + DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val); + } else { + panic("Unknown size for MMIO access: %d\n", size); + } + + if (daddr < 0x80) { + switch (daddr) { + case GEN_CHANCOUNT: + case GEN_XFERCAP: + case GEN_ATTNSTATUS: + DPRINTF(DMACopyEngine, "Warning, ignorning write to register %x\n", + daddr); + break; + case GEN_INTRCTRL: + regs.intrctrl.master_int_enable(bits(pkt->get<uint8_t>(),0,1)); + break; + default: + panic("Read request to unknown register number: %#x\n", daddr); + } + pkt->makeAtomicResponse(); + return pioDelay; + } + + // Find which channel we're accessing + int chanid = 0; + daddr -= 0x80; + while (daddr >= 0x80) { + chanid++; + daddr -= 0x80; + } + + if (chanid >= regs.chanCount) + panic("Access to channel %d (device only configured for %d channels)", + chanid, regs.chanCount); + + /// + /// Channel registers are handled here + /// + chan[chanid]->channelWrite(pkt, daddr, size); + + pkt->makeAtomicResponse(); + return pioDelay; +} + +void +CopyEngine::CopyEngineChannel::channelWrite(Packet *pkt, Addr daddr, int size) +{ + switch (daddr) { + case CHAN_CONTROL: + assert(size == sizeof(uint16_t)); + int old_int_disable; + old_int_disable = cr.ctrl.interrupt_disable(); + cr.ctrl(pkt->get<uint16_t>()); + if (cr.ctrl.interrupt_disable()) + cr.ctrl.interrupt_disable(0); + else + cr.ctrl.interrupt_disable(old_int_disable); + break; + case CHAN_STATUS: + assert(size == sizeof(uint64_t)); + DPRINTF(DMACopyEngine, "Warning, ignorning write to register %x\n", + daddr); + break; + case CHAN_CHAINADDR: + assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); + if (size == sizeof(uint64_t)) + cr.descChainAddr = pkt->get<uint64_t>(); + else + cr.descChainAddr = (uint64_t)pkt->get<uint32_t>() | + (cr.descChainAddr & ~mask(32)); + DPRINTF(DMACopyEngine, "Chain Address %x\n", cr.descChainAddr); + break; + case CHAN_CHAINADDR_HIGH: + assert(size == sizeof(uint32_t)); + cr.descChainAddr = ((uint64_t)pkt->get<uint32_t>() <<32) | + (cr.descChainAddr & mask(32)); + DPRINTF(DMACopyEngine, "Chain Address %x\n", cr.descChainAddr); + break; + case CHAN_COMMAND: + assert(size == sizeof(uint8_t)); + cr.command(pkt->get<uint8_t>()); + recvCommand(); + break; + case CHAN_CMPLNADDR: + assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); + if (size == sizeof(uint64_t)) + cr.completionAddr = pkt->get<uint64_t>(); + else + cr.completionAddr = pkt->get<uint32_t>() | + (cr.completionAddr & ~mask(32)); + break; + case CHAN_CMPLNADDR_HIGH: + assert(size == sizeof(uint32_t)); + cr.completionAddr = ((uint64_t)pkt->get<uint32_t>() <<32) | + (cr.completionAddr & mask(32)); + break; + case CHAN_ERROR: + assert(size == sizeof(uint32_t)); + cr.error(~pkt->get<uint32_t>() & cr.error()); + break; + default: + panic("Read request to unknown channel register number: (%d)%#x\n", + channelId, daddr); + } +} + +void +CopyEngine::regStats() +{ + using namespace Stats; + bytesCopied + .init(regs.chanCount) + .name(name() + ".bytes_copied") + .desc("Number of bytes copied by each engine") + .flags(total) + ; + copiesProcessed + .init(regs.chanCount) + .name(name() + ".copies_processed") + .desc("Number of copies processed by each engine") + .flags(total) + ; +} + +void +CopyEngine::CopyEngineChannel::fetchDescriptor(Addr address) +{ + anDq(); + anBegin("FetchDescriptor"); + DPRINTF(DMACopyEngine, "Reading descriptor from at memory location %#x(%#x)\n", + address, ce->platform->pciToDma(address)); + assert(address); + busy = true; + + DPRINTF(DMACopyEngine, "dmaAction: %#x, %d bytes, to addr %#x\n", + ce->platform->pciToDma(address), sizeof(DmaDesc), curDmaDesc); + + cePort->dmaAction(MemCmd::ReadReq, ce->platform->pciToDma(address), + sizeof(DmaDesc), &fetchCompleteEvent, (uint8_t*)curDmaDesc, + latBeforeBegin); + lastDescriptorAddr = address; +} + +void +CopyEngine::CopyEngineChannel::fetchDescComplete() +{ + DPRINTF(DMACopyEngine, "Read of descriptor complete\n"); + + if ((curDmaDesc->command & DESC_CTRL_NULL)) { + DPRINTF(DMACopyEngine, "Got NULL descriptor, skipping\n"); + assert(!(curDmaDesc->command & DESC_CTRL_CP_STS)); + if (curDmaDesc->command & DESC_CTRL_CP_STS) { + panic("Shouldn't be able to get here\n"); + nextState = CompletionWrite; + if (inDrain()) return; + writeCompletionStatus(); + } else { + anBegin("Idle"); + anWait(); + busy = false; + nextState = Idle; + inDrain(); + } + return; + } + + if (curDmaDesc->command & ~DESC_CTRL_CP_STS) + panic("Descriptor has flag other that completion status set\n"); + + nextState = DMARead; + if (inDrain()) return; + readCopyBytes(); +} + +void +CopyEngine::CopyEngineChannel::readCopyBytes() +{ + anBegin("ReadCopyBytes"); + DPRINTF(DMACopyEngine, "Reading %d bytes from buffer to memory location %#x(%#x)\n", + curDmaDesc->len, curDmaDesc->dest, + ce->platform->pciToDma(curDmaDesc->src)); + cePort->dmaAction(MemCmd::ReadReq, ce->platform->pciToDma(curDmaDesc->src), + curDmaDesc->len, &readCompleteEvent, copyBuffer, 0); +} + +void +CopyEngine::CopyEngineChannel::readCopyBytesComplete() +{ + DPRINTF(DMACopyEngine, "Read of bytes to copy complete\n"); + + nextState = DMAWrite; + if (inDrain()) return; + writeCopyBytes(); +} + +void +CopyEngine::CopyEngineChannel::writeCopyBytes() +{ + anBegin("WriteCopyBytes"); + DPRINTF(DMACopyEngine, "Writing %d bytes from buffer to memory location %#x(%#x)\n", + curDmaDesc->len, curDmaDesc->dest, + ce->platform->pciToDma(curDmaDesc->dest)); + + cePort->dmaAction(MemCmd::WriteReq, ce->platform->pciToDma(curDmaDesc->dest), + curDmaDesc->len, &writeCompleteEvent, copyBuffer, 0); + + ce->bytesCopied[channelId] += curDmaDesc->len; + ce->copiesProcessed[channelId]++; +} + +void +CopyEngine::CopyEngineChannel::writeCopyBytesComplete() +{ + DPRINTF(DMACopyEngine, "Write of bytes to copy complete user1: %#x\n", + curDmaDesc->user1); + + cr.status.compl_desc_addr(lastDescriptorAddr >> 6); + completionDataReg = cr.status() | 1; + + anQ("DMAUsedDescQ", channelId, 1); + anQ("AppRecvQ", curDmaDesc->user1, curDmaDesc->len); + if (curDmaDesc->command & DESC_CTRL_CP_STS) { + nextState = CompletionWrite; + if (inDrain()) return; + writeCompletionStatus(); + return; + } + + continueProcessing(); +} + +void +CopyEngine::CopyEngineChannel::continueProcessing() +{ + busy = false; + + if (underReset) { + anBegin("Reset"); + anWait(); + underReset = false; + refreshNext = false; + busy = false; + nextState = Idle; + return; + } + + if (curDmaDesc->next) { + nextState = DescriptorFetch; + fetchAddress = curDmaDesc->next; + if (inDrain()) return; + fetchDescriptor(curDmaDesc->next); + } else if (refreshNext) { + nextState = AddressFetch; + refreshNext = false; + if (inDrain()) return; + fetchNextAddr(lastDescriptorAddr); + } else { + inDrain(); + nextState = Idle; + anWait(); + anBegin("Idle"); + } +} + +void +CopyEngine::CopyEngineChannel::writeCompletionStatus() +{ + anBegin("WriteCompletionStatus"); + DPRINTF(DMACopyEngine, "Writing completion status %#x to address %#x(%#x)\n", + completionDataReg, cr.completionAddr, + ce->platform->pciToDma(cr.completionAddr)); + + cePort->dmaAction(MemCmd::WriteReq, ce->platform->pciToDma(cr.completionAddr), + sizeof(completionDataReg), &statusCompleteEvent, + (uint8_t*)&completionDataReg, latAfterCompletion); +} + +void +CopyEngine::CopyEngineChannel::writeStatusComplete() +{ + DPRINTF(DMACopyEngine, "Writing completion status complete\n"); + continueProcessing(); +} + +void +CopyEngine::CopyEngineChannel::fetchNextAddr(Addr address) +{ + anBegin("FetchNextAddr"); + DPRINTF(DMACopyEngine, "Fetching next address...\n"); + busy = true; + cePort->dmaAction(MemCmd::ReadReq, ce->platform->pciToDma(address + + offsetof(DmaDesc, next)), sizeof(Addr), &addrCompleteEvent, + (uint8_t*)curDmaDesc + offsetof(DmaDesc, next), 0); +} + +void +CopyEngine::CopyEngineChannel::fetchAddrComplete() +{ + DPRINTF(DMACopyEngine, "Fetching next address complete: %#x\n", + curDmaDesc->next); + if (!curDmaDesc->next) { + DPRINTF(DMACopyEngine, "Got NULL descriptor, nothing more to do\n"); + busy = false; + nextState = Idle; + anWait(); + anBegin("Idle"); + inDrain(); + return; + } + nextState = DescriptorFetch; + fetchAddress = curDmaDesc->next; + if (inDrain()) return; + fetchDescriptor(curDmaDesc->next); +} + +bool +CopyEngine::CopyEngineChannel::inDrain() +{ + if (ce->getState() == SimObject::Draining) { + DPRINTF(DMACopyEngine, "processing drain\n"); + assert(drainEvent); + drainEvent->process(); + drainEvent = NULL; + } + + return ce->getState() != SimObject::Running; +} + +unsigned int +CopyEngine::CopyEngineChannel::drain(Event *de) +{ + if (nextState == Idle || ce->getState() != SimObject::Running) + return 0; + unsigned int count = 1; + count += cePort->drain(de); + + DPRINTF(DMACopyEngine, "unable to drain, returning %d\n", count); + drainEvent = de; + return count; +} + +unsigned int +CopyEngine::drain(Event *de) +{ + unsigned int count; + count = pioPort->drain(de) + dmaPort->drain(de) + configPort->drain(de); + for (int x = 0;x < chan.size(); x++) + count += chan[x]->drain(de); + + if (count) + changeState(Draining); + else + changeState(Drained); + + DPRINTF(DMACopyEngine, "call to CopyEngine::drain() returning %d\n", count); + return count; +} + +void +CopyEngine::serialize(std::ostream &os) +{ + PciDev::serialize(os); + regs.serialize(os); + for (int x =0; x < chan.size(); x++) { + nameOut(os, csprintf("%s.channel%d", name(), x)); + chan[x]->serialize(os); + } +} + +void +CopyEngine::unserialize(Checkpoint *cp, const std::string §ion) +{ + PciDev::unserialize(cp, section); + regs.unserialize(cp, section); + for (int x = 0; x < chan.size(); x++) + chan[x]->unserialize(cp, csprintf("%s.channel%d", section, x)); +} + +void +CopyEngine::CopyEngineChannel::serialize(std::ostream &os) +{ + SERIALIZE_SCALAR(channelId); + SERIALIZE_SCALAR(busy); + SERIALIZE_SCALAR(underReset); + SERIALIZE_SCALAR(refreshNext); + SERIALIZE_SCALAR(lastDescriptorAddr); + SERIALIZE_SCALAR(completionDataReg); + SERIALIZE_SCALAR(fetchAddress); + int nextState = this->nextState; + SERIALIZE_SCALAR(nextState); + arrayParamOut(os, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc)); + SERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap); + cr.serialize(os); + +} +void +CopyEngine::CopyEngineChannel::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(channelId); + UNSERIALIZE_SCALAR(busy); + UNSERIALIZE_SCALAR(underReset); + UNSERIALIZE_SCALAR(refreshNext); + UNSERIALIZE_SCALAR(lastDescriptorAddr); + UNSERIALIZE_SCALAR(completionDataReg); + UNSERIALIZE_SCALAR(fetchAddress); + int nextState; + UNSERIALIZE_SCALAR(nextState); + this->nextState = (ChannelState)nextState; + arrayParamIn(cp, section, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc)); + UNSERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap); + cr.unserialize(cp, section); + +} + +void +CopyEngine::CopyEngineChannel::restartStateMachine() +{ + switch(nextState) { + case AddressFetch: + fetchNextAddr(lastDescriptorAddr); + break; + case DescriptorFetch: + fetchDescriptor(fetchAddress); + break; + case DMARead: + readCopyBytes(); + break; + case DMAWrite: + writeCopyBytes(); + break; + case CompletionWrite: + writeCompletionStatus(); + break; + case Idle: + break; + default: + panic("Unknown state for CopyEngineChannel\n"); + } +} + +void +CopyEngine::resume() +{ + SimObject::resume(); + for (int x = 0;x < chan.size(); x++) + chan[x]->resume(); +} + + +void +CopyEngine::CopyEngineChannel::resume() +{ + DPRINTF(DMACopyEngine, "Restarting state machine at state %d\n", nextState); + restartStateMachine(); +} + +CopyEngine * +CopyEngineParams::create() +{ + return new CopyEngine(this); +} diff --git a/src/dev/copy_engine.hh b/src/dev/copy_engine.hh new file mode 100644 index 000000000..dfe469588 --- /dev/null +++ b/src/dev/copy_engine.hh @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + */ + +/* @file + * Device model for Intel's I/O Acceleration Technology (I/OAT). + * A DMA asyncronous copy engine + */ + +#ifndef __DEV_COPY_ENGINE_HH__ +#define __DEV_COPY_ENGINE_HH__ + +#include <vector> + +#include "base/statistics.hh" +#include "dev/copy_engine_defs.hh" +#include "dev/pcidev.hh" +#include "params/CopyEngine.hh" +#include "sim/eventq.hh" + +class CopyEngine : public PciDev +{ + class CopyEngineChannel + { + private: + DmaPort *cePort; + CopyEngine *ce; + CopyEngineReg::ChanRegs cr; + int channelId; + CopyEngineReg::DmaDesc *curDmaDesc; + uint8_t *copyBuffer; + + bool busy; + bool underReset; + bool refreshNext; + Addr lastDescriptorAddr; + Addr fetchAddress; + + Tick latBeforeBegin; + Tick latAfterCompletion; + + uint64_t completionDataReg; + + enum ChannelState { + Idle, + AddressFetch, + DescriptorFetch, + DMARead, + DMAWrite, + CompletionWrite + }; + + ChannelState nextState; + + Event *drainEvent; + public: + CopyEngineChannel(CopyEngine *_ce, int cid); + virtual ~CopyEngineChannel(); + void init(); + + std::string name() { assert(ce); return ce->name() + csprintf("-chan%d", channelId); } + virtual void addressRanges(AddrRangeList &range_list) { range_list.clear(); } + virtual Tick read(PacketPtr pkt) + { panic("CopyEngineChannel has no I/O access\n");} + virtual Tick write(PacketPtr pkt) + { panic("CopyEngineChannel has no I/O access\n"); } + + void channelRead(PacketPtr pkt, Addr daddr, int size); + void channelWrite(PacketPtr pkt, Addr daddr, int size); + + unsigned int drain(Event *de); + void resume(); + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + private: + void fetchDescriptor(Addr address); + void fetchDescComplete(); + EventWrapper<CopyEngineChannel, &CopyEngineChannel::fetchDescComplete> + fetchCompleteEvent; + + void fetchNextAddr(Addr address); + void fetchAddrComplete(); + EventWrapper<CopyEngineChannel, &CopyEngineChannel::fetchAddrComplete> + addrCompleteEvent; + + void readCopyBytes(); + void readCopyBytesComplete(); + EventWrapper<CopyEngineChannel, &CopyEngineChannel::readCopyBytesComplete> + readCompleteEvent; + + void writeCopyBytes(); + void writeCopyBytesComplete(); + EventWrapper <CopyEngineChannel, &CopyEngineChannel::writeCopyBytesComplete> + writeCompleteEvent; + + void writeCompletionStatus(); + void writeStatusComplete(); + EventWrapper <CopyEngineChannel, &CopyEngineChannel::writeStatusComplete> + statusCompleteEvent; + + + void continueProcessing(); + void recvCommand(); + bool inDrain(); + void restartStateMachine(); + inline void anBegin(const char *s) + { + CPA::cpa()->hwBegin(CPA::FL_NONE, ce->sys, + channelId, "CopyEngine", s); + } + + inline void anWait() + { + CPA::cpa()->hwWe(CPA::FL_NONE, ce->sys, + channelId, "CopyEngine", "DMAUnusedDescQ", channelId); + } + + inline void anDq() + { + CPA::cpa()->hwDq(CPA::FL_NONE, ce->sys, + channelId, "CopyEngine", "DMAUnusedDescQ", channelId); + } + + inline void anPq() + { + CPA::cpa()->hwDq(CPA::FL_NONE, ce->sys, + channelId, "CopyEngine", "DMAUnusedDescQ", channelId); + } + + inline void anQ(const char * s, uint64_t id, int size = 1) + { + CPA::cpa()->hwQ(CPA::FL_NONE, ce->sys, channelId, + "CopyEngine", s, id, NULL, size); + } + + }; + + private: + + Stats::Vector bytesCopied; + Stats::Vector copiesProcessed; + + // device registers + CopyEngineReg::Regs regs; + + // Array of channels each one with regs/dma port/etc + std::vector<CopyEngineChannel*> chan; + + public: + typedef CopyEngineParams Params; + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + CopyEngine(const Params *params); + ~CopyEngine(); + + void regStats(); + void init(); + + virtual Tick read(PacketPtr pkt); + virtual Tick write(PacketPtr pkt); + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + virtual unsigned int drain(Event *de); + virtual void resume(); +}; + +#endif //__DEV_COPY_ENGINE_HH__ + diff --git a/src/dev/copy_engine_defs.hh b/src/dev/copy_engine_defs.hh new file mode 100644 index 000000000..16bf57d58 --- /dev/null +++ b/src/dev/copy_engine_defs.hh @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + */ + +/* @file + * Register and structure descriptions for Intel's I/O AT DMA Engine + */ +#include "base/bitfield.hh" +#include "sim/serialize.hh" + +namespace CopyEngineReg { + + +// General Channel independant registers, 128 bytes starting at 0x00 +const uint32_t GEN_CHANCOUNT = 0x00; +const uint32_t GEN_XFERCAP = 0x01; +const uint32_t GEN_INTRCTRL = 0x03; +const uint32_t GEN_ATTNSTATUS = 0x04; + + +// Channel specific registers, each block is 128 bytes, starting at 0x80 +const uint32_t CHAN_CONTROL = 0x00; +const uint32_t CHAN_STATUS = 0x04; +const uint32_t CHAN_CHAINADDR = 0x0C; +const uint32_t CHAN_CHAINADDR_LOW = 0x0C; +const uint32_t CHAN_CHAINADDR_HIGH = 0x10; +const uint32_t CHAN_COMMAND = 0x14; +const uint32_t CHAN_CMPLNADDR = 0x18; +const uint32_t CHAN_CMPLNADDR_LOW = 0x18; +const uint32_t CHAN_CMPLNADDR_HIGH = 0x1C; +const uint32_t CHAN_ERROR = 0x28; + + +const uint32_t DESC_CTRL_INT_GEN = 0x00000001; +const uint32_t DESC_CTRL_SRC_SN = 0x00000002; +const uint32_t DESC_CTRL_DST_SN = 0x00000004; +const uint32_t DESC_CTRL_CP_STS = 0x00000008; +const uint32_t DESC_CTRL_FRAME = 0x00000010; +const uint32_t DESC_CTRL_NULL = 0x00000020; + +struct DmaDesc { + uint32_t len; + uint32_t command; + Addr src; + Addr dest; + Addr next; + uint64_t reserved1; + uint64_t reserved2; + uint64_t user1; + uint64_t user2; +}; + +#define ADD_FIELD8(NAME, OFFSET, BITS) \ + inline uint8_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \ + inline void NAME(uint8_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); } + +#define ADD_FIELD16(NAME, OFFSET, BITS) \ + inline uint16_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \ + inline void NAME(uint16_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); } + +#define ADD_FIELD32(NAME, OFFSET, BITS) \ + inline uint32_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \ + inline void NAME(uint32_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); } + +#define ADD_FIELD64(NAME, OFFSET, BITS) \ + inline uint64_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \ + inline void NAME(uint64_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); } + +template<class T> +struct Reg { + T _data; + T operator()() { return _data; } + const Reg<T> &operator=(T d) { _data = d; return *this;} + bool operator==(T d) { return d == _data; } + void operator()(T d) { _data = d; } + Reg() { _data = 0; } + void serialize(std::ostream &os) + { + SERIALIZE_SCALAR(_data); + } + void unserialize(Checkpoint *cp, const std::string §ion) + { + UNSERIALIZE_SCALAR(_data); + } +}; + + +struct Regs { + uint8_t chanCount; + uint8_t xferCap; + + struct INTRCTRL : public Reg<uint8_t> { // 0x03 + using Reg<uint8_t>::operator =; + ADD_FIELD8(master_int_enable,0,1); + ADD_FIELD8(interrupt_status,1,1); + ADD_FIELD8(interrupt,2,1); + }; + INTRCTRL intrctrl; + + uint32_t attnStatus; // Read clears + + void serialize(std::ostream &os) + { + SERIALIZE_SCALAR(chanCount); + SERIALIZE_SCALAR(xferCap); + paramOut(os, "intrctrl", intrctrl._data); + SERIALIZE_SCALAR(attnStatus); + } + + void unserialize(Checkpoint *cp, const std::string §ion) + { + UNSERIALIZE_SCALAR(chanCount); + UNSERIALIZE_SCALAR(xferCap); + paramIn(cp, section, "intrctrl", intrctrl._data); + UNSERIALIZE_SCALAR(attnStatus); + } + +}; + +struct ChanRegs { + struct CHANCTRL : public Reg<uint16_t> { // channelX + 0x00 + using Reg<uint16_t>::operator =; + ADD_FIELD16(interrupt_disable,0,1); + ADD_FIELD16(error_completion_enable, 2,1); + ADD_FIELD16(any_error_abort_enable,3,1); + ADD_FIELD16(error_int_enable,4,1); + ADD_FIELD16(desc_addr_snoop_control,5,1); + ADD_FIELD16(in_use, 8,1); + }; + CHANCTRL ctrl; + + struct CHANSTS : public Reg<uint64_t> { // channelX + 0x04 + ADD_FIELD64(dma_transfer_status, 0, 3); + ADD_FIELD64(unaffiliated_error, 3, 1); + ADD_FIELD64(soft_error, 4, 1); + ADD_FIELD64(compl_desc_addr, 6, 58); + }; + CHANSTS status; + + uint64_t descChainAddr; + + struct CHANCMD : public Reg<uint8_t> { // channelX + 0x14 + ADD_FIELD8(start_dma,0,1); + ADD_FIELD8(append_dma,1,1); + ADD_FIELD8(suspend_dma,2,1); + ADD_FIELD8(abort_dma,3,1); + ADD_FIELD8(resume_dma,4,1); + ADD_FIELD8(reset_dma,5,1); + }; + CHANCMD command; + + uint64_t completionAddr; + + struct CHANERR : public Reg<uint32_t> { // channel X + 0x28 + ADD_FIELD32(source_addr_error,0,1); + ADD_FIELD32(dest_addr_error,1,1); + ADD_FIELD32(ndesc_addr_error,2,1); + ADD_FIELD32(desc_error,3,1); + ADD_FIELD32(chain_addr_error,4,1); + ADD_FIELD32(chain_cmd_error,5,1); + ADD_FIELD32(chipset_parity_error,6,1); + ADD_FIELD32(dma_parity_error,7,1); + ADD_FIELD32(read_data_error,8,1); + ADD_FIELD32(write_data_error,9,1); + ADD_FIELD32(desc_control_error,10,1); + ADD_FIELD32(desc_len_error,11,1); + ADD_FIELD32(completion_addr_error,12,1); + ADD_FIELD32(interrupt_config_error,13,1); + ADD_FIELD32(soft_error,14,1); + ADD_FIELD32(unaffiliated_error,15,1); + }; + CHANERR error; + + void serialize(std::ostream &os) + { + paramOut(os, "ctrl", ctrl._data); + paramOut(os, "status", status._data); + SERIALIZE_SCALAR(descChainAddr); + paramOut(os, "command", command._data); + SERIALIZE_SCALAR(completionAddr); + paramOut(os, "error", error._data); + } + + void unserialize(Checkpoint *cp, const std::string §ion) + { + paramIn(cp, section, "ctrl", ctrl._data); + paramIn(cp, section, "status", status._data); + UNSERIALIZE_SCALAR(descChainAddr); + paramIn(cp, section, "command", command._data); + UNSERIALIZE_SCALAR(completionAddr); + paramIn(cp, section, "error", error._data); + } + + +}; + +} //namespace CopyEngineReg + + diff --git a/src/dev/etherbus.cc b/src/dev/etherbus.cc index 2316bfed9..063a594e7 100644 --- a/src/dev/etherbus.cc +++ b/src/dev/etherbus.cc @@ -49,7 +49,7 @@ using namespace std; EtherBus::EtherBus(const Params *p) : EtherObject(p), ticksPerByte(p->speed), loopback(p->loopback), - event(&mainEventQueue, this), sender(0), dump(p->dump) + event(this), sender(0), dump(p->dump) { } @@ -99,7 +99,7 @@ EtherBus::send(EtherInt *sndr, EthPacketPtr &pkt) int delay = (int)ceil(((double)pkt->length * ticksPerByte) + 1.0); DPRINTF(Ethernet, "scheduling packet: delay=%d, (rate=%f)\n", delay, ticksPerByte); - event.schedule(curTick + delay); + schedule(event, curTick + delay); return true; } diff --git a/src/dev/etherbus.hh b/src/dev/etherbus.hh index 624ceb81a..6408f7f1f 100644 --- a/src/dev/etherbus.hh +++ b/src/dev/etherbus.hh @@ -59,8 +59,7 @@ class EtherBus : public EtherObject EtherBus *bus; public: - DoneEvent(EventQueue *q, EtherBus *b) - : Event(q), bus(b) {} + DoneEvent(EtherBus *b) : bus(b) {} virtual void process() { bus->txDone(); } virtual const char *description() const { return "ethernet bus completion"; } diff --git a/src/dev/etherdevice.cc b/src/dev/etherdevice.cc new file mode 100644 index 000000000..5341c02c4 --- /dev/null +++ b/src/dev/etherdevice.cc @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Lisa Hsu + */ + +#include "dev/etherdevice.hh" +#include "sim/stats.hh" + +void +EtherDevice::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) + ; + + txIpChecksums + .name(name() + ".txIpChecksums") + .desc("Number of tx IP Checksums done by device") + .precision(0) + .prereq(txBytes) + ; + + rxIpChecksums + .name(name() + ".rxIpChecksums") + .desc("Number of rx IP Checksums done by device") + .precision(0) + .prereq(rxBytes) + ; + + txTcpChecksums + .name(name() + ".txTcpChecksums") + .desc("Number of tx TCP Checksums done by device") + .precision(0) + .prereq(txBytes) + ; + + rxTcpChecksums + .name(name() + ".rxTcpChecksums") + .desc("Number of rx TCP Checksums done by device") + .precision(0) + .prereq(rxBytes) + ; + + txUdpChecksums + .name(name() + ".txUdpChecksums") + .desc("Number of tx UDP Checksums done by device") + .precision(0) + .prereq(txBytes) + ; + + rxUdpChecksums + .name(name() + ".rxUdpChecksums") + .desc("Number of rx UDP Checksums done by device") + .precision(0) + .prereq(rxBytes) + ; + + descDmaReads + .name(name() + ".descDMAReads") + .desc("Number of descriptors the device read w/ DMA") + .precision(0) + ; + + descDmaWrites + .name(name() + ".descDMAWrites") + .desc("Number of descriptors the device wrote w/ DMA") + .precision(0) + ; + + descDmaRdBytes + .name(name() + ".descDmaReadBytes") + .desc("number of descriptor bytes read w/ DMA") + .precision(0) + ; + + descDmaWrBytes + .name(name() + ".descDmaWriteBytes") + .desc("number of descriptor bytes write w/ DMA") + .precision(0) + ; + + 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) + ; + + totBandwidth + .name(name() + ".totBandwidth") + .desc("Total Bandwidth (bits/s)") + .precision(0) + .prereq(totBytes) + ; + + totPackets + .name(name() + ".totPackets") + .desc("Total Packets") + .precision(0) + .prereq(totBytes) + ; + + totBytes + .name(name() + ".totBytes") + .desc("Total Bytes") + .precision(0) + .prereq(totBytes) + ; + + totPacketRate + .name(name() + ".totPPS") + .desc("Total Tranmission Rate (packets/s)") + .precision(0) + .prereq(totBytes) + ; + + 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) + ; + + postedSwi + .name(name() + ".postedSwi") + .desc("number of software interrupts posted to CPU") + .precision(0) + ; + + totalSwi + .name(name() + ".totalSwi") + .desc("total number of Swi written to ISR") + .precision(0) + ; + + coalescedSwi + .name(name() + ".coalescedSwi") + .desc("average number of Swi's coalesced into each post") + .precision(0) + ; + + postedRxIdle + .name(name() + ".postedRxIdle") + .desc("number of rxIdle interrupts posted to CPU") + .precision(0) + ; + + totalRxIdle + .name(name() + ".totalRxIdle") + .desc("total number of RxIdle written to ISR") + .precision(0) + ; + + coalescedRxIdle + .name(name() + ".coalescedRxIdle") + .desc("average number of RxIdle's coalesced into each post") + .precision(0) + ; + + postedRxOk + .name(name() + ".postedRxOk") + .desc("number of RxOk interrupts posted to CPU") + .precision(0) + ; + + totalRxOk + .name(name() + ".totalRxOk") + .desc("total number of RxOk written to ISR") + .precision(0) + ; + + coalescedRxOk + .name(name() + ".coalescedRxOk") + .desc("average number of RxOk's coalesced into each post") + .precision(0) + ; + + postedRxDesc + .name(name() + ".postedRxDesc") + .desc("number of RxDesc interrupts posted to CPU") + .precision(0) + ; + + totalRxDesc + .name(name() + ".totalRxDesc") + .desc("total number of RxDesc written to ISR") + .precision(0) + ; + + coalescedRxDesc + .name(name() + ".coalescedRxDesc") + .desc("average number of RxDesc's coalesced into each post") + .precision(0) + ; + + postedTxOk + .name(name() + ".postedTxOk") + .desc("number of TxOk interrupts posted to CPU") + .precision(0) + ; + + totalTxOk + .name(name() + ".totalTxOk") + .desc("total number of TxOk written to ISR") + .precision(0) + ; + + coalescedTxOk + .name(name() + ".coalescedTxOk") + .desc("average number of TxOk's coalesced into each post") + .precision(0) + ; + + postedTxIdle + .name(name() + ".postedTxIdle") + .desc("number of TxIdle interrupts posted to CPU") + .precision(0) + ; + + totalTxIdle + .name(name() + ".totalTxIdle") + .desc("total number of TxIdle written to ISR") + .precision(0) + ; + + coalescedTxIdle + .name(name() + ".coalescedTxIdle") + .desc("average number of TxIdle's coalesced into each post") + .precision(0) + ; + + postedTxDesc + .name(name() + ".postedTxDesc") + .desc("number of TxDesc interrupts posted to CPU") + .precision(0) + ; + + totalTxDesc + .name(name() + ".totalTxDesc") + .desc("total number of TxDesc written to ISR") + .precision(0) + ; + + coalescedTxDesc + .name(name() + ".coalescedTxDesc") + .desc("average number of TxDesc's coalesced into each post") + .precision(0) + ; + + postedRxOrn + .name(name() + ".postedRxOrn") + .desc("number of RxOrn posted to CPU") + .precision(0) + ; + + totalRxOrn + .name(name() + ".totalRxOrn") + .desc("total number of RxOrn written to ISR") + .precision(0) + ; + + coalescedRxOrn + .name(name() + ".coalescedRxOrn") + .desc("average number of RxOrn's coalesced into each post") + .precision(0) + ; + + coalescedTotal + .name(name() + ".coalescedTotal") + .desc("average number of interrupts coalesced into each post") + .precision(0) + ; + + postedInterrupts + .name(name() + ".postedInterrupts") + .desc("number of posts to CPU") + .precision(0) + ; + + droppedPackets + .name(name() + ".droppedPackets") + .desc("number of packets dropped") + .precision(0) + ; + + coalescedSwi = totalSwi / postedInterrupts; + coalescedRxIdle = totalRxIdle / postedInterrupts; + coalescedRxOk = totalRxOk / postedInterrupts; + coalescedRxDesc = totalRxDesc / postedInterrupts; + coalescedTxOk = totalTxOk / postedInterrupts; + coalescedTxIdle = totalTxIdle / postedInterrupts; + coalescedTxDesc = totalTxDesc / postedInterrupts; + coalescedRxOrn = totalRxOrn / postedInterrupts; + + coalescedTotal = (totalSwi + totalRxIdle + totalRxOk + totalRxDesc + + totalTxOk + totalTxIdle + totalTxDesc + + totalRxOrn) / postedInterrupts; + + txBandwidth = txBytes * Stats::constant(8) / simSeconds; + rxBandwidth = rxBytes * Stats::constant(8) / simSeconds; + totBandwidth = txBandwidth + rxBandwidth; + totBytes = txBytes + rxBytes; + totPackets = txPackets + rxPackets; + + txPacketRate = txPackets / simSeconds; + rxPacketRate = rxPackets / simSeconds; +} diff --git a/src/dev/etherdevice.hh b/src/dev/etherdevice.hh index a0df0d741..5d86275b4 100644 --- a/src/dev/etherdevice.hh +++ b/src/dev/etherdevice.hh @@ -36,6 +36,7 @@ #ifndef __DEV_ETHERDEVICE_HH__ #define __DEV_ETHERDEVICE_HH__ +#include "base/statistics.hh" #include "dev/pcidev.hh" #include "params/EtherDevice.hh" #include "sim/sim_object.hh" @@ -64,6 +65,59 @@ class EtherDevice : public PciDev /** Additional function to return the Port of a memory object. */ virtual EtherInt *getEthPort(const std::string &if_name, int idx = -1) = 0; + public: + void regStats(); + + protected: + Stats::Scalar txBytes; + Stats::Scalar rxBytes; + Stats::Scalar txPackets; + Stats::Scalar rxPackets; + Stats::Scalar txIpChecksums; + Stats::Scalar rxIpChecksums; + Stats::Scalar txTcpChecksums; + Stats::Scalar rxTcpChecksums; + Stats::Scalar txUdpChecksums; + Stats::Scalar rxUdpChecksums; + Stats::Scalar descDmaReads; + Stats::Scalar descDmaWrites; + Stats::Scalar descDmaRdBytes; + Stats::Scalar descDmaWrBytes; + Stats::Formula totBandwidth; + Stats::Formula totPackets; + Stats::Formula totBytes; + Stats::Formula totPacketRate; + Stats::Formula txBandwidth; + Stats::Formula rxBandwidth; + Stats::Formula txPacketRate; + Stats::Formula rxPacketRate; + Stats::Scalar postedSwi; + Stats::Formula coalescedSwi; + Stats::Scalar totalSwi; + Stats::Scalar postedRxIdle; + Stats::Formula coalescedRxIdle; + Stats::Scalar totalRxIdle; + Stats::Scalar postedRxOk; + Stats::Formula coalescedRxOk; + Stats::Scalar totalRxOk; + Stats::Scalar postedRxDesc; + Stats::Formula coalescedRxDesc; + Stats::Scalar totalRxDesc; + Stats::Scalar postedTxOk; + Stats::Formula coalescedTxOk; + Stats::Scalar totalTxOk; + Stats::Scalar postedTxIdle; + Stats::Formula coalescedTxIdle; + Stats::Scalar totalTxIdle; + Stats::Scalar postedTxDesc; + Stats::Formula coalescedTxDesc; + Stats::Scalar totalTxDesc; + Stats::Scalar postedRxOrn; + Stats::Formula coalescedRxOrn; + Stats::Scalar totalRxOrn; + Stats::Formula coalescedTotal; + Stats::Scalar postedInterrupts; + Stats::Scalar droppedPackets; }; #endif //__DEV_ETHERDEVICE_HH__ diff --git a/src/dev/etherdump.cc b/src/dev/etherdump.cc index 471093521..c41ce4e1f 100644 --- a/src/dev/etherdump.cc +++ b/src/dev/etherdump.cc @@ -45,75 +45,62 @@ using std::string; EtherDump::EtherDump(const Params *p) - : SimObject(p), stream(simout.resolve(p->file).c_str()), + : SimObject(p), stream(simout.create(p->file, true)), maxlen(p->maxlen) { } -#define DLT_EN10MB 1 // Ethernet (10Mb) -#define TCPDUMP_MAGIC 0xa1b2c3d4 -#define PCAP_VERSION_MAJOR 2 -#define PCAP_VERSION_MINOR 4 +#define DLT_EN10MB 1 // Ethernet (10Mb) +#define TCPDUMP_MAGIC 0xa1b2c3d4 +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 struct pcap_file_header { uint32_t magic; uint16_t version_major; uint16_t version_minor; - int32_t thiszone; // gmt to local correction - uint32_t sigfigs; // accuracy of timestamps - uint32_t snaplen; // max length saved portion of each pkt - uint32_t linktype; // data link type (DLT_*) + int32_t thiszone; // gmt to local correction + uint32_t sigfigs; // accuracy of timestamps + uint32_t snaplen; // max length saved portion of each pkt + uint32_t linktype; // data link type (DLT_*) }; struct pcap_pkthdr { uint32_t seconds; uint32_t microseconds; - uint32_t caplen; // length of portion present - uint32_t len; // length this packet (off wire) + uint32_t caplen; // length of portion present + uint32_t len; // length this packet (off wire) }; void EtherDump::init() { - curtime = time(NULL); struct pcap_file_header hdr; hdr.magic = TCPDUMP_MAGIC; hdr.version_major = PCAP_VERSION_MAJOR; hdr.version_minor = PCAP_VERSION_MINOR; - hdr.thiszone = -5 * 3600; + hdr.thiszone = 0; hdr.snaplen = 1500; hdr.sigfigs = 0; hdr.linktype = DLT_EN10MB; - stream.write(reinterpret_cast<char *>(&hdr), sizeof(hdr)); + stream->write(reinterpret_cast<char *>(&hdr), sizeof(hdr)); - /* - * output an empty packet with the current time so that we know - * when the simulation began. This allows us to correlate packets - * to sim_cycles. - */ - pcap_pkthdr pkthdr; - pkthdr.seconds = curtime; - pkthdr.microseconds = 0; - pkthdr.caplen = 0; - pkthdr.len = 0; - stream.write(reinterpret_cast<char *>(&pkthdr), sizeof(pkthdr)); - - stream.flush(); + stream->flush(); } void EtherDump::dumpPacket(EthPacketPtr &packet) { pcap_pkthdr pkthdr; - pkthdr.seconds = curtime + (curTick / Clock::Int::s); + pkthdr.seconds = curTick / Clock::Int::s; pkthdr.microseconds = (curTick / Clock::Int::us) % ULL(1000000); pkthdr.caplen = std::min(packet->length, maxlen); pkthdr.len = packet->length; - stream.write(reinterpret_cast<char *>(&pkthdr), sizeof(pkthdr)); - stream.write(reinterpret_cast<char *>(packet->data), pkthdr.caplen); - stream.flush(); + stream->write(reinterpret_cast<char *>(&pkthdr), sizeof(pkthdr)); + stream->write(reinterpret_cast<char *>(packet->data), pkthdr.caplen); + stream->flush(); } EtherDump * diff --git a/src/dev/etherdump.hh b/src/dev/etherdump.hh index 1027ce4d0..18a5d2c44 100644 --- a/src/dev/etherdump.hh +++ b/src/dev/etherdump.hh @@ -46,13 +46,11 @@ class EtherDump : public SimObject { private: - std::ofstream stream; + std::ostream *stream; const int maxlen; void dumpPacket(EthPacketPtr &packet); void init(); - Tick curtime; - public: typedef EtherDumpParams Params; EtherDump(const Params *p); diff --git a/src/dev/etherlink.cc b/src/dev/etherlink.cc index b1266000b..f3f38fc20 100644 --- a/src/dev/etherlink.cc +++ b/src/dev/etherlink.cc @@ -135,7 +135,7 @@ class LinkDelayEvent : public Event public: // non-scheduling version for createForUnserialize() LinkDelayEvent(); - LinkDelayEvent(EtherLink::Link *link, EthPacketPtr pkt, Tick when); + LinkDelayEvent(EtherLink::Link *link, EthPacketPtr pkt); void process(); @@ -153,7 +153,8 @@ EtherLink::Link::txDone() if (linkDelay > 0) { DPRINTF(Ethernet, "packet delayed: delay=%d\n", linkDelay); - new LinkDelayEvent(this, packet, curTick + linkDelay); + Event *event = new LinkDelayEvent(this, packet); + parent->schedule(event, curTick + linkDelay); } else { txComplete(packet); } @@ -182,7 +183,7 @@ EtherLink::Link::transmit(EthPacketPtr pkt) DPRINTF(Ethernet, "scheduling packet: delay=%d, (rate=%f)\n", delay, ticksPerByte); - doneEvent.schedule(curTick + delay); + parent->schedule(doneEvent, curTick + delay); return true; } @@ -220,23 +221,22 @@ EtherLink::Link::unserialize(const string &base, Checkpoint *cp, if (event_scheduled) { Tick event_time; paramIn(cp, section, base + ".event_time", event_time); - doneEvent.schedule(event_time); + parent->schedule(doneEvent, event_time); } } LinkDelayEvent::LinkDelayEvent() - : Event(&mainEventQueue), link(NULL) + : link(NULL) { setFlags(AutoSerialize); setFlags(AutoDelete); } -LinkDelayEvent::LinkDelayEvent(EtherLink::Link *l, EthPacketPtr p, Tick when) - : Event(&mainEventQueue), link(l), packet(p) +LinkDelayEvent::LinkDelayEvent(EtherLink::Link *l, EthPacketPtr p) + : link(l), packet(p) { setFlags(AutoSerialize); setFlags(AutoDelete); - schedule(when); } void diff --git a/src/dev/etherpkt.cc b/src/dev/etherpkt.cc index 5c552b4bd..2c8343eb0 100644 --- a/src/dev/etherpkt.cc +++ b/src/dev/etherpkt.cc @@ -40,7 +40,6 @@ void EthPacketData::serialize(const string &base, ostream &os) { paramOut(os, base + ".length", length); - paramOut(os, base + ".slack", slack); arrayParamOut(os, base + ".data", data, length); } @@ -49,7 +48,6 @@ EthPacketData::unserialize(const string &base, Checkpoint *cp, const string §ion) { paramIn(cp, section, base + ".length", length); - paramIn(cp, section, base + ".slack", slack); if (length) arrayParamIn(cp, section, base + ".data", data, length); } diff --git a/src/dev/etherpkt.hh b/src/dev/etherpkt.hh index db2e0d6b5..623895ba8 100644 --- a/src/dev/etherpkt.hh +++ b/src/dev/etherpkt.hh @@ -60,24 +60,17 @@ class EthPacketData : public RefCounted */ int length; - /* - * Extra space taken up by the packet in whatever data structure - * it is in. - * - * NOTE: This can only be use by *one* data structure at a time! - */ - int slack; - public: - EthPacketData() : data(NULL), length(0), slack(0) + EthPacketData() + : data(NULL), length(0) { } explicit EthPacketData(size_t size) - : data(new uint8_t[size]), length(0), slack(0) + : data(new uint8_t[size]), length(0) { } - EthPacketData(std::auto_ptr<uint8_t> d, int l, int s = 0) - : data(d.release()), length(l), slack(s) + EthPacketData(std::auto_ptr<uint8_t> d, int l) + : data(d.release()), length(l) { } ~EthPacketData() { if (data) delete [] data; } diff --git a/src/dev/ethertap.cc b/src/dev/ethertap.cc index 81b84d179..85d6370be 100644 --- a/src/dev/ethertap.cc +++ b/src/dev/ethertap.cc @@ -130,6 +130,9 @@ EtherTap::EtherTap(const Params *p) : EtherObject(p), event(NULL), socket(-1), buflen(p->bufsz), dump(p->dump), interface(NULL), txEvent(this) { + if (ListenSocket::allDisabled()) + fatal("All listeners are disabled! EtherTap can't work!"); + buffer = new char[buflen]; listener = new TapListener(this, p->port); listener->listen(); @@ -179,8 +182,12 @@ EtherTap::recvPacket(EthPacketPtr packet) DPRINTF(Ethernet, "EtherTap output len=%d\n", packet->length); DDUMP(EthernetData, packet->data, packet->length); uint32_t len = htonl(packet->length); - write(socket, &len, sizeof(len)); - write(socket, packet->data, packet->length); + ssize_t ret = write(socket, &len, sizeof(len)); + if (ret != sizeof(len)) + return false; + ret = write(socket, packet->data, packet->length); + if (ret != packet->length) + return false; interface->recvDone(); @@ -239,7 +246,7 @@ EtherTap::process(int revent) DPRINTF(Ethernet, "bus busy...buffer for retransmission\n"); packetBuffer.push(packet); if (!txEvent.scheduled()) - txEvent.schedule(curTick + retryTime); + schedule(txEvent, curTick + retryTime); } else if (dump) { dump->dump(packet); } @@ -262,7 +269,7 @@ EtherTap::retransmit() } if (!packetBuffer.empty() && !txEvent.scheduled()) - txEvent.schedule(curTick + retryTime); + schedule(txEvent, curTick + retryTime); } EtherInt* diff --git a/src/dev/ethertap.hh b/src/dev/ethertap.hh index be3d73a24..ac287cecb 100644 --- a/src/dev/ethertap.hh +++ b/src/dev/ethertap.hh @@ -90,8 +90,7 @@ class EtherTap : public EtherObject EtherTap *tap; public: - TxEvent(EtherTap *_tap) - : Event(&mainEventQueue), tap(_tap) {} + TxEvent(EtherTap *_tap) : tap(_tap) {} void process() { tap->retransmit(); } virtual const char *description() const { return "EtherTap retransmit"; } diff --git a/src/dev/i8254xGBe.cc b/src/dev/i8254xGBe.cc index 3f56ec53a..274f60e39 100644 --- a/src/dev/i8254xGBe.cc +++ b/src/dev/i8254xGBe.cc @@ -57,10 +57,15 @@ using namespace Net; IGbE::IGbE(const Params *p) : EtherDevice(p), etherInt(NULL), drainEvent(NULL), useFlowControl(p->use_flow_control), rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size), rxTick(false), - txTick(false), txFifoTick(false), rxDmaPacket(false), rdtrEvent(this), radvEvent(this), + txTick(false), txFifoTick(false), rxDmaPacket(false), pktOffset(0), + fetchDelay(p->fetch_delay), wbDelay(p->wb_delay), + fetchCompDelay(p->fetch_comp_delay), wbCompDelay(p->wb_comp_delay), + rxWriteDelay(p->rx_write_delay), txReadDelay(p->tx_read_delay), + rdtrEvent(this), radvEvent(this), tadvEvent(this), tidvEvent(this), tickEvent(this), interEvent(this), rxDescCache(this, name()+".RxDesc", p->rx_desc_cache_size), - txDescCache(this, name()+".TxDesc", p->tx_desc_cache_size), clock(p->clock) + txDescCache(this, name()+".TxDesc", p->tx_desc_cache_size), + clock(p->clock), lastInterrupt(0) { etherInt = new IGbEInt(name() + ".int", this); @@ -80,6 +85,9 @@ IGbE::IGbE(const Params *p) regs.rxdctl.gran(1); regs.rxdctl.wthresh(1); regs.fcrth(1); + regs.tdwba = 0; + regs.rlpml = 0; + regs.sw_fw_sync = 0; regs.pba.rxa(0x30); regs.pba.txa(0x10); @@ -105,10 +113,20 @@ IGbE::IGbE(const Params *p) // Magic happy checksum value flash[EEPROM_SIZE-1] = htobe((uint16_t)(EEPROM_CSUM - csum)); + // Store the MAC address as queue ID + macAddr = p->hardware_address; + rxFifo.clear(); txFifo.clear(); } +void +IGbE::init() +{ + cpa = CPA::cpa(); + PciDev::init(); +} + EtherInt* IGbE::getEthPort(const std::string &if_name, int idx) { @@ -192,6 +210,11 @@ IGbE::read(PacketPtr pkt) regs.imr &= ~regs.iam; chkInterrupt(); break; + case REG_EICR: + // This is only useful for MSI, but the driver reads it every time + // Just don't do anything + pkt->set<uint32_t>(0); + break; case REG_ITR: pkt->set<uint32_t>(regs.itr()); break; @@ -226,6 +249,9 @@ IGbE::read(PacketPtr pkt) case REG_RDLEN: pkt->set<uint32_t>(regs.rdlen()); break; + case REG_SRRCTL: + pkt->set<uint32_t>(regs.srrctl()); + break; case REG_RDH: pkt->set<uint32_t>(regs.rdh()); break; @@ -241,6 +267,9 @@ IGbE::read(PacketPtr pkt) regs.rdtr.fpd(0); } break; + case REG_RXDCTL: + pkt->set<uint32_t>(regs.rxdctl()); + break; case REG_RADV: pkt->set<uint32_t>(regs.radv()); break; @@ -256,6 +285,9 @@ IGbE::read(PacketPtr pkt) case REG_TDH: pkt->set<uint32_t>(regs.tdh()); break; + case REG_TXDCA_CTL: + pkt->set<uint32_t>(regs.txdca_ctl()); + break; case REG_TDT: pkt->set<uint32_t>(regs.tdt()); break; @@ -268,12 +300,34 @@ IGbE::read(PacketPtr pkt) case REG_TADV: pkt->set<uint32_t>(regs.tadv()); break; + case REG_TDWBAL: + pkt->set<uint32_t>(regs.tdwba & mask(32)); + break; + case REG_TDWBAH: + pkt->set<uint32_t>(regs.tdwba >> 32); + break; case REG_RXCSUM: pkt->set<uint32_t>(regs.rxcsum()); break; + case REG_RLPML: + pkt->set<uint32_t>(regs.rlpml); + break; + case REG_RFCTL: + pkt->set<uint32_t>(regs.rfctl()); + break; case REG_MANC: pkt->set<uint32_t>(regs.manc()); break; + case REG_SWSM: + pkt->set<uint32_t>(regs.swsm()); + regs.swsm.smbi(1); + break; + case REG_FWSM: + pkt->set<uint32_t>(regs.fwsm()); + break; + case REG_SWFWSYNC: + pkt->set<uint32_t>(regs.sw_fw_sync); + break; default: if (!(daddr >= REG_VFTA && daddr < (REG_VFTA + VLAN_FILTER_TABLE_SIZE*4)) && !(daddr >= REG_RAL && daddr < (REG_RAL + RCV_ADDRESS_TABLE_SIZE*8)) && @@ -380,6 +434,14 @@ IGbE::write(PacketPtr pkt) break; case REG_EERD: regs.eerd = val; + if (regs.eerd.start()) { + regs.eerd.done(1); + assert(regs.eerd.addr() < EEPROM_SIZE); + regs.eerd.data(flash[regs.eerd.addr()]); + regs.eerd.start(0); + DPRINTF(EthernetEEPROM, "EEPROM: read addr: %#X data %#x\n", + regs.eerd.addr(), regs.eerd.data()); + } break; case REG_MDIC: regs.mdic = val; @@ -394,10 +456,10 @@ IGbE::write(PacketPtr pkt) regs.mdic.data(0x796D); // link up break; case PHY_PID: - regs.mdic.data(0x02A8); + regs.mdic.data(params()->phy_pid); break; case PHY_EPID: - regs.mdic.data(0x0380); + regs.mdic.data(params()->phy_epid); break; case PHY_GSTATUS: regs.mdic.data(0x7C00); @@ -480,6 +542,9 @@ IGbE::write(PacketPtr pkt) case REG_TIPG: ; // We don't care, so don't store anything break; + case REG_IVAR0: + warn("Writing to IVAR0, ignoring...\n"); + break; case REG_FCRTL: regs.fcrtl = val; break; @@ -498,6 +563,9 @@ IGbE::write(PacketPtr pkt) regs.rdlen = val & ~mask(7); rxDescCache.areaChanged(); break; + case REG_SRRCTL: + regs.srrctl = val; + break; case REG_RDH: regs.rdh = val; rxDescCache.areaChanged(); @@ -518,6 +586,9 @@ IGbE::write(PacketPtr pkt) case REG_RADV: regs.radv = val; break; + case REG_RXDCTL: + regs.rxdctl = val; + break; case REG_TDBAL: regs.tdba.tdbal( val & ~mask(4)); txDescCache.areaChanged(); @@ -534,6 +605,11 @@ IGbE::write(PacketPtr pkt) regs.tdh = val; txDescCache.areaChanged(); break; + case REG_TXDCA_CTL: + regs.txdca_ctl = val; + if (regs.txdca_ctl.enabled()) + panic("No support for DCA\n"); + break; case REG_TDT: regs.tdt = val; DPRINTF(EthernetSM, "TXS: TX Tail pointer updated\n"); @@ -553,12 +629,38 @@ IGbE::write(PacketPtr pkt) case REG_TADV: regs.tadv = val; break; + case REG_TDWBAL: + regs.tdwba &= ~mask(32); + regs.tdwba |= val; + txDescCache.completionWriteback(regs.tdwba & ~mask(1), regs.tdwba & mask(1)); + break; + case REG_TDWBAH: + regs.tdwba &= mask(32); + regs.tdwba |= (uint64_t)val << 32; + txDescCache.completionWriteback(regs.tdwba & ~mask(1), regs.tdwba & mask(1)); + break; case REG_RXCSUM: regs.rxcsum = val; break; + case REG_RLPML: + regs.rlpml = val; + break; + case REG_RFCTL: + regs.rfctl = val; + if (regs.rfctl.exsten()) + panic("Extended RX descriptors not implemented\n"); + break; case REG_MANC: regs.manc = val; break; + case REG_SWSM: + regs.swsm = val; + if (regs.fwsm.eep_fw_semaphore()) + regs.swsm.swesmbi(0); + break; + case REG_SWFWSYNC: + regs.sw_fw_sync = val; + break; default: if (!(daddr >= REG_VFTA && daddr < (REG_VFTA + VLAN_FILTER_TABLE_SIZE*4)) && !(daddr >= REG_RAL && daddr < (REG_RAL + RCV_ADDRESS_TABLE_SIZE*8)) && @@ -580,16 +682,23 @@ IGbE::postInterrupt(IntTypes t, bool now) return; regs.icr = regs.icr() | t; - if (regs.itr.interval() == 0 || now) { + + Tick itr_interval = Clock::Int::ns * 256 * regs.itr.interval(); + DPRINTF(EthernetIntr, "EINT: postInterrupt() curTick: %d itr: %d interval: %d\n", + curTick, regs.itr.interval(), itr_interval); + + if (regs.itr.interval() == 0 || now || lastInterrupt + itr_interval <= curTick) { if (interEvent.scheduled()) { - interEvent.deschedule(); + deschedule(interEvent); } cpuPostInt(); } else { - DPRINTF(EthernetIntr, "EINT: Scheduling timer interrupt for %d ticks\n", - Clock::Int::ns * 256 * regs.itr.interval()); + Tick int_time = lastInterrupt + itr_interval; + assert(int_time > 0); + DPRINTF(EthernetIntr, "EINT: Scheduling timer interrupt for tick %d\n", + int_time); if (!interEvent.scheduled()) { - interEvent.schedule(curTick + Clock::Int::ns * 256 * regs.itr.interval()); + schedule(interEvent, int_time); } } } @@ -605,6 +714,8 @@ void IGbE::cpuPostInt() { + postedInterrupts++; + if (!(regs.icr() & regs.imr)) { DPRINTF(Ethernet, "Interrupt Masked. Not Posting\n"); return; @@ -614,24 +725,24 @@ IGbE::cpuPostInt() if (interEvent.scheduled()) { - interEvent.deschedule(); + deschedule(interEvent); } if (rdtrEvent.scheduled()) { regs.icr.rxt0(1); - rdtrEvent.deschedule(); + deschedule(rdtrEvent); } if (radvEvent.scheduled()) { regs.icr.rxt0(1); - radvEvent.deschedule(); + deschedule(radvEvent); } if (tadvEvent.scheduled()) { regs.icr.txdw(1); - tadvEvent.deschedule(); + deschedule(tadvEvent); } if (tidvEvent.scheduled()) { regs.icr.txdw(1); - tidvEvent.deschedule(); + deschedule(tidvEvent); } regs.icr.int_assert(1); @@ -640,6 +751,7 @@ IGbE::cpuPostInt() intrPost(); + lastInterrupt = curTick; } void @@ -662,7 +774,7 @@ IGbE::chkInterrupt() if (!(regs.icr() & regs.imr)) { DPRINTF(Ethernet, "Mask cleaned all interrupts\n"); if (interEvent.scheduled()) - interEvent.deschedule(); + deschedule(interEvent); if (regs.icr.int_assert()) cpuClearInt(); } @@ -676,7 +788,8 @@ IGbE::chkInterrupt() if (!interEvent.scheduled()) { DPRINTF(Ethernet, "Scheduling for %d\n", curTick + Clock::Int::ns * 256 * regs.itr.interval()); - interEvent.schedule(curTick + Clock::Int::ns * 256 * regs.itr.interval()); + schedule(interEvent, + curTick + Clock::Int::ns * 256 * regs.itr.interval()); } } } @@ -686,27 +799,125 @@ IGbE::chkInterrupt() IGbE::RxDescCache::RxDescCache(IGbE *i, const std::string n, int s) - : DescCache<RxDesc>(i, n, s), pktDone(false), pktEvent(this) + : DescCache<RxDesc>(i, n, s), pktDone(false), splitCount(0), + pktEvent(this), pktHdrEvent(this), pktDataEvent(this) { + annSmFetch = "RX Desc Fetch"; + annSmWb = "RX Desc Writeback"; + annUnusedDescQ = "RX Unused Descriptors"; + annUnusedCacheQ = "RX Unused Descriptor Cache"; + annUsedCacheQ = "RX Used Descriptor Cache"; + annUsedDescQ = "RX Used Descriptors"; + annDescQ = "RX Descriptors"; } void -IGbE::RxDescCache::writePacket(EthPacketPtr packet) +IGbE::RxDescCache::pktSplitDone() { - // We shouldn't have to deal with any of these yet - DPRINTF(EthernetDesc, "Packet Length: %d Desc Size: %d\n", - packet->length, igbe->regs.rctl.descSize()); - assert(packet->length < igbe->regs.rctl.descSize()); + splitCount++; + DPRINTF(EthernetDesc, "Part of split packet done: splitcount now %d\n", splitCount); + assert(splitCount <= 2); + if (splitCount != 2) + return; + splitCount = 0; + DPRINTF(EthernetDesc, "Part of split packet done: calling pktComplete()\n"); + pktComplete(); +} - assert(unusedCache.size()); +int +IGbE::RxDescCache::writePacket(EthPacketPtr packet, int pkt_offset) +{ + assert(unusedCache.size()); //if (!unusedCache.size()) // return false; pktPtr = packet; pktDone = false; - igbe->dmaWrite(igbe->platform->pciToDma(unusedCache.front()->buf), - packet->length, &pktEvent, packet->data); + int buf_len, hdr_len; + + RxDesc *desc = unusedCache.front(); + switch (igbe->regs.srrctl.desctype()) { + case RXDT_LEGACY: + assert(pkt_offset == 0); + bytesCopied = packet->length; + DPRINTF(EthernetDesc, "Packet Length: %d Desc Size: %d\n", + packet->length, igbe->regs.rctl.descSize()); + assert(packet->length < igbe->regs.rctl.descSize()); + igbe->dmaWrite(igbe->platform->pciToDma(desc->legacy.buf), packet->length, &pktEvent, + packet->data, igbe->rxWriteDelay); + break; + case RXDT_ADV_ONEBUF: + assert(pkt_offset == 0); + bytesCopied = packet->length; + buf_len = igbe->regs.rctl.lpe() ? igbe->regs.srrctl.bufLen() : + igbe->regs.rctl.descSize(); + DPRINTF(EthernetDesc, "Packet Length: %d srrctl: %#x Desc Size: %d\n", + packet->length, igbe->regs.srrctl(), buf_len); + assert(packet->length < buf_len); + igbe->dmaWrite(igbe->platform->pciToDma(desc->adv_read.pkt), packet->length, &pktEvent, + packet->data, igbe->rxWriteDelay); + desc->adv_wb.header_len = htole(0); + desc->adv_wb.sph = htole(0); + desc->adv_wb.pkt_len = htole((uint16_t)(pktPtr->length)); + break; + case RXDT_ADV_SPLIT_A: + int split_point; + + buf_len = igbe->regs.rctl.lpe() ? igbe->regs.srrctl.bufLen() : + igbe->regs.rctl.descSize(); + hdr_len = igbe->regs.rctl.lpe() ? igbe->regs.srrctl.hdrLen() : 0; + DPRINTF(EthernetDesc, "lpe: %d Packet Length: %d offset: %d srrctl: %#x hdr addr: %#x Hdr Size: %d desc addr: %#x Desc Size: %d\n", + igbe->regs.rctl.lpe(), packet->length, pkt_offset, igbe->regs.srrctl(), desc->adv_read.hdr, hdr_len, desc->adv_read.pkt, buf_len); + + split_point = hsplit(pktPtr); + + if (packet->length <= hdr_len) { + bytesCopied = packet->length; + assert(pkt_offset == 0); + DPRINTF(EthernetDesc, "Header Splitting: Entire packet being placed in header\n"); + igbe->dmaWrite(igbe->platform->pciToDma(desc->adv_read.hdr), packet->length, &pktEvent, + packet->data, igbe->rxWriteDelay); + desc->adv_wb.header_len = htole((uint16_t)packet->length); + desc->adv_wb.sph = htole(0); + desc->adv_wb.pkt_len = htole(0); + } else if (split_point) { + if (pkt_offset) { + // we are only copying some data, header/data has already been + // copied + int max_to_copy = std::min(packet->length - pkt_offset, buf_len); + bytesCopied += max_to_copy; + DPRINTF(EthernetDesc, "Header Splitting: Continuing data buffer copy\n"); + igbe->dmaWrite(igbe->platform->pciToDma(desc->adv_read.pkt),max_to_copy, &pktEvent, + packet->data + pkt_offset, igbe->rxWriteDelay); + desc->adv_wb.header_len = htole(0); + desc->adv_wb.pkt_len = htole((uint16_t)max_to_copy); + desc->adv_wb.sph = htole(0); + } else { + int max_to_copy = std::min(packet->length - split_point, buf_len); + bytesCopied += max_to_copy + split_point; + + DPRINTF(EthernetDesc, "Header Splitting: splitting at %d\n", + split_point); + igbe->dmaWrite(igbe->platform->pciToDma(desc->adv_read.hdr), split_point, &pktHdrEvent, + packet->data, igbe->rxWriteDelay); + igbe->dmaWrite(igbe->platform->pciToDma(desc->adv_read.pkt), + max_to_copy, &pktDataEvent, packet->data + split_point, igbe->rxWriteDelay); + desc->adv_wb.header_len = htole(split_point); + desc->adv_wb.sph = 1; + desc->adv_wb.pkt_len = htole((uint16_t)(max_to_copy)); + } + } else { + panic("Header split not fitting within header buffer or undecodable" + " packet not fitting in header unsupported\n"); + } + break; + default: + panic("Unimplemnted RX receive buffer type: %d\n", + igbe->regs.srrctl.desctype()); + } + return bytesCopied; + } void @@ -716,10 +927,11 @@ IGbE::RxDescCache::pktComplete() RxDesc *desc; desc = unusedCache.front(); + igbe->anBegin("RXS", "Update Desc"); + uint16_t crcfixup = igbe->regs.rctl.secrc() ? 0 : 4 ; - desc->len = htole((uint16_t)(pktPtr->length + crcfixup)); - DPRINTF(EthernetDesc, "pktPtr->length: %d stripcrc offset: %d value written: %d %d\n", - pktPtr->length, crcfixup, + DPRINTF(EthernetDesc, "pktPtr->length: %d bytesCopied: %d stripcrc offset: %d value written: %d %d\n", + pktPtr->length, bytesCopied, crcfixup, htole((uint16_t)(pktPtr->length + crcfixup)), (uint16_t)(pktPtr->length + crcfixup)); @@ -728,20 +940,32 @@ IGbE::RxDescCache::pktComplete() DPRINTF(EthernetDesc, "Packet written to memory updating Descriptor\n"); - uint8_t status = RXDS_DD | RXDS_EOP; + uint16_t status = RXDS_DD; uint8_t err = 0; + uint16_t ext_err = 0; + uint16_t csum = 0; + uint16_t ptype = 0; + uint16_t ip_id = 0; + + assert(bytesCopied <= pktPtr->length); + if (bytesCopied == pktPtr->length) + status |= RXDS_EOP; IpPtr ip(pktPtr); if (ip) { DPRINTF(EthernetDesc, "Proccesing Ip packet with Id=%d\n", ip->id()); + ptype |= RXDP_IPV4; + ip_id = ip->id(); if (igbe->regs.rxcsum.ipofld()) { DPRINTF(EthernetDesc, "Checking IP checksum\n"); status |= RXDS_IPCS; - desc->csum = htole(cksum(ip)); + csum = htole(cksum(ip)); + igbe->rxIpChecksums++; if (cksum(ip) != 0) { err |= RXDE_IPE; + ext_err |= RXDEE_IPE; DPRINTF(EthernetDesc, "Checksum is bad!!\n"); } } @@ -749,10 +973,13 @@ IGbE::RxDescCache::pktComplete() if (tcp && igbe->regs.rxcsum.tuofld()) { DPRINTF(EthernetDesc, "Checking TCP checksum\n"); status |= RXDS_TCPCS; - desc->csum = htole(cksum(tcp)); + ptype |= RXDP_TCP; + csum = htole(cksum(tcp)); + igbe->rxTcpChecksums++; if (cksum(tcp) != 0) { DPRINTF(EthernetDesc, "Checksum is bad!!\n"); err |= RXDE_TCPE; + ext_err |= RXDEE_TCPE; } } @@ -760,9 +987,12 @@ IGbE::RxDescCache::pktComplete() if (udp && igbe->regs.rxcsum.tuofld()) { DPRINTF(EthernetDesc, "Checking UDP checksum\n"); status |= RXDS_UDPCS; - desc->csum = htole(cksum(udp)); + ptype |= RXDP_UDP; + csum = htole(cksum(udp)); + igbe->rxUdpChecksums++; if (cksum(udp) != 0) { DPRINTF(EthernetDesc, "Checksum is bad!!\n"); + ext_err |= RXDEE_TCPE; err |= RXDE_TCPE; } } @@ -770,53 +1000,83 @@ IGbE::RxDescCache::pktComplete() DPRINTF(EthernetSM, "Proccesing Non-Ip packet\n"); } + switch (igbe->regs.srrctl.desctype()) { + case RXDT_LEGACY: + desc->legacy.len = htole((uint16_t)(pktPtr->length + crcfixup)); + desc->legacy.status = htole(status); + desc->legacy.errors = htole(err); + // No vlan support at this point... just set it to 0 + desc->legacy.vlan = 0; + break; + case RXDT_ADV_SPLIT_A: + case RXDT_ADV_ONEBUF: + desc->adv_wb.rss_type = htole(0); + desc->adv_wb.pkt_type = htole(ptype); + if (igbe->regs.rxcsum.pcsd()) { + // no rss support right now + desc->adv_wb.rss_hash = htole(0); + } else { + desc->adv_wb.id = htole(ip_id); + desc->adv_wb.csum = htole(csum); + } + desc->adv_wb.status = htole(status); + desc->adv_wb.errors = htole(ext_err); + // no vlan support + desc->adv_wb.vlan_tag = htole(0); + break; + default: + panic("Unimplemnted RX receive buffer type %d\n", + igbe->regs.srrctl.desctype()); + } - desc->status = htole(status); - desc->errors = htole(err); - - // No vlan support at this point... just set it to 0 - desc->vlan = 0; + DPRINTF(EthernetDesc, "Descriptor complete w0: %#x w1: %#x\n", + desc->adv_read.pkt, desc->adv_read.hdr); - // Deal with the rx timer interrupts - if (igbe->regs.rdtr.delay()) { - DPRINTF(EthernetSM, "RXS: Scheduling DTR for %d\n", - igbe->regs.rdtr.delay() * igbe->intClock()); - igbe->rdtrEvent.reschedule(curTick + igbe->regs.rdtr.delay() * - igbe->intClock(),true); - } + if (bytesCopied == pktPtr->length) { + DPRINTF(EthernetDesc, "Packet completely written to descriptor buffers\n"); + // Deal with the rx timer interrupts + if (igbe->regs.rdtr.delay()) { + DPRINTF(EthernetSM, "RXS: Scheduling DTR for %d\n", + igbe->regs.rdtr.delay() * igbe->intClock()); + igbe->reschedule(igbe->rdtrEvent, + curTick + igbe->regs.rdtr.delay() * igbe->intClock(), true); + } - if (igbe->regs.radv.idv() && igbe->regs.rdtr.delay()) { - DPRINTF(EthernetSM, "RXS: Scheduling ADV for %d\n", - igbe->regs.radv.idv() * igbe->intClock()); - if (!igbe->radvEvent.scheduled()) { - igbe->radvEvent.schedule(curTick + igbe->regs.radv.idv() * - igbe->intClock()); + if (igbe->regs.radv.idv()) { + DPRINTF(EthernetSM, "RXS: Scheduling ADV for %d\n", + igbe->regs.radv.idv() * igbe->intClock()); + if (!igbe->radvEvent.scheduled()) { + igbe->schedule(igbe->radvEvent, + curTick + igbe->regs.radv.idv() * igbe->intClock()); + } } - } - // if neither radv or rdtr, maybe itr is set... - if (!igbe->regs.rdtr.delay()) { - DPRINTF(EthernetSM, "RXS: Receive interrupt delay disabled, posting IT_RXT\n"); - igbe->postInterrupt(IT_RXT); - } + // if neither radv or rdtr, maybe itr is set... + if (!igbe->regs.rdtr.delay() && !igbe->regs.radv.idv()) { + DPRINTF(EthernetSM, "RXS: Receive interrupt delay disabled, posting IT_RXT\n"); + igbe->postInterrupt(IT_RXT); + } - // If the packet is small enough, interrupt appropriately - // I wonder if this is delayed or not?! - if (pktPtr->length <= igbe->regs.rsrpd.idv()) { - DPRINTF(EthernetSM, "RXS: Posting IT_SRPD beacuse small packet received\n"); - igbe->postInterrupt(IT_SRPD); + // If the packet is small enough, interrupt appropriately + // I wonder if this is delayed or not?! + if (pktPtr->length <= igbe->regs.rsrpd.idv()) { + DPRINTF(EthernetSM, "RXS: Posting IT_SRPD beacuse small packet received\n"); + igbe->postInterrupt(IT_SRPD); + } + bytesCopied = 0; } - DPRINTF(EthernetDesc, "Processing of this descriptor complete\n"); - unusedCache.pop_front(); - usedCache.push_back(desc); - - pktPtr = NULL; + igbe->checkDrain(); enableSm(); pktDone = true; - igbe->checkDrain(); + igbe->anBegin("RXS", "Done Updating Desc"); + DPRINTF(EthernetDesc, "Processing of this descriptor complete\n"); + igbe->anDq("RXS", annUnusedCacheQ); + unusedCache.pop_front(); + igbe->anQ("RXS", annUsedCacheQ); + usedCache.push_back(desc); } void @@ -842,7 +1102,9 @@ bool IGbE::RxDescCache::hasOutstandingEvents() { return pktEvent.scheduled() || wbEvent.scheduled() || - fetchEvent.scheduled(); + fetchEvent.scheduled() || pktHdrEvent.scheduled() || + pktDataEvent.scheduled(); + } void @@ -850,6 +1112,8 @@ IGbE::RxDescCache::serialize(std::ostream &os) { DescCache<RxDesc>::serialize(os); SERIALIZE_SCALAR(pktDone); + SERIALIZE_SCALAR(splitCount); + SERIALIZE_SCALAR(bytesCopied); } void @@ -857,6 +1121,8 @@ IGbE::RxDescCache::unserialize(Checkpoint *cp, const std::string §ion) { DescCache<RxDesc>::unserialize(cp, section); UNSERIALIZE_SCALAR(pktDone); + UNSERIALIZE_SCALAR(splitCount); + UNSERIALIZE_SCALAR(bytesCopied); } @@ -864,45 +1130,154 @@ IGbE::RxDescCache::unserialize(Checkpoint *cp, const std::string §ion) IGbE::TxDescCache::TxDescCache(IGbE *i, const std::string n, int s) : DescCache<TxDesc>(i,n, s), pktDone(false), isTcp(false), pktWaiting(false), - pktEvent(this) + completionAddress(0), completionEnabled(false), + useTso(false), pktEvent(this), headerEvent(this), nullEvent(this) { + annSmFetch = "TX Desc Fetch"; + annSmWb = "TX Desc Writeback"; + annUnusedDescQ = "TX Unused Descriptors"; + annUnusedCacheQ = "TX Unused Descriptor Cache"; + annUsedCacheQ = "TX Used Descriptor Cache"; + annUsedDescQ = "TX Used Descriptors"; + annDescQ = "TX Descriptors"; } -int -IGbE::TxDescCache::getPacketSize() +void +IGbE::TxDescCache::processContextDesc() { assert(unusedCache.size()); - TxDesc *desc; + + DPRINTF(EthernetDesc, "Checking and processing context descriptors\n"); - DPRINTF(EthernetDesc, "Starting processing of descriptor\n"); - - while (unusedCache.size() && TxdOp::isContext(unusedCache.front())) { - DPRINTF(EthernetDesc, "Got context descriptor type... skipping\n"); + while (!useTso && unusedCache.size() && TxdOp::isContext(unusedCache.front())) { + DPRINTF(EthernetDesc, "Got context descriptor type...\n"); - // I think we can just ignore these for now? desc = unusedCache.front(); - DPRINTF(EthernetDesc, "Descriptor upper: %#x lower: %#X\n", desc->d1, - desc->d2); + DPRINTF(EthernetDesc, "Descriptor upper: %#x lower: %#X\n", + desc->d1, desc->d2); + + // is this going to be a tcp or udp packet? isTcp = TxdOp::tcp(desc) ? true : false; - // make sure it's ipv4 - //assert(TxdOp::ip(desc)); + // setup all the TSO variables, they'll be ignored if we don't use + // tso for this connection + tsoHeaderLen = TxdOp::hdrlen(desc); + tsoMss = TxdOp::mss(desc); + + if (TxdOp::isType(desc, TxdOp::TXD_CNXT) && TxdOp::tse(desc)) { + DPRINTF(EthernetDesc, "TCP offload enabled for packet hdrlen: %d mss: %d paylen %d\n", + TxdOp::hdrlen(desc), TxdOp::mss(desc), TxdOp::getLen(desc)); + useTso = true; + tsoTotalLen = TxdOp::getLen(desc); + tsoLoadedHeader = false; + tsoDescBytesUsed = 0; + tsoUsedLen = 0; + tsoPrevSeq = 0; + tsoPktHasHeader = false; + tsoPkts = 0; + + } TxdOp::setDd(desc); unusedCache.pop_front(); + igbe->anDq("TXS", annUnusedCacheQ); usedCache.push_back(desc); + igbe->anQ("TXS", annUsedCacheQ); } if (!unusedCache.size()) + return; + + desc = unusedCache.front(); + if (!useTso && TxdOp::isType(desc, TxdOp::TXD_ADVDATA) && TxdOp::tse(desc)) { + DPRINTF(EthernetDesc, "TCP offload(adv) enabled for packet hdrlen: %d mss: %d paylen %d\n", + tsoHeaderLen, tsoMss, TxdOp::getTsoLen(desc)); + useTso = true; + tsoTotalLen = TxdOp::getTsoLen(desc); + tsoLoadedHeader = false; + tsoDescBytesUsed = 0; + tsoUsedLen = 0; + tsoPrevSeq = 0; + tsoPktHasHeader = false; + tsoPkts = 0; + } + + if (useTso && !tsoLoadedHeader) { + // we need to fetch a header + DPRINTF(EthernetDesc, "Starting DMA of TSO header\n"); + assert(TxdOp::isData(desc) && TxdOp::getLen(desc) >= tsoHeaderLen); + pktWaiting = true; + assert(tsoHeaderLen <= 256); + igbe->dmaRead(igbe->platform->pciToDma(TxdOp::getBuf(desc)), + tsoHeaderLen, &headerEvent, tsoHeader, 0); + } +} + +void +IGbE::TxDescCache::headerComplete() +{ + DPRINTF(EthernetDesc, "TSO: Fetching TSO header complete\n"); + pktWaiting = false; + + assert(unusedCache.size()); + TxDesc *desc = unusedCache.front(); + DPRINTF(EthernetDesc, "TSO: len: %d tsoHeaderLen: %d\n", + TxdOp::getLen(desc), tsoHeaderLen); + + if (TxdOp::getLen(desc) == tsoHeaderLen) { + tsoDescBytesUsed = 0; + tsoLoadedHeader = true; + unusedCache.pop_front(); + usedCache.push_back(desc); + } else { + // I don't think this case happens, I think the headrer is always + // it's own packet, if it wasn't it might be as simple as just + // incrementing descBytesUsed by the header length, but I'm not + // completely sure + panic("TSO header part of bigger packet, not implemented\n"); + } + enableSm(); + igbe->checkDrain(); +} + +int +IGbE::TxDescCache::getPacketSize(EthPacketPtr p) +{ + TxDesc *desc; + + + if (!unusedCache.size()) return -1; + + DPRINTF(EthernetDesc, "Starting processing of descriptor\n"); - DPRINTF(EthernetDesc, "Next TX packet is %d bytes\n", - TxdOp::getLen(unusedCache.front())); + assert(!useTso || tsoLoadedHeader); + desc = unusedCache.front(); + + + if (useTso) { + DPRINTF(EthernetDesc, "getPacket(): TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2); + DPRINTF(EthernetDesc, "TSO: use: %d hdrlen: %d mss: %d total: %d used: %d loaded hdr: %d\n", + useTso, tsoHeaderLen, tsoMss, tsoTotalLen, tsoUsedLen, tsoLoadedHeader); + DPRINTF(EthernetDesc, "TSO: descBytesUsed: %d copyBytes: %d this descLen: %d\n", + tsoDescBytesUsed, tsoCopyBytes, TxdOp::getLen(desc)); + DPRINTF(EthernetDesc, "TSO: pktHasHeader: %d\n", tsoPktHasHeader); + + if (tsoPktHasHeader) + tsoCopyBytes = std::min((tsoMss + tsoHeaderLen) - p->length, TxdOp::getLen(desc) - tsoDescBytesUsed); + else + tsoCopyBytes = std::min(tsoMss, TxdOp::getLen(desc) - tsoDescBytesUsed); + Addr pkt_size = tsoCopyBytes + (tsoPktHasHeader ? 0 : tsoHeaderLen); + DPRINTF(EthernetDesc, "TSO: Next packet is %d bytes\n", pkt_size); + return pkt_size; + } - return TxdOp::getLen(unusedCache.front()); + DPRINTF(EthernetDesc, "Next TX packet is %d bytes\n", + TxdOp::getLen(unusedCache.front())); + return TxdOp::getLen(desc); } void @@ -913,17 +1288,37 @@ IGbE::TxDescCache::getPacketData(EthPacketPtr p) TxDesc *desc; desc = unusedCache.front(); + DPRINTF(EthernetDesc, "getPacketData(): TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2); assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) && TxdOp::getLen(desc)); pktPtr = p; pktWaiting = true; - DPRINTF(EthernetDesc, "Starting DMA of packet\n"); - igbe->dmaRead(igbe->platform->pciToDma(TxdOp::getBuf(desc)), - TxdOp::getLen(desc), &pktEvent, p->data + p->length); - - + DPRINTF(EthernetDesc, "Starting DMA of packet at offset %d\n", p->length); + + if (useTso) { + assert(tsoLoadedHeader); + if (!tsoPktHasHeader) { + DPRINTF(EthernetDesc, "Loading TSO header (%d bytes) into start of packet\n", + tsoHeaderLen); + memcpy(p->data, &tsoHeader,tsoHeaderLen); + p->length +=tsoHeaderLen; + tsoPktHasHeader = true; + } + } + + if (useTso) { + tsoDescBytesUsed += tsoCopyBytes; + assert(tsoDescBytesUsed <= TxdOp::getLen(desc)); + DPRINTF(EthernetDesc, "Starting DMA of packet at offset %d length: %d\n", + p->length, tsoCopyBytes); + igbe->dmaRead(igbe->platform->pciToDma(TxdOp::getBuf(desc)) + tsoDescBytesUsed, + tsoCopyBytes, &pktEvent, p->data + p->length, igbe->txReadDelay); + } else { + igbe->dmaRead(igbe->platform->pciToDma(TxdOp::getBuf(desc)), + TxdOp::getLen(desc), &pktEvent, p->data + p->length, igbe->txReadDelay); + } } void @@ -934,6 +1329,8 @@ IGbE::TxDescCache::pktComplete() assert(unusedCache.size()); assert(pktPtr); + igbe->anBegin("TXS", "Update Desc"); + DPRINTF(EthernetDesc, "DMA of packet complete\n"); @@ -941,40 +1338,83 @@ IGbE::TxDescCache::pktComplete() assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) && TxdOp::getLen(desc)); DPRINTF(EthernetDesc, "TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2); + DPRINTF(EthernetDesc, "TSO: use: %d hdrlen: %d mss: %d total: %d used: %d loaded hdr: %d\n", + useTso, tsoHeaderLen, tsoMss, tsoTotalLen, tsoUsedLen, tsoLoadedHeader); - if (!TxdOp::eop(desc)) { - // This only supports two descriptors per tx packet - assert(pktPtr->length == 0); - pktPtr->length = TxdOp::getLen(desc); + // Set the length of the data in the EtherPacket + if (useTso) { + pktPtr->length += tsoCopyBytes; + tsoUsedLen += tsoCopyBytes; + } else + pktPtr->length += TxdOp::getLen(desc); + + DPRINTF(EthernetDesc, "TSO: descBytesUsed: %d copyBytes: %d\n", + tsoDescBytesUsed, tsoCopyBytes); + + + if ((!TxdOp::eop(desc) && !useTso) || + (pktPtr->length < ( tsoMss + tsoHeaderLen) && + tsoTotalLen != tsoUsedLen && useTso)) { + assert(!useTso || (tsoDescBytesUsed == TxdOp::getLen(desc))); + igbe->anDq("TXS", annUnusedCacheQ); unusedCache.pop_front(); + igbe->anQ("TXS", annUsedCacheQ); usedCache.push_back(desc); + + tsoDescBytesUsed = 0; pktDone = true; pktWaiting = false; + pktMultiDesc = true; + + DPRINTF(EthernetDesc, "Partial Packet Descriptor of %d bytes Done\n", + pktPtr->length); pktPtr = NULL; - DPRINTF(EthernetDesc, "Partial Packet Descriptor Done\n"); enableSm(); igbe->checkDrain(); return; } - // Set the length of the data in the EtherPacket - pktPtr->length += TxdOp::getLen(desc); + pktMultiDesc = false; // no support for vlans assert(!TxdOp::vle(desc)); - // we alway report status - assert(TxdOp::rs(desc)); - // we only support single packet descriptors at this point - assert(TxdOp::eop(desc)); + if (!useTso) + assert(TxdOp::eop(desc)); // set that this packet is done - TxdOp::setDd(desc); + if (TxdOp::rs(desc)) + TxdOp::setDd(desc); DPRINTF(EthernetDesc, "TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2); + if (useTso) { + IpPtr ip(pktPtr); + if (ip) { + DPRINTF(EthernetDesc, "TSO: Modifying IP header. Id + %d\n", + tsoPkts); + ip->id(ip->id() + tsoPkts++); + ip->len(pktPtr->length - EthPtr(pktPtr)->size()); + + TcpPtr tcp(ip); + if (tcp) { + DPRINTF(EthernetDesc, "TSO: Modifying TCP header. old seq %d + %d\n", + tcp->seq(), tsoPrevSeq); + tcp->seq(tcp->seq() + tsoPrevSeq); + if (tsoUsedLen != tsoTotalLen) + tcp->flags(tcp->flags() & ~9); // clear fin & psh + } + UdpPtr udp(ip); + if (udp) { + DPRINTF(EthernetDesc, "TSO: Modifying UDP header.\n"); + udp->len(pktPtr->length - EthPtr(pktPtr)->size()); + } + } + tsoPrevSeq = tsoUsedLen; + } + if (DTRACE(EthernetDesc)) { IpPtr ip(pktPtr); if (ip) @@ -988,10 +1428,11 @@ IGbE::TxDescCache::pktComplete() if (TxdOp::isData(desc) && ( TxdOp::ixsm(desc) || TxdOp::txsm(desc)) ) { DPRINTF(EthernetDesc, "Calculating checksums for packet\n"); IpPtr ip(pktPtr); - + assert(ip); if (TxdOp::ixsm(desc)) { ip->sum(0); ip->sum(cksum(ip)); + igbe->txIpChecksums++; DPRINTF(EthernetDesc, "Calculated IP checksum\n"); } if (TxdOp::txsm(desc)) { @@ -1000,11 +1441,13 @@ IGbE::TxDescCache::pktComplete() if (tcp) { tcp->sum(0); tcp->sum(cksum(tcp)); + igbe->txTcpChecksums++; DPRINTF(EthernetDesc, "Calculated TCP checksum\n"); } else if (udp) { assert(udp); udp->sum(0); udp->sum(cksum(udp)); + igbe->txUdpChecksums++; DPRINTF(EthernetDesc, "Calculated UDP checksum\n"); } else { panic("Told to checksum, but don't know how\n"); @@ -1017,47 +1460,100 @@ IGbE::TxDescCache::pktComplete() DPRINTF(EthernetDesc, "Descriptor had IDE set\n"); if (igbe->regs.tidv.idv()) { DPRINTF(EthernetDesc, "setting tidv\n"); - igbe->tidvEvent.reschedule(curTick + igbe->regs.tidv.idv() * - igbe->intClock(), true); + igbe->reschedule(igbe->tidvEvent, + curTick + igbe->regs.tidv.idv() * igbe->intClock(), true); } if (igbe->regs.tadv.idv() && igbe->regs.tidv.idv()) { DPRINTF(EthernetDesc, "setting tadv\n"); if (!igbe->tadvEvent.scheduled()) { - igbe->tadvEvent.schedule(curTick + igbe->regs.tadv.idv() * - igbe->intClock()); + igbe->schedule(igbe->tadvEvent, + curTick + igbe->regs.tadv.idv() * igbe->intClock()); } } } + if (!useTso || TxdOp::getLen(desc) == tsoDescBytesUsed) { + DPRINTF(EthernetDesc, "Descriptor Done\n"); + igbe->anDq("TXS", annUnusedCacheQ); + unusedCache.pop_front(); + igbe->anQ("TXS", annUsedCacheQ); + usedCache.push_back(desc); + tsoDescBytesUsed = 0; + } + + if (useTso && tsoUsedLen == tsoTotalLen) + useTso = false; + - unusedCache.pop_front(); - usedCache.push_back(desc); + DPRINTF(EthernetDesc, "------Packet of %d bytes ready for transmission-------\n", + pktPtr->length); pktDone = true; pktWaiting = false; pktPtr = NULL; - - DPRINTF(EthernetDesc, "Descriptor Done\n"); + tsoPktHasHeader = false; if (igbe->regs.txdctl.wthresh() == 0) { + igbe->anBegin("TXS", "Desc Writeback"); DPRINTF(EthernetDesc, "WTHRESH == 0, writing back descriptor\n"); writeback(0); + } else if (igbe->regs.txdctl.gran() && igbe->regs.txdctl.wthresh() >= + descInBlock(usedCache.size())) { + DPRINTF(EthernetDesc, "used > WTHRESH, writing back descriptor\n"); + igbe->anBegin("TXS", "Desc Writeback"); + writeback((igbe->cacheBlockSize()-1)>>4); } else if (igbe->regs.txdctl.wthresh() >= usedCache.size()) { DPRINTF(EthernetDesc, "used > WTHRESH, writing back descriptor\n"); + igbe->anBegin("TXS", "Desc Writeback"); writeback((igbe->cacheBlockSize()-1)>>4); } + enableSm(); igbe->checkDrain(); } void +IGbE::TxDescCache::actionAfterWb() +{ + DPRINTF(EthernetDesc, "actionAfterWb() completionEnabled: %d\n", + completionEnabled); + igbe->postInterrupt(iGbReg::IT_TXDW); + if (completionEnabled) { + descEnd = igbe->regs.tdh(); + DPRINTF(EthernetDesc, "Completion writing back value: %d to addr: %#x\n", descEnd, + completionAddress); + igbe->dmaWrite(igbe->platform->pciToDma(mbits(completionAddress, 63, 2)), + sizeof(descEnd), &nullEvent, (uint8_t*)&descEnd, 0); + } +} + +void IGbE::TxDescCache::serialize(std::ostream &os) { DescCache<TxDesc>::serialize(os); SERIALIZE_SCALAR(pktDone); SERIALIZE_SCALAR(isTcp); SERIALIZE_SCALAR(pktWaiting); + SERIALIZE_SCALAR(pktMultiDesc); + + SERIALIZE_SCALAR(useTso); + SERIALIZE_SCALAR(tsoHeaderLen); + SERIALIZE_SCALAR(tsoMss); + SERIALIZE_SCALAR(tsoTotalLen); + SERIALIZE_SCALAR(tsoUsedLen); + SERIALIZE_SCALAR(tsoPrevSeq);; + SERIALIZE_SCALAR(tsoPktPayloadBytes); + SERIALIZE_SCALAR(tsoLoadedHeader); + SERIALIZE_SCALAR(tsoPktHasHeader); + SERIALIZE_ARRAY(tsoHeader, 256); + SERIALIZE_SCALAR(tsoDescBytesUsed); + SERIALIZE_SCALAR(tsoCopyBytes); + SERIALIZE_SCALAR(tsoPkts); + + SERIALIZE_SCALAR(completionAddress); + SERIALIZE_SCALAR(completionEnabled); + SERIALIZE_SCALAR(descEnd); } void @@ -1067,6 +1563,25 @@ IGbE::TxDescCache::unserialize(Checkpoint *cp, const std::string §ion) UNSERIALIZE_SCALAR(pktDone); UNSERIALIZE_SCALAR(isTcp); UNSERIALIZE_SCALAR(pktWaiting); + UNSERIALIZE_SCALAR(pktMultiDesc); + + UNSERIALIZE_SCALAR(useTso); + UNSERIALIZE_SCALAR(tsoHeaderLen); + UNSERIALIZE_SCALAR(tsoMss); + UNSERIALIZE_SCALAR(tsoTotalLen); + UNSERIALIZE_SCALAR(tsoUsedLen); + UNSERIALIZE_SCALAR(tsoPrevSeq);; + UNSERIALIZE_SCALAR(tsoPktPayloadBytes); + UNSERIALIZE_SCALAR(tsoLoadedHeader); + UNSERIALIZE_SCALAR(tsoPktHasHeader); + UNSERIALIZE_ARRAY(tsoHeader, 256); + UNSERIALIZE_SCALAR(tsoDescBytesUsed); + UNSERIALIZE_SCALAR(tsoCopyBytes); + UNSERIALIZE_SCALAR(tsoPkts); + + UNSERIALIZE_SCALAR(completionAddress); + UNSERIALIZE_SCALAR(completionEnabled); + UNSERIALIZE_SCALAR(descEnd); } bool @@ -1101,9 +1616,9 @@ IGbE::TxDescCache::hasOutstandingEvents() void IGbE::restartClock() { - if (!tickEvent.scheduled() && (rxTick || txTick || txFifoTick) && getState() == - SimObject::Running) - tickEvent.schedule((curTick/ticks(1)) * ticks(1) + ticks(1)); + if (!tickEvent.scheduled() && (rxTick || txTick || txFifoTick) && + getState() == SimObject::Running) + schedule(tickEvent, (curTick / ticks(1)) * ticks(1) + ticks(1)); } unsigned int @@ -1122,13 +1637,14 @@ IGbE::drain(Event *de) rxTick = false; if (tickEvent.scheduled()) - tickEvent.deschedule(); + deschedule(tickEvent); if (count) changeState(Draining); else changeState(Drained); + DPRINTF(EthernetSM, "got drain() returning %d", count); return count; } @@ -1142,6 +1658,7 @@ IGbE::resume() rxTick = true; restartClock(); + DPRINTF(EthernetSM, "resuming from drain"); } void @@ -1150,6 +1667,7 @@ IGbE::checkDrain() if (!drainEvent) return; + DPRINTF(EthernetSM, "checkDrain() in drain\n"); txFifoTick = false; txTick = false; rxTick = false; @@ -1172,20 +1690,22 @@ IGbE::txStateMachine() // If we have a packet available and it's length is not 0 (meaning it's not // a multidescriptor packet) put it in the fifo, otherwise an the next // iteration we'll get the rest of the data - if (txPacket && txDescCache.packetAvailable() && txPacket->length) { + if (txPacket && txDescCache.packetAvailable() + && !txDescCache.packetMultiDesc() && txPacket->length) { bool success; + anQ("TXS", "TX FIFO Q"); DPRINTF(EthernetSM, "TXS: packet placed in TX FIFO\n"); success = txFifo.push(txPacket); txFifoTick = true && !drainEvent; assert(success); txPacket = NULL; + anBegin("TXS", "Desc Writeback"); txDescCache.writeback((cacheBlockSize()-1)>>4); return; } // Only support descriptor granularity - assert(regs.txdctl.gran()); if (regs.txdctl.lwthresh() && txDescCache.descLeft() < (regs.txdctl.lwthresh() * 8)) { DPRINTF(EthernetSM, "TXS: LWTHRESH caused posting of TXDLOW\n"); postInterrupt(IT_TXDLOW); @@ -1198,7 +1718,10 @@ IGbE::txStateMachine() if (!txDescCache.packetWaiting()) { if (txDescCache.descLeft() == 0) { postInterrupt(IT_TXQE); + anBegin("TXS", "Desc Writeback"); txDescCache.writeback(0); + anBegin("TXS", "Desc Fetch"); + anWe("TXS", txDescCache.annUnusedCacheQ); txDescCache.fetchDescriptors(); DPRINTF(EthernetSM, "TXS: No descriptors left in ring, forcing " "writeback stopping ticking and posting TXQE\n"); @@ -1208,16 +1731,28 @@ IGbE::txStateMachine() if (!(txDescCache.descUnused())) { + anBegin("TXS", "Desc Fetch"); txDescCache.fetchDescriptors(); + anWe("TXS", txDescCache.annUnusedCacheQ); DPRINTF(EthernetSM, "TXS: No descriptors available in cache, fetching and stopping ticking\n"); txTick = false; return; } + anPq("TXS", txDescCache.annUnusedCacheQ); + + txDescCache.processContextDesc(); + if (txDescCache.packetWaiting()) { + DPRINTF(EthernetSM, "TXS: Fetching TSO header, stopping ticking\n"); + txTick = false; + return; + } int size; - size = txDescCache.getPacketSize(); + size = txDescCache.getPacketSize(txPacket); if (size > 0 && txFifo.avail() > size) { + anRq("TXS", "TX FIFO Q"); + anBegin("TXS", "DMA Packet"); DPRINTF(EthernetSM, "TXS: Reserving %d bytes in FIFO and begining " "DMA of next packet\n", size); txFifo.reserve(size); @@ -1225,8 +1760,10 @@ IGbE::txStateMachine() } else if (size <= 0) { DPRINTF(EthernetSM, "TXS: getPacketSize returned: %d\n", size); DPRINTF(EthernetSM, "TXS: No packets to get, writing back used descriptors\n"); + anBegin("TXS", "Desc Writeback"); txDescCache.writeback(0); } else { + anWf("TXS", "TX FIFO Q"); DPRINTF(EthernetSM, "TXS: FIFO full, stopping ticking until space " "available in FIFO\n"); txTick = false; @@ -1242,10 +1779,16 @@ IGbE::txStateMachine() bool IGbE::ethRxPkt(EthPacketPtr pkt) { + rxBytes += pkt->length; + rxPackets++; + DPRINTF(Ethernet, "RxFIFO: Receiving pcakte from wire\n"); + anBegin("RXQ", "Wire Recv"); + if (!regs.rctl.en()) { DPRINTF(Ethernet, "RxFIFO: RX not enabled, dropping\n"); + anBegin("RXQ", "FIFO Drop", CPA::FL_BAD); return true; } @@ -1259,9 +1802,23 @@ IGbE::ethRxPkt(EthPacketPtr pkt) if (!rxFifo.push(pkt)) { DPRINTF(Ethernet, "RxFIFO: Packet won't fit in fifo... dropped\n"); postInterrupt(IT_RXO, true); + anBegin("RXQ", "FIFO Drop", CPA::FL_BAD); return false; } + if (CPA::available() && cpa->enabled()) { + assert(sys->numSystemsRunning <= 2); + System *other_sys; + if (sys->systemList[0] == sys) + other_sys = sys->systemList[1]; + else + other_sys = sys->systemList[0]; + + cpa->hwDq(CPA::FL_NONE, sys, macAddr, "RXQ", "WireQ", 0, other_sys); + anQ("RXQ", "RX FIFO Q"); + cpa->hwWe(CPA::FL_NONE, sys, macAddr, "RXQ", "WireQ", 0, other_sys); + } + return true; } @@ -1280,6 +1837,8 @@ IGbE::rxStateMachine() rxDmaPacket = false; DPRINTF(EthernetSM, "RXS: Packet completed DMA to memory\n"); int descLeft = rxDescCache.descLeft(); + DPRINTF(EthernetSM, "RXS: descLeft: %d rdmts: %d rdlen: %d\n", + descLeft, regs.rctl.rdmts(), regs.rdlen()); switch (regs.rctl.rdmts()) { case 2: if (descLeft > .125 * regs.rdlen()) break; case 1: if (descLeft > .250 * regs.rdlen()) break; @@ -1289,7 +1848,11 @@ IGbE::rxStateMachine() break; } + if (rxFifo.empty()) + rxDescCache.writeback(0); + if (descLeft == 0) { + anBegin("RXS", "Writeback Descriptors"); rxDescCache.writeback(0); DPRINTF(EthernetSM, "RXS: No descriptors left in ring, forcing" " writeback and stopping ticking\n"); @@ -1301,6 +1864,7 @@ IGbE::rxStateMachine() if (regs.rxdctl.wthresh() >= rxDescCache.descUsed()) { DPRINTF(EthernetSM, "RXS: Writing back because WTHRESH >= descUsed\n"); + anBegin("RXS", "Writeback Descriptors"); if (regs.rxdctl.wthresh() < (cacheBlockSize()>>4)) rxDescCache.writeback(regs.rxdctl.wthresh()-1); else @@ -1310,11 +1874,14 @@ IGbE::rxStateMachine() if ((rxDescCache.descUnused() < regs.rxdctl.pthresh()) && ((rxDescCache.descLeft() - rxDescCache.descUnused()) > regs.rxdctl.hthresh())) { DPRINTF(EthernetSM, "RXS: Fetching descriptors because descUnused < PTHRESH\n"); + anBegin("RXS", "Fetch Descriptors"); rxDescCache.fetchDescriptors(); } if (rxDescCache.descUnused() == 0) { + anBegin("RXS", "Fetch Descriptors"); rxDescCache.fetchDescriptors(); + anWe("RXS", rxDescCache.annUnusedCacheQ); DPRINTF(EthernetSM, "RXS: No descriptors available in cache, " "fetching descriptors and stopping ticking\n"); rxTick = false; @@ -1329,42 +1896,58 @@ IGbE::rxStateMachine() } if (!rxDescCache.descUnused()) { + anBegin("RXS", "Fetch Descriptors"); rxDescCache.fetchDescriptors(); + anWe("RXS", rxDescCache.annUnusedCacheQ); DPRINTF(EthernetSM, "RXS: No descriptors available in cache, stopping ticking\n"); rxTick = false; DPRINTF(EthernetSM, "RXS: No descriptors available, fetching\n"); return; } + anPq("RXS", rxDescCache.annUnusedCacheQ); if (rxFifo.empty()) { + anWe("RXS", "RX FIFO Q"); DPRINTF(EthernetSM, "RXS: RxFIFO empty, stopping ticking\n"); rxTick = false; return; } + anPq("RXS", "RX FIFO Q"); + anBegin("RXS", "Get Desc"); EthPacketPtr pkt; pkt = rxFifo.front(); - rxDescCache.writePacket(pkt); + pktOffset = rxDescCache.writePacket(pkt, pktOffset); DPRINTF(EthernetSM, "RXS: Writing packet into memory\n"); - DPRINTF(EthernetSM, "RXS: Removing packet from FIFO\n"); - rxFifo.pop(); + if (pktOffset == pkt->length) { + anBegin( "RXS", "FIFO Dequeue"); + DPRINTF(EthernetSM, "RXS: Removing packet from FIFO\n"); + pktOffset = 0; + anDq("RXS", "RX FIFO Q"); + rxFifo.pop(); + } + DPRINTF(EthernetSM, "RXS: stopping ticking until packet DMA completes\n"); rxTick = false; rxDmaPacket = true; + anBegin("RXS", "DMA Packet"); } void IGbE::txWire() { if (txFifo.empty()) { + anWe("TXQ", "TX FIFO Q"); txFifoTick = false; return; } + anPq("TXQ", "TX FIFO Q"); if (etherInt->sendPacket(txFifo.front())) { + cpa->hwQ(CPA::FL_NONE, sys, macAddr, "TXQ", "WireQ", 0); if (DTRACE(EthernetSM)) { IpPtr ip(txFifo.front()); if (ip) @@ -1373,8 +1956,15 @@ IGbE::txWire() else DPRINTF(EthernetSM, "Transmitting Non-Ip packet\n"); } + anDq("TXQ", "TX FIFO Q"); + anBegin("TXQ", "Wire Send"); DPRINTF(EthernetSM, "TxFIFO: Successful transmit, bytes available in fifo: %d\n", txFifo.avail()); + + txBytes += txFifo.front()->length; + txPackets++; + txFifoTick = false; + txFifo.pop(); } else { // We'll get woken up when the packet ethTxDone() gets called @@ -1398,12 +1988,13 @@ IGbE::tick() if (rxTick || txTick || txFifoTick) - tickEvent.schedule(curTick + ticks(1)); + schedule(tickEvent, curTick + ticks(1)); } void IGbE::ethTxDone() { + anBegin("TXQ", "Send Done"); // restart the tx state machines if they are stopped // fifo to send another packet // tx sm to put more data into the fifo @@ -1412,6 +2003,7 @@ IGbE::ethTxDone() txTick = true; restartClock(); + txWire(); DPRINTF(EthernetSM, "TxFIFO: Transmission complete\n"); } @@ -1426,6 +2018,7 @@ IGbE::serialize(std::ostream &os) SERIALIZE_SCALAR(eeDataBits); SERIALIZE_SCALAR(eeOpcode); SERIALIZE_SCALAR(eeAddr); + SERIALIZE_SCALAR(lastInterrupt); SERIALIZE_ARRAY(flash,iGbReg::EEPROM_SIZE); rxFifo.serialize("rxfifo", os); @@ -1459,6 +2052,8 @@ IGbE::serialize(std::ostream &os) inter_time = interEvent.when(); SERIALIZE_SCALAR(inter_time); + SERIALIZE_SCALAR(pktOffset); + nameOut(os, csprintf("%s.TxDescCache", name())); txDescCache.serialize(os); @@ -1477,6 +2072,7 @@ IGbE::unserialize(Checkpoint *cp, const std::string §ion) UNSERIALIZE_SCALAR(eeDataBits); UNSERIALIZE_SCALAR(eeOpcode); UNSERIALIZE_SCALAR(eeAddr); + UNSERIALIZE_SCALAR(lastInterrupt); UNSERIALIZE_ARRAY(flash,iGbReg::EEPROM_SIZE); rxFifo.unserialize("rxfifo", cp, section); @@ -1501,19 +2097,21 @@ IGbE::unserialize(Checkpoint *cp, const std::string §ion) UNSERIALIZE_SCALAR(inter_time); if (rdtr_time) - rdtrEvent.schedule(rdtr_time); + schedule(rdtrEvent, rdtr_time); if (radv_time) - radvEvent.schedule(radv_time); + schedule(radvEvent, radv_time); if (tidv_time) - tidvEvent.schedule(tidv_time); + schedule(tidvEvent, tidv_time); if (tadv_time) - tadvEvent.schedule(tadv_time); + schedule(tadvEvent, tadv_time); if (inter_time) - interEvent.schedule(inter_time); + schedule(interEvent, inter_time); + + UNSERIALIZE_SCALAR(pktOffset); txDescCache.unserialize(cp, csprintf("%s.TxDescCache", section)); diff --git a/src/dev/i8254xGBe.hh b/src/dev/i8254xGBe.hh index 9403c87b6..308cfabde 100644 --- a/src/dev/i8254xGBe.hh +++ b/src/dev/i8254xGBe.hh @@ -38,8 +38,8 @@ #include <deque> #include <string> +#include "base/cp_annotate.hh" #include "base/inet.hh" -#include "base/statistics.hh" #include "dev/etherdevice.hh" #include "dev/etherint.hh" #include "dev/etherpkt.hh" @@ -55,6 +55,7 @@ class IGbE : public EtherDevice { private: IGbEInt *etherInt; + CPA *cpa; // device registers iGbReg::Regs regs; @@ -84,11 +85,19 @@ class IGbE : public EtherDevice bool rxDmaPacket; + // Number of bytes copied from current RX packet + int pktOffset; + + // Delays in managaging descriptors + Tick fetchDelay, wbDelay; + Tick fetchCompDelay, wbCompDelay; + Tick rxWriteDelay, txReadDelay; + // Event and function to deal with RDTR timer expiring void rdtrProcess() { rxDescCache.writeback(0); DPRINTF(EthernetIntr, "Posting RXT interrupt because RDTR timer expired\n"); - postInterrupt(iGbReg::IT_RXT, true); + postInterrupt(iGbReg::IT_RXT); } //friend class EventWrapper<IGbE, &IGbE::rdtrProcess>; @@ -98,7 +107,7 @@ class IGbE : public EtherDevice void radvProcess() { rxDescCache.writeback(0); DPRINTF(EthernetIntr, "Posting RXT interrupt because RADV timer expired\n"); - postInterrupt(iGbReg::IT_RXT, true); + postInterrupt(iGbReg::IT_RXT); } //friend class EventWrapper<IGbE, &IGbE::radvProcess>; @@ -108,7 +117,7 @@ class IGbE : public EtherDevice void tadvProcess() { txDescCache.writeback(0); DPRINTF(EthernetIntr, "Posting TXDW interrupt because TADV timer expired\n"); - postInterrupt(iGbReg::IT_TXDW, true); + postInterrupt(iGbReg::IT_TXDW); } //friend class EventWrapper<IGbE, &IGbE::tadvProcess>; @@ -118,7 +127,7 @@ class IGbE : public EtherDevice void tidvProcess() { txDescCache.writeback(0); DPRINTF(EthernetIntr, "Posting TXDW interrupt because TIDV timer expired\n"); - postInterrupt(iGbReg::IT_TXDW, true); + postInterrupt(iGbReg::IT_TXDW); } //friend class EventWrapper<IGbE, &IGbE::tidvProcess>; EventWrapper<IGbE, &IGbE::tidvProcess> tidvEvent; @@ -129,6 +138,8 @@ class IGbE : public EtherDevice EventWrapper<IGbE, &IGbE::tick> tickEvent; + uint64_t macAddr; + void rxStateMachine(); void txStateMachine(); void txWire(); @@ -167,6 +178,35 @@ class IGbE : public EtherDevice */ void checkDrain(); + void anBegin(std::string sm, std::string st, int flags = CPA::FL_NONE) { + cpa->hwBegin((CPA::flags)flags, sys, macAddr, sm, st); + } + + void anQ(std::string sm, std::string q) { + cpa->hwQ(CPA::FL_NONE, sys, macAddr, sm, q, macAddr); + } + + void anDq(std::string sm, std::string q) { + cpa->hwDq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr); + } + + void anPq(std::string sm, std::string q, int num = 1) { + cpa->hwPq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr, NULL, num); + } + + void anRq(std::string sm, std::string q, int num = 1) { + cpa->hwRq(CPA::FL_NONE, sys, macAddr, sm, q, macAddr, NULL, num); + } + + void anWe(std::string sm, std::string q) { + cpa->hwWe(CPA::FL_NONE, sys, macAddr, sm, q, macAddr); + } + + void anWf(std::string sm, std::string q) { + cpa->hwWf(CPA::FL_NONE, sys, macAddr, sm, q, macAddr); + } + + template<class T> class DescCache { @@ -177,7 +217,7 @@ class IGbE : public EtherDevice virtual long descLen() const = 0; virtual void updateHead(long h) = 0; virtual void enableSm() = 0; - virtual void intAfterWb() const {} + virtual void actionAfterWb() {} virtual void fetchAfterWb() = 0; std::deque<T*> usedCache; @@ -216,9 +256,14 @@ class IGbE : public EtherDevice EthPacketPtr pktPtr; public: + /** Annotate sm*/ + std::string annSmFetch, annSmWb, annUnusedDescQ, annUsedCacheQ, + annUsedDescQ, annUnusedCacheQ, annDescQ; + DescCache(IGbE *i, const std::string n, int s) : igbe(i), _name(n), cachePnt(0), size(s), curFetching(0), wbOut(0), - pktPtr(NULL), fetchEvent(this), wbEvent(this) + pktPtr(NULL), wbDelayEvent(this), fetchDelayEvent(this), + fetchEvent(this), wbEvent(this) { fetchBuf = new T[size]; wbBuf = new T[size]; @@ -248,62 +293,92 @@ class IGbE : public EtherDevice int curHead = descHead(); int max_to_wb = usedCache.size(); - DPRINTF(EthernetDesc, "Writing back descriptors head: %d tail: " - "%d len: %d cachePnt: %d max_to_wb: %d descleft: %d\n", - curHead, descTail(), descLen(), cachePnt, max_to_wb, - descLeft()); - // Check if this writeback is less restrictive that the previous // and if so setup another one immediately following it - if (wbOut && (aMask < wbAlignment)) { - moreToWb = true; - wbAlignment = aMask; + if (wbOut) { + if (aMask < wbAlignment) { + moreToWb = true; + wbAlignment = aMask; + } DPRINTF(EthernetDesc, "Writing back already in process, returning\n"); return; } - moreToWb = false; wbAlignment = aMask; + + + DPRINTF(EthernetDesc, "Writing back descriptors head: %d tail: " + "%d len: %d cachePnt: %d max_to_wb: %d descleft: %d\n", + curHead, descTail(), descLen(), cachePnt, max_to_wb, + descLeft()); if (max_to_wb + curHead >= descLen()) { max_to_wb = descLen() - curHead; moreToWb = true; // this is by definition aligned correctly - } else if (aMask != 0) { + } else if (wbAlignment != 0) { // align the wb point to the mask - max_to_wb = max_to_wb & ~aMask; + max_to_wb = max_to_wb & ~wbAlignment; } DPRINTF(EthernetDesc, "Writing back %d descriptors\n", max_to_wb); - if (max_to_wb <= 0 || wbOut) + if (max_to_wb <= 0) { + if (usedCache.size()) + igbe->anBegin(annSmWb, "Wait Alignment", CPA::FL_WAIT); + else + igbe->anWe(annSmWb, annUsedCacheQ); return; + } wbOut = max_to_wb; + assert(!wbDelayEvent.scheduled()); + igbe->schedule(wbDelayEvent, curTick + igbe->wbDelay); + igbe->anBegin(annSmWb, "Prepare Writeback Desc"); + } + + void writeback1() + { + // If we're draining delay issuing this DMA + if (igbe->getState() != SimObject::Running) { + igbe->schedule(wbDelayEvent, curTick + igbe->wbDelay); + return; + } + + DPRINTF(EthernetDesc, "Begining DMA of %d descriptors\n", wbOut); + for (int x = 0; x < wbOut; x++) { assert(usedCache.size()); - memcpy(&wbBuf[x], usedCache[0], sizeof(T)); - delete usedCache[0]; - usedCache.pop_front(); + memcpy(&wbBuf[x], usedCache[x], sizeof(T)); + igbe->anPq(annSmWb, annUsedCacheQ); + igbe->anPq(annSmWb, annDescQ); + igbe->anQ(annSmWb, annUsedDescQ); } + + igbe->anBegin(annSmWb, "Writeback Desc DMA"); assert(wbOut); - igbe->dmaWrite(igbe->platform->pciToDma(descBase() + curHead * sizeof(T)), - wbOut * sizeof(T), &wbEvent, (uint8_t*)wbBuf); + igbe->dmaWrite(igbe->platform->pciToDma(descBase() + descHead() * sizeof(T)), + wbOut * sizeof(T), &wbEvent, (uint8_t*)wbBuf, + igbe->wbCompDelay); } + EventWrapper<DescCache, &DescCache::writeback1> wbDelayEvent; /** Fetch a chunk of descriptors into the descriptor cache. * Calls fetchComplete when the memory system returns the data */ + void fetchDescriptors() { size_t max_to_fetch; - if (curFetching) + if (curFetching) { + DPRINTF(EthernetDesc, "Currently fetching %d descriptors, returning\n", curFetching); return; + } if (descTail() >= cachePnt) max_to_fetch = descTail() - cachePnt; @@ -312,7 +387,20 @@ class IGbE : public EtherDevice size_t free_cache = size - usedCache.size() - unusedCache.size(); + if (!max_to_fetch) + igbe->anWe(annSmFetch, annUnusedDescQ); + else + igbe->anPq(annSmFetch, annUnusedDescQ, max_to_fetch); + + if (max_to_fetch) { + if (!free_cache) + igbe->anWf(annSmFetch, annDescQ); + else + igbe->anRq(annSmFetch, annDescQ, free_cache); + } + max_to_fetch = std::min(max_to_fetch, free_cache); + DPRINTF(EthernetDesc, "Fetching descriptors head: %d tail: " "%d len: %d cachePnt: %d max_to_fetch: %d descleft: %d\n", @@ -322,31 +410,53 @@ class IGbE : public EtherDevice // Nothing to do if (max_to_fetch == 0) return; - + // So we don't have two descriptor fetches going on at once curFetching = max_to_fetch; + assert(!fetchDelayEvent.scheduled()); + igbe->schedule(fetchDelayEvent, curTick + igbe->fetchDelay); + igbe->anBegin(annSmFetch, "Prepare Fetch Desc"); + } + + void fetchDescriptors1() + { + // If we're draining delay issuing this DMA + if (igbe->getState() != SimObject::Running) { + igbe->schedule(fetchDelayEvent, curTick + igbe->fetchDelay); + return; + } + + igbe->anBegin(annSmFetch, "Fetch Desc"); + DPRINTF(EthernetDesc, "Fetching descriptors at %#x (%#x), size: %#x\n", descBase() + cachePnt * sizeof(T), igbe->platform->pciToDma(descBase() + cachePnt * sizeof(T)), curFetching * sizeof(T)); assert(curFetching); igbe->dmaRead(igbe->platform->pciToDma(descBase() + cachePnt * sizeof(T)), - curFetching * sizeof(T), &fetchEvent, (uint8_t*)fetchBuf); + curFetching * sizeof(T), &fetchEvent, (uint8_t*)fetchBuf, + igbe->fetchCompDelay); } + EventWrapper<DescCache, &DescCache::fetchDescriptors1> fetchDelayEvent; /** Called by event when dma to read descriptors is completed */ void fetchComplete() { T *newDesc; + igbe->anBegin(annSmFetch, "Fetch Complete"); for (int x = 0; x < curFetching; x++) { newDesc = new T; memcpy(newDesc, &fetchBuf[x], sizeof(T)); unusedCache.push_back(newDesc); + igbe->anDq(annSmFetch, annUnusedDescQ); + igbe->anQ(annSmFetch, annUnusedCacheQ); + igbe->anQ(annSmFetch, annDescQ); } + #ifndef NDEBUG int oldCp = cachePnt; #endif @@ -361,6 +471,16 @@ class IGbE : public EtherDevice DPRINTF(EthernetDesc, "Fetching complete cachePnt %d -> %d\n", oldCp, cachePnt); + if ((descTail() >= cachePnt ? (descTail() - cachePnt) : (descLen() - + cachePnt)) == 0) + { + igbe->anWe(annSmFetch, annUnusedDescQ); + } else if (!(size - usedCache.size() - unusedCache.size())) { + igbe->anWf(annSmFetch, annDescQ); + } else { + igbe->anBegin(annSmFetch, "Wait", CPA::FL_WAIT); + } + enableSm(); igbe->checkDrain(); } @@ -372,10 +492,21 @@ class IGbE : public EtherDevice void wbComplete() { + igbe->anBegin(annSmWb, "Finish Writeback"); + long curHead = descHead(); #ifndef NDEBUG long oldHead = curHead; #endif + + for (int x = 0; x < wbOut; x++) { + assert(usedCache.size()); + delete usedCache[0]; + usedCache.pop_front(); + + igbe->anDq(annSmWb, annUsedCacheQ); + igbe->anDq(annSmWb, annDescQ); + } curHead += wbOut; wbOut = 0; @@ -390,14 +521,19 @@ class IGbE : public EtherDevice oldHead, curHead); // If we still have more to wb, call wb now - intAfterWb(); + actionAfterWb(); if (moreToWb) { + moreToWb = false; DPRINTF(EthernetDesc, "Writeback has more todo\n"); writeback(wbAlignment); } if (!wbOut) { igbe->checkDrain(); + if (usedCache.size()) + igbe->anBegin(annSmWb, "Wait", CPA::FL_WAIT); + else + igbe->anWe(annSmWb, annUsedCacheQ); } fetchAfterWb(); } @@ -411,8 +547,8 @@ class IGbE : public EtherDevice int descLeft() const { int left = unusedCache.size(); - if (cachePnt - descTail() >= 0) - left += (cachePnt - descTail()); + if (cachePnt >= descTail()) + left += (descLen() - cachePnt + descTail()); else left += (descTail() - cachePnt); @@ -464,6 +600,16 @@ class IGbE : public EtherDevice arrayParamOut(os, csprintf("unusedCache_%d", x), (uint8_t*)unusedCache[x],sizeof(T)); } + + Tick fetch_delay = 0, wb_delay = 0; + if (fetchDelayEvent.scheduled()) + fetch_delay = fetchDelayEvent.when(); + SERIALIZE_SCALAR(fetch_delay); + if (wbDelayEvent.scheduled()) + wb_delay = wbDelayEvent.when(); + SERIALIZE_SCALAR(wb_delay); + + } virtual void unserialize(Checkpoint *cp, const std::string §ion) @@ -492,6 +638,15 @@ class IGbE : public EtherDevice (uint8_t*)temp,sizeof(T)); unusedCache.push_back(temp); } + Tick fetch_delay = 0, wb_delay = 0; + UNSERIALIZE_SCALAR(fetch_delay); + UNSERIALIZE_SCALAR(wb_delay); + if (fetch_delay) + igbe->schedule(fetchDelayEvent, fetch_delay); + if (wb_delay) + igbe->schedule(wbDelayEvent, wb_delay); + + } virtual bool hasOutstandingEvents() { return wbEvent.scheduled() || fetchEvent.scheduled(); @@ -516,6 +671,12 @@ class IGbE : public EtherDevice bool pktDone; + /** Variable to head with header/data completion events */ + int splitCount; + + /** Bytes of packet that have been copied, so we know when to set EOP */ + int bytesCopied; + public: RxDescCache(IGbE *i, std::string n, int s); @@ -523,20 +684,28 @@ class IGbE : public EtherDevice * descriptor and update the book keeping. Should only be called when * there are no dma's pending. * @param packet ethernet packet to write - * @return if the packet could be written (there was a free descriptor) + * @param pkt_offset bytes already copied from the packet to memory + * @return pkt_offset + number of bytes copied during this call */ - void writePacket(EthPacketPtr packet); + int writePacket(EthPacketPtr packet, int pkt_offset); + /** Called by event when dma to write packet is completed */ void pktComplete(); - /** Check if the dma on the packet has completed. + /** Check if the dma on the packet has completed and RX state machine + * can continue */ - bool packetDone(); EventWrapper<RxDescCache, &RxDescCache::pktComplete> pktEvent; + // Event to handle issuing header and data write at the same time + // and only callking pktComplete() when both are completed + void pktSplitDone(); + EventWrapper<RxDescCache, &RxDescCache::pktSplitDone> pktHdrEvent; + EventWrapper<RxDescCache, &RxDescCache::pktSplitDone> pktDataEvent; + virtual bool hasOutstandingEvents(); virtual void serialize(std::ostream &os); @@ -555,15 +724,37 @@ class IGbE : public EtherDevice virtual long descLen() const { return igbe->regs.tdlen() >> 4; } virtual void updateHead(long h) { igbe->regs.tdh(h); } virtual void enableSm(); - virtual void intAfterWb() const { igbe->postInterrupt(iGbReg::IT_TXDW); } + virtual void actionAfterWb(); virtual void fetchAfterWb() { if (!igbe->txTick && igbe->getState() == SimObject::Running) fetchDescriptors(); } + + bool pktDone; bool isTcp; bool pktWaiting; + bool pktMultiDesc; + Addr completionAddress; + bool completionEnabled; + uint32_t descEnd; + + + // tso variables + bool useTso; + Addr tsoHeaderLen; + Addr tsoMss; + Addr tsoTotalLen; + Addr tsoUsedLen; + Addr tsoPrevSeq;; + Addr tsoPktPayloadBytes; + bool tsoLoadedHeader; + bool tsoPktHasHeader; + uint8_t tsoHeader[256]; + Addr tsoDescBytesUsed; + Addr tsoCopyBytes; + int tsoPkts; public: TxDescCache(IGbE *i, std::string n, int s); @@ -572,9 +763,15 @@ class IGbE : public EtherDevice * return the size the of the packet to reserve space in tx fifo. * @return size of the packet */ - int getPacketSize(); + int getPacketSize(EthPacketPtr p); void getPacketData(EthPacketPtr p); + void processContextDesc(); + /** Return the number of dsecriptors in a cache block for threshold + * operations. + */ + int descInBlock(int num_desc) { return num_desc / + igbe->cacheBlockSize() / sizeof(iGbReg::TxDesc); } /** Ask if the packet has been transfered so the state machine can give * it to the fifo. * @return packet available in descriptor cache @@ -586,13 +783,35 @@ class IGbE : public EtherDevice */ bool packetWaiting() { return pktWaiting; } + /** Ask if this packet is composed of multiple descriptors + * so even if we've got data, we need to wait for more before + * we can send it out. + * @return packet can't be sent out because it's a multi-descriptor + * packet + */ + bool packetMultiDesc() { return pktMultiDesc;} + /** Called by event when dma to write packet is completed */ void pktComplete(); EventWrapper<TxDescCache, &TxDescCache::pktComplete> pktEvent; + void headerComplete(); + EventWrapper<TxDescCache, &TxDescCache::headerComplete> headerEvent; + + + void completionWriteback(Addr a, bool enabled) { + DPRINTF(EthernetDesc, "Completion writeback Addr: %#x enabled: %d\n", + a, enabled); + completionAddress = a; + completionEnabled = enabled; + } + virtual bool hasOutstandingEvents(); + void nullCallback() { DPRINTF(EthernetDesc, "Completion writeback complete\n"); } + EventWrapper<TxDescCache, &TxDescCache::nullCallback> nullEvent; + virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); @@ -610,10 +829,12 @@ class IGbE : public EtherDevice } IGbE(const Params *params); ~IGbE() {} + virtual void init(); virtual EtherInt *getEthPort(const std::string &if_name, int idx); Tick clock; + Tick lastInterrupt; inline Tick ticks(int numCycles) const { return numCycles * clock; } virtual Tick read(PacketPtr pkt); diff --git a/src/dev/i8254xGBe_defs.hh b/src/dev/i8254xGBe_defs.hh index 91b3eacc9..4634dd9a3 100644 --- a/src/dev/i8254xGBe_defs.hh +++ b/src/dev/i8254xGBe_defs.hh @@ -59,11 +59,14 @@ const uint32_t REG_FCTTV = 0x00170; const uint32_t REG_TIPG = 0x00410; const uint32_t REG_AIFS = 0x00458; const uint32_t REG_LEDCTL = 0x00e00; +const uint32_t REG_EICR = 0x01580; +const uint32_t REG_IVAR0 = 0x01700; const uint32_t REG_FCRTL = 0x02160; const uint32_t REG_FCRTH = 0x02168; const uint32_t REG_RDBAL = 0x02800; const uint32_t REG_RDBAH = 0x02804; const uint32_t REG_RDLEN = 0x02808; +const uint32_t REG_SRRCTL = 0x0280C; const uint32_t REG_RDH = 0x02810; const uint32_t REG_RDT = 0x02818; const uint32_t REG_RDTR = 0x02820; @@ -74,12 +77,17 @@ const uint32_t REG_TDBAL = 0x03800; const uint32_t REG_TDBAH = 0x03804; const uint32_t REG_TDLEN = 0x03808; const uint32_t REG_TDH = 0x03810; +const uint32_t REG_TXDCA_CTL = 0x03814; const uint32_t REG_TDT = 0x03818; const uint32_t REG_TIDV = 0x03820; const uint32_t REG_TXDCTL = 0x03828; const uint32_t REG_TADV = 0x0382C; +const uint32_t REG_TDWBAL = 0x03838; +const uint32_t REG_TDWBAH = 0x0383C; const uint32_t REG_CRCERRS = 0x04000; const uint32_t REG_RXCSUM = 0x05000; +const uint32_t REG_RLPML = 0x05004; +const uint32_t REG_RFCTL = 0x05008; const uint32_t REG_MTA = 0x05200; const uint32_t REG_RAL = 0x05400; const uint32_t REG_RAH = 0x05404; @@ -87,6 +95,9 @@ const uint32_t REG_VFTA = 0x05600; const uint32_t REG_WUC = 0x05800; const uint32_t REG_MANC = 0x05820; +const uint32_t REG_SWSM = 0x05B50; +const uint32_t REG_FWSM = 0x05B54; +const uint32_t REG_SWFWSYNC = 0x05B5C; const uint8_t EEPROM_READ_OPCODE_SPI = 0x03; const uint8_t EEPROM_RDSR_OPCODE_SPI = 0x05; @@ -94,9 +105,9 @@ const uint8_t EEPROM_SIZE = 64; const uint16_t EEPROM_CSUM = 0xBABA; const uint8_t VLAN_FILTER_TABLE_SIZE = 128; -const uint8_t RCV_ADDRESS_TABLE_SIZE = 16; +const uint8_t RCV_ADDRESS_TABLE_SIZE = 24; const uint8_t MULTICAST_TABLE_SIZE = 128; -const uint32_t STATS_REGS_SIZE = 0x124; +const uint32_t STATS_REGS_SIZE = 0x228; // Registers in that are accessed in the PHY @@ -108,14 +119,17 @@ const uint8_t PHY_EPSTATUS = 15; const uint8_t PHY_AGC = 18; // Receive Descriptor Status Flags -const uint8_t RXDS_PIF = 0x80; -const uint8_t RXDS_IPCS = 0x40; -const uint8_t RXDS_TCPCS = 0x20; -const uint8_t RXDS_UDPCS = 0x10; -const uint8_t RXDS_VP = 0x08; -const uint8_t RXDS_IXSM = 0x04; -const uint8_t RXDS_EOP = 0x02; -const uint8_t RXDS_DD = 0x01; +const uint16_t RXDS_DYNINT = 0x800; +const uint16_t RXDS_UDPV = 0x400; +const uint16_t RXDS_CRCV = 0x100; +const uint16_t RXDS_PIF = 0x080; +const uint16_t RXDS_IPCS = 0x040; +const uint16_t RXDS_TCPCS = 0x020; +const uint16_t RXDS_UDPCS = 0x010; +const uint16_t RXDS_VP = 0x008; +const uint16_t RXDS_IXSM = 0x004; +const uint16_t RXDS_EOP = 0x002; +const uint16_t RXDS_DD = 0x001; // Receive Descriptor Error Flags const uint8_t RXDE_RXE = 0x80; @@ -125,6 +139,32 @@ const uint8_t RXDE_SEQ = 0x04; const uint8_t RXDE_SE = 0x02; const uint8_t RXDE_CE = 0x01; +// Receive Descriptor Extended Error Flags +const uint16_t RXDEE_HBO = 0x008; +const uint16_t RXDEE_CE = 0x010; +const uint16_t RXDEE_LE = 0x020; +const uint16_t RXDEE_PE = 0x080; +const uint16_t RXDEE_OSE = 0x100; +const uint16_t RXDEE_USE = 0x200; +const uint16_t RXDEE_TCPE = 0x400; +const uint16_t RXDEE_IPE = 0x800; + + +// Receive Descriptor Types +const uint8_t RXDT_LEGACY = 0x00; +const uint8_t RXDT_ADV_ONEBUF = 0x01; +const uint8_t RXDT_ADV_SPLIT_A = 0x05; + +// Receive Descriptor Packet Types +const uint16_t RXDP_IPV4 = 0x001; +const uint16_t RXDP_IPV4E = 0x002; +const uint16_t RXDP_IPV6 = 0x004; +const uint16_t RXDP_IPV6E = 0x008; +const uint16_t RXDP_TCP = 0x010; +const uint16_t RXDP_UDP = 0x020; +const uint16_t RXDP_SCTP = 0x040; +const uint16_t RXDP_NFS = 0x080; + // Interrupt types enum IntTypes { @@ -147,12 +187,38 @@ enum IntTypes // Receive Descriptor struct struct RxDesc { - Addr buf; - uint16_t len; - uint16_t csum; - uint8_t status; - uint8_t errors; - uint16_t vlan; + union { + struct { + Addr buf; + uint16_t len; + uint16_t csum; + uint8_t status; + uint8_t errors; + uint16_t vlan; + } legacy; + struct { + Addr pkt; + Addr hdr; + } adv_read; + struct { + uint16_t rss_type:4; + uint16_t pkt_type:12; + uint16_t __reserved1:5; + uint16_t header_len:10; + uint16_t sph:1; + union { + struct { + uint16_t id; + uint16_t csum; + }; + uint32_t rss_hash; + }; + uint32_t status:20; + uint32_t errors:12; + uint16_t pkt_len; + uint16_t vlan_tag; + } adv_wb ; + }; }; struct TxDesc { @@ -163,24 +229,33 @@ struct TxDesc { namespace TxdOp { const uint8_t TXD_CNXT = 0x0; const uint8_t TXD_DATA = 0x1; +const uint8_t TXD_ADVCNXT = 0x2; +const uint8_t TXD_ADVDATA = 0x3; bool isLegacy(TxDesc *d) { return !bits(d->d2,29,29); } uint8_t getType(TxDesc *d) { return bits(d->d2, 23,20); } -bool isContext(TxDesc *d) { return !isLegacy(d) && getType(d) == TXD_CNXT; } -bool isData(TxDesc *d) { return !isLegacy(d) && getType(d) == TXD_DATA; } +bool isType(TxDesc *d, uint8_t type) { return getType(d) == type; } +bool isTypes(TxDesc *d, uint8_t t1, uint8_t t2) { return isType(d, t1) || isType(d, t2); } +bool isAdvDesc(TxDesc *d) { return !isLegacy(d) && isTypes(d, TXD_ADVDATA,TXD_ADVCNXT); } +bool isContext(TxDesc *d) { return !isLegacy(d) && isTypes(d,TXD_CNXT, TXD_ADVCNXT); } +bool isData(TxDesc *d) { return !isLegacy(d) && isTypes(d, TXD_DATA, TXD_ADVDATA); } Addr getBuf(TxDesc *d) { assert(isLegacy(d) || isData(d)); return d->d1; } Addr getLen(TxDesc *d) { if (isLegacy(d)) return bits(d->d2,15,0); else return bits(d->d2, 19,0); } -void setDd(TxDesc *d) -{ - replaceBits(d->d2, 35, 32, ULL(1)); -} +void setDd(TxDesc *d) { replaceBits(d->d2, 35, 32, ULL(1)); } -bool ide(TxDesc *d) { return bits(d->d2, 31,31); } +bool ide(TxDesc *d) { return bits(d->d2, 31,31) && (getType(d) == TXD_DATA || isLegacy(d)); } bool vle(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 30,30); } bool rs(TxDesc *d) { return bits(d->d2, 27,27); } bool ic(TxDesc *d) { assert(isLegacy(d) || isData(d)); return isLegacy(d) && bits(d->d2, 26,26); } -bool tse(TxDesc *d) { return (isData(d) || isContext(d)) && bits(d->d2, 26,26); } +bool tse(TxDesc *d) { + if (isTypes(d, TXD_CNXT, TXD_DATA)) + return bits(d->d2, 26,26); + if (isType(d, TXD_ADVDATA)) + return bits(d->d2, 31, 31); + return false; +} + bool ifcs(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 25,25); } bool eop(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 24,24); } bool ip(TxDesc *d) { assert(isContext(d)); return bits(d->d2, 25,25); } @@ -199,7 +274,15 @@ int ipcse(TxDesc *d) { assert(isContext(d)); return bits(d->d1,31,16); } int ipcso(TxDesc *d) { assert(isContext(d)); return bits(d->d1,15,8); } int ipcss(TxDesc *d) { assert(isContext(d)); return bits(d->d1,7,0); } int mss(TxDesc *d) { assert(isContext(d)); return bits(d->d2,63,48); } -int hdrlen(TxDesc *d) { assert(isContext(d)); return bits(d->d2,47,40); } +int hdrlen(TxDesc *d) { + assert(isContext(d)); + if (!isAdvDesc(d)) + return bits(d->d2,47,40); + return bits(d->d2, 47,40) + bits(d->d1, 8,0) + bits(d->d1, 15, 9); +} + +int getTsoLen(TxDesc *d) { assert(isType(d, TXD_ADVDATA)); return bits(d->d2, 63,46); } +int utcmd(TxDesc *d) { assert(isContext(d)); return bits(d->d2,24,31); } } // namespace TxdOp @@ -303,8 +386,8 @@ struct Regs { struct EERD : public Reg<uint32_t> { // 0x0014 EERD Register using Reg<uint32_t>::operator=; ADD_FIELD32(start,0,1); // start read - ADD_FIELD32(done,4,1); // done read - ADD_FIELD32(addr,8,8); // address + ADD_FIELD32(done,1,1); // done read + ADD_FIELD32(addr,2,14); // address ADD_FIELD32(data,16,16); // data }; EERD eerd; @@ -470,6 +553,17 @@ struct Regs { }; RDLEN rdlen; + struct SRRCTL : public Reg<uint32_t> { // 0x280C SRRCTL Register + using Reg<uint32_t>::operator=; + ADD_FIELD32(pktlen, 0, 8); + ADD_FIELD32(hdrlen, 8, 8); // guess based on header, not documented + ADD_FIELD32(desctype, 25,3); // type of descriptor 000 legacy, 001 adv, + //101 hdr split + int bufLen() { return pktlen() << 10; } + int hdrLen() { return hdrlen() << 6; } + }; + SRRCTL srrctl; + struct RDH : public Reg<uint32_t> { // 0x2810 RDH Register using Reg<uint32_t>::operator=; ADD_FIELD32(rdh,0,16); // head of the descriptor ring @@ -531,6 +625,14 @@ struct Regs { }; TDH tdh; + struct TXDCA_CTL : public Reg<uint32_t> { // 0x3814 TXDCA_CTL Register + using Reg<uint32_t>::operator=; + ADD_FIELD32(cpu_mask, 0, 5); + ADD_FIELD32(enabled, 5,1); + ADD_FIELD32(relax_ordering, 6, 1); + }; + TXDCA_CTL txdca_ctl; + struct TDT : public Reg<uint32_t> { // 0x3818 TDT Register using Reg<uint32_t>::operator=; ADD_FIELD32(tdt,0,16); // tail of the descriptor ring @@ -563,15 +665,42 @@ struct Regs { ADD_FIELD32(idv,0,16); // absolute interrupt delay }; TADV tadv; +/* + struct TDWBA : public Reg<uint64_t> { // 0x3838 TDWBA Register + using Reg<uint64_t>::operator=; + ADD_FIELD64(en,0,1); // enable transmit description ring address writeback + ADD_FIELD64(tdwbal,2,32); // base address of transmit descriptor ring address writeback + ADD_FIELD64(tdwbah,32,32); // base address of transmit descriptor ring + }; + TDWBA tdwba;*/ + uint64_t tdwba; struct RXCSUM : public Reg<uint32_t> { // 0x5000 RXCSUM Register using Reg<uint32_t>::operator=; ADD_FIELD32(pcss,0,8); ADD_FIELD32(ipofld,8,1); ADD_FIELD32(tuofld,9,1); + ADD_FIELD32(pcsd, 13,1); }; RXCSUM rxcsum; + uint32_t rlpml; // 0x5004 RLPML probably maximum accepted packet size + + struct RFCTL : public Reg<uint32_t> { // 0x5008 RFCTL Register + using Reg<uint32_t>::operator=; + ADD_FIELD32(iscsi_dis,0,1); + ADD_FIELD32(iscsi_dwc,1,5); + ADD_FIELD32(nfsw_dis,6,1); + ADD_FIELD32(nfsr_dis,7,1); + ADD_FIELD32(nfs_ver,8,2); + ADD_FIELD32(ipv6_dis,10,1); + ADD_FIELD32(ipv6xsum_dis,11,1); + ADD_FIELD32(ackdis,13,1); + ADD_FIELD32(ipfrsp_dis,14,1); + ADD_FIELD32(exsten,15,1); + }; + RFCTL rfctl; + struct MANC : public Reg<uint32_t> { // 0x5820 MANC Register using Reg<uint32_t>::operator=; ADD_FIELD32(smbus,0,1); // SMBus enabled ##### @@ -604,6 +733,32 @@ struct Regs { }; MANC manc; + struct SWSM : public Reg<uint32_t> { // 0x5B50 SWSM register + using Reg<uint32_t>::operator=; + ADD_FIELD32(smbi,0,1); // Semaphone bit + ADD_FIELD32(swesmbi, 1,1); // Software eeporm semaphore + ADD_FIELD32(wmng, 2,1); // Wake MNG clock + ADD_FIELD32(reserved, 3, 29); + }; + SWSM swsm; + + struct FWSM : public Reg<uint32_t> { // 0x5B54 FWSM register + using Reg<uint32_t>::operator=; + ADD_FIELD32(eep_fw_semaphore,0,1); + ADD_FIELD32(fw_mode, 1,3); + ADD_FIELD32(ide, 4,1); + ADD_FIELD32(sol, 5,1); + ADD_FIELD32(eep_roload, 6,1); + ADD_FIELD32(reserved, 7,8); + ADD_FIELD32(fw_val_bit, 15, 1); + ADD_FIELD32(reset_cnt, 16, 3); + ADD_FIELD32(ext_err_ind, 19, 6); + ADD_FIELD32(reserved2, 25, 7); + }; + FWSM fwsm; + + uint32_t sw_fw_sync; + void serialize(std::ostream &os) { paramOut(os, "ctrl", ctrl._data); @@ -624,6 +779,7 @@ struct Regs { paramOut(os, "fcrth", fcrth._data); paramOut(os, "rdba", rdba._data); paramOut(os, "rdlen", rdlen._data); + paramOut(os, "srrctl", srrctl._data); paramOut(os, "rdh", rdh._data); paramOut(os, "rdt", rdt._data); paramOut(os, "rdtr", rdtr._data); @@ -633,12 +789,20 @@ struct Regs { paramOut(os, "tdba", tdba._data); paramOut(os, "tdlen", tdlen._data); paramOut(os, "tdh", tdh._data); + paramOut(os, "txdca_ctl", txdca_ctl._data); paramOut(os, "tdt", tdt._data); paramOut(os, "tidv", tidv._data); paramOut(os, "txdctl", txdctl._data); paramOut(os, "tadv", tadv._data); + //paramOut(os, "tdwba", tdwba._data); + SERIALIZE_SCALAR(tdwba); paramOut(os, "rxcsum", rxcsum._data); + SERIALIZE_SCALAR(rlpml); + paramOut(os, "rfctl", rfctl._data); paramOut(os, "manc", manc._data); + paramOut(os, "swsm", swsm._data); + paramOut(os, "fwsm", fwsm._data); + SERIALIZE_SCALAR(sw_fw_sync); } void unserialize(Checkpoint *cp, const std::string §ion) @@ -661,6 +825,7 @@ struct Regs { paramIn(cp, section, "fcrth", fcrth._data); paramIn(cp, section, "rdba", rdba._data); paramIn(cp, section, "rdlen", rdlen._data); + paramIn(cp, section, "srrctl", srrctl._data); paramIn(cp, section, "rdh", rdh._data); paramIn(cp, section, "rdt", rdt._data); paramIn(cp, section, "rdtr", rdtr._data); @@ -670,12 +835,20 @@ struct Regs { paramIn(cp, section, "tdba", tdba._data); paramIn(cp, section, "tdlen", tdlen._data); paramIn(cp, section, "tdh", tdh._data); + paramIn(cp, section, "txdca_ctl", txdca_ctl._data); paramIn(cp, section, "tdt", tdt._data); paramIn(cp, section, "tidv", tidv._data); paramIn(cp, section, "txdctl", txdctl._data); paramIn(cp, section, "tadv", tadv._data); + UNSERIALIZE_SCALAR(tdwba); + //paramIn(cp, section, "tdwba", tdwba._data); paramIn(cp, section, "rxcsum", rxcsum._data); + UNSERIALIZE_SCALAR(rlpml); + paramIn(cp, section, "rfctl", rfctl._data); paramIn(cp, section, "manc", manc._data); + paramIn(cp, section, "swsm", swsm._data); + paramIn(cp, section, "fwsm", fwsm._data); + UNSERIALIZE_SCALAR(sw_fw_sync); } }; } // iGbReg namespace diff --git a/src/dev/ide_ctrl.cc b/src/dev/ide_ctrl.cc index 6d7f1baf1..3d4e71888 100644 --- a/src/dev/ide_ctrl.cc +++ b/src/dev/ide_ctrl.cc @@ -30,203 +30,114 @@ * Miguel Serrano */ -#include <cstddef> -#include <cstdlib> #include <string> -#include <vector> #include "base/trace.hh" #include "cpu/intr_control.hh" #include "dev/ide_ctrl.hh" #include "dev/ide_disk.hh" -#include "dev/pciconfigall.hh" -#include "dev/pcireg.h" -#include "dev/platform.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" #include "params/IdeController.hh" -#include "sim/sim_object.hh" #include "sim/byteswap.hh" using namespace std; -//// -// Initialization and destruction -//// - -IdeController::IdeController(Params *p) - : PciDev(p) +// Bus master IDE registers +enum BMIRegOffset { + BMICommand = 0x0, + BMIStatus = 0x2, + BMIDescTablePtr = 0x4 +}; + +// PCI config space registers +enum ConfRegOffset { + PrimaryTiming = 0x40, + SecondaryTiming = 0x42, + DeviceTiming = 0x44, + UDMAControl = 0x48, + UDMATiming = 0x4A, + IDEConfig = 0x54 +}; + +static const uint16_t timeRegWithDecodeEn = 0x8000; + +IdeController::Channel::Channel( + string newName, Addr _cmdSize, Addr _ctrlSize) : + _name(newName), + cmdAddr(0), cmdSize(_cmdSize), ctrlAddr(0), ctrlSize(_ctrlSize), + master(NULL), slave(NULL), selected(NULL) { - // 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.data, 0, sizeof(bmi_regs)); - memset(config_regs.data, 0, sizeof(config_regs.data)); - - // setup initial values - // enable both channels - config_regs.idetim0 = htole((uint16_t)IDETIM_DECODE_EN); - config_regs.idetim1 = htole((uint16_t)IDETIM_DECODE_EN); - bmi_regs.bmis0 = DMA1CAP | DMA0CAP; - bmi_regs.bmis1 = DMA1CAP | DMA0CAP; - - // reset all internal variables - io_enabled = false; - bm_enabled = false; - memset(cmd_in_progress, 0, sizeof(cmd_in_progress)); - - // setup the disks attached to controller - memset(disks, 0, sizeof(disks)); - dev[0] = 0; - dev[1] = 0; - - if (params()->disks.size() > 3) - panic("IDE controllers support a maximum of 4 devices attached!\n"); - - for (int i = 0; i < params()->disks.size(); i++) { - disks[i] = params()->disks[i]; - disks[i]->setController(this); - } + memset(&bmiRegs, 0, sizeof(bmiRegs)); + bmiRegs.status.dmaCap0 = 1; + bmiRegs.status.dmaCap1 = 1; } -IdeController::~IdeController() +IdeController::Channel::~Channel() { - for (int i = 0; i < 4; i++) - if (disks[i]) - delete disks[i]; + delete master; + delete slave; } -//// -// Utility functions -/// - -void -IdeController::parseAddr(const Addr &addr, Addr &offset, IdeChannel &channel, - IdeRegType ®_type) -{ - offset = addr; - - if (addr >= pri_cmd_addr && addr < (pri_cmd_addr + pri_cmd_size)) { - offset -= pri_cmd_addr; - reg_type = COMMAND_BLOCK; - channel = PRIMARY; - } else if (addr >= pri_ctrl_addr && - addr < (pri_ctrl_addr + pri_ctrl_size)) { - offset -= pri_ctrl_addr; - reg_type = CONTROL_BLOCK; - channel = PRIMARY; - } else if (addr >= sec_cmd_addr && - addr < (sec_cmd_addr + sec_cmd_size)) { - offset -= sec_cmd_addr; - reg_type = COMMAND_BLOCK; - channel = SECONDARY; - } else if (addr >= sec_ctrl_addr && - addr < (sec_ctrl_addr + sec_ctrl_size)) { - offset -= sec_ctrl_addr; - reg_type = CONTROL_BLOCK; - channel = SECONDARY; - } else if (addr >= bmi_addr && addr < (bmi_addr + bmi_size)) { - offset -= bmi_addr; - reg_type = BMI_BLOCK; - channel = (offset < BMIC1) ? PRIMARY : SECONDARY; - } else { - panic("IDE controller access to invalid address: %#x\n", addr); - } -} - -int -IdeController::getDisk(IdeChannel channel) +IdeController::IdeController(Params *p) + : PciDev(p), primary(name() + ".primary", BARSize[0], BARSize[1]), + secondary(name() + ".secondary", BARSize[2], BARSize[3]), + bmiAddr(0), bmiSize(BARSize[4]), + primaryTiming(htole(timeRegWithDecodeEn)), + secondaryTiming(htole(timeRegWithDecodeEn)), + deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0), + ioEnabled(false), bmEnabled(false) { - int disk = 0; - uint8_t *devBit = &dev[0]; - - if (channel == SECONDARY) { - disk += 2; - devBit = &dev[1]; - } - - disk += *devBit; + if (params()->disks.size() > 3) + panic("IDE controllers support a maximum of 4 devices attached!\n"); - assert(*devBit == 0 || *devBit == 1); + // Assign the disks to channels + int numDisks = params()->disks.size(); + if (numDisks > 0) + primary.master = params()->disks[0]; + if (numDisks > 1) + primary.slave = params()->disks[1]; + if (numDisks > 2) + secondary.master = params()->disks[2]; + if (numDisks > 3) + secondary.slave = params()->disks[3]; - return disk; -} - -int -IdeController::getDisk(IdeDisk *diskPtr) -{ - for (int i = 0; i < 4; i++) { - if ((long)diskPtr == (long)disks[i]) - return i; + for (int i = 0; i < params()->disks.size(); i++) { + params()->disks[i]->setController(this); } - return -1; + primary.select(false); + secondary.select(false); } 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"); + return (primary.selected == diskPtr || secondary.selected == diskPtr); } -//// -// Command completion -//// +void +IdeController::intrPost() +{ + primary.bmiRegs.status.intStatus = 1; + PciDev::intrPost(); +} 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; + Channel *channel; + if (disk == primary.master || disk == primary.slave) { + channel = &primary; + } else if (disk == secondary.master || disk == secondary.slave) { + channel = &secondary; } 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; + panic("Unable to find disk based on pointer %#x\n", disk); } -} - -//// -// Read and write handling -//// + channel->bmiRegs.command.startStop = 0; + channel->bmiRegs.status.active = 0; + channel->bmiRegs.status.intStatus = 1; +} Tick IdeController::readConfig(PacketPtr pkt) @@ -236,30 +147,28 @@ IdeController::readConfig(PacketPtr pkt) return PciDev::readConfig(pkt); } - assert(offset >= IDE_CTRL_CONF_START && (offset + 1) <= IDE_CTRL_CONF_END); - pkt->allocate(); switch (pkt->getSize()) { case sizeof(uint8_t): switch (offset) { - case IDE_CTRL_CONF_DEV_TIMING: - pkt->set<uint8_t>(config_regs.sidetim); + case DeviceTiming: + pkt->set<uint8_t>(deviceTiming); break; - case IDE_CTRL_CONF_UDMA_CNTRL: - pkt->set<uint8_t>(config_regs.udmactl); + case UDMAControl: + pkt->set<uint8_t>(udmaControl); break; - case IDE_CTRL_CONF_PRIM_TIMING+1: - pkt->set<uint8_t>(htole(config_regs.idetim0) >> 8); + case PrimaryTiming + 1: + pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8)); break; - case IDE_CTRL_CONF_SEC_TIMING+1: - pkt->set<uint8_t>(htole(config_regs.idetim1) >> 8); + case SecondaryTiming + 1: + pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8)); break; - case IDE_CTRL_CONF_IDE_CONFIG: - pkt->set<uint8_t>(htole(config_regs.ideconfig) & 0xFF); + case IDEConfig: + pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0)); break; - case IDE_CTRL_CONF_IDE_CONFIG+1: - pkt->set<uint8_t>(htole(config_regs.ideconfig) >> 8); + case IDEConfig + 1: + pkt->set<uint8_t>(bits(htole(ideConfig), 15, 8)); break; default: panic("Invalid PCI configuration read for size 1 at offset: %#x!\n", @@ -270,17 +179,17 @@ IdeController::readConfig(PacketPtr pkt) break; case sizeof(uint16_t): switch (offset) { - case IDE_CTRL_CONF_PRIM_TIMING: - pkt->set<uint16_t>(config_regs.idetim0); + case PrimaryTiming: + pkt->set<uint16_t>(primaryTiming); break; - case IDE_CTRL_CONF_SEC_TIMING: - pkt->set<uint16_t>(config_regs.idetim1); + case SecondaryTiming: + pkt->set<uint16_t>(secondaryTiming); break; - case IDE_CTRL_CONF_UDMA_TIMING: - pkt->set<uint16_t>(config_regs.udmatim); + case UDMATiming: + pkt->set<uint16_t>(udmaTiming); break; - case IDE_CTRL_CONF_IDE_CONFIG: - pkt->set<uint16_t>(config_regs.ideconfig); + case IDEConfig: + pkt->set<uint16_t>(ideConfig); break; default: panic("Invalid PCI configuration read for size 2 offset: %#x!\n", @@ -309,48 +218,45 @@ IdeController::writeConfig(PacketPtr pkt) if (offset < PCI_DEVICE_SPECIFIC) { PciDev::writeConfig(pkt); } else { - assert(offset >= IDE_CTRL_CONF_START && (offset + 1) <= IDE_CTRL_CONF_END); - switch (pkt->getSize()) { case sizeof(uint8_t): switch (offset) { - case IDE_CTRL_CONF_DEV_TIMING: - config_regs.sidetim = pkt->get<uint8_t>(); + case DeviceTiming: + deviceTiming = pkt->get<uint8_t>(); break; - case IDE_CTRL_CONF_UDMA_CNTRL: - config_regs.udmactl = pkt->get<uint8_t>(); + case UDMAControl: + udmaControl = pkt->get<uint8_t>(); break; - case IDE_CTRL_CONF_IDE_CONFIG: - config_regs.ideconfig = (config_regs.ideconfig & 0xFF00) | - (pkt->get<uint8_t>()); + case IDEConfig: + replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>()); break; - case IDE_CTRL_CONF_IDE_CONFIG+1: - config_regs.ideconfig = (config_regs.ideconfig & 0x00FF) | - pkt->get<uint8_t>() << 8; + case IDEConfig + 1: + replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>()); break; default: - panic("Invalid PCI configuration write for size 1 offset: %#x!\n", - offset); + panic("Invalid PCI configuration write " + "for size 1 offset: %#x!\n", offset); } DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n", offset, (uint32_t)pkt->get<uint8_t>()); break; case sizeof(uint16_t): switch (offset) { - case IDE_CTRL_CONF_PRIM_TIMING: - config_regs.idetim0 = pkt->get<uint16_t>(); + case PrimaryTiming: + primaryTiming = pkt->get<uint16_t>(); break; - case IDE_CTRL_CONF_SEC_TIMING: - config_regs.idetim1 = pkt->get<uint16_t>(); + case SecondaryTiming: + secondaryTiming = pkt->get<uint16_t>(); break; - case IDE_CTRL_CONF_UDMA_TIMING: - config_regs.udmatim = pkt->get<uint16_t>(); + case UDMATiming: + udmaTiming = pkt->get<uint16_t>(); break; - case IDE_CTRL_CONF_IDE_CONFIG: - config_regs.ideconfig = pkt->get<uint16_t>(); + case IDEConfig: + ideConfig = pkt->get<uint16_t>(); break; default: - panic("Invalid PCI configuration write for size 2 offset: %#x!\n", + panic("Invalid PCI configuration write " + "for size 2 offset: %#x!\n", offset); } DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n", @@ -370,317 +276,223 @@ IdeController::writeConfig(PacketPtr pkt) switch(offset) { case PCI0_BASE_ADDR0: if (BARAddrs[0] != 0) - pri_cmd_addr = BARAddrs[0]; + primary.cmdAddr = BARAddrs[0]; break; case PCI0_BASE_ADDR1: if (BARAddrs[1] != 0) - pri_ctrl_addr = BARAddrs[1]; + primary.ctrlAddr = BARAddrs[1]; break; case PCI0_BASE_ADDR2: if (BARAddrs[2] != 0) - sec_cmd_addr = BARAddrs[2]; + secondary.cmdAddr = BARAddrs[2]; break; case PCI0_BASE_ADDR3: if (BARAddrs[3] != 0) - sec_ctrl_addr = BARAddrs[3]; + secondary.ctrlAddr = BARAddrs[3]; break; case PCI0_BASE_ADDR4: if (BARAddrs[4] != 0) - bmi_addr = BARAddrs[4]; + bmiAddr = BARAddrs[4]; break; case PCI_COMMAND: - if (letoh(config.command) & PCI_CMD_IOSE) - io_enabled = true; - else - io_enabled = false; - - if (letoh(config.command) & PCI_CMD_BME) - bm_enabled = true; - else - bm_enabled = false; + ioEnabled = (config.command & htole(PCI_CMD_IOSE)); + bmEnabled = (config.command & htole(PCI_CMD_BME)); break; } return configDelay; } - -Tick -IdeController::read(PacketPtr pkt) +void +IdeController::Channel::accessCommand(Addr offset, + int size, uint8_t *data, bool read) { - Addr offset; - IdeChannel channel; - IdeRegType reg_type; - int disk; - - pkt->allocate(); - if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) - panic("Bad IDE read size: %d\n", pkt->getSize()); - - parseAddr(pkt->getAddr(), offset, channel, reg_type); - - if (!io_enabled) { - pkt->makeAtomicResponse(); - return pioDelay; - } - - switch (reg_type) { - case BMI_BLOCK: - switch (pkt->getSize()) { - case sizeof(uint8_t): - pkt->set(bmi_regs.data[offset]); - break; - case sizeof(uint16_t): - pkt->set(*(uint16_t*)&bmi_regs.data[offset]); - break; - case sizeof(uint32_t): - pkt->set(*(uint32_t*)&bmi_regs.data[offset]); - break; - default: - panic("IDE read of BMI reg invalid size: %#x\n", pkt->getSize()); - } - break; - - case COMMAND_BLOCK: - case CONTROL_BLOCK: - disk = getDisk(channel); + const Addr SelectOffset = 6; + const uint8_t SelectDevBit = 0x10; - if (disks[disk] == NULL) { - pkt->set<uint8_t>(0); - break; - } + if (!read && offset == SelectOffset) + select(*data & SelectDevBit); - switch (offset) { - case DATA_OFFSET: - switch (pkt->getSize()) { - case sizeof(uint16_t): - disks[disk]->read(offset, reg_type, pkt->getPtr<uint8_t>()); - break; - - case sizeof(uint32_t): - disks[disk]->read(offset, reg_type, pkt->getPtr<uint8_t>()); - disks[disk]->read(offset, reg_type, - pkt->getPtr<uint8_t>() + sizeof(uint16_t)); - break; - - default: - panic("IDE read of data reg invalid size: %#x\n", pkt->getSize()); - } - break; - default: - if (pkt->getSize() == sizeof(uint8_t)) { - disks[disk]->read(offset, reg_type, pkt->getPtr<uint8_t>()); - } else - panic("IDE read of command reg of invalid size: %#x\n", pkt->getSize()); - } - break; - default: - panic("IDE controller read of unknown register block type!\n"); + if (selected == NULL) { + assert(size == sizeof(uint8_t)); + *data = 0; + } else if (read) { + selected->readCommand(offset, size, data); + } else { + selected->writeCommand(offset, size, data); } - if (pkt->getSize() == 1) - DPRINTF(IdeCtrl, "read from offset: %#x size: %#x data: %#x\n", - offset, pkt->getSize(), (uint32_t)pkt->get<uint8_t>()); - else if (pkt->getSize() == 2) - DPRINTF(IdeCtrl, "read from offset: %#x size: %#x data: %#x\n", - offset, pkt->getSize(), pkt->get<uint16_t>()); - else - DPRINTF(IdeCtrl, "read from offset: %#x size: %#x data: %#x\n", - offset, pkt->getSize(), pkt->get<uint32_t>()); - - pkt->makeAtomicResponse(); - return pioDelay; } -Tick -IdeController::write(PacketPtr pkt) +void +IdeController::Channel::accessControl(Addr offset, + int size, uint8_t *data, bool read) { - Addr offset; - IdeChannel channel; - IdeRegType reg_type; - int disk; - uint8_t oldVal, newVal; - - parseAddr(pkt->getAddr(), offset, channel, reg_type); - - if (!io_enabled) { - pkt->makeAtomicResponse(); - DPRINTF(IdeCtrl, "io not enabled\n"); - return pioDelay; + if (selected == NULL) { + assert(size == sizeof(uint8_t)); + *data = 0; + } else if (read) { + selected->readControl(offset, size, data); + } else { + selected->writeControl(offset, size, data); } +} - switch (reg_type) { - case BMI_BLOCK: - if (!bm_enabled) { - pkt->makeAtomicResponse(); - return pioDelay; - } - +void +IdeController::Channel::accessBMI(Addr offset, + int size, uint8_t *data, bool read) +{ + assert(offset + size <= sizeof(BMIRegs)); + if (read) { + memcpy(data, (uint8_t *)&bmiRegs + offset, size); + } else { switch (offset) { - // Bus master IDE command register - case BMIC1: - case BMIC0: - if (pkt->getSize() != sizeof(uint8_t)) - panic("Invalid BMIC write size: %x\n", pkt->getSize()); - - // select the current disk based on DEV bit - disk = getDisk(channel); - - oldVal = bmi_regs.chan[channel].bmic; - newVal = pkt->get<uint8_t>(); - - // 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"); + case BMICommand: + { + if (size != sizeof(uint8_t)) + panic("Invalid BMIC write size: %x\n", size); - // clear the BMIDEA bit - bmi_regs.chan[channel].bmis = - bmi_regs.chan[channel].bmis & ~BMIDEA; + BMICommandReg oldVal = bmiRegs.command; + BMICommandReg newVal = *data; - if (disks[disk] == NULL) - panic("DMA stop for disk %d which does not exist\n", - disk); + // if a DMA transfer is in progress, R/W control cannot change + if (oldVal.startStop && oldVal.rw != newVal.rw) + oldVal.rw = newVal.rw; - // inform the disk of the DMA transfer abort - disks[disk]->abortDma(); - } else { - // starting DMA transfer - DPRINTF(IdeCtrl, "Starting DMA transfer\n"); + if (oldVal.startStop != newVal.startStop) { + if (selected == NULL) + panic("DMA start for disk which does not exist\n"); - // set the BMIDEA bit - bmi_regs.chan[channel].bmis = - bmi_regs.chan[channel].bmis | BMIDEA; + if (oldVal.startStop) { + DPRINTF(IdeCtrl, "Stopping DMA transfer\n"); + bmiRegs.status.active = 0; - if (disks[disk] == NULL) - panic("DMA start for disk %d which does not exist\n", - disk); + selected->abortDma(); + } else { + DPRINTF(IdeCtrl, "Starting DMA transfer\n"); + bmiRegs.status.active = 1; - // inform the disk of the DMA transfer start - disks[disk]->startDma(letoh(bmi_regs.chan[channel].bmidtp)); + selected->startDma(letoh(bmiRegs.bmidtp)); + } } - } - - // update the register value - bmi_regs.chan[channel].bmic = newVal; - break; - - // Bus master IDE status register - case BMIS0: - case BMIS1: - if (pkt->getSize() != sizeof(uint8_t)) - panic("Invalid BMIS write size: %x\n", pkt->getSize()); - - oldVal = bmi_regs.chan[channel].bmis; - newVal = pkt->get<uint8_t>(); - - // 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.chan[channel].bmis = newVal; + bmiRegs.command = newVal; + } break; - - // Bus master IDE descriptor table pointer register - case BMIDTP0: - case BMIDTP1: + case BMIStatus: { - if (pkt->getSize() != sizeof(uint32_t)) - panic("Invalid BMIDTP write size: %x\n", pkt->getSize()); - - bmi_regs.chan[channel].bmidtp = htole(pkt->get<uint32_t>() & ~0x3); + if (size != sizeof(uint8_t)) + panic("Invalid BMIS write size: %x\n", size); + + BMIStatusReg oldVal = bmiRegs.status; + BMIStatusReg newVal = *data; + + // the BMIDEA bit is read only + newVal.active = oldVal.active; + + // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each + if (oldVal.intStatus && newVal.intStatus) + newVal.intStatus = 0; // clear the interrupt? + else + newVal.intStatus = oldVal.intStatus; + if (oldVal.dmaError && newVal.dmaError) + newVal.dmaError = 0; + else + newVal.dmaError = oldVal.dmaError; + + bmiRegs.status = newVal; } break; - + case BMIDescTablePtr: + if (size != sizeof(uint32_t)) + panic("Invalid BMIDTP write size: %x\n", size); + bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3); + break; default: - if (pkt->getSize() != sizeof(uint8_t) && - pkt->getSize() != sizeof(uint16_t) && - pkt->getSize() != sizeof(uint32_t)) - panic("IDE controller write of invalid write size: %x\n", - pkt->getSize()); - - // do a default copy of data into the registers - memcpy(&bmi_regs.data[offset], pkt->getPtr<uint8_t>(), pkt->getSize()); + if (size != sizeof(uint8_t) && size != sizeof(uint16_t) && + size != sizeof(uint32_t)) + panic("IDE controller write of invalid write size: %x\n", size); + memcpy((uint8_t *)&bmiRegs + offset, data, size); } - break; - case COMMAND_BLOCK: - if (offset == IDE_SELECT_OFFSET) { - uint8_t *devBit = &dev[channel]; - *devBit = (letoh(pkt->get<uint8_t>()) & IDE_SELECT_DEV_BIT) ? 1 : 0; - } - // fall-through ok! - case CONTROL_BLOCK: - disk = getDisk(channel); + } +} - if (disks[disk] == NULL) - break; +void +IdeController::dispatchAccess(PacketPtr pkt, bool read) +{ + pkt->allocate(); + if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) + panic("Bad IDE read size: %d\n", pkt->getSize()); - switch (offset) { - case DATA_OFFSET: - switch (pkt->getSize()) { - case sizeof(uint16_t): - disks[disk]->write(offset, reg_type, pkt->getPtr<uint8_t>()); - break; + if (!ioEnabled) { + pkt->makeAtomicResponse(); + DPRINTF(IdeCtrl, "io not enabled\n"); + return; + } - case sizeof(uint32_t): - disks[disk]->write(offset, reg_type, pkt->getPtr<uint8_t>()); - disks[disk]->write(offset, reg_type, pkt->getPtr<uint8_t>() + - sizeof(uint16_t)); - break; - default: - panic("IDE write of data reg invalid size: %#x\n", pkt->getSize()); - } - break; - default: - if (pkt->getSize() == sizeof(uint8_t)) { - disks[disk]->write(offset, reg_type, pkt->getPtr<uint8_t>()); - } else - panic("IDE write of command reg of invalid size: %#x\n", pkt->getSize()); + Addr addr = pkt->getAddr(); + int size = pkt->getSize(); + uint8_t *dataPtr = pkt->getPtr<uint8_t>(); + + if (addr >= primary.cmdAddr && + addr < (primary.cmdAddr + primary.cmdSize)) { + addr -= primary.cmdAddr; + primary.accessCommand(addr, size, dataPtr, read); + } else if (addr >= primary.ctrlAddr && + addr < (primary.ctrlAddr + primary.ctrlSize)) { + addr -= primary.ctrlAddr; + primary.accessControl(addr, size, dataPtr, read); + } else if (addr >= secondary.cmdAddr && + addr < (secondary.cmdAddr + secondary.cmdSize)) { + addr -= secondary.cmdAddr; + secondary.accessCommand(addr, size, dataPtr, read); + } else if (addr >= secondary.ctrlAddr && + addr < (secondary.ctrlAddr + secondary.ctrlSize)) { + addr -= secondary.ctrlAddr; + secondary.accessControl(addr, size, dataPtr, read); + } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) { + if (!read && !bmEnabled) + return; + addr -= bmiAddr; + if (addr < sizeof(Channel::BMIRegs)) { + primary.accessBMI(addr, size, dataPtr, read); + } else { + addr -= sizeof(Channel::BMIRegs); + secondary.accessBMI(addr, size, dataPtr, read); } - break; - default: - panic("IDE controller write of unknown register block type!\n"); + } else { + panic("IDE controller access to invalid address: %#x\n", addr); } + uint32_t data; if (pkt->getSize() == 1) - DPRINTF(IdeCtrl, "write to offset: %#x size: %#x data: %#x\n", - offset, pkt->getSize(), (uint32_t)pkt->get<uint8_t>()); + data = pkt->get<uint8_t>(); else if (pkt->getSize() == 2) - DPRINTF(IdeCtrl, "write to offset: %#x size: %#x data: %#x\n", - offset, pkt->getSize(), pkt->get<uint16_t>()); + data = pkt->get<uint16_t>(); else - DPRINTF(IdeCtrl, "write to offset: %#x size: %#x data: %#x\n", - offset, pkt->getSize(), pkt->get<uint32_t>()); - + data = pkt->get<uint32_t>(); + DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n", + read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data); pkt->makeAtomicResponse(); +} + +Tick +IdeController::read(PacketPtr pkt) +{ + dispatchAccess(pkt, true); return pioDelay; } -//// -// Serialization -//// +Tick +IdeController::write(PacketPtr pkt) +{ + dispatchAccess(pkt, false); + return pioDelay; +} void IdeController::serialize(std::ostream &os) @@ -688,30 +500,40 @@ 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.data, - sizeof(bmi_regs.data) / sizeof(bmi_regs.data[0])); - SERIALIZE_ARRAY(dev, sizeof(dev) / sizeof(dev[0])); - SERIALIZE_ARRAY(config_regs.data, - sizeof(config_regs.data) / sizeof(config_regs.data[0])); + // Serialize channels + primary.serialize("primary", os); + secondary.serialize("secondary", os); + + // Serialize config registers + SERIALIZE_SCALAR(primaryTiming); + SERIALIZE_SCALAR(secondaryTiming); + SERIALIZE_SCALAR(deviceTiming); + SERIALIZE_SCALAR(udmaControl); + SERIALIZE_SCALAR(udmaTiming); + SERIALIZE_SCALAR(ideConfig); // Serialize internal state - SERIALIZE_SCALAR(io_enabled); - SERIALIZE_SCALAR(bm_enabled); - SERIALIZE_ARRAY(cmd_in_progress, - sizeof(cmd_in_progress) / sizeof(cmd_in_progress[0])); + SERIALIZE_SCALAR(ioEnabled); + SERIALIZE_SCALAR(bmEnabled); + SERIALIZE_SCALAR(bmiAddr); + SERIALIZE_SCALAR(bmiSize); +} + +void +IdeController::Channel::serialize(const std::string &base, std::ostream &os) +{ + paramOut(os, base + ".cmdAddr", cmdAddr); + paramOut(os, base + ".cmdSize", cmdSize); + paramOut(os, base + ".ctrlAddr", ctrlAddr); + paramOut(os, base + ".ctrlSize", ctrlSize); + uint8_t command = bmiRegs.command; + paramOut(os, base + ".bmiRegs.command", command); + paramOut(os, base + ".bmiRegs.reserved0", bmiRegs.reserved0); + uint8_t status = bmiRegs.status; + paramOut(os, base + ".bmiRegs.status", status); + paramOut(os, base + ".bmiRegs.reserved1", bmiRegs.reserved1); + paramOut(os, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); + paramOut(os, base + ".selectBit", selectBit); } void @@ -720,30 +542,44 @@ 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.data, - sizeof(bmi_regs.data) / sizeof(bmi_regs.data[0])); - UNSERIALIZE_ARRAY(dev, sizeof(dev) / sizeof(dev[0])); - UNSERIALIZE_ARRAY(config_regs.data, - sizeof(config_regs.data) / sizeof(config_regs.data[0])); + // Unserialize channels + primary.unserialize("primary", cp, section); + secondary.unserialize("secondary", cp, section); + + // Unserialize config registers + UNSERIALIZE_SCALAR(primaryTiming); + UNSERIALIZE_SCALAR(secondaryTiming); + UNSERIALIZE_SCALAR(deviceTiming); + UNSERIALIZE_SCALAR(udmaControl); + UNSERIALIZE_SCALAR(udmaTiming); + UNSERIALIZE_SCALAR(ideConfig); // Unserialize internal state - UNSERIALIZE_SCALAR(io_enabled); - UNSERIALIZE_SCALAR(bm_enabled); - UNSERIALIZE_ARRAY(cmd_in_progress, - sizeof(cmd_in_progress) / sizeof(cmd_in_progress[0])); + UNSERIALIZE_SCALAR(ioEnabled); + UNSERIALIZE_SCALAR(bmEnabled); + UNSERIALIZE_SCALAR(bmiAddr); + UNSERIALIZE_SCALAR(bmiSize); +} + +void +IdeController::Channel::unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion) +{ + paramIn(cp, section, base + ".cmdAddr", cmdAddr); + paramIn(cp, section, base + ".cmdSize", cmdSize); + paramIn(cp, section, base + ".ctrlAddr", ctrlAddr); + paramIn(cp, section, base + ".ctrlSize", ctrlSize); + uint8_t command; + paramIn(cp, section, base +".bmiRegs.command", command); + bmiRegs.command = command; + paramIn(cp, section, base + ".bmiRegs.reserved0", bmiRegs.reserved0); + uint8_t status; + paramIn(cp, section, base + ".bmiRegs.status", status); + bmiRegs.status = status; + paramIn(cp, section, base + ".bmiRegs.reserved1", bmiRegs.reserved1); + paramIn(cp, section, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); + paramIn(cp, section, base + ".selectBit", selectBit); + select(selectBit); } IdeController * diff --git a/src/dev/ide_ctrl.hh b/src/dev/ide_ctrl.hh index f22d83e9c..89dc1ee9d 100644 --- a/src/dev/ide_ctrl.hh +++ b/src/dev/ide_ctrl.hh @@ -37,61 +37,13 @@ #ifndef __IDE_CTRL_HH__ #define __IDE_CTRL_HH__ +#include "base/bitunion.hh" #include "dev/pcidev.hh" #include "dev/pcireg.h" #include "dev/io_device.hh" #include "params/IdeController.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 - -// IDE Timing Register bit fields -#define IDETIM_DECODE_EN 0x8000 - -// PCI device specific register byte offsets -#define IDE_CTRL_CONF_START 0x40 -#define IDE_CTRL_CONF_END ((IDE_CTRL_CONF_START) + sizeof(config_regs)) - -#define IDE_CTRL_CONF_PRIM_TIMING 0x40 -#define IDE_CTRL_CONF_SEC_TIMING 0x42 -#define IDE_CTRL_CONF_DEV_TIMING 0x44 -#define IDE_CTRL_CONF_UDMA_CNTRL 0x48 -#define IDE_CTRL_CONF_UDMA_TIMING 0x4A -#define IDE_CTRL_CONF_IDE_CONFIG 0x54 - - -enum IdeRegType { - COMMAND_BLOCK, - CONTROL_BLOCK, - BMI_BLOCK -}; - class IdeDisk; -class IntrControl; -class PciConfigAll; -class Platform; /** * Device model for an Intel PIIX4 IDE controller @@ -99,137 +51,109 @@ class Platform; class IdeController : public PciDev { - friend class IdeDisk; + private: + // Bus master IDE status register bit fields + BitUnion8(BMIStatusReg) + Bitfield<6> dmaCap0; + Bitfield<5> dmaCap1; + Bitfield<2> intStatus; + Bitfield<1> dmaError; + Bitfield<0> active; + EndBitUnion(BMIStatusReg) + + BitUnion8(BMICommandReg) + Bitfield<3> rw; + Bitfield<0> startStop; + EndBitUnion(BMICommandReg) + + struct Channel + { + std::string _name; + + const std::string + name() + { + return _name; + } + + /** Command and control block registers */ + Addr cmdAddr, cmdSize, ctrlAddr, ctrlSize; + + /** Registers used for bus master interface */ + struct BMIRegs + { + BMICommandReg command; + uint8_t reserved0; + BMIStatusReg status; + uint8_t reserved1; + uint32_t bmidtp; + } bmiRegs; - enum IdeChannel { - PRIMARY = 0, - SECONDARY = 1 - }; + /** IDE disks connected to this controller */ + IdeDisk *master, *slave; - 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; + /** Currently selected disk */ + IdeDisk *selected; - private: - /** Registers used for bus master interface */ - union { - uint8_t data[16]; - - struct { - uint8_t bmic0; - uint8_t reserved_0; - uint8_t bmis0; - uint8_t reserved_1; - uint32_t bmidtp0; - uint8_t bmic1; - uint8_t reserved_2; - uint8_t bmis1; - uint8_t reserved_3; - uint32_t bmidtp1; - }; - - struct { - uint8_t bmic; - uint8_t reserved_4; - uint8_t bmis; - uint8_t reserved_5; - uint32_t bmidtp; - } chan[2]; + bool selectBit; - } bmi_regs; - /** Shadows of the device select bit */ - uint8_t dev[2]; - /** Registers used in device specific PCI configuration */ - union { - uint8_t data[22]; - - struct { - uint16_t idetim0; - uint16_t idetim1; - uint8_t sidetim; - uint8_t reserved_0[3]; - uint8_t udmactl; - uint8_t reserved_1; - uint16_t udmatim; - uint8_t reserved_2[8]; - uint16_t ideconfig; - }; - } config_regs; + void + select(bool selSlave) + { + selectBit = selSlave; + selected = selectBit ? slave : master; + } - // Internal management variables - bool io_enabled; - bool bm_enabled; - bool cmd_in_progress[4]; + void accessCommand(Addr offset, int size, uint8_t *data, bool read); + void accessControl(Addr offset, int size, uint8_t *data, bool read); + void accessBMI(Addr offset, int size, uint8_t *data, bool read); - private: - /** IDE disks connected to controller */ - IdeDisk *disks[4]; + Channel(std::string newName, Addr _cmdSize, Addr _ctrlSize); + ~Channel(); - private: - /** Parse the access address to pass on to device */ - void parseAddr(const Addr &addr, Addr &offset, IdeChannel &channel, - IdeRegType ®_type); + void serialize(const std::string &base, std::ostream &os); + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); + }; - /** Select the disk based on the channel and device bit */ - int getDisk(IdeChannel channel); + Channel primary; + Channel secondary; - /** Select the disk based on a pointer */ - int getDisk(IdeDisk *diskPtr); + /** Bus master interface (BMI) registers */ + Addr bmiAddr, bmiSize; - public: - /** See if a disk is selected based on its pointer */ - bool isDiskSelected(IdeDisk *diskPtr); + /** Registers used in device specific PCI configuration */ + uint16_t primaryTiming, secondaryTiming; + uint8_t deviceTiming; + uint8_t udmaControl; + uint16_t udmaTiming; + uint16_t ideConfig; + + // Internal management variables + bool ioEnabled; + bool bmEnabled; + + void dispatchAccess(PacketPtr pkt, bool read); public: typedef IdeControllerParams Params; const Params *params() const { return (const Params *)_params; } IdeController(Params *p); - ~IdeController(); - virtual Tick writeConfig(PacketPtr pkt); - virtual Tick readConfig(PacketPtr pkt); + /** See if a disk is selected based on its pointer */ + bool isDiskSelected(IdeDisk *diskPtr); + + void intrPost(); + + Tick writeConfig(PacketPtr pkt); + Tick readConfig(PacketPtr pkt); void setDmaComplete(IdeDisk *disk); - /** - * Read a done field for a given target. - * @param pkt Packet describing what is to be read - * @return The amount of time to complete this request - */ - virtual Tick read(PacketPtr pkt); - - /** - * Write a done field for a given target. - * @param pkt Packet describing what is to be written - * @return The amount of time to complete this request - */ - virtual Tick write(PacketPtr pkt); - - /** - * 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 read(PacketPtr pkt); + Tick write(PacketPtr pkt); + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); }; #endif // __IDE_CTRL_HH_ diff --git a/src/dev/ide_disk.cc b/src/dev/ide_disk.cc index 8f9999beb..83faf508e 100644 --- a/src/dev/ide_disk.cc +++ b/src/dev/ide_disk.cc @@ -177,7 +177,7 @@ Addr IdeDisk::pciToDma(Addr pciAddr) { if (ctrl) - return ctrl->plat->pciToDma(pciAddr); + return ctrl->pciToDma(pciAddr); else panic("Access to unset controller!\n"); } @@ -187,120 +187,127 @@ IdeDisk::pciToDma(Addr pciAddr) //// void -IdeDisk::read(const Addr &offset, IdeRegType reg_type, uint8_t *data) +IdeDisk::readCommand(const Addr offset, int size, uint8_t *data) { - DevAction_t action = ACT_NONE; - - switch (reg_type) { - case COMMAND_BLOCK: - switch (offset) { - // Data transfers occur two bytes at a time - case DATA_OFFSET: - *(uint16_t*)data = cmdReg.data; - action = ACT_DATA_READ_SHORT; - break; - case ERROR_OFFSET: - *data = cmdReg.error; - break; - case NSECTOR_OFFSET: - *data = cmdReg.sec_count; - break; - case SECTOR_OFFSET: - *data = cmdReg.sec_num; - break; - case LCYL_OFFSET: - *data = cmdReg.cyl_low; - break; - case HCYL_OFFSET: - *data = cmdReg.cyl_high; - break; - case DRIVE_OFFSET: - *data = cmdReg.drive; - break; - case STATUS_OFFSET: - *data = status; - action = ACT_STAT_READ; - break; - default: - panic("Invalid IDE command register offset: %#x\n", offset); + if (offset == DATA_OFFSET) { + if (size == sizeof(uint16_t)) { + *(uint16_t *)data = cmdReg.data; + } else if (size == sizeof(uint32_t)) { + *(uint16_t *)data = cmdReg.data; + updateState(ACT_DATA_READ_SHORT); + *((uint16_t *)data + 1) = cmdReg.data; + } else { + panic("Data read of unsupported size %d.\n", size); } + updateState(ACT_DATA_READ_SHORT); + return; + } + assert(size == sizeof(uint8_t)); + switch (offset) { + case ERROR_OFFSET: + *data = cmdReg.error; break; - case CONTROL_BLOCK: - if (offset == ALTSTAT_OFFSET) - *data = status; - else - panic("Invalid IDE control register offset: %#x\n", offset); + case NSECTOR_OFFSET: + *data = cmdReg.sec_count; + break; + case SECTOR_OFFSET: + *data = cmdReg.sec_num; + break; + case LCYL_OFFSET: + *data = cmdReg.cyl_low; + break; + case HCYL_OFFSET: + *data = cmdReg.cyl_high; + break; + case DRIVE_OFFSET: + *data = cmdReg.drive; + break; + case STATUS_OFFSET: + *data = status; + updateState(ACT_STAT_READ); break; default: - panic("Unknown register block!\n"); + panic("Invalid IDE command register offset: %#x\n", offset); } - DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, - (uint32_t)*data); - - if (action != ACT_NONE) - updateState(action); + DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data); } void -IdeDisk::write(const Addr &offset, IdeRegType reg_type, const uint8_t *data) +IdeDisk::readControl(const Addr offset, int size, uint8_t *data) { - DevAction_t action = ACT_NONE; + assert(size == sizeof(uint8_t)); + *data = status; + if (offset != ALTSTAT_OFFSET) + panic("Invalid IDE control register offset: %#x\n", offset); + DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data); +} - switch (reg_type) { - case COMMAND_BLOCK: - switch (offset) { - case DATA_OFFSET: - cmdReg.data = *(uint16_t*)data; - action = ACT_DATA_WRITE_SHORT; - break; - case FEATURES_OFFSET: - break; - case NSECTOR_OFFSET: - cmdReg.sec_count = *data; - break; - case SECTOR_OFFSET: - cmdReg.sec_num = *data; - break; - case LCYL_OFFSET: - cmdReg.cyl_low = *data; - break; - case HCYL_OFFSET: - cmdReg.cyl_high = *data; - break; - case DRIVE_OFFSET: - cmdReg.drive = *data; - action = ACT_SELECT_WRITE; - break; - case COMMAND_OFFSET: - cmdReg.command = *data; - action = ACT_CMD_WRITE; - break; - default: - panic("Invalid IDE command register offset: %#x\n", offset); +void +IdeDisk::writeCommand(const Addr offset, int size, const uint8_t *data) +{ + if (offset == DATA_OFFSET) { + if (size == sizeof(uint16_t)) { + cmdReg.data = *(const uint16_t *)data; + } else if (size == sizeof(uint32_t)) { + cmdReg.data = *(const uint16_t *)data; + updateState(ACT_DATA_WRITE_SHORT); + cmdReg.data = *((const uint16_t *)data + 1); + } else { + panic("Data write of unsupported size %d.\n", size); } + updateState(ACT_DATA_WRITE_SHORT); + return; + } + + assert(size == sizeof(uint8_t)); + switch (offset) { + case FEATURES_OFFSET: break; - case CONTROL_BLOCK: - if (offset == CONTROL_OFFSET) { - 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; - } - else - panic("Invalid IDE control register offset: %#x\n", offset); + case NSECTOR_OFFSET: + cmdReg.sec_count = *data; + break; + case SECTOR_OFFSET: + cmdReg.sec_num = *data; + break; + case LCYL_OFFSET: + cmdReg.cyl_low = *data; + break; + case HCYL_OFFSET: + cmdReg.cyl_high = *data; + break; + case DRIVE_OFFSET: + cmdReg.drive = *data; + updateState(ACT_SELECT_WRITE); + break; + case COMMAND_OFFSET: + cmdReg.command = *data; + updateState(ACT_CMD_WRITE); break; default: - panic("Unknown register block!\n"); + panic("Invalid IDE command register offset: %#x\n", offset); } + DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset, + (uint32_t)*data); +} + +void +IdeDisk::writeControl(const Addr offset, int size, const uint8_t *data) +{ + if (offset != CONTROL_OFFSET) + panic("Invalid IDE control register offset: %#x\n", offset); + + if (*data & CONTROL_RST_BIT) { + // force the device into the reset state + devState = Device_Srst; + updateState(ACT_SRST_SET); + } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) { + updateState(ACT_SRST_CLEAR); + } + + nIENBit = *data & CONTROL_IEN_BIT; DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset, (uint32_t)*data); - if (action != ACT_NONE) - updateState(action); } //// @@ -315,7 +322,7 @@ IdeDisk::doDmaTransfer() dmaState, devState); if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { - dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + schedule(dmaTransferEvent, curTick + DMA_BACKOFF_PERIOD); return; } else ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent, @@ -349,7 +356,7 @@ IdeDisk::doDmaDataRead() DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n", diskDelay, totalDiskDelay); - dmaReadWaitEvent.schedule(curTick + totalDiskDelay); + schedule(dmaReadWaitEvent, curTick + totalDiskDelay); } void @@ -395,7 +402,7 @@ IdeDisk::doDmaRead() } if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { - dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + schedule(dmaReadWaitEvent, curTick + DMA_BACKOFF_PERIOD); return; } else if (!dmaReadCG->done()) { assert(dmaReadCG->complete() < MAX_DMA_SIZE); @@ -457,7 +464,7 @@ IdeDisk::doDmaDataWrite() cmdBytesLeft -= SectorSize; } - dmaWriteWaitEvent.schedule(curTick + totalDiskDelay); + schedule(dmaWriteWaitEvent, curTick + totalDiskDelay); } void @@ -470,7 +477,7 @@ IdeDisk::doDmaWrite() curPrd.getByteCount(), TheISA::PageBytes); } if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { - dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + schedule(dmaWriteWaitEvent, curTick + DMA_BACKOFF_PERIOD); return; } else if (!dmaWriteCG->done()) { assert(dmaWriteCG->complete() < MAX_DMA_SIZE); @@ -545,7 +552,7 @@ IdeDisk::startDma(const uint32_t &prdTableBase) dmaState = Dma_Transfer; // schedule dma transfer (doDmaTransfer) - dmaTransferEvent.schedule(curTick + 1); + schedule(dmaTransferEvent, curTick + 1); } void @@ -683,7 +690,6 @@ IdeDisk::intrPost() // talk to controller to set interrupt if (ctrl) { - ctrl->bmi_regs.bmis0 |= IDEINTS; ctrl->intrPost(); } } @@ -1073,12 +1079,12 @@ IdeDisk::unserialize(Checkpoint *cp, const string §ion) 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; + case Transfer : schedule(dmaTransferEvent, reschedule); break; + case ReadWait : schedule(dmaReadWaitEvent, reschedule); break; + case WriteWait : schedule(dmaWriteWaitEvent, reschedule); break; + case PrdRead : schedule(dmaPrdReadEvent, reschedule); break; + case DmaRead : schedule(dmaReadEvent, reschedule); break; + case DmaWrite : schedule(dmaWriteEvent, reschedule); break; } // Unserialize device registers diff --git a/src/dev/ide_disk.hh b/src/dev/ide_disk.hh index 62c89add4..1b455e8ad 100644 --- a/src/dev/ide_disk.hh +++ b/src/dev/ide_disk.hh @@ -238,12 +238,12 @@ class IdeDisk : public SimObject /** Interrupt pending */ bool intrPending; - Stats::Scalar<> dmaReadFullPages; - Stats::Scalar<> dmaReadBytes; - Stats::Scalar<> dmaReadTxs; - Stats::Scalar<> dmaWriteFullPages; - Stats::Scalar<> dmaWriteBytes; - Stats::Scalar<> dmaWriteTxs; + Stats::Scalar dmaReadFullPages; + Stats::Scalar dmaReadBytes; + Stats::Scalar dmaReadTxs; + Stats::Scalar dmaWriteFullPages; + Stats::Scalar dmaWriteBytes; + Stats::Scalar dmaWriteTxs; Stats::Formula rdBandwidth; Stats::Formula wrBandwidth; Stats::Formula totBandwidth; @@ -278,8 +278,10 @@ class IdeDisk : public SimObject } // Device register read/write - void read(const Addr &offset, IdeRegType regtype, uint8_t *data); - void write(const Addr &offset, IdeRegType regtype, const uint8_t *data); + void readCommand(const Addr offset, int size, uint8_t *data); + void readControl(const Addr offset, int size, uint8_t *data); + void writeCommand(const Addr offset, int size, const uint8_t *data); + void writeControl(const Addr offset, int size, const uint8_t *data); // Start/abort functions void startDma(const uint32_t &prdTableBase); diff --git a/src/dev/intel_8254_timer.cc b/src/dev/intel_8254_timer.cc new file mode 100644 index 000000000..d5dd043e1 --- /dev/null +++ b/src/dev/intel_8254_timer.cc @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2004, 2005 + * The Regents of The University of Michigan + * All Rights Reserved + * + * This code is part of the M5 simulator. + * + * Permission is granted to use, copy, create derivative works and + * redistribute this software and such derivative works for any + * purpose, so long as the copyright notice above, this grant of + * permission, and the disclaimer below appear in all copies made; and + * so long as the name of The University of Michigan is not used in + * any advertising or publicity pertaining to the use or distribution + * of this software without specific, written prior authorization. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE + * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND + * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE + * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT, + * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM + * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGES. + * + * Authors: Ali G. Saidi + * Andrew L. Schultz + * Miguel J. Serrano + */ + +#include "base/misc.hh" +#include "dev/intel_8254_timer.hh" + +using namespace std; + +Intel8254Timer::Intel8254Timer(EventManager *em, const string &name, + Counter *counter0, Counter *counter1, Counter *counter2) : + EventManager(em), _name(name) +{ + counter[0] = counter0; + counter[1] = counter1; + counter[2] = counter2; +} + +Intel8254Timer::Intel8254Timer(EventManager *em, const string &name) : + EventManager(em), _name(name) +{ + counter[0] = new Counter(this, name + ".counter0", 0); + counter[1] = new Counter(this, name + ".counter1", 1); + counter[2] = new Counter(this, name + ".counter2", 2); +} + +void +Intel8254Timer::writeControl(const CtrlReg data) +{ + int sel = data.sel; + + if (sel == ReadBackCommand) + panic("PITimer Read-Back Command is not implemented.\n"); + + if (data.rw == LatchCommand) + counter[sel]->latchCount(); + else { + counter[sel]->setRW(data.rw); + counter[sel]->setMode(data.mode); + counter[sel]->setBCD(data.bcd); + } +} + +void +Intel8254Timer::serialize(const string &base, ostream &os) +{ + // serialize the counters + counter[0]->serialize(base + ".counter0", os); + counter[1]->serialize(base + ".counter1", os); + counter[2]->serialize(base + ".counter2", os); +} + +void +Intel8254Timer::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + // unserialze the counters + counter[0]->unserialize(base + ".counter0", cp, section); + counter[1]->unserialize(base + ".counter1", cp, section); + counter[2]->unserialize(base + ".counter2", cp, section); +} + +Intel8254Timer::Counter::Counter(Intel8254Timer *p, + const string &name, unsigned int _num) + : _name(name), num(_num), event(this), count(0), + latched_count(0), period(0), mode(0), output_high(false), + latch_on(false), read_byte(LSB), write_byte(LSB), parent(p) +{ + +} + +void +Intel8254Timer::Counter::latchCount() +{ + // behave like a real latch + if(!latch_on) { + latch_on = true; + read_byte = LSB; + latched_count = count; + } +} + +uint8_t +Intel8254Timer::Counter::read() +{ + if (latch_on) { + switch (read_byte) { + case LSB: + read_byte = MSB; + return (uint8_t)latched_count; + break; + case MSB: + read_byte = LSB; + latch_on = false; + return latched_count >> 8; + break; + default: + panic("Shouldn't be here"); + } + } else { + switch (read_byte) { + case LSB: + read_byte = MSB; + return (uint8_t)count; + break; + case MSB: + read_byte = LSB; + return count >> 8; + break; + default: + panic("Shouldn't be here"); + } + } +} + +void +Intel8254Timer::Counter::write(const uint8_t data) +{ + switch (write_byte) { + case LSB: + count = (count & 0xFF00) | data; + + if (event.scheduled()) + parent->deschedule(event); + output_high = false; + write_byte = MSB; + break; + + case MSB: + count = (count & 0x00FF) | (data << 8); + // In the RateGen or SquareWave modes, the timer wraps around and + // triggers on a value of 1, not 0. + if (mode == RateGen || mode == SquareWave) + period = count - 1; + else + period = count; + + if (period > 0) + event.setTo(period); + + write_byte = LSB; + break; + } +} + +void +Intel8254Timer::Counter::setRW(int rw_val) +{ + if (rw_val != TwoPhase) + panic("Only LSB/MSB read/write is implemented.\n"); +} + +void +Intel8254Timer::Counter::setMode(int mode_val) +{ + if(mode_val != InitTc && mode_val != RateGen && + mode_val != SquareWave) + panic("PIT mode %#x is not implemented: \n", mode_val); + + mode = mode_val; +} + +void +Intel8254Timer::Counter::setBCD(int bcd_val) +{ + if (bcd_val) + panic("PITimer does not implement BCD counts.\n"); +} + +bool +Intel8254Timer::Counter::outputHigh() +{ + return output_high; +} + +void +Intel8254Timer::Counter::serialize(const string &base, ostream &os) +{ + paramOut(os, base + ".count", count); + paramOut(os, base + ".latched_count", latched_count); + paramOut(os, base + ".period", period); + paramOut(os, base + ".mode", mode); + paramOut(os, base + ".output_high", output_high); + paramOut(os, base + ".latch_on", latch_on); + paramOut(os, base + ".read_byte", read_byte); + paramOut(os, base + ".write_byte", write_byte); + + Tick event_tick = 0; + if (event.scheduled()) + event_tick = event.when(); + paramOut(os, base + ".event_tick", event_tick); +} + +void +Intel8254Timer::Counter::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + paramIn(cp, section, base + ".count", count); + paramIn(cp, section, base + ".latched_count", latched_count); + paramIn(cp, section, base + ".period", period); + paramIn(cp, section, base + ".mode", mode); + paramIn(cp, section, base + ".output_high", output_high); + paramIn(cp, section, base + ".latch_on", latch_on); + paramIn(cp, section, base + ".read_byte", read_byte); + paramIn(cp, section, base + ".write_byte", write_byte); + + Tick event_tick; + paramIn(cp, section, base + ".event_tick", event_tick); + if (event_tick) + parent->schedule(event, event_tick); +} + +Intel8254Timer::Counter::CounterEvent::CounterEvent(Counter* c_ptr) +{ + interval = (Tick)(Clock::Float::s / 1193180.0); + counter = c_ptr; +} + +void +Intel8254Timer::Counter::CounterEvent::process() +{ + switch (counter->mode) { + case InitTc: + counter->output_high = true; + break; + case RateGen: + case SquareWave: + setTo(counter->period); + break; + default: + panic("Unimplemented PITimer mode.\n"); + } + counter->parent->counterInterrupt(counter->num); +} + +void +Intel8254Timer::Counter::CounterEvent::setTo(int clocks) +{ + if (clocks == 0) + panic("Timer can't be set to go off instantly.\n"); + DPRINTF(Intel8254Timer, "Timer set to curTick + %d\n", + clocks * interval); + counter->parent->schedule(this, curTick + clocks * interval); +} + +const char * +Intel8254Timer::Counter::CounterEvent::description() const +{ + return "Intel 8254 Interval timer"; +} diff --git a/src/dev/intel_8254_timer.hh b/src/dev/intel_8254_timer.hh new file mode 100644 index 000000000..bb650d33b --- /dev/null +++ b/src/dev/intel_8254_timer.hh @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2004, 2005 + * The Regents of The University of Michigan + * All Rights Reserved + * + * This code is part of the M5 simulator. + * + * Permission is granted to use, copy, create derivative works and + * redistribute this software and such derivative works for any + * purpose, so long as the copyright notice above, this grant of + * permission, and the disclaimer below appear in all copies made; and + * so long as the name of The University of Michigan is not used in + * any advertising or publicity pertaining to the use or distribution + * of this software without specific, written prior authorization. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE + * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND + * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE + * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT, + * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM + * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGES. + * + * Authors: Ali G. Saidi + * Andrew L. Schultz + * Miguel J. Serrano + */ + +#ifndef __DEV_8254_HH__ +#define __DEV_8254_HH__ + +#include <string> +#include <iostream> + +#include "base/bitunion.hh" +#include "sim/eventq.hh" +#include "sim/host.hh" +#include "sim/serialize.hh" + +/** Programmable Interval Timer (Intel 8254) */ +class Intel8254Timer : public EventManager +{ + protected: + BitUnion8(CtrlReg) + Bitfield<7, 6> sel; + Bitfield<5, 4> rw; + Bitfield<3, 1> mode; + Bitfield<0> bcd; + EndBitUnion(CtrlReg) + + enum SelectVal { + SelectCounter0, + SelectCounter1, + SelectCounter2, + ReadBackCommand + }; + + enum ReadWriteVal { + LatchCommand, + LsbOnly, + MsbOnly, + TwoPhase + }; + + enum ModeVal { + InitTc, + OneShot, + RateGen, + SquareWave, + SoftwareStrobe, + HardwareStrobe + }; + + /** Counter element for PIT */ + class Counter + { + /** Event for counter interrupt */ + class CounterEvent : public Event + { + private: + /** Pointer back to Counter */ + Counter* counter; + Tick interval; + + public: + CounterEvent(Counter*); + + /** Event process */ + void process(); + + /** Event description */ + virtual const char *description() const; + + friend class Counter; + + void setTo(int clocks); + }; + + private: + std::string _name; + const std::string &name() const { return _name; } + + unsigned int num; + + CounterEvent event; + + /** Current count value */ + uint16_t count; + + /** Latched count */ + uint16_t latched_count; + + /** Interrupt period */ + uint16_t period; + + /** Current mode of operation */ + uint8_t mode; + + /** Output goes high when the counter reaches zero */ + bool output_high; + + /** State of the count latch */ + bool latch_on; + + /** Set of values for read_byte and write_byte */ + enum {LSB, MSB}; + + /** Determine which byte of a 16-bit count value to read/write */ + uint8_t read_byte, write_byte; + + /** Pointer to container */ + Intel8254Timer *parent; + + public: + Counter(Intel8254Timer *p, const std::string &name, unsigned int num); + + /** Latch the current count (if one is not already latched) */ + void latchCount(); + + /** Set the read/write mode */ + void setRW(int rw_val); + + /** Set operational mode */ + void setMode(int mode_val); + + /** Set count encoding */ + void setBCD(int bcd_val); + + /** Read a count byte */ + uint8_t read(); + + /** Write a count byte */ + void write(const uint8_t data); + + /** Is the output high? */ + bool outputHigh(); + + /** + * Serialize this object to the given output stream. + * @param base The base name of the counter object. + * @param os The stream to serialize to. + */ + void serialize(const std::string &base, std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param base The base name of the counter object. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); + }; + + protected: + std::string _name; + const std::string &name() const { return _name; } + + /** PIT has three seperate counters */ + Counter *counter[3]; + + virtual void + counterInterrupt(unsigned int num) + { + DPRINTF(Intel8254Timer, "Timer interrupt from counter %d.\n", num); + } + + public: + + virtual + ~Intel8254Timer() + {} + + Intel8254Timer(EventManager *em, const std::string &name, + Counter *counter0, Counter *counter1, Counter *counter2); + + Intel8254Timer(EventManager *em, const std::string &name); + + /** Write control word */ + void writeControl(const CtrlReg data); + + uint8_t + readCounter(unsigned int num) + { + assert(num < 3); + return counter[num]->read(); + } + + void + writeCounter(unsigned int num, const uint8_t data) + { + assert(num < 3); + counter[num]->write(data); + } + + bool + outputHigh(unsigned int num) + { + assert(num < 3); + return counter[num]->outputHigh(); + } + + /** + * Serialize this object to the given output stream. + * @param base The base name of the counter object. + * @param os The stream to serialize to. + */ + void serialize(const std::string &base, std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param base The base name of the counter object. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); +}; + +#endif // __DEV_8254_HH__ diff --git a/src/dev/io_device.cc b/src/dev/io_device.cc index 527397ed8..cdba171a6 100644 --- a/src/dev/io_device.cc +++ b/src/dev/io_device.cc @@ -117,7 +117,7 @@ DmaPort::recvTiming(PacketPtr pkt) else if (backoffTime < device->maxBackoffDelay) backoffTime <<= 1; - backoffEvent.reschedule(curTick + backoffTime, true); + reschedule(backoffEvent, curTick + backoffTime, true); DPRINTF(DMA, "Backoff time set to %d ticks\n", backoffTime); @@ -138,7 +138,10 @@ DmaPort::recvTiming(PacketPtr pkt) state->numBytes += pkt->req->getSize(); assert(state->totBytes >= state->numBytes); if (state->totBytes == state->numBytes) { - state->completionEvent->process(); + if (state->delay) + schedule(state->completionEvent, curTick + state->delay); + else + state->completionEvent->process(); delete state; } delete pkt->req; @@ -187,9 +190,9 @@ void DmaPort::recvRetry() { assert(transmitList.size()); - PacketPtr pkt = transmitList.front(); bool result = true; do { + PacketPtr pkt = transmitList.front(); DPRINTF(DMA, "Retry on %s addr %#x\n", pkt->cmdString(), pkt->getAddr()); result = sendTiming(pkt); @@ -206,7 +209,7 @@ DmaPort::recvRetry() if (transmitList.size() && backoffTime && !inRetry) { DPRINTF(DMA, "Scheduling backoff for %d\n", curTick+backoffTime); if (!backoffEvent.scheduled()) - backoffEvent.schedule(backoffTime+curTick); + schedule(backoffEvent, backoffTime + curTick); } DPRINTF(DMA, "TransmitList: %d, backoffTime: %d inRetry: %d es: %d\n", transmitList.size(), backoffTime, inRetry, @@ -216,13 +219,13 @@ DmaPort::recvRetry() void DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, - uint8_t *data) + uint8_t *data, Tick delay) { assert(event); assert(device->getState() == SimObject::Running); - DmaReqState *reqState = new DmaReqState(event, this, size); + DmaReqState *reqState = new DmaReqState(event, this, size, delay); DPRINTF(DMA, "Starting DMA for addr: %#x size: %d sched: %d\n", addr, size, @@ -294,7 +297,7 @@ DmaPort::sendDma() !backoffEvent.scheduled()) { DPRINTF(DMA, "-- Scheduling backoff timer for %d\n", backoffTime+curTick); - backoffEvent.schedule(backoffTime+curTick); + schedule(backoffEvent, backoffTime + curTick); } } else if (state == Enums::atomic) { transmitList.pop_front(); @@ -314,7 +317,7 @@ DmaPort::sendDma() if (state->totBytes == state->numBytes) { assert(!state->completionEvent->scheduled()); - state->completionEvent->schedule(curTick + lat); + schedule(state->completionEvent, curTick + lat + state->delay); delete state; delete pkt->req; } diff --git a/src/dev/io_device.hh b/src/dev/io_device.hh index 876166adb..70af6093d 100644 --- a/src/dev/io_device.hh +++ b/src/dev/io_device.hh @@ -32,6 +32,7 @@ #ifndef __DEV_IO_DEVICE_HH__ #define __DEV_IO_DEVICE_HH__ +#include "base/fast_alloc.hh" #include "mem/mem_object.hh" #include "mem/packet.hh" #include "mem/tport.hh" @@ -73,7 +74,7 @@ class PioPort : public SimpleTimingPort class DmaPort : public Port { protected: - struct DmaReqState : public Packet::SenderState + struct DmaReqState : public Packet::SenderState, public FastAlloc { /** Event to call on the device when this transaction (all packets) * complete. */ @@ -88,8 +89,12 @@ class DmaPort : public Port /** Number of bytes that have been acked for this transaction. */ Addr numBytes; - DmaReqState(Event *ce, Port *p, Addr tb) - : completionEvent(ce), outPort(p), totBytes(tb), numBytes(0) + /** Amount to delay completion of dma by */ + Tick delay; + + DmaReqState(Event *ce, Port *p, Addr tb, Tick _delay) + : completionEvent(ce), outPort(p), totBytes(tb), numBytes(0), + delay(_delay) {} }; @@ -143,7 +148,7 @@ class DmaPort : public Port DmaPort(DmaDevice *dev, System *s); void dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, - uint8_t *data = NULL); + uint8_t *data, Tick delay); bool dmaPending() { return pendingCount > 0; } @@ -207,7 +212,8 @@ class PioDevice : public MemObject { if (if_name == "pio") { if (pioPort != NULL) - panic("pio port already connected to."); + fatal("%s: pio port already connected to %s", + name(), pioPort->getPeer()->name()); pioPort = new PioPort(this, sys); return pioPort; } else @@ -264,14 +270,14 @@ class DmaDevice : public PioDevice return dynamic_cast<const Params *>(_params); } - void dmaWrite(Addr addr, int size, Event *event, uint8_t *data) + void dmaWrite(Addr addr, int size, Event *event, uint8_t *data, Tick delay = 0) { - dmaPort->dmaAction(MemCmd::WriteReq, addr, size, event, data); + dmaPort->dmaAction(MemCmd::WriteReq, addr, size, event, data, delay); } - void dmaRead(Addr addr, int size, Event *event, uint8_t *data) + void dmaRead(Addr addr, int size, Event *event, uint8_t *data, Tick delay = 0) { - dmaPort->dmaAction(MemCmd::ReadReq, addr, size, event, data); + dmaPort->dmaAction(MemCmd::ReadReq, addr, size, event, data, delay); } bool dmaPending() { return dmaPort->dmaPending(); } @@ -284,12 +290,14 @@ class DmaDevice : public PioDevice { if (if_name == "pio") { if (pioPort != NULL) - panic("pio port already connected to."); + fatal("%s: pio port already connected to %s", + name(), pioPort->getPeer()->name()); pioPort = new PioPort(this, sys); return pioPort; } else if (if_name == "dma") { if (dmaPort != NULL) - panic("dma port already connected to."); + fatal("%s: dma port already connected to %s", + name(), dmaPort->getPeer()->name()); dmaPort = new DmaPort(this, sys); return dmaPort; } else diff --git a/src/dev/mc146818.cc b/src/dev/mc146818.cc new file mode 100644 index 000000000..c01d945b3 --- /dev/null +++ b/src/dev/mc146818.cc @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano + */ + +#include <sys/time.h> +#include <time.h> + +#include <string> + +#include "base/bitfield.hh" +#include "base/time.hh" +#include "base/trace.hh" +#include "dev/mc146818.hh" +#include "dev/rtcreg.h" + +using namespace std; + +MC146818::MC146818(EventManager *em, const string &n, const struct tm time, + bool bcd, Tick frequency) + : EventManager(em), _name(n), event(this, frequency) +{ + memset(clock_data, 0, sizeof(clock_data)); + stat_regA = RTCA_32768HZ | RTCA_1024HZ; + stat_regB = RTCB_PRDC_IE | RTCB_24HR; + if (!bcd) + stat_regB |= RTCB_BIN; + + year = time.tm_year; + + if (bcd) { + // The datasheet says that the year field can be either BCD or + // years since 1900. Linux seems to be happy with years since + // 1900. + year = year % 100; + int tens = year / 10; + int ones = year % 10; + year = (tens << 4) + ones; + } + + // Unix is 0-11 for month, data seet says start at 1 + mon = time.tm_mon + 1; + mday = time.tm_mday; + hour = time.tm_hour; + min = time.tm_min; + sec = time.tm_sec; + + // Datasheet says 1 is sunday + wday = time.tm_wday + 1; + + DPRINTFN("Real-time clock set to %s", asctime(&time)); +} + +MC146818::~MC146818() +{ +} + +void +MC146818::writeData(const uint8_t addr, const uint8_t data) +{ + if (addr < RTC_STAT_REGA) + clock_data[addr] = data; + else { + switch (addr) { + case RTC_STAT_REGA: + // The "update in progress" bit is read only. + if ((data & ~RTCA_UIP) != (RTCA_32768HZ | RTCA_1024HZ)) + panic("Unimplemented RTC register A value write!\n"); + replaceBits(stat_regA, data, 6, 0); + break; + case RTC_STAT_REGB: + if ((data & ~(RTCB_PRDC_IE | RTCB_SQWE)) != (RTCB_BIN | RTCB_24HR)) + panic("Write to RTC reg B bits that are not implemented!\n"); + + if (data & RTCB_PRDC_IE) { + if (!event.scheduled()) + event.scheduleIntr(); + } else { + if (event.scheduled()) + deschedule(event); + } + stat_regB = data; + break; + case RTC_STAT_REGC: + case RTC_STAT_REGD: + panic("RTC status registers C and D are not implemented.\n"); + break; + } + } +} + +uint8_t +MC146818::readData(uint8_t addr) +{ + if (addr < RTC_STAT_REGA) + return clock_data[addr]; + else { + switch (addr) { + case RTC_STAT_REGA: + // toggle UIP bit for linux + stat_regA ^= RTCA_UIP; + return stat_regA; + break; + case RTC_STAT_REGB: + return stat_regB; + break; + case RTC_STAT_REGC: + case RTC_STAT_REGD: + return 0x00; + break; + default: + panic("Shouldn't be here"); + } + } +} + +void +MC146818::serialize(const string &base, ostream &os) +{ + arrayParamOut(os, base + ".clock_data", clock_data, sizeof(clock_data)); + paramOut(os, base + ".stat_regA", stat_regA); + paramOut(os, base + ".stat_regB", stat_regB); +} + +void +MC146818::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + arrayParamIn(cp, section, base + ".clock_data", clock_data, + sizeof(clock_data)); + paramIn(cp, section, base + ".stat_regA", stat_regA); + paramIn(cp, section, base + ".stat_regB", stat_regB); + + // We're not unserializing the event here, but we need to + // rescehedule the event since curTick was moved forward by the + // checkpoint + reschedule(event, curTick + event.interval); +} + +MC146818::RTCEvent::RTCEvent(MC146818 * _parent, Tick i) + : parent(_parent), interval(i) +{ + DPRINTF(MC146818, "RTC Event Initilizing\n"); + parent->schedule(this, curTick + interval); +} + +void +MC146818::RTCEvent::scheduleIntr() +{ + parent->schedule(this, curTick + interval); +} + +void +MC146818::RTCEvent::process() +{ + DPRINTF(MC146818, "RTC Timer Interrupt\n"); + parent->schedule(this, curTick + interval); + parent->handleEvent(); +} + +const char * +MC146818::RTCEvent::description() const +{ + return "RTC interrupt"; +} diff --git a/src/dev/mc146818.hh b/src/dev/mc146818.hh new file mode 100644 index 000000000..e145ad3fd --- /dev/null +++ b/src/dev/mc146818.hh @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano + */ + +#ifndef __DEV_MC146818_HH__ +#define __DEV_MC146818_HH__ + +#include "base/range.hh" +#include "sim/eventq.hh" + +/** Real-Time Clock (MC146818) */ +class MC146818 : public EventManager +{ + protected: + virtual void handleEvent() + { + warn("No RTC event handler defined.\n"); + } + + private: + /** Event for RTC periodic interrupt */ + struct RTCEvent : public Event + { + MC146818 * parent; + Tick interval; + + RTCEvent(MC146818 * _parent, Tick i); + + /** Schedule the RTC periodic interrupt */ + void scheduleIntr(); + + /** Event process to occur at interrupt*/ + virtual void process(); + + /** Event description */ + virtual const char *description() const; + }; + + private: + std::string _name; + const std::string &name() const { return _name; } + + /** RTC periodic interrupt event */ + RTCEvent event; + + /** Data for real-time clock function */ + union { + uint8_t clock_data[10]; + + struct { + uint8_t sec; + uint8_t sec_alrm; + uint8_t min; + uint8_t min_alrm; + uint8_t hour; + uint8_t hour_alrm; + uint8_t wday; + uint8_t mday; + uint8_t mon; + uint8_t year; + }; + }; + + /** RTC status register A */ + uint8_t stat_regA; + + /** RTC status register B */ + uint8_t stat_regB; + + public: + MC146818(EventManager *em, const std::string &name, const struct tm time, + bool bcd, Tick frequency); + virtual ~MC146818(); + + /** RTC write data */ + void writeData(const uint8_t addr, const uint8_t data); + + /** RTC read data */ + uint8_t readData(const uint8_t addr); + + /** + * Serialize this object to the given output stream. + * @param base The base name of the counter object. + * @param os The stream to serialize to. + */ + void serialize(const std::string &base, std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param base The base name of the counter object. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); +}; + +#endif // __DEV_MC146818_HH__ diff --git a/src/dev/mips/Malta.py b/src/dev/mips/Malta.py index d321a6361..d215bf329 100755 --- a/src/dev/mips/Malta.py +++ b/src/dev/mips/Malta.py @@ -28,12 +28,13 @@ from m5.params import * from m5.proxy import * + +from BadDevice import BadDevice from Device import BasicPioDevice +from MipsBackdoor import MipsBackdoor +from Pci import PciConfigAll from Platform import Platform -from MipsConsole import MipsConsole from Uart import Uart8250 -from Pci import PciConfigAll -from BadDevice import BadDevice class MaltaCChip(BasicPioDevice): type = 'MaltaCChip' @@ -56,7 +57,7 @@ class Malta(Platform): cchip = MaltaCChip(pio_addr=0x801a0000000) io = MaltaIO(pio_addr=0x801fc000000) uart = Uart8250(pio_addr=0xBFD003F8) - console = MipsConsole(pio_addr=0xBFD00F00, disk=Parent.simple_disk) + backdoor = MipsBackdoor(pio_addr=0xBFD00F00, disk=Parent.simple_disk) # Attach I/O devices to specified bus object. Can't do this # earlier, since the bus object itself is typically defined at the @@ -65,4 +66,4 @@ class Malta(Platform): self.cchip.pio = bus.port self.io.pio = bus.port self.uart.pio = bus.port - self.console.pio = bus.port + self.backdoor.pio = bus.port diff --git a/src/dev/mips/MipsConsole.py b/src/dev/mips/MipsBackdoor.py index 36575677a..f65250238 100644 --- a/src/dev/mips/MipsConsole.py +++ b/src/dev/mips/MipsBackdoor.py @@ -30,9 +30,9 @@ from m5.params import * from m5.proxy import * from Device import BasicPioDevice -class MipsConsole(BasicPioDevice): - type = 'MipsConsole' +class MipsBackdoor(BasicPioDevice): + type = 'MipsBackdoor' cpu = Param.BaseCPU(Parent.cpu[0], "Processor") disk = Param.SimpleDisk("Simple Disk") - sim_console = Param.SimConsole(Parent.any, "The Simulator Console") + terminal = Param.Terminal(Parent.any, "The console terminal") system = Param.MipsSystem(Parent.any, "system object") diff --git a/src/dev/mips/SConscript b/src/dev/mips/SConscript index 22e91ff09..e83e47ebd 100755 --- a/src/dev/mips/SConscript +++ b/src/dev/mips/SConscript @@ -32,13 +32,13 @@ Import('*') if env['FULL_SYSTEM'] and env['TARGET_ISA'] == 'mips': - SimObject('MipsConsole.py') + SimObject('MipsBackdoor.py') SimObject('Malta.py') TraceFlag('Malta') TraceFlag('MC146818') - Source('console.cc') + Source('backdoor.cc') Source('malta.cc') Source('malta_cchip.cc') Source('malta_io.cc') diff --git a/src/dev/mips/access.h b/src/dev/mips/access.h index dbf3661b3..416b80590 100755 --- a/src/dev/mips/access.h +++ b/src/dev/mips/access.h @@ -48,37 +48,37 @@ typedef unsigned long uint64_t; // This structure hacked up from simos struct MipsAccess { - uint32_t inputChar; // 00: Placeholder for input - uint32_t last_offset; // 04: must be first field - uint32_t version; // 08: - uint32_t numCPUs; // 0C: - uint32_t intrClockFrequency; // 10: Hz + uint32_t inputChar; // 00: Placeholder for input + uint32_t last_offset; // 04: must be first field + uint32_t version; // 08: + uint32_t numCPUs; // 0C: + uint32_t intrClockFrequency; // 10: Hz // Loaded kernel - uint32_t kernStart; // 14: - uint32_t kernEnd; // 18: - uint32_t entryPoint; // 1c: + uint32_t kernStart; // 14: + uint32_t kernEnd; // 18: + uint32_t entryPoint; // 1c: // console simple output stuff - uint32_t outputChar; // 20: Placeholder for output + uint32_t outputChar; // 20: Placeholder for output // console disk stuff - uint32_t diskUnit; // 24: - uint32_t diskCount; // 28: - uint32_t diskPAddr; // 2c: - uint32_t diskBlock; // 30: - uint32_t diskOperation; // 34: + uint32_t diskUnit; // 24: + uint32_t diskCount; // 28: + uint32_t diskPAddr; // 2c: + uint32_t diskBlock; // 30: + uint32_t diskOperation; // 34: // MP boot - uint32_t cpuStack[64]; // 70: + uint32_t cpuStack[64]; // 70: /* XXX There appears to be a problem in accessing * unit64_t in the console.c file. They are treated * like uint32_int and result in the wrong address for * everything below. This problem should be investigated. */ - uint64_t cpuClock; // 38: MHz - uint64_t mem_size; // 40: + uint64_t cpuClock; // 38: MHz + uint64_t mem_size; // 40: }; #endif // __MIPS_ACCESS_H__ diff --git a/src/dev/mips/console.cc b/src/dev/mips/backdoor.cc index 185e1acbc..313f12567 100755 --- a/src/dev/mips/console.cc +++ b/src/dev/mips/backdoor.cc @@ -32,7 +32,7 @@ */ /** @file - * Mips Console Definition + * Mips Console Backdoor Definition */ #include <cstddef> #include <string> @@ -43,22 +43,22 @@ #include "base/trace.hh" #include "cpu/base.hh" #include "cpu/thread_context.hh" -#include "dev/mips/console.hh" +#include "dev/mips/backdoor.hh" #include "dev/platform.hh" -#include "dev/simconsole.hh" #include "dev/simple_disk.hh" +#include "dev/terminal.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" #include "mem/physical.hh" -#include "params/MipsConsole.hh" +#include "params/MipsBackdoor.hh" #include "sim/sim_object.hh" using namespace std; using namespace MipsISA; -MipsConsole::MipsConsole(const Params *p) - : BasicPioDevice(p), disk(p->disk), console(p->sim_console), +MipsBackdoor::MipsBackdoor(const Params *p) + : BasicPioDevice(p), disk(p->disk), terminal(p->terminal), system(p->system), cpu(p->cpu) { @@ -81,7 +81,7 @@ MipsConsole::MipsConsole(const Params *p) } void -MipsConsole::startup() +MipsBackdoor::startup() { system->setMipsAccess(pioAddr); mipsAccess->numCPUs = system->getNumCPUs(); @@ -94,7 +94,7 @@ MipsConsole::startup() } Tick -MipsConsole::read(PacketPtr pkt) +MipsBackdoor::read(PacketPtr pkt) { /** XXX Do we want to push the addr munging to a bus brige or something? So @@ -125,7 +125,7 @@ MipsConsole::read(PacketPtr pkt) pkt->set(mipsAccess->intrClockFrequency); break; case offsetof(MipsAccess, inputChar): - pkt->set(console->console_in()); + pkt->set(terminal->console_in()); break; case offsetof(MipsAccess, cpuClock): pkt->set(mipsAccess->cpuClock); @@ -169,7 +169,7 @@ MipsConsole::read(PacketPtr pkt) else panic("Unknown 32bit access, %#x\n", daddr); } - //DPRINTF(MipsConsole, "read: offset=%#x val=%#x\n", daddr, + //DPRINTF(MipsBackdoor, "read: offset=%#x val=%#x\n", daddr, // pkt->get<uint64_t>()); break; default: @@ -181,7 +181,7 @@ MipsConsole::read(PacketPtr pkt) } Tick -MipsConsole::write(PacketPtr pkt) +MipsBackdoor::write(PacketPtr pkt) { assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; @@ -215,7 +215,7 @@ MipsConsole::write(PacketPtr pkt) break; case offsetof(MipsAccess, outputChar): - console->out((char)(val & 0xff)); + terminal->out((char)(val & 0xff)); break; default: @@ -235,7 +235,7 @@ MipsConsole::write(PacketPtr pkt) } void -MipsConsole::Access::serialize(ostream &os) +MipsBackdoor::Access::serialize(ostream &os) { SERIALIZE_SCALAR(last_offset); SERIALIZE_SCALAR(version); @@ -257,7 +257,7 @@ MipsConsole::Access::serialize(ostream &os) } void -MipsConsole::Access::unserialize(Checkpoint *cp, const std::string §ion) +MipsBackdoor::Access::unserialize(Checkpoint *cp, const std::string §ion) { UNSERIALIZE_SCALAR(last_offset); UNSERIALIZE_SCALAR(version); @@ -279,19 +279,19 @@ MipsConsole::Access::unserialize(Checkpoint *cp, const std::string §ion) } void -MipsConsole::serialize(ostream &os) +MipsBackdoor::serialize(ostream &os) { mipsAccess->serialize(os); } void -MipsConsole::unserialize(Checkpoint *cp, const std::string §ion) +MipsBackdoor::unserialize(Checkpoint *cp, const std::string §ion) { mipsAccess->unserialize(cp, section); } -MipsConsole * -MipsConsoleParams::create() +MipsBackdoor * +MipsBackdoorParams::create() { - return new MipsConsole(this); + return new MipsBackdoor(this); } diff --git a/src/dev/mips/console.hh b/src/dev/mips/backdoor.hh index 34792090d..b8cc0ae46 100755 --- a/src/dev/mips/console.hh +++ b/src/dev/mips/backdoor.hh @@ -29,28 +29,28 @@ */ /** @file - * System Console Interface + * System Console Backdoor Interface */ -#ifndef __MIPS_CONSOLE_HH__ -#define __MIPS_CONSOLE_HH__ +#ifndef __DEV_MIPS_BACKDOOR_HH__ +#define __DEV_MIPS_BACKDOOR_HH__ #include "base/range.hh" #include "dev/mips/access.h" #include "dev/io_device.hh" -#include "params/MipsConsole.hh" +#include "params/MipsBackdoor.hh" #include "sim/host.hh" #include "sim/sim_object.hh" class BaseCPU; -class SimConsole; +class Terminal; class MipsSystem; class SimpleDisk; /** * Memory mapped interface to the system console. This device * represents a shared data region between the OS Kernel and the - * System Console. + * System Console Backdoor. * * The system console is a small standalone program that is initially * run when the system boots. It contains the necessary code to @@ -72,7 +72,7 @@ class SimpleDisk; * primarily used doing boot before the kernel has loaded its device * drivers. */ -class MipsConsole : public BasicPioDevice +class MipsBackdoor : public BasicPioDevice { protected: struct Access : public MipsAccess @@ -89,8 +89,8 @@ class MipsConsole : public BasicPioDevice /** the disk must be accessed from the console */ SimpleDisk *disk; - /** the system console (the terminal) is accessable from the console */ - SimConsole *console; + /** the system terminal is accessable from the console */ + Terminal *terminal; /** a pointer to the system we are running in */ MipsSystem *system; @@ -99,8 +99,8 @@ class MipsConsole : public BasicPioDevice BaseCPU *cpu; public: - typedef MipsConsoleParams Params; - MipsConsole(const Params *p); + typedef MipsBackdoorParams Params; + MipsBackdoor(const Params *p); const Params * params() const @@ -123,4 +123,4 @@ class MipsConsole : public BasicPioDevice virtual void unserialize(Checkpoint *cp, const std::string §ion); }; -#endif // __MIPS_CONSOLE_HH__ +#endif // __DEV_MIPS_BACKDOOR_HH__ diff --git a/src/dev/mips/malta.cc b/src/dev/mips/malta.cc index 0b1fa15ba..21e79d999 100755 --- a/src/dev/mips/malta.cc +++ b/src/dev/mips/malta.cc @@ -38,11 +38,11 @@ #include <vector> #include "cpu/intr_control.hh" -#include "dev/simconsole.hh" #include "dev/mips/malta_cchip.hh" #include "dev/mips/malta_pchip.hh" #include "dev/mips/malta_io.hh" #include "dev/mips/malta.hh" +#include "dev/terminal.hh" #include "params/Malta.hh" #include "sim/system.hh" diff --git a/src/dev/mips/malta_cchip.cc b/src/dev/mips/malta_cchip.cc index 5a4ea4585..265977665 100755 --- a/src/dev/mips/malta_cchip.cc +++ b/src/dev/mips/malta_cchip.cc @@ -103,7 +103,7 @@ MaltaCChip::read(PacketPtr pkt) break; case TSDEV_CC_MISC: pkt->set((ipint << 8) & 0xF | (itint << 4) & 0xF | - (pkt->req->getCpuNum() & 0x3)); + (pkt->req->contextId() & 0x3)); break; case TSDEV_CC_AAR0: case TSDEV_CC_AAR1: diff --git a/src/dev/ns_gige.cc b/src/dev/ns_gige.cc index bd48bdca5..fb3446299 100644 --- a/src/dev/ns_gige.cc +++ b/src/dev/ns_gige.cc @@ -36,6 +36,7 @@ #include <deque> #include <string> +#include "base/debug.hh" #include "base/inet.hh" #include "cpu/thread_context.hh" #include "dev/etherlink.hh" @@ -44,9 +45,7 @@ #include "mem/packet.hh" #include "mem/packet_access.hh" #include "params/NSGigE.hh" -#include "sim/debug.hh" #include "sim/host.hh" -#include "sim/stats.hh" #include "sim/system.hh" const char *NsRxStateStrings[] = @@ -131,341 +130,6 @@ NSGigE::NSGigE(Params *p) 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) - ; - - txIpChecksums - .name(name() + ".txIpChecksums") - .desc("Number of tx IP Checksums done by device") - .precision(0) - .prereq(txBytes) - ; - - rxIpChecksums - .name(name() + ".rxIpChecksums") - .desc("Number of rx IP Checksums done by device") - .precision(0) - .prereq(rxBytes) - ; - - txTcpChecksums - .name(name() + ".txTcpChecksums") - .desc("Number of tx TCP Checksums done by device") - .precision(0) - .prereq(txBytes) - ; - - rxTcpChecksums - .name(name() + ".rxTcpChecksums") - .desc("Number of rx TCP Checksums done by device") - .precision(0) - .prereq(rxBytes) - ; - - txUdpChecksums - .name(name() + ".txUdpChecksums") - .desc("Number of tx UDP Checksums done by device") - .precision(0) - .prereq(txBytes) - ; - - rxUdpChecksums - .name(name() + ".rxUdpChecksums") - .desc("Number of rx UDP Checksums done by device") - .precision(0) - .prereq(rxBytes) - ; - - descDmaReads - .name(name() + ".descDMAReads") - .desc("Number of descriptors the device read w/ DMA") - .precision(0) - ; - - descDmaWrites - .name(name() + ".descDMAWrites") - .desc("Number of descriptors the device wrote w/ DMA") - .precision(0) - ; - - descDmaRdBytes - .name(name() + ".descDmaReadBytes") - .desc("number of descriptor bytes read w/ DMA") - .precision(0) - ; - - descDmaWrBytes - .name(name() + ".descDmaWriteBytes") - .desc("number of descriptor bytes write w/ DMA") - .precision(0) - ; - - 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) - ; - - totBandwidth - .name(name() + ".totBandwidth") - .desc("Total Bandwidth (bits/s)") - .precision(0) - .prereq(totBytes) - ; - - totPackets - .name(name() + ".totPackets") - .desc("Total Packets") - .precision(0) - .prereq(totBytes) - ; - - totBytes - .name(name() + ".totBytes") - .desc("Total Bytes") - .precision(0) - .prereq(totBytes) - ; - - totPacketRate - .name(name() + ".totPPS") - .desc("Total Tranmission Rate (packets/s)") - .precision(0) - .prereq(totBytes) - ; - - 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) - ; - - postedSwi - .name(name() + ".postedSwi") - .desc("number of software interrupts posted to CPU") - .precision(0) - ; - - totalSwi - .name(name() + ".totalSwi") - .desc("total number of Swi written to ISR") - .precision(0) - ; - - coalescedSwi - .name(name() + ".coalescedSwi") - .desc("average number of Swi's coalesced into each post") - .precision(0) - ; - - postedRxIdle - .name(name() + ".postedRxIdle") - .desc("number of rxIdle interrupts posted to CPU") - .precision(0) - ; - - totalRxIdle - .name(name() + ".totalRxIdle") - .desc("total number of RxIdle written to ISR") - .precision(0) - ; - - coalescedRxIdle - .name(name() + ".coalescedRxIdle") - .desc("average number of RxIdle's coalesced into each post") - .precision(0) - ; - - postedRxOk - .name(name() + ".postedRxOk") - .desc("number of RxOk interrupts posted to CPU") - .precision(0) - ; - - totalRxOk - .name(name() + ".totalRxOk") - .desc("total number of RxOk written to ISR") - .precision(0) - ; - - coalescedRxOk - .name(name() + ".coalescedRxOk") - .desc("average number of RxOk's coalesced into each post") - .precision(0) - ; - - postedRxDesc - .name(name() + ".postedRxDesc") - .desc("number of RxDesc interrupts posted to CPU") - .precision(0) - ; - - totalRxDesc - .name(name() + ".totalRxDesc") - .desc("total number of RxDesc written to ISR") - .precision(0) - ; - - coalescedRxDesc - .name(name() + ".coalescedRxDesc") - .desc("average number of RxDesc's coalesced into each post") - .precision(0) - ; - - postedTxOk - .name(name() + ".postedTxOk") - .desc("number of TxOk interrupts posted to CPU") - .precision(0) - ; - - totalTxOk - .name(name() + ".totalTxOk") - .desc("total number of TxOk written to ISR") - .precision(0) - ; - - coalescedTxOk - .name(name() + ".coalescedTxOk") - .desc("average number of TxOk's coalesced into each post") - .precision(0) - ; - - postedTxIdle - .name(name() + ".postedTxIdle") - .desc("number of TxIdle interrupts posted to CPU") - .precision(0) - ; - - totalTxIdle - .name(name() + ".totalTxIdle") - .desc("total number of TxIdle written to ISR") - .precision(0) - ; - - coalescedTxIdle - .name(name() + ".coalescedTxIdle") - .desc("average number of TxIdle's coalesced into each post") - .precision(0) - ; - - postedTxDesc - .name(name() + ".postedTxDesc") - .desc("number of TxDesc interrupts posted to CPU") - .precision(0) - ; - - totalTxDesc - .name(name() + ".totalTxDesc") - .desc("total number of TxDesc written to ISR") - .precision(0) - ; - - coalescedTxDesc - .name(name() + ".coalescedTxDesc") - .desc("average number of TxDesc's coalesced into each post") - .precision(0) - ; - - postedRxOrn - .name(name() + ".postedRxOrn") - .desc("number of RxOrn posted to CPU") - .precision(0) - ; - - totalRxOrn - .name(name() + ".totalRxOrn") - .desc("total number of RxOrn written to ISR") - .precision(0) - ; - - coalescedRxOrn - .name(name() + ".coalescedRxOrn") - .desc("average number of RxOrn's coalesced into each post") - .precision(0) - ; - - coalescedTotal - .name(name() + ".coalescedTotal") - .desc("average number of interrupts coalesced into each post") - .precision(0) - ; - - postedInterrupts - .name(name() + ".postedInterrupts") - .desc("number of posts to CPU") - .precision(0) - ; - - droppedPackets - .name(name() + ".droppedPackets") - .desc("number of packets dropped") - .precision(0) - ; - - coalescedSwi = totalSwi / postedInterrupts; - coalescedRxIdle = totalRxIdle / postedInterrupts; - coalescedRxOk = totalRxOk / postedInterrupts; - coalescedRxDesc = totalRxDesc / postedInterrupts; - coalescedTxOk = totalTxOk / postedInterrupts; - coalescedTxIdle = totalTxIdle / postedInterrupts; - coalescedTxDesc = totalTxDesc / postedInterrupts; - coalescedRxOrn = totalRxOrn / postedInterrupts; - - coalescedTotal = (totalSwi + totalRxIdle + totalRxOk + totalRxDesc + - totalTxOk + totalTxIdle + totalTxDesc + - totalRxOrn) / postedInterrupts; - - txBandwidth = txBytes * Stats::constant(8) / simSeconds; - rxBandwidth = rxBytes * Stats::constant(8) / simSeconds; - totBandwidth = txBandwidth + rxBandwidth; - totBytes = txBytes + rxBytes; - totPackets = txPackets + rxPackets; - - txPacketRate = txPackets / simSeconds; - rxPacketRate = rxPackets / simSeconds; -} - - /** * This is to write to the PCI general configuration registers */ @@ -1186,6 +850,7 @@ NSGigE::devIntrPost(uint32_t interrupts) Tick when = curTick; if ((regs.isr & regs.imr & ISR_NODELAY) == 0) when += intrDelay; + postedInterrupts++; cpuIntrPost(when); } } @@ -1226,9 +891,6 @@ NSGigE::devIntrClear(uint32_t interrupts) postedRxOrn++; } - if (regs.isr & regs.imr & ISR_IMPL) - postedInterrupts++; - interrupts &= ~ISR_NOIMPL; regs.isr &= ~interrupts; @@ -1283,7 +945,8 @@ NSGigE::cpuIntrPost(Tick when) if (intrEvent) intrEvent->squash(); - intrEvent = new IntrEvent(this, intrTick, true); + intrEvent = new IntrEvent(this, true); + schedule(intrEvent, intrTick); } void @@ -1780,7 +1443,7 @@ NSGigE::rxKick() NsRxStateStrings[rxState]); if (clock && !rxKickEvent.scheduled()) - rxKickEvent.schedule(rxKickTick); + schedule(rxKickEvent, rxKickTick); } void @@ -1830,7 +1493,7 @@ NSGigE::transmit() if (!txFifo.empty() && !txEvent.scheduled()) { DPRINTF(Ethernet, "reschedule transmit\n"); - txEvent.schedule(curTick + retryTime); + schedule(txEvent, curTick + retryTime); } } @@ -2036,19 +1699,34 @@ NSGigE::txKick() IpPtr ip(txPacket); if (extsts & EXTSTS_UDPPKT) { UdpPtr udp(ip); - udp->sum(0); - udp->sum(cksum(udp)); - txUdpChecksums++; + if (udp) { + udp->sum(0); + udp->sum(cksum(udp)); + txUdpChecksums++; + } else { + debug_break(); + warn_once("UDPPKT set, but not UDP!\n"); + } } else if (extsts & EXTSTS_TCPPKT) { TcpPtr tcp(ip); - tcp->sum(0); - tcp->sum(cksum(tcp)); - txTcpChecksums++; + if (tcp) { + tcp->sum(0); + tcp->sum(cksum(tcp)); + txTcpChecksums++; + } else { + debug_break(); + warn_once("TCPPKT set, but not UDP!\n"); + } } if (extsts & EXTSTS_IPPKT) { - ip->sum(0); - ip->sum(cksum(ip)); - txIpChecksums++; + if (ip) { + ip->sum(0); + ip->sum(cksum(ip)); + txIpChecksums++; + } else { + debug_break(); + warn_once("IPPKT set, but not UDP!\n"); + } } } @@ -2208,7 +1886,7 @@ NSGigE::txKick() NsTxStateStrings[txState]); if (clock && !txKickEvent.scheduled()) - txKickEvent.schedule(txKickTick); + schedule(txKickEvent, txKickTick); } /** @@ -2322,7 +2000,7 @@ NSGigE::transferDone() DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n"); - txEvent.reschedule(curTick + ticks(1), true); + reschedule(txEvent, curTick + ticks(1), true); } bool @@ -2723,7 +2401,7 @@ NSGigE::unserialize(Checkpoint *cp, const std::string §ion) this->txDmaState = (DmaState) txDmaState; UNSERIALIZE_SCALAR(txKickTick); if (txKickTick) - txKickEvent.schedule(txKickTick); + schedule(txKickEvent, txKickTick); /* * unserialize rx state machine @@ -2741,7 +2419,7 @@ NSGigE::unserialize(Checkpoint *cp, const std::string §ion) this->rxDmaState = (DmaState) rxDmaState; UNSERIALIZE_SCALAR(rxKickTick); if (rxKickTick) - rxKickEvent.schedule(rxKickTick); + schedule(rxKickEvent, rxKickTick); /* * Unserialize EEPROM state machine @@ -2761,7 +2439,7 @@ NSGigE::unserialize(Checkpoint *cp, const std::string §ion) Tick transmitTick; UNSERIALIZE_SCALAR(transmitTick); if (transmitTick) - txEvent.schedule(curTick + transmitTick); + schedule(txEvent, curTick + transmitTick); /* * unserialize receive address filter settings @@ -2782,7 +2460,8 @@ NSGigE::unserialize(Checkpoint *cp, const std::string §ion) Tick intrEventTick; UNSERIALIZE_SCALAR(intrEventTick); if (intrEventTick) { - intrEvent = new IntrEvent(this, intrEventTick, true); + intrEvent = new IntrEvent(this, true); + schedule(intrEvent, intrEventTick); } } diff --git a/src/dev/ns_gige.hh b/src/dev/ns_gige.hh index dfdd81b66..87cf56962 100644 --- a/src/dev/ns_gige.hh +++ b/src/dev/ns_gige.hh @@ -38,7 +38,6 @@ #define __DEV_NS_GIGE_HH__ #include "base/inet.hh" -#include "base/statistics.hh" #include "dev/etherdevice.hh" #include "dev/etherint.hh" #include "dev/etherpkt.hh" @@ -63,10 +62,10 @@ const uint8_t EEPROM_PMATCH0_ADDR = 0xC; // EEPROM Address of PMATCH word 0 * Ethernet device registers */ struct dp_regs { - uint32_t command; - uint32_t config; - uint32_t mear; - uint32_t ptscr; + uint32_t command; + uint32_t config; + uint32_t mear; + uint32_t ptscr; uint32_t isr; uint32_t imr; uint32_t ier; @@ -372,60 +371,6 @@ class NSGigE : public EtherDevice virtual void unserialize(Checkpoint *cp, const std::string §ion); virtual void resume(); - - public: - void regStats(); - - private: - Stats::Scalar<> txBytes; - Stats::Scalar<> rxBytes; - Stats::Scalar<> txPackets; - Stats::Scalar<> rxPackets; - Stats::Scalar<> txIpChecksums; - Stats::Scalar<> rxIpChecksums; - Stats::Scalar<> txTcpChecksums; - Stats::Scalar<> rxTcpChecksums; - Stats::Scalar<> txUdpChecksums; - Stats::Scalar<> rxUdpChecksums; - Stats::Scalar<> descDmaReads; - Stats::Scalar<> descDmaWrites; - Stats::Scalar<> descDmaRdBytes; - Stats::Scalar<> descDmaWrBytes; - Stats::Formula totBandwidth; - Stats::Formula totPackets; - Stats::Formula totBytes; - Stats::Formula totPacketRate; - Stats::Formula txBandwidth; - Stats::Formula rxBandwidth; - Stats::Formula txPacketRate; - Stats::Formula rxPacketRate; - Stats::Scalar<> postedSwi; - Stats::Formula coalescedSwi; - Stats::Scalar<> totalSwi; - Stats::Scalar<> postedRxIdle; - Stats::Formula coalescedRxIdle; - Stats::Scalar<> totalRxIdle; - Stats::Scalar<> postedRxOk; - Stats::Formula coalescedRxOk; - Stats::Scalar<> totalRxOk; - Stats::Scalar<> postedRxDesc; - Stats::Formula coalescedRxDesc; - Stats::Scalar<> totalRxDesc; - Stats::Scalar<> postedTxOk; - Stats::Formula coalescedTxOk; - Stats::Scalar<> totalTxOk; - Stats::Scalar<> postedTxIdle; - Stats::Formula coalescedTxIdle; - Stats::Scalar<> totalTxIdle; - Stats::Scalar<> postedTxDesc; - Stats::Formula coalescedTxDesc; - Stats::Scalar<> totalTxDesc; - Stats::Scalar<> postedRxOrn; - Stats::Formula coalescedRxOrn; - Stats::Scalar<> totalRxOrn; - Stats::Formula coalescedTotal; - Stats::Scalar<> postedInterrupts; - Stats::Scalar<> droppedPackets; }; /* diff --git a/src/dev/pciconfigall.cc b/src/dev/pciconfigall.cc index faf033705..74396be5d 100644 --- a/src/dev/pciconfigall.cc +++ b/src/dev/pciconfigall.cc @@ -47,7 +47,7 @@ using namespace std; PciConfigAll::PciConfigAll(const Params *p) : PioDevice(p) { - pioAddr = p->platform->calcConfigAddr(params()->bus,0,0); + pioAddr = p->platform->calcPciConfigAddr(params()->bus,0,0); } diff --git a/src/dev/pcidev.cc b/src/dev/pcidev.cc index aa532414c..b311ed8cf 100644 --- a/src/dev/pcidev.cc +++ b/src/dev/pcidev.cc @@ -41,7 +41,7 @@ #include "base/inifile.hh" #include "base/intmath.hh" // for isPowerOf2( #include "base/misc.hh" -#include "base/str.hh" // for to_number +#include "base/str.hh" // for to_number #include "base/trace.hh" #include "dev/pciconfigall.hh" #include "dev/pcidev.hh" @@ -56,10 +56,10 @@ using namespace std; PciDev::PciConfigPort::PciConfigPort(PciDev *dev, int busid, int devid, int funcid, Platform *p) - : SimpleTimingPort(dev->name() + "-pciconf"), device(dev), platform(p), - busId(busid), deviceId(devid), functionId(funcid) + : SimpleTimingPort(dev->name() + "-pciconf", dev), device(dev), + platform(p), busId(busid), deviceId(devid), functionId(funcid) { - configAddr = platform->calcConfigAddr(busId, deviceId, functionId); + configAddr = platform->calcPciConfigAddr(busId, deviceId, functionId); } @@ -121,15 +121,26 @@ PciDev::PciDev(const Params *p) BARSize[4] = p->BAR4Size; BARSize[5] = p->BAR5Size; + legacyIO[0] = p->BAR0LegacyIO; + legacyIO[1] = p->BAR1LegacyIO; + legacyIO[2] = p->BAR2LegacyIO; + legacyIO[3] = p->BAR3LegacyIO; + legacyIO[4] = p->BAR4LegacyIO; + legacyIO[5] = p->BAR5LegacyIO; + for (int i = 0; i < 6; ++i) { - uint32_t barsize = BARSize[i]; - if (barsize != 0 && !isPowerOf2(barsize)) { - fatal("BAR %d size %d is not a power of 2\n", i, BARSize[i]); + if (legacyIO[i]) { + BARAddrs[i] = platform->calcPciIOAddr(letoh(config.baseAddr[i])); + config.baseAddr[i] = 0; + } else { + BARAddrs[i] = 0; + uint32_t barsize = BARSize[i]; + if (barsize != 0 && !isPowerOf2(barsize)) { + fatal("BAR %d size %d is not a power of 2\n", i, BARSize[i]); + } } } - memset(BARAddrs, 0, sizeof(BARAddrs)); - plat->registerPciDevice(0, p->pci_dev, p->pci_func, letoh(config.interruptLine)); } @@ -216,8 +227,10 @@ PciDev::writeConfig(PacketPtr pkt) switch (offset) { case PCI0_INTERRUPT_LINE: config.interruptLine = pkt->get<uint8_t>(); + break; case PCI_CACHE_LINE_SIZE: config.cacheLineSize = pkt->get<uint8_t>(); + break; case PCI_LATENCY_TIMER: config.latencyTimer = pkt->get<uint8_t>(); break; @@ -240,8 +253,10 @@ PciDev::writeConfig(PacketPtr pkt) switch (offset) { case PCI_COMMAND: config.command = pkt->get<uint8_t>(); + break; case PCI_STATUS: config.status = pkt->get<uint8_t>(); + break; case PCI_CACHE_LINE_SIZE: config.cacheLineSize = pkt->get<uint8_t>(); break; @@ -264,30 +279,32 @@ PciDev::writeConfig(PacketPtr pkt) { int barnum = BAR_NUMBER(offset); - // convert BAR values to host endianness - uint32_t he_old_bar = letoh(config.baseAddr[barnum]); - uint32_t he_new_bar = letoh(pkt->get<uint32_t>()); - - uint32_t bar_mask = - BAR_IO_SPACE(he_old_bar) ? BAR_IO_MASK : BAR_MEM_MASK; - - // Writing 0xffffffff to a BAR tells the card to set the - // value of the bar to a bitmask indicating the size of - // memory it needs - if (he_new_bar == 0xffffffff) { - he_new_bar = ~(BARSize[barnum] - 1); - } else { - // does it mean something special to write 0 to a BAR? - he_new_bar &= ~bar_mask; - if (he_new_bar) { - Addr space_base = BAR_IO_SPACE(he_old_bar) ? - TSUNAMI_PCI0_IO : TSUNAMI_PCI0_MEMORY; - BARAddrs[barnum] = he_new_bar + space_base; - pioPort->sendStatusChange(Port::RangeChange); + if (!legacyIO[barnum]) { + // convert BAR values to host endianness + uint32_t he_old_bar = letoh(config.baseAddr[barnum]); + uint32_t he_new_bar = letoh(pkt->get<uint32_t>()); + + uint32_t bar_mask = + BAR_IO_SPACE(he_old_bar) ? BAR_IO_MASK : BAR_MEM_MASK; + + // Writing 0xffffffff to a BAR tells the card to set the + // value of the bar to a bitmask indicating the size of + // memory it needs + if (he_new_bar == 0xffffffff) { + he_new_bar = ~(BARSize[barnum] - 1); + } else { + // does it mean something special to write 0 to a BAR? + he_new_bar &= ~bar_mask; + if (he_new_bar) { + BARAddrs[barnum] = BAR_IO_SPACE(he_old_bar) ? + platform->calcPciIOAddr(he_new_bar) : + platform->calcPciMemAddr(he_new_bar); + pioPort->sendStatusChange(Port::RangeChange); + } } + config.baseAddr[barnum] = htole((he_new_bar & ~bar_mask) | + (he_old_bar & bar_mask)); } - config.baseAddr[barnum] = htole((he_new_bar & ~bar_mask) | - (he_old_bar & bar_mask)); } break; diff --git a/src/dev/pcidev.hh b/src/dev/pcidev.hh index a584a957d..5da8b2dfc 100644 --- a/src/dev/pcidev.hh +++ b/src/dev/pcidev.hh @@ -99,6 +99,9 @@ class PciDev : public DmaDevice /** The current address mapping of the BARs */ Addr BARAddrs[6]; + /** Whether the BARs are really hardwired legacy IO locations. */ + bool legacyIO[6]; + /** * Does the given address lie within the space mapped by the given * base address register? diff --git a/src/dev/pcireg.h b/src/dev/pcireg.h index df57acdb0..5639d8e29 100644 --- a/src/dev/pcireg.h +++ b/src/dev/pcireg.h @@ -69,18 +69,18 @@ union PCIConfig { }; // Common PCI offsets -#define PCI_VENDOR_ID 0x00 // Vendor ID ro -#define PCI_DEVICE_ID 0x02 // Device ID ro -#define PCI_COMMAND 0x04 // Command rw -#define PCI_STATUS 0x06 // Status rw -#define PCI_REVISION_ID 0x08 // Revision ID ro -#define PCI_CLASS_CODE 0x09 // Class Code ro -#define PCI_SUB_CLASS_CODE 0x0A // Sub Class Code ro -#define PCI_BASE_CLASS_CODE 0x0B // Base Class Code ro -#define PCI_CACHE_LINE_SIZE 0x0C // Cache Line Size ro+ -#define PCI_LATENCY_TIMER 0x0D // Latency Timer ro+ -#define PCI_HEADER_TYPE 0x0E // Header Type ro -#define PCI_BIST 0x0F // Built in self test rw +#define PCI_VENDOR_ID 0x00 // Vendor ID ro +#define PCI_DEVICE_ID 0x02 // Device ID ro +#define PCI_COMMAND 0x04 // Command rw +#define PCI_STATUS 0x06 // Status rw +#define PCI_REVISION_ID 0x08 // Revision ID ro +#define PCI_CLASS_CODE 0x09 // Class Code ro +#define PCI_SUB_CLASS_CODE 0x0A // Sub Class Code ro +#define PCI_BASE_CLASS_CODE 0x0B // Base Class Code ro +#define PCI_CACHE_LINE_SIZE 0x0C // Cache Line Size ro+ +#define PCI_LATENCY_TIMER 0x0D // Latency Timer ro+ +#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 @@ -88,62 +88,62 @@ union PCIConfig { #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 -#define PCI0_BASE_ADDR2 0x18 // Base Address 2 rw -#define PCI0_BASE_ADDR3 0x1C // Base Address 3 rw -#define PCI0_BASE_ADDR4 0x20 // Base Address 4 rw -#define PCI0_BASE_ADDR5 0x24 // Base Address 5 rw -#define PCI0_CIS 0x28 // CardBus CIS Pointer ro -#define PCI0_SUB_VENDOR_ID 0x2C // Sub-Vendor ID ro -#define PCI0_SUB_SYSTEM_ID 0x2E // Sub-System ID ro -#define PCI0_ROM_BASE_ADDR 0x30 // Expansion ROM Base Address rw -#define PCI0_RESERVED0 0x34 -#define PCI0_RESERVED1 0x38 -#define PCI0_INTERRUPT_LINE 0x3C // Interrupt Line rw -#define PCI0_INTERRUPT_PIN 0x3D // Interrupt Pin ro -#define PCI0_MINIMUM_GRANT 0x3E // Maximum Grant ro -#define PCI0_MAXIMUM_LATENCY 0x3F // Maximum Latency ro +#define PCI0_BASE_ADDR0 0x10 // Base Address 0 rw +#define PCI0_BASE_ADDR1 0x14 // Base Address 1 rw +#define PCI0_BASE_ADDR2 0x18 // Base Address 2 rw +#define PCI0_BASE_ADDR3 0x1C // Base Address 3 rw +#define PCI0_BASE_ADDR4 0x20 // Base Address 4 rw +#define PCI0_BASE_ADDR5 0x24 // Base Address 5 rw +#define PCI0_CIS 0x28 // CardBus CIS Pointer ro +#define PCI0_SUB_VENDOR_ID 0x2C // Sub-Vendor ID ro +#define PCI0_SUB_SYSTEM_ID 0x2E // Sub-System ID ro +#define PCI0_ROM_BASE_ADDR 0x30 // Expansion ROM Base Address rw +#define PCI0_RESERVED0 0x34 +#define PCI0_RESERVED1 0x38 +#define PCI0_INTERRUPT_LINE 0x3C // Interrupt Line rw +#define PCI0_INTERRUPT_PIN 0x3D // Interrupt Pin ro +#define PCI0_MINIMUM_GRANT 0x3E // Maximum Grant ro +#define PCI0_MAXIMUM_LATENCY 0x3F // Maximum Latency ro // Type 1 PCI offsets -#define PCI1_BASE_ADDR0 0x10 // Base Address 0 rw -#define PCI1_BASE_ADDR1 0x14 // Base Address 1 rw -#define PCI1_PRI_BUS_NUM 0x18 // Primary Bus Number rw -#define PCI1_SEC_BUS_NUM 0x19 // Secondary Bus Number rw -#define PCI1_SUB_BUS_NUM 0x1A // Subordinate Bus Number rw -#define PCI1_SEC_LAT_TIMER 0x1B // Secondary Latency Timer ro+ -#define PCI1_IO_BASE 0x1C // I/O Base rw -#define PCI1_IO_LIMIT 0x1D // I/O Limit rw -#define PCI1_SECONDARY_STATUS 0x1E // Secondary Status rw -#define PCI1_MEM_BASE 0x20 // Memory Base rw -#define PCI1_MEM_LIMIT 0x22 // Memory Limit rw -#define PCI1_PRF_MEM_BASE 0x24 // Prefetchable Memory Base rw -#define PCI1_PRF_MEM_LIMIT 0x26 // Prefetchable Memory Limit rw -#define PCI1_PRF_BASE_UPPER 0x28 // Prefetchable Base Upper 32 rw -#define PCI1_PRF_LIMIT_UPPER 0x2C // Prefetchable Limit Upper 32 rw -#define PCI1_IO_BASE_UPPER 0x30 // I/O Base Upper 16 bits rw -#define PCI1_IO_LIMIT_UPPER 0x32 // I/O Limit Upper 16 bits rw -#define PCI1_RESERVED 0x34 // Reserved ro -#define PCI1_ROM_BASE_ADDR 0x38 // Expansion ROM Base Address rw -#define PCI1_INTR_LINE 0x3C // Interrupt Line rw -#define PCI1_INTR_PIN 0x3D // Interrupt Pin ro -#define PCI1_BRIDGE_CTRL 0x3E // Bridge Control rw +#define PCI1_BASE_ADDR0 0x10 // Base Address 0 rw +#define PCI1_BASE_ADDR1 0x14 // Base Address 1 rw +#define PCI1_PRI_BUS_NUM 0x18 // Primary Bus Number rw +#define PCI1_SEC_BUS_NUM 0x19 // Secondary Bus Number rw +#define PCI1_SUB_BUS_NUM 0x1A // Subordinate Bus Number rw +#define PCI1_SEC_LAT_TIMER 0x1B // Secondary Latency Timer ro+ +#define PCI1_IO_BASE 0x1C // I/O Base rw +#define PCI1_IO_LIMIT 0x1D // I/O Limit rw +#define PCI1_SECONDARY_STATUS 0x1E // Secondary Status rw +#define PCI1_MEM_BASE 0x20 // Memory Base rw +#define PCI1_MEM_LIMIT 0x22 // Memory Limit rw +#define PCI1_PRF_MEM_BASE 0x24 // Prefetchable Memory Base rw +#define PCI1_PRF_MEM_LIMIT 0x26 // Prefetchable Memory Limit rw +#define PCI1_PRF_BASE_UPPER 0x28 // Prefetchable Base Upper 32 rw +#define PCI1_PRF_LIMIT_UPPER 0x2C // Prefetchable Limit Upper 32 rw +#define PCI1_IO_BASE_UPPER 0x30 // I/O Base Upper 16 bits rw +#define PCI1_IO_LIMIT_UPPER 0x32 // I/O Limit Upper 16 bits rw +#define PCI1_RESERVED 0x34 // Reserved ro +#define PCI1_ROM_BASE_ADDR 0x38 // Expansion ROM Base Address rw +#define PCI1_INTR_LINE 0x3C // Interrupt Line rw +#define PCI1_INTR_PIN 0x3D // Interrupt Pin ro +#define PCI1_BRIDGE_CTRL 0x3E // Bridge Control rw // Device specific offsets -#define PCI_DEVICE_SPECIFIC 0x40 // 192 bytes +#define PCI_DEVICE_SPECIFIC 0x40 // 192 bytes #define PCI_CONFIG_SIZE 0xFF // Some Vendor IDs -#define PCI_VENDOR_DEC 0x1011 -#define PCI_VENDOR_NCR 0x101A -#define PCI_VENDOR_QLOGIC 0x1077 -#define PCI_VENDOR_SIMOS 0x1291 +#define PCI_VENDOR_DEC 0x1011 +#define PCI_VENDOR_NCR 0x101A +#define PCI_VENDOR_QLOGIC 0x1077 +#define PCI_VENDOR_SIMOS 0x1291 // Some Product IDs -#define PCI_PRODUCT_DEC_PZA 0x0008 -#define PCI_PRODUCT_NCR_810 0x0001 -#define PCI_PRODUCT_QLOGIC_ISP1020 0x1020 -#define PCI_PRODUCT_SIMOS_SIMOS 0x1291 -#define PCI_PRODUCT_SIMOS_ETHER 0x1292 +#define PCI_PRODUCT_DEC_PZA 0x0008 +#define PCI_PRODUCT_NCR_810 0x0001 +#define PCI_PRODUCT_QLOGIC_ISP1020 0x1020 +#define PCI_PRODUCT_SIMOS_SIMOS 0x1291 +#define PCI_PRODUCT_SIMOS_ETHER 0x1292 #endif // __PCIREG_H__ diff --git a/src/dev/pktfifo.cc b/src/dev/pktfifo.cc index 37f7ff680..97d6c04af 100644 --- a/src/dev/pktfifo.cc +++ b/src/dev/pktfifo.cc @@ -40,23 +40,24 @@ PacketFifo::copyout(void *dest, int offset, int len) if (offset + len >= size()) return false; - list<EthPacketPtr>::iterator p = fifo.begin(); - list<EthPacketPtr>::iterator end = fifo.end(); + iterator i = fifo.begin(); + iterator end = fifo.end(); while (len > 0) { - while (offset >= (*p)->length) { - offset -= (*p)->length; - ++p; + EthPacketPtr &pkt = i->packet; + while (offset >= pkt->length) { + offset -= pkt->length; + ++i; } - if (p == end) + if (i == end) panic("invalid fifo"); - int size = min((*p)->length - offset, len); - memcpy(data, (*p)->data, size); + int size = min(pkt->length - offset, len); + memcpy(data, pkt->data, size); offset = 0; len -= size; data += size; - ++p; + ++i; } return true; @@ -64,6 +65,26 @@ PacketFifo::copyout(void *dest, int offset, int len) void +PacketFifoEntry::serialize(const string &base, ostream &os) +{ + packet->serialize(base + ".packet", os); + paramOut(os, base + ".slack", slack); + paramOut(os, base + ".number", number); + paramOut(os, base + ".priv", priv); +} + +void +PacketFifoEntry::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + packet = new EthPacketData(16384); + packet->unserialize(base + ".packet", cp, section); + paramIn(cp, section, base + ".slack", slack); + paramIn(cp, section, base + ".number", number); + paramIn(cp, section, base + ".priv", priv); +} + +void PacketFifo::serialize(const string &base, ostream &os) { paramOut(os, base + ".size", _size); @@ -72,11 +93,11 @@ PacketFifo::serialize(const string &base, ostream &os) paramOut(os, base + ".packets", fifo.size()); int i = 0; - list<EthPacketPtr>::iterator p = fifo.begin(); - list<EthPacketPtr>::iterator end = fifo.end(); - while (p != end) { - (*p)->serialize(csprintf("%s.packet%d", base, i), os); - ++p; + iterator entry = fifo.begin(); + iterator end = fifo.end(); + while (entry != end) { + entry->serialize(csprintf("%s.entry%d", base, i), os); + ++entry; ++i; } } @@ -94,8 +115,8 @@ PacketFifo::unserialize(const string &base, Checkpoint *cp, fifo.clear(); for (int i = 0; i < fifosize; ++i) { - EthPacketPtr p = new EthPacketData(16384); - p->unserialize(csprintf("%s.packet%d", base, i), cp, section); - fifo.push_back(p); + PacketFifoEntry entry; + entry.unserialize(csprintf("%s.entry%d", base, i), cp, section); + fifo.push_back(entry); } } diff --git a/src/dev/pktfifo.hh b/src/dev/pktfifo.hh index 45157ba41..6ded248be 100644 --- a/src/dev/pktfifo.hh +++ b/src/dev/pktfifo.hh @@ -39,20 +39,59 @@ #include "sim/serialize.hh" class Checkpoint; + +struct PacketFifoEntry +{ + EthPacketPtr packet; + uint64_t number; + int slack; + int priv; + + PacketFifoEntry() + { + clear(); + } + + PacketFifoEntry(const PacketFifoEntry &s) + : packet(s.packet), number(s.number), slack(s.slack), priv(s.priv) + { + } + + PacketFifoEntry(EthPacketPtr p, uint64_t n) + : packet(p), number(n), slack(0), priv(-1) + { + } + + void clear() + { + packet = NULL; + number = 0; + slack = 0; + priv = -1; + } + + void serialize(const std::string &base, std::ostream &os); + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); +}; + class PacketFifo { public: - typedef std::list<EthPacketPtr> fifo_list; + + typedef std::list<PacketFifoEntry> fifo_list; typedef fifo_list::iterator iterator; protected: - std::list<EthPacketPtr> fifo; + std::list<PacketFifoEntry> fifo; + uint64_t _counter; int _maxsize; int _size; int _reserved; public: - explicit PacketFifo(int max) : _maxsize(max), _size(0), _reserved(0) {} + explicit PacketFifo(int max) + : _counter(0), _maxsize(max), _size(0), _reserved(0) {} virtual ~PacketFifo() {} int packets() const { return fifo.size(); } @@ -73,18 +112,21 @@ class PacketFifo iterator begin() { return fifo.begin(); } iterator end() { return fifo.end(); } - EthPacketPtr front() { return fifo.front(); } + EthPacketPtr front() { return fifo.begin()->packet; } bool push(EthPacketPtr ptr) { assert(ptr->length); assert(_reserved <= ptr->length); - assert(ptr->slack == 0); if (avail() < ptr->length - _reserved) return false; _size += ptr->length; - fifo.push_back(ptr); + + PacketFifoEntry entry; + entry.packet = ptr; + entry.number = _counter++; + fifo.push_back(entry); _reserved = 0; return true; } @@ -94,18 +136,17 @@ class PacketFifo if (empty()) return; - EthPacketPtr &packet = fifo.front(); - _size -= packet->length; - _size -= packet->slack; - packet->slack = 0; - packet = NULL; + iterator entry = fifo.begin(); + _size -= entry->packet->length; + _size -= entry->slack; + entry->packet = NULL; fifo.pop_front(); } void clear() { for (iterator i = begin(); i != end(); ++i) - (*i)->slack = 0; + i->clear(); fifo.clear(); _size = 0; _reserved = 0; @@ -113,51 +154,48 @@ class PacketFifo void remove(iterator i) { - EthPacketPtr &packet = *i; if (i != fifo.begin()) { iterator prev = i; --prev; assert(prev != fifo.end()); - (*prev)->slack += packet->length; + prev->slack += i->packet->length; + prev->slack += i->slack; } else { - _size -= packet->length; - _size -= packet->slack; + _size -= i->packet->length; + _size -= i->slack; } - packet->slack = 0; - packet = NULL; + i->clear(); fifo.erase(i); } bool copyout(void *dest, int offset, int len); - int countPacketsBefore(iterator end) + int countPacketsBefore(iterator i) { - iterator i = fifo.begin(); - int count = 0; - - while (i != end) { - ++count; - ++i; - } - - return count; + if (i == fifo.end()) + return 0; + return i->number - fifo.begin()->number; } int countPacketsAfter(iterator i) { iterator end = fifo.end(); - int count = 0; + if (i == end) + return 0; + return (--end)->number - i->number; + } - while (i != end) { - ++count; - ++i; - } + void check() + { + int total = 0; + for (iterator i = begin(); i != end(); ++i) + total += i->packet->length + i->slack; - return count; + if (total != _size) + panic("total (%d) is not == to size (%d)\n", total, _size); } - /** * Serialization stuff */ diff --git a/src/dev/platform.hh b/src/dev/platform.hh index 699b168ce..5f6f1df81 100644 --- a/src/dev/platform.hh +++ b/src/dev/platform.hh @@ -46,7 +46,7 @@ class PciConfigAll; class IntrControl; -class SimConsole; +class Terminal; class Uart; class System; @@ -69,7 +69,9 @@ class Platform : public SimObject virtual void postPciInt(int line); virtual void clearPciInt(int line); virtual Addr pciToDma(Addr pciAddr) const; - virtual Addr calcConfigAddr(int bus, int dev, int func) = 0; + virtual Addr calcPciConfigAddr(int bus, int dev, int func) = 0; + virtual Addr calcPciIOAddr(Addr addr) = 0; + virtual Addr calcPciMemAddr(Addr addr) = 0; virtual void registerPciDevice(uint8_t bus, uint8_t dev, uint8_t func, uint8_t intr); diff --git a/src/dev/rtcreg.h b/src/dev/rtcreg.h index 37255777b..b1406c464 100644 --- a/src/dev/rtcreg.h +++ b/src/dev/rtcreg.h @@ -30,32 +30,32 @@ * Nathan Binkert */ -#define RTC_SEC 0x00 -#define RTC_SEC_ALRM 0x01 -#define RTC_MIN 0x02 -#define RTC_MIN_ALRM 0x03 -#define RTC_HR 0x04 -#define RTC_HR_ALRM 0x05 -#define RTC_DOW 0x06 -#define RTC_DOM 0x07 -#define RTC_MON 0x08 -#define RTC_YEAR 0x09 +static const int RTC_SEC = 0x00; +static const int RTC_SEC_ALRM = 0x01; +static const int RTC_MIN = 0x02; +static const int RTC_MIN_ALRM = 0x03; +static const int RTC_HR = 0x04; +static const int RTC_HR_ALRM = 0x05; +static const int RTC_DOW = 0x06; +static const int RTC_DOM = 0x07; +static const int RTC_MON = 0x08; +static const int RTC_YEAR = 0x09; -#define RTC_STAT_REGA 0x0A -#define RTCA_1024HZ 0x06 /* 1024Hz periodic interrupt frequency */ -#define RTCA_32768HZ 0x20 /* 22-stage divider, 32.768KHz timebase */ -#define RTCA_UIP 0x80 /* 1 = date and time update in progress */ +static const int RTC_STAT_REGA = 0x0A; +static const int RTCA_1024HZ = 0x06; /* 1024Hz periodic interrupt frequency */ +static const int RTCA_32768HZ = 0x20; /* 22-stage divider, 32.768KHz timebase */ +static const int RTCA_UIP = 0x80; /* 1 = date and time update in progress */ -#define RTC_STAT_REGB 0x0B -#define RTCB_DST 0x01 /* USA Daylight Savings Time enable */ -#define RTCB_24HR 0x02 /* 0 = 12 hours, 1 = 24 hours */ -#define RTCB_BIN 0x04 /* 0 = BCD, 1 = Binary coded time */ -#define RTCB_SQWE 0x08 /* 1 = output sqare wave at SQW pin */ -#define RTCB_UPDT_IE 0x10 /* 1 = enable update-ended interrupt */ -#define RTCB_ALRM_IE 0x20 /* 1 = enable alarm interrupt */ -#define RTCB_PRDC_IE 0x40 /* 1 = enable periodic clock interrupt */ -#define RTCB_NO_UPDT 0x80 /* stop clock updates */ +static const int RTC_STAT_REGB = 0x0B; +static const int RTCB_DST = 0x01; /* USA Daylight Savings Time enable */ +static const int RTCB_24HR = 0x02; /* 0 = 12 hours, 1 = 24 hours */ +static const int RTCB_BIN = 0x04; /* 0 = BCD, 1 = Binary coded time */ +static const int RTCB_SQWE = 0x08; /* 1 = output sqare wave at SQW pin */ +static const int RTCB_UPDT_IE = 0x10; /* 1 = enable update-ended interrupt */ +static const int RTCB_ALRM_IE = 0x20; /* 1 = enable alarm interrupt */ +static const int RTCB_PRDC_IE = 0x40; /* 1 = enable periodic clock interrupt */ +static const int RTCB_NO_UPDT = 0x80; /* stop clock updates */ -#define RTC_STAT_REGC 0x0C -#define RTC_STAT_REGD 0x0D +static const int RTC_STAT_REGC = 0x0C; +static const int RTC_STAT_REGD = 0x0D; diff --git a/src/dev/sinic.cc b/src/dev/sinic.cc index c63966528..37c6a8259 100644 --- a/src/dev/sinic.cc +++ b/src/dev/sinic.cc @@ -33,6 +33,7 @@ #include <string> #include "arch/vtophys.hh" +#include "base/debug.hh" #include "base/inet.hh" #include "cpu/thread_context.hh" #include "cpu/intr_control.hh" @@ -40,11 +41,11 @@ #include "dev/sinic.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" -#include "sim/debug.hh" #include "sim/eventq.hh" #include "sim/host.hh" #include "sim/stats.hh" +using namespace std; using namespace Net; using namespace TheISA; @@ -265,6 +266,35 @@ Device::regStats() totPackets = txPackets + rxPackets; txPacketRate = txPackets / simSeconds; rxPacketRate = rxPackets / simSeconds; + + _maxVnicDistance = 0; + + maxVnicDistance + .name(name() + ".maxVnicDistance") + .desc("maximum vnic distance") + ; + + totalVnicDistance + .name(name() + ".totalVnicDistance") + .desc("total vnic distance") + ; + numVnicDistance + .name(name() + ".numVnicDistance") + .desc("number of vnic distance measurements") + ; + + avgVnicDistance + .name(name() + ".avgVnicDistance") + .desc("average vnic distance") + ; + + avgVnicDistance = totalVnicDistance / numVnicDistance; +} + +void +Device::resetStats() +{ + _maxVnicDistance = 0; } EtherInt* @@ -289,6 +319,10 @@ Device::prepareIO(int cpu, int index) index, size); } +//add stats for head of line blocking +//add stats for average fifo length +//add stats for average number of vnics busy + void Device::prepareRead(int cpu, int index) { @@ -301,7 +335,7 @@ Device::prepareRead(int cpu, int index) uint64_t rxdone = vnic.RxDone; rxdone = set_RxDone_Packets(rxdone, rxFifo.countPacketsAfter(rxFifoPtr)); rxdone = set_RxDone_Empty(rxdone, rxFifo.empty()); - rxdone = set_RxDone_High(rxdone, rxFifo.size() > regs.RxFifoMark); + rxdone = set_RxDone_High(rxdone, rxFifo.size() > regs.RxFifoHigh); rxdone = set_RxDone_NotHigh(rxdone, rxLow); regs.RxData = vnic.RxData; regs.RxDone = rxdone; @@ -311,10 +345,23 @@ Device::prepareRead(int cpu, int index) uint64_t txdone = vnic.TxDone; txdone = set_TxDone_Packets(txdone, txFifo.packets()); txdone = set_TxDone_Full(txdone, txFifo.avail() < regs.TxMaxCopy); - txdone = set_TxDone_Low(txdone, txFifo.size() < regs.TxFifoMark); + txdone = set_TxDone_Low(txdone, txFifo.size() < regs.TxFifoLow); regs.TxData = vnic.TxData; regs.TxDone = txdone; regs.TxWait = txdone; + + int head = 0xffff; + + if (!rxFifo.empty()) { + int vnic = rxFifo.begin()->priv; + if (vnic != -1 && virtualRegs[vnic].rxPacketOffset > 0) + head = vnic; + } + + regs.RxStatus = set_RxStatus_Head(regs.RxStatus, head); + regs.RxStatus = set_RxStatus_Busy(regs.RxStatus, rxBusyCount); + regs.RxStatus = set_RxStatus_Mapped(regs.RxStatus, rxMappedCount); + regs.RxStatus = set_RxStatus_Dirty(regs.RxStatus, rxDirtyCount); } void @@ -332,7 +379,7 @@ Device::read(PacketPtr pkt) assert(config.command & PCI_CMD_MSE); assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]); - int cpu = pkt->req->getCpuNum(); + int cpu = pkt->req->contextId(); Addr daddr = pkt->getAddr() - BARAddrs[0]; Addr index = daddr >> Regs::VirtualShift; Addr raddr = daddr & Regs::VirtualMask; @@ -419,7 +466,7 @@ Device::write(PacketPtr pkt) assert(config.command & PCI_CMD_MSE); assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]); - int cpu = pkt->req->getCpuNum(); + int cpu = pkt->req->contextId(); Addr daddr = pkt->getAddr() - BARAddrs[0]; Addr index = daddr >> Regs::VirtualShift; Addr raddr = daddr & Regs::VirtualMask; @@ -473,22 +520,25 @@ Device::write(PacketPtr pkt) vnic.rxUnique = rxUnique++; vnic.RxDone = Regs::RxDone_Busy; vnic.RxData = pkt->get<uint64_t>(); + rxBusyCount++; if (Regs::get_RxData_Vaddr(pkt->get<uint64_t>())) { panic("vtophys not implemented in newmem"); -/* Addr vaddr = Regs::get_RxData_Addr(reg64); +#ifdef SINIC_VTOPHYS + Addr vaddr = Regs::get_RxData_Addr(reg64); Addr paddr = vtophys(req->xc, vaddr); DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d): " "vaddr=%#x, paddr=%#x\n", index, vnic.rxUnique, vaddr, paddr); - vnic.RxData = Regs::set_RxData_Addr(vnic.RxData, paddr);*/ + vnic.RxData = Regs::set_RxData_Addr(vnic.RxData, paddr); +#endif } else { DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d)\n", index, vnic.rxUnique); } - if (vnic.rxPacket == rxFifo.end()) { + if (vnic.rxIndex == rxFifo.end()) { DPRINTF(EthernetPIO, "request new packet...appending to rxList\n"); rxList.push_back(index); } else { @@ -512,15 +562,17 @@ Device::write(PacketPtr pkt) if (Regs::get_TxData_Vaddr(pkt->get<uint64_t>())) { panic("vtophys won't work here in newmem.\n"); - /*Addr vaddr = Regs::get_TxData_Addr(reg64); +#ifdef SINIC_VTOPHYS + Addr vaddr = Regs::get_TxData_Addr(reg64); Addr paddr = vtophys(req->xc, vaddr); - DPRINTF(EthernetPIO, "write TxData vnic %d (rxunique %d): " + DPRINTF(EthernetPIO, "write TxData vnic %d (txunique %d): " "vaddr=%#x, paddr=%#x\n", index, vnic.txUnique, vaddr, paddr); - vnic.TxData = Regs::set_TxData_Addr(vnic.TxData, paddr);*/ + vnic.TxData = Regs::set_TxData_Addr(vnic.TxData, paddr); +#endif } else { - DPRINTF(EthernetPIO, "write TxData vnic %d (rxunique %d)\n", + DPRINTF(EthernetPIO, "write TxData vnic %d (txunique %d)\n", index, vnic.txUnique); } @@ -643,7 +695,8 @@ Base::cpuIntrPost(Tick when) if (intrEvent) intrEvent->squash(); - intrEvent = new IntrEvent(this, intrTick, true); + intrEvent = new IntrEvent(this, true); + schedule(intrEvent, intrTick); } void @@ -761,18 +814,31 @@ Device::reset() regs.IntrMask = Intr_Soft | Intr_RxHigh | Intr_RxPacket | Intr_TxLow; regs.RxMaxCopy = params()->rx_max_copy; regs.TxMaxCopy = params()->tx_max_copy; - regs.RxMaxIntr = params()->rx_max_intr; + regs.ZeroCopySize = params()->zero_copy_size; + regs.ZeroCopyMark = params()->zero_copy_threshold; regs.VirtualCount = params()->virtual_count; + regs.RxMaxIntr = params()->rx_max_intr; regs.RxFifoSize = params()->rx_fifo_size; regs.TxFifoSize = params()->tx_fifo_size; - regs.RxFifoMark = params()->rx_fifo_threshold; - regs.TxFifoMark = params()->tx_fifo_threshold; + regs.RxFifoLow = params()->rx_fifo_low_mark; + regs.TxFifoLow = params()->tx_fifo_threshold; + regs.RxFifoHigh = params()->rx_fifo_threshold; + regs.TxFifoHigh = params()->tx_fifo_high_mark; regs.HwAddr = params()->hardware_address; + if (regs.RxMaxCopy < regs.ZeroCopyMark) + panic("Must be able to copy at least as many bytes as the threshold"); + + if (regs.ZeroCopySize >= regs.ZeroCopyMark) + panic("The number of bytes to copy must be less than the threshold"); + rxList.clear(); rxBusy.clear(); rxActive = -1; txList.clear(); + rxBusyCount = 0; + rxDirtyCount = 0; + rxMappedCount = 0; rxState = rxIdle; txState = txIdle; @@ -788,7 +854,7 @@ Device::reset() virtualRegs.clear(); virtualRegs.resize(size); for (int i = 0; i < size; ++i) - virtualRegs[i].rxPacket = rxFifo.end(); + virtualRegs[i].rxIndex = rxFifo.end(); } void @@ -822,6 +888,7 @@ Device::rxKick() } next: + rxFifo.check(); if (rxState == rxIdle) goto exit; @@ -845,12 +912,28 @@ Device::rxKick() int size = virtualRegs.size(); for (int i = 0; i < size; ++i) { VirtualReg *vn = &virtualRegs[i]; - if (vn->rxPacket != end && - !Regs::get_RxDone_Busy(vn->RxDone)) { + bool busy = Regs::get_RxDone_Busy(vn->RxDone); + if (vn->rxIndex != end) { + bool dirty = vn->rxPacketOffset > 0; + const char *status; + + if (busy && dirty) + status = "busy,dirty"; + else if (busy) + status = "busy"; + else if (dirty) + status = "dirty"; + else + status = "mapped"; + DPRINTF(EthernetSM, - "vnic %d (rxunique %d), has outstanding packet %d\n", - i, vn->rxUnique, - rxFifo.countPacketsBefore(vn->rxPacket)); + "vnic %d %s (rxunique %d), packet %d, slack %d\n", + i, status, vn->rxUnique, + rxFifo.countPacketsBefore(vn->rxIndex), + vn->rxIndex->slack); + } else if (busy) { + DPRINTF(EthernetSM, "vnic %d unmapped (rxunique %d)\n", + i, vn->rxUnique); } } } @@ -860,7 +943,7 @@ Device::rxKick() rxBusy.pop_front(); vnic = &virtualRegs[rxActive]; - if (vnic->rxPacket == rxFifo.end()) + if (vnic->rxIndex == rxFifo.end()) panic("continuing vnic without packet\n"); DPRINTF(EthernetSM, @@ -869,6 +952,14 @@ Device::rxKick() rxState = rxBeginCopy; + int vnic_distance = rxFifo.countPacketsBefore(vnic->rxIndex); + totalVnicDistance += vnic_distance; + numVnicDistance += 1; + if (vnic_distance > _maxVnicDistance) { + maxVnicDistance = vnic_distance; + _maxVnicDistance = vnic_distance; + } + break; } @@ -891,14 +982,16 @@ Device::rxKick() rxActive, vnic->rxUnique); // Grab a new packet from the fifo. - vnic->rxPacket = rxFifoPtr++; + vnic->rxIndex = rxFifoPtr++; + vnic->rxIndex->priv = rxActive; vnic->rxPacketOffset = 0; - vnic->rxPacketBytes = (*vnic->rxPacket)->length; + vnic->rxPacketBytes = vnic->rxIndex->packet->length; assert(vnic->rxPacketBytes); + rxMappedCount++; vnic->rxDoneData = 0; /* scope for variables */ { - IpPtr ip(*vnic->rxPacket); + IpPtr ip(vnic->rxIndex->packet); if (ip) { DPRINTF(Ethernet, "ID is %d\n", ip->id()); vnic->rxDoneData |= Regs::RxDone_IpPacket; @@ -939,16 +1032,26 @@ Device::rxKick() rxDmaAddr = params()->platform->pciToDma( Regs::get_RxData_Addr(vnic->RxData)); - rxDmaLen = std::min<int>(Regs::get_RxData_Len(vnic->RxData), + rxDmaLen = min<int>(Regs::get_RxData_Len(vnic->RxData), vnic->rxPacketBytes); - rxDmaData = (*vnic->rxPacket)->data + vnic->rxPacketOffset; + + /* + * if we're doing zero/delay copy and we're below the fifo + * threshold, see if we should try to do the zero/defer copy + */ + if ((Regs::get_Config_ZeroCopy(regs.Config) || + Regs::get_Config_DelayCopy(regs.Config)) && + !Regs::get_RxData_NoDelay(vnic->RxData) && rxLow) { + if (rxDmaLen > regs.ZeroCopyMark) + rxDmaLen = regs.ZeroCopySize; + } + rxDmaData = vnic->rxIndex->packet->data + vnic->rxPacketOffset; rxState = rxCopy; if (rxDmaAddr == 1LL) { rxState = rxCopyDone; break; } - dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaEvent, rxDmaData); break; @@ -959,17 +1062,25 @@ Device::rxKick() case rxCopyDone: vnic->RxDone = vnic->rxDoneData; vnic->RxDone |= Regs::RxDone_Complete; + rxBusyCount--; if (vnic->rxPacketBytes == rxDmaLen) { + if (vnic->rxPacketOffset) + rxDirtyCount--; + // Packet is complete. Indicate how many bytes were copied vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone, rxDmaLen); DPRINTF(EthernetSM, "rxKick: packet complete on vnic %d (rxunique %d)\n", rxActive, vnic->rxUnique); - rxFifo.remove(vnic->rxPacket); - vnic->rxPacket = rxFifo.end(); + rxFifo.remove(vnic->rxIndex); + vnic->rxIndex = rxFifo.end(); + rxMappedCount--; } else { + if (!vnic->rxPacketOffset) + rxDirtyCount++; + vnic->rxPacketBytes -= rxDmaLen; vnic->rxPacketOffset += rxDmaLen; vnic->RxDone |= Regs::RxDone_More; @@ -989,10 +1100,10 @@ Device::rxKick() rxEmpty = true; } - if (rxFifo.size() < params()->rx_fifo_low_mark) + if (rxFifo.size() < regs.RxFifoLow) rxLow = true; - if (rxFifo.size() > params()->rx_fifo_threshold) + if (rxFifo.size() > regs.RxFifoHigh) rxLow = false; devIntrPost(Regs::Intr_RxDMA); @@ -1044,7 +1155,7 @@ Device::transmit() if (!interface->sendPacket(packet)) { DPRINTF(Ethernet, "Packet Transmit: failed txFifo available %d\n", txFifo.avail()); - goto reschedule; + return; } txFifo.pop(); @@ -1072,15 +1183,9 @@ Device::transmit() txFifo.avail()); interrupts = Regs::Intr_TxPacket; - if (txFifo.size() < regs.TxFifoMark) + if (txFifo.size() < regs.TxFifoLow) interrupts |= Regs::Intr_TxLow; devIntrPost(interrupts); - - reschedule: - if (!txFifo.empty() && !txEvent.scheduled()) { - DPRINTF(Ethernet, "reschedule transmit\n"); - txEvent.schedule(curTick + retryTime); - } } void @@ -1211,7 +1316,7 @@ Device::transferDone() DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n"); - txEvent.reschedule(curTick + ticks(1), true); + reschedule(txEvent, curTick + ticks(1), true); } bool @@ -1278,7 +1383,7 @@ Device::recvPacket(EthPacketPtr packet) return true; } - if (rxFifo.size() >= regs.RxFifoMark) + if (rxFifo.size() >= regs.RxFifoHigh) devIntrPost(Regs::Intr_RxHigh); if (!rxFifo.push(packet)) { @@ -1351,7 +1456,8 @@ Base::unserialize(Checkpoint *cp, const std::string §ion) Tick intrEventTick; UNSERIALIZE_SCALAR(intrEventTick); if (intrEventTick) { - intrEvent = new IntrEvent(this, intrEventTick, true); + intrEvent = new IntrEvent(this, true); + schedule(intrEvent, intrEventTick); } } @@ -1372,19 +1478,13 @@ Device::serialize(std::ostream &os) TxStateStrings[txState]); /* - * Serialize the device registers + * Serialize the device registers that could be modified by the OS. */ SERIALIZE_SCALAR(regs.Config); SERIALIZE_SCALAR(regs.IntrStatus); SERIALIZE_SCALAR(regs.IntrMask); - SERIALIZE_SCALAR(regs.RxMaxCopy); - SERIALIZE_SCALAR(regs.TxMaxCopy); - SERIALIZE_SCALAR(regs.RxMaxIntr); - SERIALIZE_SCALAR(regs.VirtualCount); SERIALIZE_SCALAR(regs.RxData); - SERIALIZE_SCALAR(regs.RxDone); SERIALIZE_SCALAR(regs.TxData); - SERIALIZE_SCALAR(regs.TxDone); /* * Serialize the virtual nic state @@ -1400,12 +1500,12 @@ Device::serialize(std::ostream &os) paramOut(os, reg + ".TxData", vnic->TxData); paramOut(os, reg + ".TxDone", vnic->TxDone); - bool rxPacketExists = vnic->rxPacket != rxFifo.end(); + bool rxPacketExists = vnic->rxIndex != rxFifo.end(); paramOut(os, reg + ".rxPacketExists", rxPacketExists); if (rxPacketExists) { int rxPacket = 0; PacketFifo::iterator i = rxFifo.begin(); - while (i != vnic->rxPacket) { + while (i != vnic->rxIndex) { assert(i != rxFifo.end()); ++i; ++rxPacket; @@ -1418,10 +1518,15 @@ Device::serialize(std::ostream &os) paramOut(os, reg + ".rxDoneData", vnic->rxDoneData); } - int rxFifoPtr = rxFifo.countPacketsBefore(this->rxFifoPtr); + int rxFifoPtr = -1; + if (this->rxFifoPtr != rxFifo.end()) + rxFifoPtr = rxFifo.countPacketsBefore(this->rxFifoPtr); SERIALIZE_SCALAR(rxFifoPtr); SERIALIZE_SCALAR(rxActive); + SERIALIZE_SCALAR(rxBusyCount); + SERIALIZE_SCALAR(rxDirtyCount); + SERIALIZE_SCALAR(rxMappedCount); VirtualList::iterator i, end; for (count = 0, i = rxList.begin(), end = rxList.end(); i != end; ++i) @@ -1478,21 +1583,18 @@ Device::unserialize(Checkpoint *cp, const std::string §ion) Base::unserialize(cp, section); /* - * Unserialize the device registers + * Unserialize the device registers that may have been written by the OS. */ UNSERIALIZE_SCALAR(regs.Config); UNSERIALIZE_SCALAR(regs.IntrStatus); UNSERIALIZE_SCALAR(regs.IntrMask); - UNSERIALIZE_SCALAR(regs.RxMaxCopy); - UNSERIALIZE_SCALAR(regs.TxMaxCopy); - UNSERIALIZE_SCALAR(regs.RxMaxIntr); - UNSERIALIZE_SCALAR(regs.VirtualCount); UNSERIALIZE_SCALAR(regs.RxData); - UNSERIALIZE_SCALAR(regs.RxDone); UNSERIALIZE_SCALAR(regs.TxData); - UNSERIALIZE_SCALAR(regs.TxDone); UNSERIALIZE_SCALAR(rxActive); + UNSERIALIZE_SCALAR(rxBusyCount); + UNSERIALIZE_SCALAR(rxDirtyCount); + UNSERIALIZE_SCALAR(rxMappedCount); int rxListSize; UNSERIALIZE_SCALAR(rxListSize); @@ -1533,9 +1635,13 @@ Device::unserialize(Checkpoint *cp, const std::string §ion) int rxFifoPtr; UNSERIALIZE_SCALAR(rxFifoPtr); - this->rxFifoPtr = rxFifo.begin(); - for (int i = 0; i < rxFifoPtr; ++i) - ++this->rxFifoPtr; + if (rxFifoPtr >= 0) { + this->rxFifoPtr = rxFifo.begin(); + for (int i = 0; i < rxFifoPtr; ++i) + ++this->rxFifoPtr; + } else { + this->rxFifoPtr = rxFifo.end(); + } /* * Unserialize tx state machine @@ -1582,15 +1688,15 @@ Device::unserialize(Checkpoint *cp, const std::string §ion) if (rxPacketExists) { int rxPacket; paramIn(cp, section, reg + ".rxPacket", rxPacket); - vnic->rxPacket = rxFifo.begin(); + vnic->rxIndex = rxFifo.begin(); while (rxPacket--) - ++vnic->rxPacket; + ++vnic->rxIndex; paramIn(cp, section, reg + ".rxPacketOffset", vnic->rxPacketOffset); paramIn(cp, section, reg + ".rxPacketBytes", vnic->rxPacketBytes); } else { - vnic->rxPacket = rxFifo.end(); + vnic->rxIndex = rxFifo.end(); } paramIn(cp, section, reg + ".rxDoneData", vnic->rxDoneData); } @@ -1601,7 +1707,7 @@ Device::unserialize(Checkpoint *cp, const std::string §ion) Tick transmitTick; UNSERIALIZE_SCALAR(transmitTick); if (transmitTick) - txEvent.schedule(curTick + transmitTick); + schedule(txEvent, curTick + transmitTick); pioPort->sendStatusChange(Port::RangeChange); diff --git a/src/dev/sinic.hh b/src/dev/sinic.hh index e85d93fe4..cd8412ef4 100644 --- a/src/dev/sinic.hh +++ b/src/dev/sinic.hh @@ -115,19 +115,24 @@ class Device : public Base uint32_t IntrMask; // 0x0c uint32_t RxMaxCopy; // 0x10 uint32_t TxMaxCopy; // 0x14 - uint32_t RxMaxIntr; // 0x18 - uint32_t VirtualCount; // 0x1c - uint32_t RxFifoSize; // 0x20 - uint32_t TxFifoSize; // 0x24 - uint32_t RxFifoMark; // 0x28 - uint32_t TxFifoMark; // 0x2c - uint64_t RxData; // 0x30 - uint64_t RxDone; // 0x38 - uint64_t RxWait; // 0x40 - uint64_t TxData; // 0x48 - uint64_t TxDone; // 0x50 - uint64_t TxWait; // 0x58 - uint64_t HwAddr; // 0x60 + uint32_t ZeroCopySize; // 0x18 + uint32_t ZeroCopyMark; // 0x1c + uint32_t VirtualCount; // 0x20 + uint32_t RxMaxIntr; // 0x24 + uint32_t RxFifoSize; // 0x28 + uint32_t TxFifoSize; // 0x2c + uint32_t RxFifoLow; // 0x30 + uint32_t TxFifoLow; // 0x34 + uint32_t RxFifoHigh; // 0x38 + uint32_t TxFifoHigh; // 0x3c + uint64_t RxData; // 0x40 + uint64_t RxDone; // 0x48 + uint64_t RxWait; // 0x50 + uint64_t TxData; // 0x58 + uint64_t TxDone; // 0x60 + uint64_t TxWait; // 0x68 + uint64_t HwAddr; // 0x70 + uint64_t RxStatus; // 0x78 } regs; struct VirtualReg { @@ -136,7 +141,7 @@ class Device : public Base uint64_t TxData; uint64_t TxDone; - PacketFifo::iterator rxPacket; + PacketFifo::iterator rxIndex; int rxPacketOffset; int rxPacketBytes; uint64_t rxDoneData; @@ -159,6 +164,10 @@ class Device : public Base int rxActive; VirtualList txList; + int rxBusyCount; + int rxMappedCount; + int rxDirtyCount; + uint8_t ®Data8(Addr daddr) { return *((uint8_t *)®s + daddr); } uint32_t ®Data32(Addr daddr) { return *(uint32_t *)®Data8(daddr); } uint64_t ®Data64(Addr daddr) { return *(uint64_t *)®Data8(daddr); } @@ -274,34 +283,42 @@ class Device : public Base * Statistics */ private: - Stats::Scalar<> rxBytes; + Stats::Scalar rxBytes; Stats::Formula rxBandwidth; - Stats::Scalar<> rxPackets; + Stats::Scalar rxPackets; Stats::Formula rxPacketRate; - Stats::Scalar<> rxIpPackets; - Stats::Scalar<> rxTcpPackets; - Stats::Scalar<> rxUdpPackets; - Stats::Scalar<> rxIpChecksums; - Stats::Scalar<> rxTcpChecksums; - Stats::Scalar<> rxUdpChecksums; - - Stats::Scalar<> txBytes; + Stats::Scalar rxIpPackets; + Stats::Scalar rxTcpPackets; + Stats::Scalar rxUdpPackets; + Stats::Scalar rxIpChecksums; + Stats::Scalar rxTcpChecksums; + Stats::Scalar rxUdpChecksums; + + Stats::Scalar txBytes; Stats::Formula txBandwidth; Stats::Formula totBandwidth; Stats::Formula totPackets; Stats::Formula totBytes; Stats::Formula totPacketRate; - Stats::Scalar<> txPackets; + Stats::Scalar txPackets; Stats::Formula txPacketRate; - Stats::Scalar<> txIpPackets; - Stats::Scalar<> txTcpPackets; - Stats::Scalar<> txUdpPackets; - Stats::Scalar<> txIpChecksums; - Stats::Scalar<> txTcpChecksums; - Stats::Scalar<> txUdpChecksums; + Stats::Scalar txIpPackets; + Stats::Scalar txTcpPackets; + Stats::Scalar txUdpPackets; + Stats::Scalar txIpChecksums; + Stats::Scalar txTcpChecksums; + Stats::Scalar txUdpChecksums; + + Stats::Scalar totalVnicDistance; + Stats::Scalar numVnicDistance; + Stats::Scalar maxVnicDistance; + Stats::Formula avgVnicDistance; + + int _maxVnicDistance; public: virtual void regStats(); + virtual void resetStats(); /** * Serialization stuff diff --git a/src/dev/sinicreg.hh b/src/dev/sinicreg.hh index de4188145..7ac7abad0 100644 --- a/src/dev/sinicreg.hh +++ b/src/dev/sinicreg.hh @@ -48,7 +48,7 @@ static const uint64_t NAME##_width = WIDTH; \ static const uint64_t NAME##_offset = OFFSET; \ static const uint64_t NAME##_mask = (ULL(1) << WIDTH) - 1; \ - static const uint64_t NAME = ((ULL(1) << WIDTH) - 1) << OFFSET; \ + static const uint64_t NAME = ((ULL(1) << WIDTH) - 1) << OFFSET; \ static inline uint64_t get_##NAME(uint64_t reg) \ { return (reg & NAME) >> OFFSET; } \ static inline uint64_t set_##NAME(uint64_t reg, uint64_t val) \ @@ -67,20 +67,25 @@ __SINIC_REG32(IntrStatus, 0x08); // 32: interrupt status __SINIC_REG32(IntrMask, 0x0c); // 32: interrupt mask __SINIC_REG32(RxMaxCopy, 0x10); // 32: max bytes per rx copy __SINIC_REG32(TxMaxCopy, 0x14); // 32: max bytes per tx copy -__SINIC_REG32(RxMaxIntr, 0x18); // 32: max receives per interrupt -__SINIC_REG32(VirtualCount, 0x1c); // 32: number of virutal NICs -__SINIC_REG32(RxFifoSize, 0x20); // 32: rx fifo capacity in bytes -__SINIC_REG32(TxFifoSize, 0x24); // 32: tx fifo capacity in bytes -__SINIC_REG32(RxFifoMark, 0x28); // 32: rx fifo high watermark -__SINIC_REG32(TxFifoMark, 0x2c); // 32: tx fifo low watermark -__SINIC_REG32(RxData, 0x30); // 64: receive data -__SINIC_REG32(RxDone, 0x38); // 64: receive done -__SINIC_REG32(RxWait, 0x40); // 64: receive done (busy wait) -__SINIC_REG32(TxData, 0x48); // 64: transmit data -__SINIC_REG32(TxDone, 0x50); // 64: transmit done -__SINIC_REG32(TxWait, 0x58); // 64: transmit done (busy wait) -__SINIC_REG32(HwAddr, 0x60); // 64: mac address -__SINIC_REG32(Size, 0x68); // register addres space size +__SINIC_REG32(ZeroCopySize, 0x18); // 32: bytes to copy if below threshold +__SINIC_REG32(ZeroCopyMark, 0x1c); // 32: only zero-copy above this threshold +__SINIC_REG32(VirtualCount, 0x20); // 32: number of virutal NICs +__SINIC_REG32(RxMaxIntr, 0x24); // 32: max receives per interrupt +__SINIC_REG32(RxFifoSize, 0x28); // 32: rx fifo capacity in bytes +__SINIC_REG32(TxFifoSize, 0x2c); // 32: tx fifo capacity in bytes +__SINIC_REG32(RxFifoLow, 0x30); // 32: rx fifo low watermark +__SINIC_REG32(TxFifoLow, 0x34); // 32: tx fifo low watermark +__SINIC_REG32(RxFifoHigh, 0x38); // 32: rx fifo high watermark +__SINIC_REG32(TxFifoHigh, 0x3c); // 32: tx fifo high watermark +__SINIC_REG32(RxData, 0x40); // 64: receive data +__SINIC_REG32(RxDone, 0x48); // 64: receive done +__SINIC_REG32(RxWait, 0x50); // 64: receive done (busy wait) +__SINIC_REG32(TxData, 0x58); // 64: transmit data +__SINIC_REG32(TxDone, 0x60); // 64: transmit done +__SINIC_REG32(TxWait, 0x68); // 64: transmit done (busy wait) +__SINIC_REG32(HwAddr, 0x70); // 64: mac address +__SINIC_REG32(RxStatus, 0x78); +__SINIC_REG32(Size, 0x80); // register addres space size // Config register bits __SINIC_VAL32(Config_ZeroCopy, 12, 1); // enable zero copy @@ -116,9 +121,10 @@ __SINIC_REG32(Intr_NoDelay, 0x01cc); // interrupts that aren't coalesced __SINIC_REG32(Intr_Res, ~0x01ff); // reserved interrupt bits // RX Data Description -__SINIC_VAL64(RxData_Vaddr, 60, 1); // Addr is virtual -__SINIC_VAL64(RxData_Len, 40, 20); // 0 - 256k -__SINIC_VAL64(RxData_Addr, 0, 40); // Address 1TB +__SINIC_VAL64(RxData_NoDelay, 61, 1); // Don't Delay this copy +__SINIC_VAL64(RxData_Vaddr, 60, 1); // Addr is virtual +__SINIC_VAL64(RxData_Len, 40, 20); // 0 - 256k +__SINIC_VAL64(RxData_Addr, 0, 40); // Address 1TB // TX Data Description __SINIC_VAL64(TxData_More, 63, 1); // Packet not complete (will dma more) @@ -159,6 +165,11 @@ __SINIC_VAL64(TxDone_Res6, 21, 1); // reserved __SINIC_VAL64(TxDone_Res7, 20, 1); // reserved __SINIC_VAL64(TxDone_CopyLen, 0, 20); // up to 256k +__SINIC_VAL64(RxStatus_Dirty, 48, 16); +__SINIC_VAL64(RxStatus_Mapped, 32, 16); +__SINIC_VAL64(RxStatus_Busy, 16, 16); +__SINIC_VAL64(RxStatus_Head, 0, 16); + struct Info { uint8_t size; @@ -174,31 +185,37 @@ regInfo(Addr daddr) { static Regs::Info invalid = { 0, false, false, "invalid" }; static Regs::Info info [] = { - { 4, true, true, "Config" }, - { 4, false, true, "Command" }, - { 4, true, true, "IntrStatus" }, - { 4, true, true, "IntrMask" }, - { 4, true, false, "RxMaxCopy" }, - { 4, true, false, "TxMaxCopy" }, - { 4, true, false, "RxMaxIntr" }, - { 4, true, false, "VirtualCount" }, - { 4, true, false, "RxFifoSize" }, - { 4, true, false, "TxFifoSize" }, - { 4, true, false, "RxFifoMark" }, - { 4, true, false, "TxFifoMark" }, - { 8, true, true, "RxData" }, + { 4, true, true, "Config" }, + { 4, false, true, "Command" }, + { 4, true, true, "IntrStatus" }, + { 4, true, true, "IntrMask" }, + { 4, true, false, "RxMaxCopy" }, + { 4, true, false, "TxMaxCopy" }, + { 4, true, false, "ZeroCopySize" }, + { 4, true, false, "ZeroCopyMark" }, + { 4, true, false, "VirtualCount" }, + { 4, true, false, "RxMaxIntr" }, + { 4, true, false, "RxFifoSize" }, + { 4, true, false, "TxFifoSize" }, + { 4, true, false, "RxFifoLow" }, + { 4, true, false, "TxFifoLow" }, + { 4, true, false, "RxFifoHigh" }, + { 4, true, false, "TxFifoHigh" }, + { 8, true, true, "RxData" }, + invalid, + { 8, true, false, "RxDone" }, invalid, - { 8, true, false, "RxDone" }, + { 8, true, false, "RxWait" }, invalid, - { 8, true, false, "RxWait" }, + { 8, true, true, "TxData" }, invalid, - { 8, true, true, "TxData" }, + { 8, true, false, "TxDone" }, invalid, - { 8, true, false, "TxDone" }, + { 8, true, false, "TxWait" }, invalid, - { 8, true, false, "TxWait" }, + { 8, true, false, "HwAddr" }, invalid, - { 8, true, false, "HwAddr" }, + { 8, true, false, "RxStatus" }, invalid, }; diff --git a/src/dev/sparc/T1000.py b/src/dev/sparc/T1000.py index a033e27e2..cbf390737 100644 --- a/src/dev/sparc/T1000.py +++ b/src/dev/sparc/T1000.py @@ -29,9 +29,9 @@ from m5.params import * from m5.proxy import * from Device import BasicPioDevice, PioDevice, IsaFake, BadAddr -from Uart import Uart8250 from Platform import Platform -from SimConsole import SimConsole +from Terminal import Terminal +from Uart import Uart8250 class MmDisk(BasicPioDevice): @@ -98,11 +98,11 @@ class T1000(Platform): fake_ssi = IsaFake(pio_addr=0xff00000000, pio_size=0x10000000) #warn_access="Accessing SSI -- Unimplemented!") - hconsole = SimConsole() + hterm = Terminal() hvuart = Uart8250(pio_addr=0xfff0c2c000) htod = DumbTOD() - pconsole = SimConsole() + pterm = Terminal() puart0 = Uart8250(pio_addr=0x1f10000000) iob = Iob() @@ -116,8 +116,8 @@ class T1000(Platform): # earlier, since the bus object itself is typically defined at the # System level. def attachIO(self, bus): - self.hvuart.sim_console = self.hconsole - self.puart0.sim_console = self.pconsole + self.hvuart.terminal = self.hterm + self.puart0.terminal = self.pterm self.fake_clk.pio = bus.port self.fake_membnks.pio = bus.port self.fake_l2_1.pio = bus.port diff --git a/src/dev/sparc/iob.cc b/src/dev/sparc/iob.cc index 6608fc64a..4543dd07b 100644 --- a/src/dev/sparc/iob.cc +++ b/src/dev/sparc/iob.cc @@ -120,7 +120,7 @@ void Iob::readJBus(PacketPtr pkt) { Addr accessAddr = pkt->getAddr() - iobJBusAddr; - int cpuid = pkt->req->getCpuNum(); + int cpuid = pkt->req->contextId(); int index; uint64_t data; @@ -235,7 +235,7 @@ void Iob::writeJBus(PacketPtr pkt) { Addr accessAddr = pkt->getAddr() - iobJBusAddr; - int cpuid = pkt->req->getCpuNum(); + int cpuid = pkt->req->contextId(); int index; uint64_t data; @@ -276,7 +276,7 @@ void Iob::generateIpi(Type type, int cpu_id, int vector) { SparcISA::SparcFault<SparcISA::PowerOnReset> *por = new SparcISA::PowerOnReset(); - if (cpu_id >= sys->getNumCPUs()) + if (cpu_id >= sys->numContexts()) return; switch (type) { diff --git a/src/dev/sparc/t1000.cc b/src/dev/sparc/t1000.cc index 49e44af55..88fb358ef 100644 --- a/src/dev/sparc/t1000.cc +++ b/src/dev/sparc/t1000.cc @@ -37,8 +37,8 @@ #include <vector> #include "cpu/intr_control.hh" -#include "dev/simconsole.hh" #include "dev/sparc/t1000.hh" +#include "dev/terminal.hh" #include "sim/system.hh" using namespace std; @@ -94,7 +94,21 @@ T1000::pciToDma(Addr pciAddr) const Addr -T1000::calcConfigAddr(int bus, int dev, int func) +T1000::calcPciConfigAddr(int bus, int dev, int func) +{ + panic("Need implementation\n"); + M5_DUMMY_RETURN +} + +Addr +T1000::calcPciIOAddr(Addr addr) +{ + panic("Need implementation\n"); + M5_DUMMY_RETURN +} + +Addr +T1000::calcPciMemAddr(Addr addr) { panic("Need implementation\n"); M5_DUMMY_RETURN diff --git a/src/dev/sparc/t1000.hh b/src/dev/sparc/t1000.hh index 76de0a550..01ff3d319 100644 --- a/src/dev/sparc/t1000.hh +++ b/src/dev/sparc/t1000.hh @@ -91,7 +91,17 @@ class T1000 : public Platform /** * Calculate the configuration address given a bus/dev/func. */ - virtual Addr calcConfigAddr(int bus, int dev, int func); + virtual Addr calcPciConfigAddr(int bus, int dev, int func); + + /** + * Calculate the address for an IO location on the PCI bus. + */ + virtual Addr calcPciIOAddr(Addr addr); + + /** + * Calculate the address for a memory location on the PCI bus. + */ + virtual Addr calcPciMemAddr(Addr addr); }; #endif // __DEV_T1000_HH__ diff --git a/src/dev/simconsole.cc b/src/dev/terminal.cc index e8dc1b210..fba0c6130 100644 --- a/src/dev/simconsole.cc +++ b/src/dev/terminal.cc @@ -30,27 +30,28 @@ */ /* @file - * Implements the user interface to a serial console + * Implements the user interface to a serial terminal */ #include <sys/ioctl.h> #include <sys/termios.h> -#include <sys/types.h> #include <errno.h> #include <poll.h> #include <unistd.h> +#include <cctype> #include <iostream> #include <fstream> #include <sstream> #include <string> +#include "base/atomicio.hh" #include "base/misc.hh" #include "base/output.hh" #include "base/socket.hh" #include "base/trace.hh" #include "dev/platform.hh" -#include "dev/simconsole.hh" +#include "dev/terminal.hh" #include "dev/uart.hh" using namespace std; @@ -59,50 +60,46 @@ using namespace std; /* * Poll event for the listen socket */ -SimConsole::ListenEvent::ListenEvent(SimConsole *c, int fd, int e) - : PollEvent(fd, e), cons(c) +Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e) + : PollEvent(fd, e), term(t) { } void -SimConsole::ListenEvent::process(int revent) +Terminal::ListenEvent::process(int revent) { - cons->accept(); + term->accept(); } /* * Poll event for the data socket */ -SimConsole::DataEvent::DataEvent(SimConsole *c, int fd, int e) - : PollEvent(fd, e), cons(c) +Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e) + : PollEvent(fd, e), term(t) { } void -SimConsole::DataEvent::process(int revent) +Terminal::DataEvent::process(int revent) { if (revent & POLLIN) - cons->data(); + term->data(); else if (revent & POLLNVAL) - cons->detach(); + term->detach(); } /* - * SimConsole code + * Terminal code */ -SimConsole::SimConsole(const Params *p) +Terminal::Terminal(const Params *p) : SimObject(p), listenEvent(NULL), dataEvent(NULL), number(p->number), data_fd(-1), txbuf(16384), rxbuf(16384), outfile(NULL) #if TRACING_ON == 1 , linebuf(16384) #endif { - if (!p->output.empty()) { - if (p->append_name) - outfile = simout.find(p->output + "." + p->name); - else - outfile = simout.find(p->output); - + if (p->output) { + outfile = simout.find(p->name); outfile->setf(ios::unitbuf); } @@ -110,7 +107,7 @@ SimConsole::SimConsole(const Params *p) listen(p->port); } -SimConsole::~SimConsole() +Terminal::~Terminal() { if (data_fd != -1) ::close(data_fd); @@ -123,15 +120,20 @@ SimConsole::~SimConsole() } /////////////////////////////////////////////////////////////////////// -// socket creation and console attach +// socket creation and terminal attach // void -SimConsole::listen(int port) +Terminal::listen(int port) { + if (ListenSocket::allDisabled()) { + warn_once("Sockets disabled, not accepting terminal connections"); + return; + } + while (!listener.listen(port, true)) { - DPRINTF(Console, - ": can't bind address console port %d inuse PID %d\n", + DPRINTF(Terminal, + ": can't bind address terminal port %d inuse PID %d\n", port, getpid()); port++; } @@ -147,15 +149,15 @@ SimConsole::listen(int port) } void -SimConsole::accept() +Terminal::accept() { if (!listener.islistening()) panic("%s: cannot accept a connection if not listening!", name()); int fd = listener.accept(true); if (data_fd != -1) { - char message[] = "console already attached!\n"; - ::write(fd, message, sizeof(message)); + char message[] = "terminal already attached!\n"; + atomic_write(fd, message, sizeof(message)); ::close(fd); return; } @@ -165,7 +167,7 @@ SimConsole::accept() pollQueue.schedule(dataEvent); stringstream stream; - ccprintf(stream, "==== m5 slave console: Console %d ====", number); + ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number); // we need an actual carriage return followed by a newline for the // terminal @@ -173,13 +175,13 @@ SimConsole::accept() write((const uint8_t *)stream.str().c_str(), stream.str().size()); - DPRINTFN("attach console %d\n", number); + DPRINTFN("attach terminal %d\n", number); txbuf.readall(data_fd); } void -SimConsole::detach() +Terminal::detach() { if (data_fd != -1) { ::close(data_fd); @@ -190,11 +192,11 @@ SimConsole::detach() delete dataEvent; dataEvent = NULL; - DPRINTFN("detach console %d\n", number); + DPRINTFN("detach terminal %d\n", number); } void -SimConsole::data() +Terminal::data() { uint8_t buf[1024]; int len; @@ -208,10 +210,10 @@ SimConsole::data() } size_t -SimConsole::read(uint8_t *buf, size_t len) +Terminal::read(uint8_t *buf, size_t len) { if (data_fd < 0) - panic("Console not properly attached.\n"); + panic("Terminal not properly attached.\n"); size_t ret; do { @@ -230,23 +232,16 @@ SimConsole::read(uint8_t *buf, size_t len) return ret; } -// Console output. +// Terminal output. size_t -SimConsole::write(const uint8_t *buf, size_t len) +Terminal::write(const uint8_t *buf, size_t len) { if (data_fd < 0) - panic("Console not properly attached.\n"); + panic("Terminal not properly attached.\n"); - size_t ret; - for (;;) { - ret = ::write(data_fd, buf, len); - - if (ret >= 0) - break; - - if (errno != EINTR) - detach(); - } + ssize_t ret = atomic_write(data_fd, buf, len); + if (ret < len) + detach(); return ret; } @@ -257,7 +252,7 @@ SimConsole::write(const uint8_t *buf, size_t len) #define RECEIVE_ERROR (ULL(3) << 62) uint8_t -SimConsole::in() +Terminal::in() { bool empty; uint8_t c; @@ -268,14 +263,14 @@ SimConsole::in() empty = rxbuf.empty(); - DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d\n", + DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n", isprint(c) ? c : ' ', c, !empty); return c; } uint64_t -SimConsole::console_in() +Terminal::console_in() { uint64_t value; @@ -287,26 +282,25 @@ SimConsole::console_in() value = RECEIVE_NONE; } - DPRINTF(ConsoleVerbose, "console_in: return: %#x\n", value); + DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value); return value; } void -SimConsole::out(char c) +Terminal::out(char c) { #if TRACING_ON == 1 - if (DTRACE(Console)) { + if (DTRACE(Terminal)) { static char last = '\0'; - if (c != '\n' && c != '\r' || - last != '\n' && last != '\r') { + if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) { if (c == '\n' || c == '\r') { int size = linebuf.size(); char *buffer = new char[size + 1]; linebuf.read(buffer, size); buffer[size] = '\0'; - DPRINTF(Console, "%s\n", buffer); + DPRINTF(Terminal, "%s\n", buffer); delete [] buffer; } else { linebuf.write(c); @@ -325,13 +319,13 @@ SimConsole::out(char c) if (outfile) outfile->write(&c, 1); - DPRINTF(ConsoleVerbose, "out: \'%c\' %#02x\n", + DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n", isprint(c) ? c : ' ', (int)c); } -SimConsole * -SimConsoleParams::create() +Terminal * +TerminalParams::create() { - return new SimConsole(this); + return new Terminal(this); } diff --git a/src/dev/simconsole.hh b/src/dev/terminal.hh index c8d453960..d2499b6b2 100644 --- a/src/dev/simconsole.hh +++ b/src/dev/terminal.hh @@ -30,11 +30,11 @@ */ /* @file - * User Console Interface + * User Terminal Interface */ -#ifndef __CONSOLE_HH__ -#define __CONSOLE_HH__ +#ifndef __DEV_TERMINAL_HH__ +#define __DEV_TERMINAL_HH__ #include <iostream> @@ -43,12 +43,12 @@ #include "base/pollevent.hh" #include "base/socket.hh" #include "sim/sim_object.hh" -#include "params/SimConsole.hh" +#include "params/Terminal.hh" -class ConsoleListener; +class TerminalListener; class Uart; -class SimConsole : public SimObject +class Terminal : public SimObject { public: Uart *uart; @@ -57,10 +57,10 @@ class SimConsole : public SimObject class ListenEvent : public PollEvent { protected: - SimConsole *cons; + Terminal *term; public: - ListenEvent(SimConsole *c, int fd, int e); + ListenEvent(Terminal *t, int fd, int e); void process(int revent); }; @@ -70,10 +70,10 @@ class SimConsole : public SimObject class DataEvent : public PollEvent { protected: - SimConsole *cons; + Terminal *term; public: - DataEvent(SimConsole *c, int fd, int e); + DataEvent(Terminal *t, int fd, int e); void process(int revent); }; @@ -85,9 +85,9 @@ class SimConsole : public SimObject int data_fd; public: - typedef SimConsoleParams Params; - SimConsole(const Params *p); - ~SimConsole(); + typedef TerminalParams Params; + Terminal(const Params *p); + ~Terminal(); protected: ListenSocket listener; @@ -119,10 +119,10 @@ class SimConsole : public SimObject ///////////////// // OS interface - // Get a character from the console. + // Get a character from the terminal. uint8_t in(); - // get a character from the console in the console specific format + // get a character from the terminal in the console specific format // corresponds to GETC: // retval<63:61> // 000: success: character received @@ -136,11 +136,11 @@ class SimConsole : public SimObject // Interrupts are cleared when the buffer is empty. uint64_t console_in(); - // Send a character to the console + // Send a character to the terminal void out(char c); - //Ask the console if data is available + // Ask the terminal if data is available bool dataAvailable() { return !rxbuf.empty(); } }; -#endif // __CONSOLE_HH__ +#endif // __DEV_TERMINAL_HH__ diff --git a/src/dev/uart.cc b/src/dev/uart.cc index c9a2ae964..ab0ebde2c 100644 --- a/src/dev/uart.cc +++ b/src/dev/uart.cc @@ -32,17 +32,17 @@ * Implements a base class for UARTs */ -#include "dev/simconsole.hh" -#include "dev/uart.hh" #include "dev/platform.hh" +#include "dev/terminal.hh" +#include "dev/uart.hh" using namespace std; Uart::Uart(const Params *p) - : BasicPioDevice(p), platform(p->platform), cons(p->sim_console) + : BasicPioDevice(p), platform(p->platform), term(p->terminal) { status = 0; // set back pointers - cons->uart = this; + term->uart = this; } diff --git a/src/dev/uart.hh b/src/dev/uart.hh index f5d5e2855..ba10c204c 100644 --- a/src/dev/uart.hh +++ b/src/dev/uart.hh @@ -39,7 +39,7 @@ #include "dev/io_device.hh" #include "params/Uart.hh" -class SimConsole; +class Terminal; class Platform; const int RX_INT = 0x1; @@ -51,7 +51,7 @@ class Uart : public BasicPioDevice protected: int status; Platform *platform; - SimConsole *cons; + Terminal *term; public: typedef UartParams Params; diff --git a/src/dev/uart8250.cc b/src/dev/uart8250.cc index b4dc93645..93f71f49b 100644 --- a/src/dev/uart8250.cc +++ b/src/dev/uart8250.cc @@ -38,9 +38,9 @@ #include "base/inifile.hh" #include "base/str.hh" // for to_number #include "base/trace.hh" -#include "dev/simconsole.hh" -#include "dev/uart8250.hh" #include "dev/platform.hh" +#include "dev/terminal.hh" +#include "dev/uart8250.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" @@ -48,7 +48,7 @@ using namespace std; using namespace TheISA; Uart8250::IntrEvent::IntrEvent(Uart8250 *u, int bit) - : Event(&mainEventQueue), uart(u) + : uart(u) { DPRINTF(Uart, "UART Interrupt Event Initilizing\n"); intrBit = bit; @@ -93,9 +93,9 @@ Uart8250::IntrEvent::scheduleIntr() DPRINTF(Uart, "Scheduling IER interrupt for %#x, at cycle %lld\n", intrBit, curTick + interval); if (!scheduled()) - schedule(curTick + interval); + uart->schedule(this, curTick + interval); else - reschedule(curTick + interval); + uart->reschedule(this, curTick + interval); } @@ -120,8 +120,8 @@ Uart8250::read(PacketPtr pkt) switch (daddr) { case 0x0: if (!(LCR & 0x80)) { // read byte - if (cons->dataAvailable()) - pkt->set(cons->in()); + if (term->dataAvailable()) + pkt->set(term->in()); else { pkt->set((uint8_t)0); // A limited amount of these are ok. @@ -130,7 +130,7 @@ Uart8250::read(PacketPtr pkt) status &= ~RX_INT; platform->clearConsoleInt(); - if (cons->dataAvailable() && (IER & UART_IER_RDI)) + if (term->dataAvailable() && (IER & UART_IER_RDI)) rxIntrEvent.scheduleIntr(); } else { // dll divisor latch ; @@ -165,7 +165,7 @@ Uart8250::read(PacketPtr pkt) uint8_t lsr; lsr = 0; // check if there are any bytes to be read - if (cons->dataAvailable()) + if (term->dataAvailable()) lsr = UART_LSR_DR; lsr |= UART_LSR_TEMT | UART_LSR_THRE; pkt->set(lsr); @@ -201,7 +201,7 @@ Uart8250::write(PacketPtr pkt) switch (daddr) { case 0x0: if (!(LCR & 0x80)) { // write byte - cons->out(pkt->get<uint8_t>()); + term->out(pkt->get<uint8_t>()); platform->clearConsoleInt(); status &= ~TX_INT; if (UART_IER_THRI & IER) @@ -231,19 +231,19 @@ Uart8250::write(PacketPtr pkt) { DPRINTF(Uart, "IER: IER_THRI cleared, descheduling TX intrrupt\n"); if (txIntrEvent.scheduled()) - txIntrEvent.deschedule(); + deschedule(txIntrEvent); if (status & TX_INT) platform->clearConsoleInt(); status &= ~TX_INT; } - if ((UART_IER_RDI & IER) && cons->dataAvailable()) { + if ((UART_IER_RDI & IER) && term->dataAvailable()) { DPRINTF(Uart, "IER: IER_RDI set, scheduling RX intrrupt\n"); rxIntrEvent.scheduleIntr(); } else { DPRINTF(Uart, "IER: IER_RDI cleared, descheduling RX intrrupt\n"); if (rxIntrEvent.scheduled()) - rxIntrEvent.deschedule(); + deschedule(rxIntrEvent); if (status & RX_INT) platform->clearConsoleInt(); status &= ~RX_INT; @@ -329,9 +329,9 @@ Uart8250::unserialize(Checkpoint *cp, const std::string §ion) UNSERIALIZE_SCALAR(rxintrwhen); UNSERIALIZE_SCALAR(txintrwhen); if (rxintrwhen != 0) - rxIntrEvent.schedule(rxintrwhen); + schedule(rxIntrEvent, rxintrwhen); if (txintrwhen != 0) - txIntrEvent.schedule(txintrwhen); + schedule(txIntrEvent, txintrwhen); } Uart8250 * diff --git a/src/dev/uart8250.hh b/src/dev/uart8250.hh index 2c69667e1..79c31d5cf 100644 --- a/src/dev/uart8250.hh +++ b/src/dev/uart8250.hh @@ -65,7 +65,7 @@ const uint8_t UART_LSR_DR = 0x01; const uint8_t UART_MCR_LOOP = 0x10; -class SimConsole; +class Terminal; class Platform; class Uart8250 : public Uart diff --git a/src/dev/x86/Cmos.py b/src/dev/x86/Cmos.py new file mode 100644 index 000000000..0a92145e2 --- /dev/null +++ b/src/dev/x86/Cmos.py @@ -0,0 +1,41 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice +from X86IntPin import X86IntSourcePin + +class Cmos(BasicPioDevice): + type = 'Cmos' + cxx_class='X86ISA::Cmos' + time = Param.Time('01/01/2009', + "System time to use ('Now' for actual time)") + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") + int_pin = Param.X86IntSourcePin(X86IntSourcePin(), + 'Pin to signal RTC alarm interrupts to') diff --git a/src/dev/x86/I8042.py b/src/dev/x86/I8042.py new file mode 100644 index 000000000..31192adcd --- /dev/null +++ b/src/dev/x86/I8042.py @@ -0,0 +1,45 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice +from X86IntPin import X86IntSourcePin + +class I8042(BasicPioDevice): + type = 'I8042' + cxx_class = 'X86ISA::I8042' + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") + # This isn't actually used for anything here. + pio_addr = 0x0 + data_port = Param.Addr('Data port address') + command_port = Param.Addr('Command/status port address') + mouse_int_pin = Param.X86IntSourcePin(X86IntSourcePin(), + 'Pin to signal the mouse has data') + keyboard_int_pin = Param.X86IntSourcePin(X86IntSourcePin(), + 'Pin to signal the keyboard has data') diff --git a/src/dev/x86/I82094AA.py b/src/dev/x86/I82094AA.py new file mode 100644 index 000000000..9d57beed1 --- /dev/null +++ b/src/dev/x86/I82094AA.py @@ -0,0 +1,43 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice +from X86IntPin import X86IntSinkPin + +class I82094AA(BasicPioDevice): + type = 'I82094AA' + cxx_class = 'X86ISA::I82094AA' + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") + pio_addr = Param.Addr("Device address") + int_port = Port("Port for sending and receiving interrupt messages") + external_int_pic = Param.I8259(NULL, "External PIC, if any") + + def pin(self, line): + return X86IntSinkPin(device=self, number=line) diff --git a/src/dev/x86/I8237.py b/src/dev/x86/I8237.py new file mode 100644 index 000000000..20788a164 --- /dev/null +++ b/src/dev/x86/I8237.py @@ -0,0 +1,36 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice + +class I8237(BasicPioDevice): + type = 'I8237' + cxx_class = 'X86ISA::I8237' + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") diff --git a/src/dev/x86/I8254.py b/src/dev/x86/I8254.py new file mode 100644 index 000000000..f468717cc --- /dev/null +++ b/src/dev/x86/I8254.py @@ -0,0 +1,39 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice +from X86IntPin import X86IntSourcePin + +class I8254(BasicPioDevice): + type = 'I8254' + cxx_class = 'X86ISA::I8254' + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") + int_pin = Param.X86IntSourcePin(X86IntSourcePin(), + 'Pin to signal timer interrupts to') diff --git a/src/dev/x86/I8259.py b/src/dev/x86/I8259.py new file mode 100644 index 000000000..0a516d30a --- /dev/null +++ b/src/dev/x86/I8259.py @@ -0,0 +1,50 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice +from X86IntPin import X86IntSourcePin, X86IntSinkPin + +class X86I8259CascadeMode(Enum): + map = {'I8259Master' : 0, + 'I8259Slave' : 1, + 'I8259Single' : 2 + } + +class I8259(BasicPioDevice): + type = 'I8259' + cxx_class='X86ISA::I8259' + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") + output = Param.X86IntSourcePin(X86IntSourcePin(), + 'The pin this I8259 drives') + mode = Param.X86I8259CascadeMode('How this I8259 is cascaded') + slave = Param.I8259(NULL, 'Slave I8259, if any') + + def pin(self, line): + return X86IntSinkPin(device=self, number=line) diff --git a/src/dev/x86/Opteron.py b/src/dev/x86/Opteron.py deleted file mode 100644 index cb015e2e7..000000000 --- a/src/dev/x86/Opteron.py +++ /dev/null @@ -1,18 +0,0 @@ -from m5.params import * -from m5.proxy import * -from Device import BasicPioDevice, PioDevice, IsaFake, BadAddr -from Uart import Uart8250 -from Platform import Platform -from Pci import PciConfigAll -from SimConsole import SimConsole - -class Opteron(Platform): - type = 'Opteron' - system = Param.System(Parent.any, "system") - - pciconfig = PciConfigAll() - - def attachIO(self, bus): - self.pciconfig.pio = bus.default - bus.responder_set = True - bus.responder = self.pciconfig diff --git a/src/dev/x86/Pc.py b/src/dev/x86/Pc.py new file mode 100644 index 000000000..6f315cbcb --- /dev/null +++ b/src/dev/x86/Pc.py @@ -0,0 +1,83 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.proxy import * + +from Device import IsaFake +from Pci import PciConfigAll +from Platform import Platform +from SouthBridge import SouthBridge +from Terminal import Terminal +from Uart import Uart8250 + +def x86IOAddress(port): + IO_address_space_base = 0x8000000000000000 + return IO_address_space_base + port; + +class Pc(Platform): + type = 'Pc' + system = Param.System(Parent.any, "system") + + pciconfig = PciConfigAll() + + south_bridge = SouthBridge() + + # "Non-existant" port used for timing purposes by the linux kernel + i_dont_exist = IsaFake(pio_addr=x86IOAddress(0x80), pio_size=1) + + # Ports behind the pci config and data regsiters. These don't do anything, + # but the linux kernel fiddles with them anway. + behind_pci = IsaFake(pio_addr=x86IOAddress(0xcf8), pio_size=8) + + # Serial port and terminal + terminal = Terminal() + com_1 = Uart8250() + com_1.pio_addr = x86IOAddress(0x3f8) + com_1.terminal = terminal + + # Devices to catch access to non-existant serial ports. + fake_com_2 = IsaFake(pio_addr=x86IOAddress(0x2f8), pio_size=8) + fake_com_3 = IsaFake(pio_addr=x86IOAddress(0x3e8), pio_size=8) + fake_com_4 = IsaFake(pio_addr=x86IOAddress(0x2e8), pio_size=8) + + # A device to catch accesses to the non-existant floppy controller. + fake_floppy = IsaFake(pio_addr=x86IOAddress(0x3f2), pio_size=2) + + def attachIO(self, bus): + self.south_bridge.attachIO(bus) + self.i_dont_exist.pio = bus.port + self.behind_pci.pio = bus.port + self.com_1.pio = bus.port + self.fake_com_2.pio = bus.port + self.fake_com_3.pio = bus.port + self.fake_com_4.pio = bus.port + self.fake_floppy.pio = bus.port + self.pciconfig.pio = bus.default + bus.responder_set = True + bus.responder = self.pciconfig diff --git a/src/dev/x86/PcSpeaker.py b/src/dev/x86/PcSpeaker.py new file mode 100644 index 000000000..7ca62ec1e --- /dev/null +++ b/src/dev/x86/PcSpeaker.py @@ -0,0 +1,37 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice + +class PcSpeaker(BasicPioDevice): + type = 'PcSpeaker' + cxx_class = 'X86ISA::Speaker' + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") + i8254 = Param.I8254('Timer that drives the speaker') diff --git a/src/dev/x86/SConscript b/src/dev/x86/SConscript index c500531b1..e7543dfdf 100644 --- a/src/dev/x86/SConscript +++ b/src/dev/x86/SConscript @@ -26,12 +26,44 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -# Authors: Steve Reinhardt -# Gabe Black +# Authors: Gabe Black Import('*') if env['FULL_SYSTEM'] and env['TARGET_ISA'] == 'x86': - SimObject('Opteron.py') + SimObject('Pc.py') + Source('pc.cc') - Source('opteron.cc') + SimObject('SouthBridge.py') + Source('south_bridge.cc') + + SimObject('Cmos.py') + Source('cmos.cc') + TraceFlag('CMOS', 'Accesses to CMOS devices') + + SimObject('I8259.py') + Source('i8259.cc') + TraceFlag('I8259', 'Accesses to the I8259 PIC devices') + + SimObject('I8254.py') + Source('i8254.cc') + TraceFlag('I8254', 'Interrupts from the I8254 timer'); + + SimObject('I8237.py') + Source('i8237.cc') + TraceFlag('I8237', 'The I8237 dma controller'); + + SimObject('I8042.py') + Source('i8042.cc') + TraceFlag('I8042', 'The I8042 keyboard controller'); + + SimObject('PcSpeaker.py') + Source('speaker.cc') + TraceFlag('PcSpeaker') + + SimObject('I82094AA.py') + Source('i82094aa.cc') + TraceFlag('I82094AA') + + SimObject('X86IntPin.py') + Source('intdev.cc') diff --git a/src/dev/x86/SouthBridge.py b/src/dev/x86/SouthBridge.py new file mode 100644 index 000000000..d89ed9dc6 --- /dev/null +++ b/src/dev/x86/SouthBridge.py @@ -0,0 +1,122 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.proxy import * +from Cmos import Cmos +from I8042 import I8042 +from I82094AA import I82094AA +from I8237 import I8237 +from I8254 import I8254 +from I8259 import I8259 +from Ide import IdeController +from PcSpeaker import PcSpeaker +from X86IntPin import X86IntLine +from m5.SimObject import SimObject + +def x86IOAddress(port): + IO_address_space_base = 0x8000000000000000 + return IO_address_space_base + port; + +class SouthBridge(SimObject): + type = 'SouthBridge' + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") + platform = Param.Platform(Parent.any, "Platform this device is part of") + + _pic1 = I8259(pio_addr=x86IOAddress(0x20), mode='I8259Master') + _pic2 = I8259(pio_addr=x86IOAddress(0xA0), mode='I8259Slave') + _cmos = Cmos(pio_addr=x86IOAddress(0x70)) + _dma1 = I8237(pio_addr=x86IOAddress(0x0)) + _keyboard = I8042(data_port=x86IOAddress(0x60), \ + command_port=x86IOAddress(0x64)) + _pit = I8254(pio_addr=x86IOAddress(0x40)) + _speaker = PcSpeaker(pio_addr=x86IOAddress(0x61)) + _io_apic = I82094AA(pio_addr=0xFEC00000) + # This is to make sure the interrupt lines are instantiated. Don't use + # it for anything directly. + int_lines = VectorParam.X86IntLine([], "Interrupt lines") + + pic1 = Param.I8259(_pic1, "Master PIC") + pic2 = Param.I8259(_pic2, "Slave PIC") + cmos = Param.Cmos(_cmos, "CMOS memory and real time clock device") + dma1 = Param.I8237(_dma1, "The first dma controller") + keyboard = Param.I8042(_keyboard, "The keyboard controller") + pit = Param.I8254(_pit, "Programmable interval timer") + speaker = Param.PcSpeaker(_speaker, "PC speaker") + io_apic = Param.I82094AA(_io_apic, "I/O APIC") + + def connectPins(self, source, sink): + self.int_lines.append(X86IntLine(source=source, sink=sink)) + + # IDE controller + ide = IdeController(disks=[], pci_func=0, pci_dev=4, pci_bus=0) + ide.BAR0 = 0x1f0 + ide.BAR0LegacyIO = True + ide.BAR1 = 0x3f4 + ide.BAR1Size = '3B' + ide.BAR1LegacyIO = True + ide.BAR2 = 0x170 + ide.BAR2LegacyIO = True + ide.BAR3 = 0x374 + ide.BAR3Size = '3B' + ide.BAR3LegacyIO = True + ide.BAR4 = 1 + ide.Command = 1 + ide.InterruptLine = 14 + ide.InterruptPin = 1 + + def attachIO(self, bus): + # Route interupt signals + self.connectPins(self.pic1.output, self.io_apic.pin(0)) + self.connectPins(self.pic2.output, self.pic1.pin(2)) + self.connectPins(self.cmos.int_pin, self.pic2.pin(0)) + self.connectPins(self.pit.int_pin, self.pic1.pin(0)) + self.connectPins(self.pit.int_pin, self.io_apic.pin(2)) +# self.connectPins(self.keyboard.keyboard_int_pin, +# self.pic1.pin(1)) + self.connectPins(self.keyboard.keyboard_int_pin, + self.io_apic.pin(1)) +# self.connectPins(self.keyboard.mouse_int_pin, +# self.pic2.pin(4)) + self.connectPins(self.keyboard.mouse_int_pin, + self.io_apic.pin(12)) + # Tell the devices about each other + self.pic1.slave = self.pic2 + self.speaker.i8254 = self.pit + self.io_apic.external_int_pic = self.pic1 + # Connect to the bus + self.cmos.pio = bus.port + self.dma1.pio = bus.port + self.ide.pio = bus.port + self.keyboard.pio = bus.port + self.pic1.pio = bus.port + self.pic2.pio = bus.port + self.pit.pio = bus.port + self.speaker.pio = bus.port + self.io_apic.pio = bus.port + self.io_apic.int_port = bus.port diff --git a/src/dev/x86/X86IntPin.py b/src/dev/x86/X86IntPin.py new file mode 100644 index 000000000..35e274624 --- /dev/null +++ b/src/dev/x86/X86IntPin.py @@ -0,0 +1,51 @@ +# Copyright (c) 2008 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Gabe Black + +from m5.params import * +from m5.SimObject import SimObject + +# A generic pin to drive an interrupt signal generated by a device. +class X86IntSourcePin(SimObject): + type = 'X86IntSourcePin' + cxx_class = 'X86ISA::IntSourcePin' + +# A generic pin to receive an interrupt signal generated by another device. +class X86IntSinkPin(SimObject): + type = 'X86IntSinkPin' + cxx_class = 'X86ISA::IntSinkPin' + + device = Param.SimObject("Device this pin belongs to") + number = Param.Int("The pin number on the device") + +# An interrupt line which is driven by a source pin and drives a sink pin. +class X86IntLine(SimObject): + type = 'X86IntLine' + cxx_class = 'X86ISA::IntLine' + + source = Param.X86IntSourcePin("Pin driving this line") + sink = Param.X86IntSinkPin("Pin driven by this line") diff --git a/src/dev/x86/cmos.cc b/src/dev/x86/cmos.cc new file mode 100644 index 000000000..e08c56e8c --- /dev/null +++ b/src/dev/x86/cmos.cc @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include "dev/x86/cmos.hh" +#include "dev/x86/intdev.hh" +#include "mem/packet_access.hh" + +void +X86ISA::Cmos::X86RTC::handleEvent() +{ + assert(intPin); + intPin->raise(); + //XXX This is a hack. + intPin->lower(); +} + +Tick +X86ISA::Cmos::read(PacketPtr pkt) +{ + assert(pkt->getSize() == 1); + switch(pkt->getAddr() - pioAddr) + { + case 0x0: + pkt->set(address); + break; + case 0x1: + pkt->set(readRegister(address)); + break; + default: + panic("Read from undefined CMOS port.\n"); + } + pkt->makeAtomicResponse(); + return latency; +} + +Tick +X86ISA::Cmos::write(PacketPtr pkt) +{ + assert(pkt->getSize() == 1); + switch(pkt->getAddr() - pioAddr) + { + case 0x0: + address = pkt->get<uint8_t>(); + break; + case 0x1: + writeRegister(address, pkt->get<uint8_t>()); + break; + default: + panic("Write to undefined CMOS port.\n"); + } + pkt->makeAtomicResponse(); + return latency; +} + +uint8_t +X86ISA::Cmos::readRegister(uint8_t reg) +{ + assert(reg < numRegs); + uint8_t val; + if (reg <= 0xD) { + val = rtc.readData(reg); + DPRINTF(CMOS, + "Reading CMOS RTC reg %x as %x.\n", reg, val); + } else { + val = regs[reg]; + DPRINTF(CMOS, + "Reading non-volitile CMOS address %x as %x.\n", reg, val); + } + return val; +} + +void +X86ISA::Cmos::writeRegister(uint8_t reg, uint8_t val) +{ + assert(reg < numRegs); + if (reg <= 0xD) { + DPRINTF(CMOS, "Writing CMOS RTC reg %x with %x.\n", + reg, val); + rtc.writeData(reg, val); + } else { + DPRINTF(CMOS, "Writing non-volitile CMOS address %x with %x.\n", + reg, val); + regs[reg] = val; + } +} + +X86ISA::Cmos * +CmosParams::create() +{ + return new X86ISA::Cmos(this); +} diff --git a/src/dev/x86/cmos.hh b/src/dev/x86/cmos.hh new file mode 100644 index 000000000..76276dbc1 --- /dev/null +++ b/src/dev/x86/cmos.hh @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#ifndef __DEV_X86_CMOS_HH__ +#define __DEV_X86_CMOS_HH__ + +#include "dev/io_device.hh" +#include "dev/mc146818.hh" +#include "params/Cmos.hh" + +namespace X86ISA +{ + +class IntSourcePin; + +class Cmos : public BasicPioDevice +{ + protected: + Tick latency; + + uint8_t address; + + static const int numRegs = 128; + + uint8_t regs[numRegs]; + + uint8_t readRegister(uint8_t reg); + void writeRegister(uint8_t reg, uint8_t val); + + class X86RTC : public MC146818 + { + protected: + IntSourcePin * intPin; + public: + X86RTC(EventManager *em, const std::string &n, const struct tm time, + bool bcd, Tick frequency, IntSourcePin * _intPin) : + MC146818(em, n, time, bcd, frequency), intPin(_intPin) + { + } + protected: + void handleEvent(); + } rtc; + + public: + typedef CmosParams Params; + + Cmos(const Params *p) : BasicPioDevice(p), latency(p->pio_latency), + rtc(this, "rtc", p->time, true, ULL(5000000000), p->int_pin) + { + pioSize = 2; + memset(regs, 0, numRegs * sizeof(uint8_t)); + address = 0; + } + + Tick read(PacketPtr pkt); + + Tick write(PacketPtr pkt); +}; + +}; // namespace X86ISA + +#endif //__DEV_X86_CMOS_HH__ diff --git a/src/dev/x86/i8042.cc b/src/dev/x86/i8042.cc new file mode 100644 index 000000000..afcbfdfb4 --- /dev/null +++ b/src/dev/x86/i8042.cc @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include "base/bitunion.hh" +#include "dev/x86/i8042.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" + +// The 8042 has a whopping 32 bytes of internal RAM. +const uint8_t RamSize = 32; +const uint8_t NumOutputBits = 14; +const uint8_t X86ISA::PS2Keyboard::ID[] = {0xab, 0x83}; +const uint8_t X86ISA::PS2Mouse::ID[] = {0x00}; +const uint8_t CommandAck = 0xfa; +const uint8_t CommandNack = 0xfe; +const uint8_t BatSuccessful = 0xaa; + +void +X86ISA::I8042::addressRanges(AddrRangeList &range_list) +{ + range_list.clear(); + range_list.push_back(RangeSize(dataPort, 1)); + range_list.push_back(RangeSize(commandPort, 1)); +} + +void +X86ISA::I8042::writeData(uint8_t newData, bool mouse) +{ + DPRINTF(I8042, "Set data %#02x.\n", newData); + dataReg = newData; + statusReg.outputFull = 1; + statusReg.mouseOutputFull = (mouse ? 1 : 0); + if (!mouse && commandByte.keyboardFullInt) { + DPRINTF(I8042, "Sending keyboard interrupt.\n"); + keyboardIntPin->raise(); + //This is a hack + keyboardIntPin->lower(); + } else if (mouse && commandByte.mouseFullInt) { + DPRINTF(I8042, "Sending mouse interrupt.\n"); + mouseIntPin->raise(); + //This is a hack + mouseIntPin->lower(); + } +} + +void +X86ISA::PS2Device::ack() +{ + bufferData(&CommandAck, sizeof(CommandAck)); +} + +void +X86ISA::PS2Device::nack() +{ + bufferData(&CommandNack, sizeof(CommandNack)); +} + +void +X86ISA::PS2Device::bufferData(const uint8_t *data, int size) +{ + assert(data || size == 0); + while (size) { + outBuffer.push(*(data++)); + size--; + } +} + +uint8_t +X86ISA::I8042::readDataOut() +{ + uint8_t data = dataReg; + statusReg.outputFull = 0; + statusReg.mouseOutputFull = 0; + if (keyboard.hasData()) { + writeData(keyboard.getData(), false); + } else if (mouse.hasData()) { + writeData(mouse.getData(), true); + } + return data; +} + +bool +X86ISA::PS2Keyboard::processData(uint8_t data) +{ + if (lastCommand != NoCommand) { + switch (lastCommand) { + case LEDWrite: + DPRINTF(I8042, "Setting LEDs: " + "caps lock %s, num lock %s, scroll lock %s\n", + bits(data, 2) ? "on" : "off", + bits(data, 1) ? "on" : "off", + bits(data, 0) ? "on" : "off"); + ack(); + lastCommand = NoCommand; + break; + case TypematicInfo: + DPRINTF(I8042, "Setting typematic info to %#02x.\n", data); + ack(); + lastCommand = NoCommand; + break; + } + return hasData(); + } + switch (data) { + case LEDWrite: + DPRINTF(I8042, "Got LED write command.\n"); + ack(); + lastCommand = LEDWrite; + break; + case DiagnosticEcho: + panic("Keyboard diagnostic echo unimplemented.\n"); + case AlternateScanCodes: + panic("Accessing alternate scan codes unimplemented.\n"); + case ReadID: + DPRINTF(I8042, "Got keyboard read ID command.\n"); + ack(); + bufferData((uint8_t *)&ID, sizeof(ID)); + break; + case TypematicInfo: + DPRINTF(I8042, "Setting typematic info.\n"); + ack(); + lastCommand = TypematicInfo; + break; + case Enable: + DPRINTF(I8042, "Enabling the keyboard.\n"); + ack(); + break; + case Disable: + DPRINTF(I8042, "Disabling the keyboard.\n"); + ack(); + break; + case DefaultsAndDisable: + DPRINTF(I8042, "Disabling and resetting the keyboard.\n"); + ack(); + break; + case AllKeysToTypematic: + panic("Setting all keys to typemantic unimplemented.\n"); + case AllKeysToMakeRelease: + panic("Setting all keys to make/release unimplemented.\n"); + case AllKeysToMake: + panic("Setting all keys to make unimplemented.\n"); + case AllKeysToTypematicMakeRelease: + panic("Setting all keys to " + "typematic/make/release unimplemented.\n"); + case KeyToTypematic: + panic("Setting a key to typematic unimplemented.\n"); + case KeyToMakeRelease: + panic("Setting a key to make/release unimplemented.\n"); + case KeyToMakeOnly: + panic("Setting key to make only unimplemented.\n"); + case Resend: + panic("Keyboard resend unimplemented.\n"); + case Reset: + panic("Keyboard reset unimplemented.\n"); + default: + panic("Unknown keyboard command %#02x.\n", data); + } + return hasData(); +} + +bool +X86ISA::PS2Mouse::processData(uint8_t data) +{ + if (lastCommand != NoCommand) { + switch(lastCommand) { + case SetResolution: + DPRINTF(I8042, "Mouse resolution set to %d.\n", data); + resolution = data; + ack(); + lastCommand = NoCommand; + break; + case SampleRate: + DPRINTF(I8042, "Mouse sample rate %d samples " + "per second.\n", data); + sampleRate = data; + ack(); + lastCommand = NoCommand; + break; + default: + panic("Not expecting data for a mouse command.\n"); + } + return hasData(); + } + switch (data) { + case Scale1to1: + DPRINTF(I8042, "Setting mouse scale to 1:1.\n"); + status.twoToOne = 0; + ack(); + break; + case Scale2to1: + DPRINTF(I8042, "Setting mouse scale to 2:1.\n"); + status.twoToOne = 1; + ack(); + break; + case SetResolution: + DPRINTF(I8042, "Setting mouse resolution.\n"); + lastCommand = SetResolution; + ack(); + break; + case GetStatus: + DPRINTF(I8042, "Getting mouse status.\n"); + ack(); + bufferData((uint8_t *)&(status), 1); + bufferData(&resolution, sizeof(resolution)); + bufferData(&sampleRate, sizeof(sampleRate)); + break; + case ReadData: + panic("Reading mouse data unimplemented.\n"); + case ResetWrapMode: + panic("Resetting mouse wrap mode unimplemented.\n"); + case WrapMode: + panic("Setting mouse wrap mode unimplemented.\n"); + case RemoteMode: + panic("Setting mouse remote mode unimplemented.\n"); + case ReadID: + DPRINTF(I8042, "Mouse ID requested.\n"); + ack(); + bufferData(ID, sizeof(ID)); + break; + case SampleRate: + DPRINTF(I8042, "Setting mouse sample rate.\n"); + lastCommand = SampleRate; + ack(); + break; + case DisableReporting: + DPRINTF(I8042, "Disabling data reporting.\n"); + status.enabled = 0; + ack(); + break; + case EnableReporting: + DPRINTF(I8042, "Enabling data reporting.\n"); + status.enabled = 1; + ack(); + break; + case DefaultsAndDisable: + DPRINTF(I8042, "Disabling and resetting mouse.\n"); + sampleRate = 100; + resolution = 4; + status.twoToOne = 0; + status.enabled = 0; + ack(); + break; + case Resend: + panic("Mouse resend unimplemented.\n"); + case Reset: + DPRINTF(I8042, "Resetting the mouse.\n"); + sampleRate = 100; + resolution = 4; + status.twoToOne = 0; + status.enabled = 0; + ack(); + bufferData(&BatSuccessful, sizeof(BatSuccessful)); + bufferData(ID, sizeof(ID)); + break; + default: + warn("Unknown mouse command %#02x.\n", data); + nack(); + break; + } + return hasData(); +} + + +Tick +X86ISA::I8042::read(PacketPtr pkt) +{ + assert(pkt->getSize() == 1); + Addr addr = pkt->getAddr(); + if (addr == dataPort) { + uint8_t data = readDataOut(); + //DPRINTF(I8042, "Read from data port got %#02x.\n", data); + pkt->set<uint8_t>(data); + } else if (addr == commandPort) { + //DPRINTF(I8042, "Read status as %#02x.\n", (uint8_t)statusReg); + pkt->set<uint8_t>((uint8_t)statusReg); + } else { + panic("Read from unrecognized port %#x.\n", addr); + } + pkt->makeAtomicResponse(); + return latency; +} + +Tick +X86ISA::I8042::write(PacketPtr pkt) +{ + assert(pkt->getSize() == 1); + Addr addr = pkt->getAddr(); + uint8_t data = pkt->get<uint8_t>(); + if (addr == dataPort) { + statusReg.commandLast = 0; + switch (lastCommand) { + case NoCommand: + if (keyboard.processData(data)) { + writeData(keyboard.getData(), false); + } + break; + case WriteToMouse: + if (mouse.processData(data)) { + writeData(mouse.getData(), true); + } + break; + case WriteCommandByte: + commandByte = data; + DPRINTF(I8042, "Got data %#02x for \"Write " + "command byte\" command.\n", data); + statusReg.passedSelfTest = (uint8_t)commandByte.passedSelfTest; + break; + case WriteMouseOutputBuff: + DPRINTF(I8042, "Got data %#02x for \"Write " + "mouse output buffer\" command.\n", data); + writeData(data, true); + break; + default: + panic("Data written for unrecognized " + "command %#02x\n", lastCommand); + } + lastCommand = NoCommand; + } else if (addr == commandPort) { + DPRINTF(I8042, "Got command %#02x.\n", data); + statusReg.commandLast = 1; + // These purposefully leave off the first byte of the controller RAM + // so it can be handled specially. + if (data > ReadControllerRamBase && + data < ReadControllerRamBase + RamSize) { + panic("Attempted to use i8042 read controller RAM command to " + "get byte %d.\n", data - ReadControllerRamBase); + } else if (data > WriteControllerRamBase && + data < WriteControllerRamBase + RamSize) { + panic("Attempted to use i8042 read controller RAM command to " + "get byte %d.\n", data - ReadControllerRamBase); + } else if (data >= PulseOutputBitBase && + data < PulseOutputBitBase + NumOutputBits) { + panic("Attempted to use i8042 pulse output bit command to " + "to pulse bit %d.\n", data - PulseOutputBitBase); + } + switch (data) { + case GetCommandByte: + DPRINTF(I8042, "Getting command byte.\n"); + writeData(commandByte); + break; + case WriteCommandByte: + DPRINTF(I8042, "Setting command byte.\n"); + lastCommand = WriteCommandByte; + break; + case CheckForPassword: + panic("i8042 \"Check for password\" command not implemented.\n"); + case LoadPassword: + panic("i8042 \"Load password\" command not implemented.\n"); + case CheckPassword: + panic("i8042 \"Check password\" command not implemented.\n"); + case DisableMouse: + DPRINTF(I8042, "Disabling mouse at controller.\n"); + commandByte.disableMouse = 1; + break; + case EnableMouse: + DPRINTF(I8042, "Enabling mouse at controller.\n"); + commandByte.disableMouse = 0; + break; + case TestMouse: + panic("i8042 \"Test mouse\" command not implemented.\n"); + case SelfTest: + panic("i8042 \"Self test\" command not implemented.\n"); + case InterfaceTest: + panic("i8042 \"Interface test\" command not implemented.\n"); + case DiagnosticDump: + panic("i8042 \"Diagnostic dump\" command not implemented.\n"); + case DisableKeyboard: + DPRINTF(I8042, "Disabling keyboard at controller.\n"); + commandByte.disableKeyboard = 1; + break; + case EnableKeyboard: + DPRINTF(I8042, "Enabling keyboard at controller.\n"); + commandByte.disableKeyboard = 0; + break; + case ReadInputPort: + panic("i8042 \"Read input port\" command not implemented.\n"); + case ContinuousPollLow: + panic("i8042 \"Continuous poll low\" command not implemented.\n"); + case ContinuousPollHigh: + panic("i8042 \"Continuous poll high\" command not implemented.\n"); + case ReadOutputPort: + panic("i8042 \"Read output port\" command not implemented.\n"); + case WriteOutputPort: + panic("i8042 \"Write output port\" command not implemented.\n"); + case WriteKeyboardOutputBuff: + panic("i8042 \"Write keyboard output buffer\" " + "command not implemented.\n"); + case WriteMouseOutputBuff: + DPRINTF(I8042, "Got command to write to mouse output buffer.\n"); + lastCommand = WriteMouseOutputBuff; + break; + case WriteToMouse: + DPRINTF(I8042, "Expecting mouse command.\n"); + lastCommand = WriteToMouse; + break; + case DisableA20: + panic("i8042 \"Disable A20\" command not implemented.\n"); + case EnableA20: + panic("i8042 \"Enable A20\" command not implemented.\n"); + case ReadTestInputs: + panic("i8042 \"Read test inputs\" command not implemented.\n"); + case SystemReset: + panic("i8042 \"System reset\" command not implemented.\n"); + default: + panic("Write to unknown i8042 " + "(keyboard controller) command port.\n"); + } + } else { + panic("Write to unrecognized port %#x.\n", addr); + } + pkt->makeAtomicResponse(); + return latency; +} + +X86ISA::I8042 * +I8042Params::create() +{ + return new X86ISA::I8042(this); +} diff --git a/src/dev/x86/i8042.hh b/src/dev/x86/i8042.hh new file mode 100644 index 000000000..8a941f9a5 --- /dev/null +++ b/src/dev/x86/i8042.hh @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#ifndef __DEV_X86_I8042_HH__ +#define __DEV_X86_I8042_HH__ + +#include "dev/io_device.hh" +#include "dev/x86/intdev.hh" +#include "params/I8042.hh" + +#include <queue> + +namespace X86ISA +{ + +class IntPin; + +class PS2Device +{ + protected: + std::queue<uint8_t> outBuffer; + + static const uint16_t NoCommand = (uint16_t)(-1); + + uint16_t lastCommand; + void bufferData(const uint8_t *data, int size); + void ack(); + void nack(); + + public: + virtual ~PS2Device() + {}; + + PS2Device() : lastCommand(NoCommand) + {} + + bool hasData() + { + return !outBuffer.empty(); + } + + uint8_t getData() + { + uint8_t data = outBuffer.front(); + outBuffer.pop(); + return data; + } + + virtual bool processData(uint8_t data) = 0; +}; + +class PS2Mouse : public PS2Device +{ + protected: + static const uint8_t ID[]; + + enum Command + { + Scale1to1 = 0xE6, + Scale2to1 = 0xE7, + SetResolution = 0xE8, + GetStatus = 0xE9, + ReadData = 0xEB, + ResetWrapMode = 0xEC, + WrapMode = 0xEE, + RemoteMode = 0xF0, + ReadID = 0xF2, + SampleRate = 0xF3, + EnableReporting = 0xF4, + DisableReporting = 0xF5, + DefaultsAndDisable = 0xF6, + Resend = 0xFE, + Reset = 0xFF + }; + + BitUnion8(Status) + Bitfield<6> remote; + Bitfield<5> enabled; + Bitfield<4> twoToOne; + Bitfield<2> leftButton; + Bitfield<0> rightButton; + EndBitUnion(Status) + + Status status; + uint8_t resolution; + uint8_t sampleRate; + public: + PS2Mouse() : PS2Device(), status(0), resolution(4), sampleRate(100) + {} + + bool processData(uint8_t data); +}; + +class PS2Keyboard : public PS2Device +{ + protected: + static const uint8_t ID[]; + + enum Command + { + LEDWrite = 0xED, + DiagnosticEcho = 0xEE, + AlternateScanCodes = 0xF0, + ReadID = 0xF2, + TypematicInfo = 0xF3, + Enable = 0xF4, + Disable = 0xF5, + DefaultsAndDisable = 0xF6, + AllKeysToTypematic = 0xF7, + AllKeysToMakeRelease = 0xF8, + AllKeysToMake = 0xF9, + AllKeysToTypematicMakeRelease = 0xFA, + KeyToTypematic = 0xFB, + KeyToMakeRelease = 0xFC, + KeyToMakeOnly = 0xFD, + Resend = 0xFE, + Reset = 0xFF + }; + + public: + bool processData(uint8_t data); +}; + +class I8042 : public BasicPioDevice +{ + protected: + enum Command + { + GetCommandByte = 0x20, + ReadControllerRamBase = 0x20, + WriteCommandByte = 0x60, + WriteControllerRamBase = 0x60, + CheckForPassword = 0xA4, + LoadPassword = 0xA5, + CheckPassword = 0xA6, + DisableMouse = 0xA7, + EnableMouse = 0xA8, + TestMouse = 0xA9, + SelfTest = 0xAA, + InterfaceTest = 0xAB, + DiagnosticDump = 0xAC, + DisableKeyboard = 0xAD, + EnableKeyboard = 0xAE, + ReadInputPort = 0xC0, + ContinuousPollLow = 0xC1, + ContinuousPollHigh = 0xC2, + ReadOutputPort = 0xD0, + WriteOutputPort = 0xD1, + WriteKeyboardOutputBuff = 0xD2, + WriteMouseOutputBuff = 0xD3, + WriteToMouse = 0xD4, + DisableA20 = 0xDD, + EnableA20 = 0xDF, + ReadTestInputs = 0xE0, + PulseOutputBitBase = 0xF0, + SystemReset = 0xFE + }; + + BitUnion8(StatusReg) + Bitfield<7> parityError; + Bitfield<6> timeout; + Bitfield<5> mouseOutputFull; + Bitfield<4> keyboardUnlocked; + Bitfield<3> commandLast; + Bitfield<2> passedSelfTest; + Bitfield<1> inputFull; + Bitfield<0> outputFull; + EndBitUnion(StatusReg) + + BitUnion8(CommandByte) + Bitfield<6> convertScanCodes; + Bitfield<5> disableMouse; + Bitfield<4> disableKeyboard; + Bitfield<2> passedSelfTest; + Bitfield<1> mouseFullInt; + Bitfield<0> keyboardFullInt; + EndBitUnion(CommandByte) + + Tick latency; + Addr dataPort; + Addr commandPort; + + StatusReg statusReg; + CommandByte commandByte; + + uint8_t dataReg; + + static const uint16_t NoCommand = (uint16_t)(-1); + uint16_t lastCommand; + + IntSourcePin *mouseIntPin; + IntSourcePin *keyboardIntPin; + + PS2Mouse mouse; + PS2Keyboard keyboard; + + void writeData(uint8_t newData, bool mouse = false); + uint8_t readDataOut(); + + public: + typedef I8042Params Params; + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + + I8042(Params *p) : BasicPioDevice(p), latency(p->pio_latency), + dataPort(p->data_port), commandPort(p->command_port), + statusReg(0), commandByte(0), dataReg(0), lastCommand(NoCommand), + mouseIntPin(p->mouse_int_pin), keyboardIntPin(p->keyboard_int_pin) + { + statusReg.passedSelfTest = 1; + statusReg.commandLast = 1; + statusReg.keyboardUnlocked = 1; + + commandByte.convertScanCodes = 1; + commandByte.passedSelfTest = 1; + commandByte.keyboardFullInt = 1; + } + + void addressRanges(AddrRangeList &range_list); + + Tick read(PacketPtr pkt); + + Tick write(PacketPtr pkt); +}; + +}; // namespace X86ISA + +#endif //__DEV_X86_I8042_HH__ diff --git a/src/dev/x86/i82094aa.cc b/src/dev/x86/i82094aa.cc new file mode 100644 index 000000000..d160fcb24 --- /dev/null +++ b/src/dev/x86/i82094aa.cc @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include "arch/x86/intmessage.hh" +#include "dev/x86/i82094aa.hh" +#include "dev/x86/i8259.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" +#include "sim/system.hh" + +X86ISA::I82094AA::I82094AA(Params *p) : PioDevice(p), IntDev(this), + latency(p->pio_latency), pioAddr(p->pio_addr), + extIntPic(p->external_int_pic) +{ + // This assumes there's only one I/O APIC in the system + id = sys->numContexts(); + assert(id <= 0xf); + arbId = id; + regSel = 0; + RedirTableEntry entry = 0; + entry.mask = 1; + for (int i = 0; i < TableSize; i++) { + redirTable[i] = entry; + pinStates[i] = false; + } +} + +Tick +X86ISA::I82094AA::read(PacketPtr pkt) +{ + assert(pkt->getSize() == 4); + Addr offset = pkt->getAddr() - pioAddr; + switch(offset) { + case 0: + pkt->set<uint32_t>(regSel); + break; + case 16: + pkt->set<uint32_t>(readReg(regSel)); + break; + default: + panic("Illegal read from I/O APIC.\n"); + } + pkt->makeAtomicResponse(); + return latency; +} + +Tick +X86ISA::I82094AA::write(PacketPtr pkt) +{ + assert(pkt->getSize() == 4); + Addr offset = pkt->getAddr() - pioAddr; + switch(offset) { + case 0: + regSel = pkt->get<uint32_t>(); + break; + case 16: + writeReg(regSel, pkt->get<uint32_t>()); + break; + default: + panic("Illegal write to I/O APIC.\n"); + } + pkt->makeAtomicResponse(); + return latency; +} + +void +X86ISA::I82094AA::writeReg(uint8_t offset, uint32_t value) +{ + if (offset == 0x0) { + id = bits(value, 27, 24); + } else if (offset == 0x1) { + // The IOAPICVER register is read only. + } else if (offset == 0x2) { + arbId = bits(value, 27, 24); + } else if (offset >= 0x10 && offset <= (0x10 + TableSize * 2)) { + int index = (offset - 0x10) / 2; + if (offset % 2) { + redirTable[index].topDW = value; + redirTable[index].topReserved = 0; + } else { + redirTable[index].bottomDW = value; + redirTable[index].bottomReserved = 0; + } + } else { + warn("Access to undefined I/O APIC register %#x.\n", offset); + } + DPRINTF(I82094AA, + "Wrote %#x to I/O APIC register %#x .\n", value, offset); +} + +uint32_t +X86ISA::I82094AA::readReg(uint8_t offset) +{ + uint32_t result = 0; + if (offset == 0x0) { + result = id << 24; + } else if (offset == 0x1) { + result = ((TableSize - 1) << 16) | APICVersion; + } else if (offset == 0x2) { + result = arbId << 24; + } else if (offset >= 0x10 && offset <= (0x10 + TableSize * 2)) { + int index = (offset - 0x10) / 2; + if (offset % 2) { + result = redirTable[index].topDW; + } else { + result = redirTable[index].bottomDW; + } + } else { + warn("Access to undefined I/O APIC register %#x.\n", offset); + } + DPRINTF(I82094AA, + "Read %#x from I/O APIC register %#x.\n", result, offset); + return result; +} + +void +X86ISA::I82094AA::signalInterrupt(int line) +{ + DPRINTF(I82094AA, "Received interrupt %d.\n", line); + assert(line < TableSize); + RedirTableEntry entry = redirTable[line]; + if (entry.mask) { + DPRINTF(I82094AA, "Entry was masked.\n"); + return; + } else { + TriggerIntMessage message; + message.destination = entry.dest; + if (entry.deliveryMode == DeliveryMode::ExtInt) { + assert(extIntPic); + message.vector = extIntPic->getVector(); + } else { + message.vector = entry.vector; + } + message.deliveryMode = entry.deliveryMode; + message.destMode = entry.destMode; + message.level = entry.polarity; + message.trigger = entry.trigger; + + if (DeliveryMode::isReserved(entry.deliveryMode)) { + fatal("Tried to use reserved delivery mode " + "for IO APIC entry %d.\n", line); + } else if (DTRACE(I82094AA)) { + DPRINTF(I82094AA, "Delivery mode is: %s.\n", + DeliveryMode::names[entry.deliveryMode]); + DPRINTF(I82094AA, "Vector is %#x.\n", message.vector); + } + + if (entry.destMode == 0) { + DPRINTF(I82094AA, + "Sending interrupt to APIC ID %d.\n", entry.dest); + PacketPtr pkt = buildIntRequest(entry.dest, message); + if (sys->getMemoryMode() == Enums::timing) + intPort->sendMessageTiming(pkt, latency); + else if (sys->getMemoryMode() == Enums::atomic) + intPort->sendMessageAtomic(pkt); + else + panic("Unrecognized memory mode.\n"); + } else { + DPRINTF(I82094AA, "Sending interrupts to APIC IDs:" + "%s%s%s%s%s%s%s%s\n", + bits((int)entry.dest, 0) ? " 0": "", + bits((int)entry.dest, 1) ? " 1": "", + bits((int)entry.dest, 2) ? " 2": "", + bits((int)entry.dest, 3) ? " 3": "", + bits((int)entry.dest, 4) ? " 4": "", + bits((int)entry.dest, 5) ? " 5": "", + bits((int)entry.dest, 6) ? " 6": "", + bits((int)entry.dest, 7) ? " 7": "" + ); + uint8_t dests = entry.dest; + uint8_t id = 0; + while(dests) { + if (dests & 0x1) { + PacketPtr pkt = buildIntRequest(id, message); + if (sys->getMemoryMode() == Enums::timing) + intPort->sendMessageTiming(pkt, latency); + else if (sys->getMemoryMode() == Enums::atomic) + intPort->sendMessageAtomic(pkt); + else + panic("Unrecognized memory mode.\n"); + } + dests >>= 1; + id++; + } + } + } +} + +void +X86ISA::I82094AA::raiseInterruptPin(int number) +{ + assert(number < TableSize); + if (!pinStates[number]) + signalInterrupt(number); + pinStates[number] = true; +} + +void +X86ISA::I82094AA::lowerInterruptPin(int number) +{ + assert(number < TableSize); + pinStates[number] = false; +} + +X86ISA::I82094AA * +I82094AAParams::create() +{ + return new X86ISA::I82094AA(this); +} diff --git a/src/dev/x86/i82094aa.hh b/src/dev/x86/i82094aa.hh new file mode 100644 index 000000000..b11e2bcb1 --- /dev/null +++ b/src/dev/x86/i82094aa.hh @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#ifndef __DEV_X86_I82094AA_HH__ +#define __DEV_X86_I82094AA_HH__ + +#include "base/bitunion.hh" +#include "base/range_map.hh" +#include "dev/io_device.hh" +#include "dev/x86/intdev.hh" +#include "params/I82094AA.hh" + +namespace X86ISA +{ + +class I8259; + +class I82094AA : public PioDevice, public IntDev +{ + public: + BitUnion64(RedirTableEntry) + Bitfield<63, 32> topDW; + Bitfield<55, 32> topReserved; + Bitfield<31, 0> bottomDW; + Bitfield<31, 17> bottomReserved; + Bitfield<63, 56> dest; + Bitfield<16> mask; + Bitfield<15> trigger; + Bitfield<14> remoteIRR; + Bitfield<13> polarity; + Bitfield<12> deliveryStatus; + Bitfield<11> destMode; + Bitfield<10, 8> deliveryMode; + Bitfield<7, 0> vector; + EndBitUnion(RedirTableEntry) + + protected: + Tick latency; + Addr pioAddr; + + I8259 * extIntPic; + + uint8_t regSel; + uint8_t id; + uint8_t arbId; + + static const uint8_t TableSize = 24; + // This implementation is based on version 0x11, but 0x14 avoids having + // to deal with the arbitration and APIC bus guck. + static const uint8_t APICVersion = 0x14; + + RedirTableEntry redirTable[TableSize]; + bool pinStates[TableSize]; + + public: + typedef I82094AAParams Params; + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + + I82094AA(Params *p); + + Tick read(PacketPtr pkt); + Tick write(PacketPtr pkt); + + void addressRanges(AddrRangeList &range_list) + { + range_list.clear(); + range_list.push_back(RangeEx(pioAddr, pioAddr + 4)); + range_list.push_back(RangeEx(pioAddr + 16, pioAddr + 20)); + } + + void getIntAddrRange(AddrRangeList &range_list) + { + range_list.clear(); + range_list.push_back(RangeEx(x86InterruptAddress(1, 0), + x86InterruptAddress(1, 0) + PhysAddrAPICRangeSize)); + } + + void writeReg(uint8_t offset, uint32_t value); + uint32_t readReg(uint8_t offset); + + Port *getPort(const std::string &if_name, int idx = -1) + { + if (if_name == "int_port") + return intPort; + return PioDevice::getPort(if_name, idx); + } + + void signalInterrupt(int line); + void raiseInterruptPin(int number); + void lowerInterruptPin(int number); +}; + +}; // namespace X86ISA + +#endif //__DEV_X86_SOUTH_BRIDGE_I8254_HH__ diff --git a/src/dev/x86/i8237.cc b/src/dev/x86/i8237.cc new file mode 100644 index 000000000..f6ea9d75f --- /dev/null +++ b/src/dev/x86/i8237.cc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include "dev/x86/i8237.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" + +Tick +X86ISA::I8237::read(PacketPtr pkt) +{ + assert(pkt->getSize() == 1); + Addr offset = pkt->getAddr() - pioAddr; + switch (offset) { + case 0x0: + panic("Read from i8237 channel 0 current address unimplemented.\n"); + case 0x1: + panic("Read from i8237 channel 0 remaining " + "word count unimplemented.\n"); + case 0x2: + panic("Read from i8237 channel 1 current address unimplemented.\n"); + case 0x3: + panic("Read from i8237 channel 1 remaining " + "word count unimplemented.\n"); + case 0x4: + panic("Read from i8237 channel 2 current address unimplemented.\n"); + case 0x5: + panic("Read from i8237 channel 2 remaining " + "word count unimplemented.\n"); + case 0x6: + panic("Read from i8237 channel 3 current address unimplemented.\n"); + case 0x7: + panic("Read from i8237 channel 3 remaining " + "word count unimplemented.\n"); + case 0x8: + panic("Read from i8237 status register unimplemented.\n"); + default: + panic("Read from undefined i8237 register %d.\n", offset); + } + pkt->makeAtomicResponse(); + return latency; +} + +Tick +X86ISA::I8237::write(PacketPtr pkt) +{ + assert(pkt->getSize() == 1); + Addr offset = pkt->getAddr() - pioAddr; + switch (offset) { + case 0x0: + panic("Write to i8237 channel 0 starting address unimplemented.\n"); + case 0x1: + panic("Write to i8237 channel 0 starting " + "word count unimplemented.\n"); + case 0x2: + panic("Write to i8237 channel 1 starting address unimplemented.\n"); + case 0x3: + panic("Write to i8237 channel 1 starting " + "word count unimplemented.\n"); + case 0x4: + panic("Write to i8237 channel 2 starting address unimplemented.\n"); + case 0x5: + panic("Write to i8237 channel 2 starting " + "word count unimplemented.\n"); + case 0x6: + panic("Write to i8237 channel 3 starting address unimplemented.\n"); + case 0x7: + panic("Write to i8237 channel 3 starting " + "word count unimplemented.\n"); + case 0x8: + panic("Write to i8237 command register unimplemented.\n"); + case 0x9: + panic("Write to i8237 request register unimplemented.\n"); + case 0xa: + { + uint8_t command = pkt->get<uint8_t>(); + uint8_t select = bits(command, 1, 0); + uint8_t bitVal = bits(command, 2); + if (!bitVal) + panic("Turning on i8237 channels unimplemented.\n"); + replaceBits(maskReg, select, bitVal); + } + break; + case 0xb: + panic("Write to i8237 mode register unimplemented.\n"); + case 0xc: + panic("Write to i8237 clear LSB/MSB flip-flop " + "register unimplemented.\n"); + case 0xd: + panic("Write to i8237 master clear/reset register unimplemented.\n"); + case 0xe: + panic("Write to i8237 clear mask register unimplemented.\n"); + case 0xf: + panic("Write to i8237 write all mask register bits unimplemented.\n"); + default: + panic("Write to undefined i8254 register.\n"); + } + pkt->makeAtomicResponse(); + return latency; +} + +X86ISA::I8237 * +I8237Params::create() +{ + return new X86ISA::I8237(this); +} diff --git a/src/dev/x86/i8237.hh b/src/dev/x86/i8237.hh new file mode 100644 index 000000000..2d73b8ab5 --- /dev/null +++ b/src/dev/x86/i8237.hh @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#ifndef __DEV_X86_I8237_HH__ +#define __DEV_X86_I8237_HH__ + +#include "dev/io_device.hh" +#include "params/I8237.hh" + +namespace X86ISA +{ + +class I8237 : public BasicPioDevice +{ + protected: + Tick latency; + uint8_t maskReg; + + public: + typedef I8237Params Params; + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + + I8237(Params *p) : BasicPioDevice(p), latency(p->pio_latency), maskReg(0) + { + pioSize = 16; + } + Tick read(PacketPtr pkt); + + Tick write(PacketPtr pkt); +}; + +}; // namespace X86ISA + +#endif //__DEV_X86_I8237_HH__ diff --git a/src/dev/x86/opteron.cc b/src/dev/x86/i8254.cc index ba46f2dfa..5eb28844a 100644 --- a/src/dev/x86/opteron.cc +++ b/src/dev/x86/i8254.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2008 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,82 +28,56 @@ * Authors: Gabe Black */ -/** @file - * Implementation of Opteron platform. - */ - -#include <deque> -#include <string> -#include <vector> - -#include "arch/x86/x86_traits.hh" -#include "cpu/intr_control.hh" -#include "dev/simconsole.hh" -#include "dev/x86/opteron.hh" -#include "sim/system.hh" - -using namespace std; -using namespace TheISA; - -Opteron::Opteron(const Params *p) - : Platform(p), system(p->system) -{ - // set the back pointer from the system to myself - system->platform = this; -} - -Tick -Opteron::intrFrequency() -{ - panic("Need implementation\n"); - M5_DUMMY_RETURN -} - -void -Opteron::postConsoleInt() -{ - warn_once("Don't know what interrupt to post for console.\n"); - //panic("Need implementation\n"); -} +#include "dev/x86/i8254.hh" +#include "dev/x86/intdev.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" void -Opteron::clearConsoleInt() +X86ISA::I8254::counterInterrupt(unsigned int num) { - warn_once("Don't know what interrupt to clear for console.\n"); - //panic("Need implementation\n"); + DPRINTF(I8254, "Interrupt from counter %d.\n", num); + if (num == 0) { + intPin->raise(); + //XXX This is a hack. + intPin->lower(); + } } -void -Opteron::postPciInt(int line) -{ - panic("Need implementation\n"); -} - -void -Opteron::clearPciInt(int line) -{ - panic("Need implementation\n"); -} - -Addr -Opteron::pciToDma(Addr pciAddr) const +Tick +X86ISA::I8254::read(PacketPtr pkt) { - panic("Need implementation\n"); - M5_DUMMY_RETURN + assert(pkt->getSize() == 1); + Addr offset = pkt->getAddr() - pioAddr; + if (offset < 3) { + pkt->set(pit.readCounter(offset)); + } else if (offset == 3) { + pkt->set(uint8_t(-1)); + } else { + panic("Read from undefined i8254 register.\n"); + } + pkt->makeAtomicResponse(); + return latency; } - -Addr -Opteron::calcConfigAddr(int bus, int dev, int func) +Tick +X86ISA::I8254::write(PacketPtr pkt) { - assert(func < 8); - assert(dev < 32); - assert(bus == 0); - return (PhysAddrPrefixPciConfig | (func << 8) | (dev << 11)); + assert(pkt->getSize() == 1); + Addr offset = pkt->getAddr() - pioAddr; + if (offset < 3) { + pit.writeCounter(offset, pkt->get<uint8_t>()); + } else if (offset == 3) { + pit.writeControl(pkt->get<uint8_t>()); + } else { + panic("Write to undefined i8254 register.\n"); + } + pkt->makeAtomicResponse(); + return latency; } -Opteron * -OpteronParams::create() +X86ISA::I8254 * +I8254Params::create() { - return new Opteron(this); + return new X86ISA::I8254(this); } diff --git a/src/dev/x86/i8254.hh b/src/dev/x86/i8254.hh new file mode 100644 index 000000000..7de20dfd4 --- /dev/null +++ b/src/dev/x86/i8254.hh @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#ifndef __DEV_X86_I8254_HH__ +#define __DEV_X86_I8254_HH__ + +#include "dev/intel_8254_timer.hh" +#include "dev/io_device.hh" +#include "params/I8254.hh" + +namespace X86ISA +{ + +class IntSourcePin; + +class I8254 : public BasicPioDevice +{ + protected: + Tick latency; + class X86Intel8254Timer : public Intel8254Timer + { + protected: + I8254 * parent; + + void + counterInterrupt(unsigned int num) + { + parent->counterInterrupt(num); + } + + public: + X86Intel8254Timer(const std::string &name, I8254 * _parent) : + Intel8254Timer(_parent, name), parent(_parent) + {} + }; + + + X86Intel8254Timer pit; + + IntSourcePin *intPin; + + void counterInterrupt(unsigned int num); + + public: + typedef I8254Params Params; + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + + I8254(Params *p) : BasicPioDevice(p), latency(p->pio_latency), + pit(p->name, this), intPin(p->int_pin) + { + pioSize = 4; + } + Tick read(PacketPtr pkt); + + Tick write(PacketPtr pkt); + + bool + outputHigh(unsigned int num) + { + return pit.outputHigh(num); + } + + uint8_t + readCounter(unsigned int num) + { + return pit.readCounter(num); + } + + void + writeCounter(unsigned int num, const uint8_t data) + { + pit.writeCounter(num, data); + } + + void + writeControl(uint8_t val) + { + pit.writeControl(val); + } +}; + +}; // namespace X86ISA + +#endif //__DEV_X86_SOUTH_BRIDGE_I8254_HH__ diff --git a/src/dev/x86/i8259.cc b/src/dev/x86/i8259.cc new file mode 100644 index 000000000..b868295eb --- /dev/null +++ b/src/dev/x86/i8259.cc @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include "base/bitfield.hh" +#include "dev/x86/i82094aa.hh" +#include "dev/x86/i8259.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" + +X86ISA::I8259::I8259(Params * p) : BasicPioDevice(p), IntDev(this), + latency(p->pio_latency), output(p->output), + mode(p->mode), slave(p->slave), + IRR(0), ISR(0), IMR(0), + readIRR(true), initControlWord(0), autoEOI(false) +{ + for (int i = 0; i < NumLines; i++) + pinStates[i] = false; + pioSize = 2; +} + +Tick +X86ISA::I8259::read(PacketPtr pkt) +{ + assert(pkt->getSize() == 1); + switch(pkt->getAddr() - pioAddr) + { + case 0x0: + if (readIRR) { + DPRINTF(I8259, "Reading IRR as %#x.\n", IRR); + pkt->set(IRR); + } else { + DPRINTF(I8259, "Reading ISR as %#x.\n", ISR); + pkt->set(ISR); + } + break; + case 0x1: + DPRINTF(I8259, "Reading IMR as %#x.\n", IMR); + pkt->set(IMR); + break; + } + pkt->makeAtomicResponse(); + return latency; +} + +Tick +X86ISA::I8259::write(PacketPtr pkt) +{ + assert(pkt->getSize() == 1); + uint8_t val = pkt->get<uint8_t>(); + switch (pkt->getAddr() - pioAddr) { + case 0x0: + if (bits(val, 4)) { + DPRINTF(I8259, "Received initialization command word 1.\n"); + IMR = 0; + edgeTriggered = bits(val, 3); + DPRINTF(I8259, "%s triggered mode.\n", + edgeTriggered ? "Edge" : "Level"); + cascadeMode = !bits(val, 1); + DPRINTF(I8259, "%s mode.\n", + cascadeMode ? "Cascade" : "Single"); + expectICW4 = bits(val, 0); + if (!expectICW4) { + autoEOI = false; + } + initControlWord = 1; + DPRINTF(I8259, "Expecting %d more bytes.\n", expectICW4 ? 3 : 2); + } else if (bits(val, 4, 3) == 0) { + DPRINTF(I8259, "Received operation command word 2.\n"); + switch (bits(val, 7, 5)) { + case 0x0: + DPRINTF(I8259, + "Subcommand: Rotate in auto-EOI mode (clear).\n"); + break; + case 0x1: + { + int line = findMsbSet(ISR); + DPRINTF(I8259, "Subcommand: Nonspecific EOI on line %d.\n", + line); + handleEOI(line); + } + break; + case 0x2: + DPRINTF(I8259, "Subcommand: No operation.\n"); + break; + case 0x3: + { + int line = bits(val, 2, 0); + DPRINTF(I8259, "Subcommand: Specific EIO on line %d.\n", + line); + handleEOI(line); + } + break; + case 0x4: + DPRINTF(I8259, "Subcommand: Rotate in auto-EOI mode (set).\n"); + break; + case 0x5: + DPRINTF(I8259, "Subcommand: Rotate on nonspecific EOI.\n"); + break; + case 0x6: + DPRINTF(I8259, "Subcommand: Set priority command.\n"); + DPRINTF(I8259, "Lowest: IRQ%d Highest IRQ%d.\n", + bits(val, 2, 0), (bits(val, 2, 0) + 1) % 8); + break; + case 0x7: + DPRINTF(I8259, "Subcommand: Rotate on specific EOI.\n"); + DPRINTF(I8259, "Lowest: IRQ%d Highest IRQ%d.\n", + bits(val, 2, 0), (bits(val, 2, 0) + 1) % 8); + break; + } + } else if (bits(val, 4, 3) == 1) { + DPRINTF(I8259, "Received operation command word 3.\n"); + if (bits(val, 7)) { + DPRINTF(I8259, "%s special mask mode.\n", + bits(val, 6) ? "Set" : "Clear"); + } + if (bits(val, 1)) { + readIRR = bits(val, 0); + DPRINTF(I8259, "Read %s.\n", readIRR ? "IRR" : "ISR"); + } + } + break; + case 0x1: + switch (initControlWord) { + case 0x0: + DPRINTF(I8259, "Received operation command word 1.\n"); + DPRINTF(I8259, "Wrote IMR value %#x.\n", val); + IMR = val; + break; + case 0x1: + DPRINTF(I8259, "Received initialization command word 2.\n"); + vectorOffset = val & ~mask(3); + DPRINTF(I8259, "Responsible for vectors %#x-%#x.\n", + vectorOffset, vectorOffset | mask(3)); + if (cascadeMode) { + initControlWord++; + } else { + cascadeBits = 0; + initControlWord = 0; + } + break; + case 0x2: + DPRINTF(I8259, "Received initialization command word 3.\n"); + if (mode == Enums::I8259Master) { + DPRINTF(I8259, "Slaves attached to IRQs:%s%s%s%s%s%s%s%s\n", + bits(val, 0) ? " 0" : "", + bits(val, 1) ? " 1" : "", + bits(val, 2) ? " 2" : "", + bits(val, 3) ? " 3" : "", + bits(val, 4) ? " 4" : "", + bits(val, 5) ? " 5" : "", + bits(val, 6) ? " 6" : "", + bits(val, 7) ? " 7" : ""); + cascadeBits = val; + } else { + DPRINTF(I8259, "Slave ID is %d.\n", val & mask(3)); + cascadeBits = val & mask(3); + } + if (expectICW4) + initControlWord++; + else + initControlWord = 0; + break; + case 0x3: + DPRINTF(I8259, "Received initialization command word 4.\n"); + if (bits(val, 4)) { + DPRINTF(I8259, "Special fully nested mode.\n"); + } else { + DPRINTF(I8259, "Not special fully nested mode.\n"); + } + if (bits(val, 3) == 0) { + DPRINTF(I8259, "Nonbuffered.\n"); + } else if (bits(val, 2) == 0) { + DPRINTF(I8259, "Buffered.\n"); + } else { + DPRINTF(I8259, "Unrecognized buffer mode.\n"); + } + autoEOI = bits(val, 1); + DPRINTF(I8259, "%s End Of Interrupt.\n", + autoEOI ? "Automatic" : "Normal"); + + DPRINTF(I8259, "%s mode.\n", bits(val, 0) ? "80x86" : "MCX-80/85"); + initControlWord = 0; + break; + } + break; + } + pkt->makeAtomicResponse(); + return latency; +} + +void +X86ISA::I8259::handleEOI(int line) +{ + ISR &= ~(1 << line); + // There may be an interrupt that was waiting which can + // now be sent. + if (IRR) + requestInterrupt(findMsbSet(IRR)); +} + +void +X86ISA::I8259::requestInterrupt(int line) +{ + if (bits(ISR, 7, line) == 0) { + if (output) { + DPRINTF(I8259, "Propogating interrupt.\n"); + output->raise(); + //XXX This is a hack. + output->lower(); + } else { + warn("Received interrupt but didn't have " + "anyone to tell about it.\n"); + } + } +} + +void +X86ISA::I8259::signalInterrupt(int line) +{ + DPRINTF(I8259, "Interrupt requested for line %d.\n", line); + if (line >= NumLines) + fatal("Line number %d doesn't exist. The max is %d.\n", + line, NumLines - 1); + if (bits(IMR, line)) { + DPRINTF(I8259, "Interrupt %d was masked.\n", line); + } else { + IRR |= 1 << line; + requestInterrupt(line); + } +} + +void +X86ISA::I8259::raiseInterruptPin(int number) +{ + DPRINTF(I8259, "Interrupt signal raised for pin %d.\n", number); + if (number >= NumLines) + fatal("Line number %d doesn't exist. The max is %d.\n", + number, NumLines - 1); + if (!pinStates[number]) + signalInterrupt(number); + pinStates[number] = true; +} + +void +X86ISA::I8259::lowerInterruptPin(int number) +{ + DPRINTF(I8259, "Interrupt signal lowered for pin %d.\n", number); + if (number >= NumLines) + fatal("Line number %d doesn't exist. The max is %d.\n", + number, NumLines - 1); + pinStates[number] = false; +} + +int +X86ISA::I8259::getVector() +{ + /* + * This code only handles one slave. Since that's how the PC platform + * always uses the 8259 PIC, there shouldn't be any need for more. If + * there -is- a need for more for some reason, "slave" can become a + * vector of slaves. + */ + int line = findMsbSet(IRR); + IRR &= ~(1 << line); + DPRINTF(I8259, "Interrupt %d was accepted.\n", line); + if (autoEOI) { + handleEOI(line); + } else { + ISR |= 1 << line; + } + if (slave && bits(cascadeBits, line)) { + DPRINTF(I8259, "Interrupt was from slave who will " + "provide the vector.\n"); + return slave->getVector(); + } + return line | vectorOffset; +} + +X86ISA::I8259 * +I8259Params::create() +{ + return new X86ISA::I8259(this); +} diff --git a/src/dev/x86/i8259.hh b/src/dev/x86/i8259.hh new file mode 100644 index 000000000..dfb56646a --- /dev/null +++ b/src/dev/x86/i8259.hh @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#ifndef __DEV_X86_I8259_HH__ +#define __DEV_X86_I8259_HH__ + +#include "dev/io_device.hh" +#include "dev/x86/intdev.hh" +#include "params/I8259.hh" +#include "enums/X86I8259CascadeMode.hh" + +namespace X86ISA +{ + +class I82094AA; + +class I8259 : public BasicPioDevice, public IntDev +{ + protected: + static const int NumLines = 8; + bool pinStates[NumLines]; + + Tick latency; + IntSourcePin *output; + Enums::X86I8259CascadeMode mode; + I8259 * slave; + + // Interrupt Request Register + uint8_t IRR; + // In Service Register + uint8_t ISR; + // Interrupt Mask Register + uint8_t IMR; + + // The higher order bits of the vector to return + uint8_t vectorOffset; + + bool cascadeMode; + // A bit vector of lines with slaves attached, or the slave id, depending + // on if this is a master or slave PIC. + uint8_t cascadeBits; + + bool edgeTriggered; + bool readIRR; + + // State machine information for reading in initialization control words. + bool expectICW4; + int initControlWord; + + // Whether or not the PIC is in auto EOI mode. + bool autoEOI; + + void requestInterrupt(int line); + void handleEOI(int line); + + public: + typedef I8259Params Params; + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + + I8259(Params * p); + + Tick read(PacketPtr pkt); + Tick write(PacketPtr pkt); + + void signalInterrupt(int line); + void raiseInterruptPin(int number); + void lowerInterruptPin(int number); + int getVector(); +}; + +}; // namespace X86ISA + +#endif //__DEV_X86_I8259_HH__ diff --git a/src/dev/x86/intdev.cc b/src/dev/x86/intdev.cc new file mode 100644 index 000000000..e386687a9 --- /dev/null +++ b/src/dev/x86/intdev.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include "dev/x86/intdev.hh" + +X86ISA::IntSourcePin * +X86IntSourcePinParams::create() +{ + return new X86ISA::IntSourcePin(this); +} + +X86ISA::IntSinkPin * +X86IntSinkPinParams::create() +{ + return new X86ISA::IntSinkPin(this); +} + +X86ISA::IntLine * +X86IntLineParams::create() +{ + return new X86ISA::IntLine(this); +} diff --git a/src/dev/x86/intdev.hh b/src/dev/x86/intdev.hh new file mode 100644 index 000000000..ca8e7eea5 --- /dev/null +++ b/src/dev/x86/intdev.hh @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#ifndef __DEV_X86_INTDEV_HH__ +#define __DEV_X86_INTDEV_HH__ + +#include <assert.h> +#include <string> + +#include "arch/x86/x86_traits.hh" +#include "mem/mem_object.hh" +#include "mem/mport.hh" +#include "sim/sim_object.hh" +#include "params/X86IntSourcePin.hh" +#include "params/X86IntSinkPin.hh" +#include "params/X86IntLine.hh" + +namespace X86ISA { + +class IntDev +{ + protected: + class IntPort : public MessagePort + { + IntDev * device; + Tick latency; + Addr intAddr; + public: + IntPort(const std::string &_name, MemObject * _parent, + IntDev *dev, Tick _latency) : + MessagePort(_name, _parent), device(dev), latency(_latency) + { + } + + void getDeviceAddressRanges(AddrRangeList &resp, bool &snoop) + { + snoop = false; + device->getIntAddrRange(resp); + } + + Tick recvMessage(PacketPtr pkt) + { + return device->recvMessage(pkt); + } + + void recvStatusChange(Status status) + { + if (status == RangeChange) { + sendStatusChange(Port::RangeChange); + } + } + + }; + + IntPort * intPort; + + public: + IntDev(MemObject * parent, Tick latency = 0) + { + if (parent != NULL) { + intPort = new IntPort(parent->name() + ".int_port", + parent, this, latency); + } else { + intPort = NULL; + } + } + + virtual ~IntDev() + {} + + virtual void + signalInterrupt(int line) + { + panic("signalInterrupt not implemented.\n"); + } + + virtual void + raiseInterruptPin(int number) + { + panic("raiseInterruptPin not implemented.\n"); + } + + virtual void + lowerInterruptPin(int number) + { + panic("lowerInterruptPin not implemented.\n"); + } + + virtual Tick + recvMessage(PacketPtr pkt) + { + panic("recvMessage not implemented.\n"); + return 0; + } + + virtual void + getIntAddrRange(AddrRangeList &range_list) + { + panic("intAddrRange not implemented.\n"); + } +}; + +class IntSinkPin : public SimObject +{ + public: + IntDev * device; + int number; + + typedef X86IntSinkPinParams Params; + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + + IntSinkPin(Params *p) : SimObject(p), + device(dynamic_cast<IntDev *>(p->device)), number(p->number) + { + assert(device); + } +}; + +class IntSourcePin : public SimObject +{ + protected: + std::vector<IntSinkPin *> sinks; + + public: + typedef X86IntSourcePinParams Params; + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + + void + addSink(IntSinkPin *sink) + { + sinks.push_back(sink); + } + + void + raise() + { + for (int i = 0; i < sinks.size(); i++) { + const IntSinkPin &pin = *sinks[i]; + pin.device->raiseInterruptPin(pin.number); + } + } + + void + lower() + { + for (int i = 0; i < sinks.size(); i++) { + const IntSinkPin &pin = *sinks[i]; + pin.device->lowerInterruptPin(pin.number); + } + } + + IntSourcePin(Params *p) : SimObject(p) + {} +}; + +class IntLine : public SimObject +{ + protected: + IntSourcePin *source; + IntSinkPin *sink; + + public: + typedef X86IntLineParams Params; + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + + IntLine(Params *p) : SimObject(p), source(p->source), sink(p->sink) + { + source->addSink(sink); + } +}; + +}; // namespace X86ISA + +#endif //__DEV_X86_INTDEV_HH__ diff --git a/src/dev/x86/pc.cc b/src/dev/x86/pc.cc new file mode 100644 index 000000000..d1ab4af7f --- /dev/null +++ b/src/dev/x86/pc.cc @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +/** @file + * Implementation of PC platform. + */ + +#include <deque> +#include <string> +#include <vector> + +#include "arch/x86/intmessage.hh" +#include "arch/x86/x86_traits.hh" +#include "cpu/intr_control.hh" +#include "dev/terminal.hh" +#include "dev/x86/i82094aa.hh" +#include "dev/x86/i8254.hh" +#include "dev/x86/i8259.hh" +#include "dev/x86/pc.hh" +#include "dev/x86/south_bridge.hh" +#include "sim/system.hh" + +using namespace std; +using namespace TheISA; + +Pc::Pc(const Params *p) + : Platform(p), system(p->system) +{ + southBridge = NULL; + // set the back pointer from the system to myself + system->platform = this; +} + +void +Pc::init() +{ + assert(southBridge); + + /* + * Initialize the timer. + */ + I8254 & timer = *southBridge->pit; + //Timer 0, mode 2, no bcd, 16 bit count + timer.writeControl(0x34); + //Timer 0, latch command + timer.writeControl(0x00); + //Write a 16 bit count of 0 + timer.writeCounter(0, 0); + timer.writeCounter(0, 0); + + /* + * Initialize the I/O APIC. + */ + I82094AA & ioApic = *southBridge->ioApic; + I82094AA::RedirTableEntry entry = 0; + entry.deliveryMode = DeliveryMode::ExtInt; + entry.vector = 0x20; + ioApic.writeReg(0x10, entry.bottomDW); + ioApic.writeReg(0x11, entry.topDW); + entry.deliveryMode = DeliveryMode::Fixed; + entry.vector = 0x24; + ioApic.writeReg(0x18, entry.bottomDW); + ioApic.writeReg(0x19, entry.topDW); + entry.mask = 1; + entry.vector = 0x21; + ioApic.writeReg(0x12, entry.bottomDW); + ioApic.writeReg(0x13, entry.topDW); + entry.vector = 0x20; + ioApic.writeReg(0x14, entry.bottomDW); + ioApic.writeReg(0x15, entry.topDW); + entry.vector = 0x28; + ioApic.writeReg(0x20, entry.bottomDW); + ioApic.writeReg(0x21, entry.topDW); + entry.vector = 0x2C; + ioApic.writeReg(0x28, entry.bottomDW); + ioApic.writeReg(0x29, entry.topDW); + entry.vector = 0x2E; + ioApic.writeReg(0x2C, entry.bottomDW); + ioApic.writeReg(0x2D, entry.topDW); + entry.vector = 0x30; + ioApic.writeReg(0x30, entry.bottomDW); + ioApic.writeReg(0x31, entry.topDW); +} + +Tick +Pc::intrFrequency() +{ + panic("Need implementation for intrFrequency\n"); + M5_DUMMY_RETURN +} + +void +Pc::postConsoleInt() +{ + southBridge->ioApic->signalInterrupt(4); + southBridge->pic1->signalInterrupt(4); +} + +void +Pc::clearConsoleInt() +{ + warn_once("Don't know what interrupt to clear for console.\n"); + //panic("Need implementation\n"); +} + +void +Pc::postPciInt(int line) +{ + southBridge->ioApic->signalInterrupt(line); +} + +void +Pc::clearPciInt(int line) +{ + warn_once("Tried to clear PCI interrupt %d\n", line); +} + +Addr +Pc::pciToDma(Addr pciAddr) const +{ + return pciAddr; +} + +Addr +Pc::calcPciConfigAddr(int bus, int dev, int func) +{ + assert(func < 8); + assert(dev < 32); + assert(bus == 0); + return (PhysAddrPrefixPciConfig | (func << 8) | (dev << 11)); +} + +Addr +Pc::calcPciIOAddr(Addr addr) +{ + return PhysAddrPrefixIO + addr; +} + +Addr +Pc::calcPciMemAddr(Addr addr) +{ + return addr; +} + +Pc * +PcParams::create() +{ + return new Pc(this); +} diff --git a/src/dev/x86/opteron.hh b/src/dev/x86/pc.hh index 3026bce73..427cc4165 100644 --- a/src/dev/x86/opteron.hh +++ b/src/dev/x86/pc.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2008 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,29 +30,36 @@ /** * @file - * Declaration of top level class for the Opteron platform chips. This class + * Declaration of top level class for PC platform components. This class * just retains pointers to all its children so the children can communicate. */ -#ifndef __DEV_Opteron_HH__ -#define __DEV_Opteron_HH__ +#ifndef __DEV_PC_HH__ +#define __DEV_PC_HH__ #include "dev/platform.hh" -#include "params/Opteron.hh" +#include "params/Pc.hh" class IdeController; class System; +class SouthBridge; -class Opteron : public Platform +class Pc : public Platform { public: /** Pointer to the system */ System *system; + SouthBridge *southBridge; public: - typedef OpteronParams Params; + typedef PcParams Params; - Opteron(const Params *p); + /** + * Do platform initialization stuff + */ + void init(); + + Pc(const Params *p); /** * Return the interrupting frequency to AlphaAccess @@ -86,7 +93,17 @@ class Opteron : public Platform /** * Calculate the configuration address given a bus/dev/func. */ - virtual Addr calcConfigAddr(int bus, int dev, int func); + virtual Addr calcPciConfigAddr(int bus, int dev, int func); + + /** + * Calculate the address for an IO location on the PCI bus. + */ + virtual Addr calcPciIOAddr(Addr addr); + + /** + * Calculate the address for a memory location on the PCI bus. + */ + virtual Addr calcPciMemAddr(Addr addr); }; -#endif // __DEV_OPTERON_HH__ +#endif // __DEV_PC_HH__ diff --git a/src/dev/x86/south_bridge.cc b/src/dev/x86/south_bridge.cc new file mode 100644 index 000000000..c456f478d --- /dev/null +++ b/src/dev/x86/south_bridge.cc @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include <assert.h> + +#include "dev/x86/pc.hh" +#include "dev/x86/south_bridge.hh" + +using namespace X86ISA; + +SouthBridge::SouthBridge(const Params *p) : SimObject(p), + platform(p->platform), pit(p->pit), pic1(p->pic1), pic2(p->pic2), + cmos(p->cmos), speaker(p->speaker), ioApic(p->io_apic) +{ + // Let the platform know where we are + Pc * pc = dynamic_cast<Pc *>(platform); + assert(pc); + pc->southBridge = this; +} + +SouthBridge * +SouthBridgeParams::create() +{ + return new SouthBridge(this); +} diff --git a/src/dev/x86/south_bridge.hh b/src/dev/x86/south_bridge.hh new file mode 100644 index 000000000..61d6d387a --- /dev/null +++ b/src/dev/x86/south_bridge.hh @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#ifndef __DEV_X86_SOUTH_BRIDGE_HH__ +#define __DEV_X86_SOUTH_BRIDGE_HH__ + +#include "sim/sim_object.hh" +#include "params/SouthBridge.hh" + +namespace X86ISA +{ + class I8254; + class I8259; + class Cmos; + class Speaker; + class I82094AA; +} + +class SouthBridge : public SimObject +{ + protected: + Platform * platform; + + public: + X86ISA::I8254 * pit; + X86ISA::I8259 * pic1; + X86ISA::I8259 * pic2; + X86ISA::Cmos * cmos; + X86ISA::Speaker * speaker; + X86ISA::I82094AA * ioApic; + + public: + typedef SouthBridgeParams Params; + SouthBridge(const Params *p); + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } +}; + +#endif //__DEV_X86_SOUTH_BRIDGE_HH__ diff --git a/src/dev/x86/speaker.cc b/src/dev/x86/speaker.cc new file mode 100644 index 000000000..c6eb9db9e --- /dev/null +++ b/src/dev/x86/speaker.cc @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2008 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Gabe Black + */ + +#include "base/bitunion.hh" +#include "base/trace.hh" +#include "dev/x86/i8254.hh" +#include "dev/x86/speaker.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" + +Tick +X86ISA::Speaker::read(PacketPtr pkt) +{ + assert(pkt->getAddr() == pioAddr); + assert(pkt->getSize() == 1); + controlVal.timer = timer->outputHigh(2) ? 1 : 0; + DPRINTF(PcSpeaker, + "Reading from speaker device: gate %s, speaker %s, output %s.\n", + controlVal.gate ? "on" : "off", + controlVal.speaker ? "on" : "off", + controlVal.timer ? "on" : "off"); + pkt->set((uint8_t)controlVal); + pkt->makeAtomicResponse(); + return latency; +} + +Tick +X86ISA::Speaker::write(PacketPtr pkt) +{ + assert(pkt->getAddr() == pioAddr); + assert(pkt->getSize() == 1); + SpeakerControl val = pkt->get<uint8_t>(); + controlVal.gate = val.gate; + //Change the gate value in the timer. + if (!val.gate) + warn("The gate bit of the pc speaker isn't implemented and " + "is always on.\n"); + //This would control whether the timer output is hooked up to a physical + //speaker. Since M5 can't make noise, it's value doesn't actually do + //anything. + controlVal.speaker = val.speaker; + DPRINTF(PcSpeaker, "Writing to speaker device: gate %s, speaker %s.\n", + controlVal.gate ? "on" : "off", controlVal.speaker ? "on" : "off"); + pkt->makeAtomicResponse(); + return latency; +} + +X86ISA::Speaker * +PcSpeakerParams::create() +{ + return new X86ISA::Speaker(this); +} diff --git a/src/dev/pitreg.h b/src/dev/x86/speaker.hh index d42925a41..6778dcb28 100644 --- a/src/dev/pitreg.h +++ b/src/dev/x86/speaker.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2005 The Regents of The University of Michigan + * Copyright (c) 2008 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,51 +25,55 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Authors: Miguel Serrano + * Authors: Gabe Black */ -/* @file - * Device register definitions for a device's PCI config space - */ +#ifndef __DEV_X86_SPEAKER_HH__ +#define __DEV_X86_SPEAKER_HH__ + +#include "base/bitunion.hh" +#include "params/PcSpeaker.hh" + +namespace X86ISA +{ + +class I8254; -#ifndef __PITREG_H__ -#define __PITREG_H__ +class Speaker : public BasicPioDevice +{ + protected: + Tick latency; -#include <sys/types.h> + BitUnion8(SpeakerControl) + Bitfield<0> gate; + Bitfield<1> speaker; + Bitfield<5> timer; + EndBitUnion(SpeakerControl) -// Control Word Format + SpeakerControl controlVal; -#define PIT_SEL_SHFT 0x6 -#define PIT_RW_SHFT 0x4 -#define PIT_MODE_SHFT 0x1 -#define PIT_BCD_SHFT 0x0 + I8254 * timer; -#define PIT_SEL_MASK 0x3 -#define PIT_RW_MASK 0x3 -#define PIT_MODE_MASK 0x7 -#define PIT_BCD_MASK 0x1 + public: + typedef PcSpeakerParams Params; -#define GET_CTRL_FIELD(x, s, m) (((x) >> s) & m) -#define GET_CTRL_SEL(x) GET_CTRL_FIELD(x, PIT_SEL_SHFT, PIT_SEL_MASK) -#define GET_CTRL_RW(x) GET_CTRL_FIELD(x, PIT_RW_SHFT, PIT_RW_MASK) -#define GET_CTRL_MODE(x) GET_CTRL_FIELD(x, PIT_MODE_SHFT, PIT_MODE_MASK) -#define GET_CTRL_BCD(x) GET_CTRL_FIELD(x, PIT_BCD_SHFT, PIT_BCD_MASK) + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } -#define PIT_READ_BACK 0x3 + Speaker(Params *p) : BasicPioDevice(p), + latency(p->pio_latency), controlVal(0), timer(p->i8254) + { + pioSize = 1; + } -#define PIT_RW_LATCH_COMMAND 0x0 -#define PIT_RW_LSB_ONLY 0x1 -#define PIT_RW_MSB_ONLY 0x2 -#define PIT_RW_16BIT 0x3 + Tick read(PacketPtr pkt); -#define PIT_MODE_INTTC 0x0 -#define PIT_MODE_ONESHOT 0x1 -#define PIT_MODE_RATEGEN 0x2 -#define PIT_MODE_SQWAVE 0x3 -#define PIT_MODE_SWSTROBE 0x4 -#define PIT_MODE_HWSTROBE 0x5 + Tick write(PacketPtr pkt); +}; -#define PIT_BCD_FALSE 0x0 -#define PIT_BCD_TRUE 0x1 +}; // namespace X86ISA -#endif // __PITREG_H__ +#endif //__DEV_X86_SPEAKER_HH__ |