summaryrefslogtreecommitdiff
path: root/src/dev/storage
diff options
context:
space:
mode:
authorAndreas Sandberg <andreas.sandberg@arm.com>2015-12-10 10:35:23 +0000
committerAndreas Sandberg <andreas.sandberg@arm.com>2015-12-10 10:35:23 +0000
commit8ec5fc66321549ebda555e7c16ddaf158b021d2b (patch)
tree7053dc6b5b74e51efba35e24efcb203499c066b9 /src/dev/storage
parent23c961a0fd97251ee7c760bc2ff2a011a417ad9b (diff)
downloadgem5-8ec5fc66321549ebda555e7c16ddaf158b021d2b.tar.xz
dev: Move storage devices to src/dev/storage/
Move the IDE controller and the disk implementations to src/dev/storage. --HG-- rename : src/dev/DiskImage.py => src/dev/storage/DiskImage.py rename : src/dev/Ide.py => src/dev/storage/Ide.py rename : src/dev/SimpleDisk.py => src/dev/storage/SimpleDisk.py rename : src/dev/disk_image.cc => src/dev/storage/disk_image.cc rename : src/dev/disk_image.hh => src/dev/storage/disk_image.hh rename : src/dev/ide_atareg.h => src/dev/storage/ide_atareg.h rename : src/dev/ide_ctrl.cc => src/dev/storage/ide_ctrl.cc rename : src/dev/ide_ctrl.hh => src/dev/storage/ide_ctrl.hh rename : src/dev/ide_disk.cc => src/dev/storage/ide_disk.cc rename : src/dev/ide_disk.hh => src/dev/storage/ide_disk.hh rename : src/dev/ide_wdcreg.h => src/dev/storage/ide_wdcreg.h rename : src/dev/simple_disk.cc => src/dev/storage/simple_disk.cc rename : src/dev/simple_disk.hh => src/dev/storage/simple_disk.hh
Diffstat (limited to 'src/dev/storage')
-rw-r--r--src/dev/storage/DiskImage.py48
-rw-r--r--src/dev/storage/Ide.py70
-rw-r--r--src/dev/storage/SConscript73
-rw-r--r--src/dev/storage/SimpleDisk.py37
-rw-r--r--src/dev/storage/disk_image.cc432
-rw-r--r--src/dev/storage/disk_image.hh157
-rw-r--r--src/dev/storage/ide_atareg.h293
-rw-r--r--src/dev/storage/ide_ctrl.cc650
-rw-r--r--src/dev/storage/ide_ctrl.hh159
-rw-r--r--src/dev/storage/ide_disk.cc1203
-rw-r--r--src/dev/storage/ide_disk.hh373
-rw-r--r--src/dev/storage/ide_wdcreg.h197
-rw-r--r--src/dev/storage/simple_disk.cc100
-rw-r--r--src/dev/storage/simple_disk.hh65
14 files changed, 3857 insertions, 0 deletions
diff --git a/src/dev/storage/DiskImage.py b/src/dev/storage/DiskImage.py
new file mode 100644
index 000000000..5a3bc54e1
--- /dev/null
+++ b/src/dev/storage/DiskImage.py
@@ -0,0 +1,48 @@
+# Copyright (c) 2005-2007 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
+
+from m5.SimObject import SimObject
+from m5.params import *
+class DiskImage(SimObject):
+ type = 'DiskImage'
+ abstract = True
+ cxx_header = "dev/storage/disk_image.hh"
+ image_file = Param.String("disk image file")
+ read_only = Param.Bool(False, "read only image")
+
+class RawDiskImage(DiskImage):
+ type = 'RawDiskImage'
+ cxx_header = "dev/storage/disk_image.hh"
+
+class CowDiskImage(DiskImage):
+ type = 'CowDiskImage'
+ cxx_header = "dev/storage/disk_image.hh"
+ child = Param.DiskImage(RawDiskImage(read_only=True),
+ "child image")
+ table_size = Param.Int(65536, "initial table size")
+ image_file = ""
diff --git a/src/dev/storage/Ide.py b/src/dev/storage/Ide.py
new file mode 100644
index 000000000..fc3f356f0
--- /dev/null
+++ b/src/dev/storage/Ide.py
@@ -0,0 +1,70 @@
+# Copyright (c) 2005-2007 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
+
+from m5.SimObject import SimObject
+from m5.params import *
+from PciDevice import PciDevice
+
+class IdeID(Enum): vals = ['master', 'slave']
+
+class IdeDisk(SimObject):
+ type = 'IdeDisk'
+ cxx_header = "dev/storage/ide_disk.hh"
+ delay = Param.Latency('1us', "Fixed disk delay in microseconds")
+ driveID = Param.IdeID('master', "Drive ID")
+ image = Param.DiskImage("Disk image")
+
+class IdeController(PciDevice):
+ type = 'IdeController'
+ cxx_header = "dev/storage/ide_ctrl.hh"
+ disks = VectorParam.IdeDisk("IDE disks attached to this controller")
+
+ VendorID = 0x8086
+ DeviceID = 0x7111
+ Command = 0x0
+ Status = 0x280
+ Revision = 0x0
+ ClassCode = 0x01
+ SubClassCode = 0x01
+ ProgIF = 0x85
+ BAR0 = 0x00000001
+ BAR1 = 0x00000001
+ BAR2 = 0x00000001
+ BAR3 = 0x00000001
+ BAR4 = 0x00000001
+ BAR5 = 0x00000001
+ InterruptLine = 0x1f
+ InterruptPin = 0x01
+ BAR0Size = '8B'
+ BAR1Size = '4B'
+ BAR2Size = '8B'
+ BAR3Size = '4B'
+ BAR4Size = '16B'
+
+ io_shift = Param.UInt32(0x0, "IO port shift");
+ ctrl_offset = Param.UInt32(0x0, "IDE disk control offset")
diff --git a/src/dev/storage/SConscript b/src/dev/storage/SConscript
new file mode 100644
index 000000000..b5ddece04
--- /dev/null
+++ b/src/dev/storage/SConscript
@@ -0,0 +1,73 @@
+# -*- mode:python -*-
+
+# Copyright (c) 2015 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Copyright (c) 2006 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: Steve Reinhardt
+# Gabe Black
+# Andreas Sandberg
+
+Import('*')
+
+if env['TARGET_ISA'] == 'null':
+ Return()
+
+# Controllers
+SimObject('Ide.py')
+
+Source('ide_ctrl.cc')
+Source('ide_disk.cc')
+
+DebugFlag('IdeCtrl')
+DebugFlag('IdeDisk')
+
+# Disk models
+SimObject('DiskImage.py')
+SimObject('SimpleDisk.py')
+
+Source('disk_image.cc')
+Source('simple_disk.cc')
+
+DebugFlag('DiskImageRead')
+DebugFlag('DiskImageWrite')
+DebugFlag('SimpleDisk')
+DebugFlag('SimpleDiskData')
+
+
+CompoundFlag('DiskImageAll', [ 'DiskImageRead', 'DiskImageWrite' ])
+CompoundFlag('IdeAll', [ 'IdeCtrl', 'IdeDisk' ])
diff --git a/src/dev/storage/SimpleDisk.py b/src/dev/storage/SimpleDisk.py
new file mode 100644
index 000000000..01b41ee0e
--- /dev/null
+++ b/src/dev/storage/SimpleDisk.py
@@ -0,0 +1,37 @@
+# Copyright (c) 2005-2007 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
+
+from m5.SimObject import SimObject
+from m5.params import *
+from m5.proxy import *
+
+class SimpleDisk(SimObject):
+ type = 'SimpleDisk'
+ cxx_header = "dev/storage/simple_disk.hh"
+ disk = Param.DiskImage("Disk Image")
+ system = Param.System(Parent.any, "System Pointer")
diff --git a/src/dev/storage/disk_image.cc b/src/dev/storage/disk_image.cc
new file mode 100644
index 000000000..24688f55a
--- /dev/null
+++ b/src/dev/storage/disk_image.cc
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2001-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
+ */
+
+/** @file
+ * Disk Image Definitions
+ */
+
+#include "dev/storage/disk_image.hh"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstring>
+#include <fstream>
+#include <string>
+
+#include "base/callback.hh"
+#include "base/misc.hh"
+#include "base/trace.hh"
+#include "debug/DiskImageRead.hh"
+#include "debug/DiskImageWrite.hh"
+#include "sim/byteswap.hh"
+#include "sim/sim_exit.hh"
+
+using namespace std;
+
+////////////////////////////////////////////////////////////////////////
+//
+// Raw Disk image
+//
+RawDiskImage::RawDiskImage(const Params* p)
+ : DiskImage(p), disk_size(0)
+{ open(p->image_file, p->read_only); }
+
+RawDiskImage::~RawDiskImage()
+{ close(); }
+
+void
+RawDiskImage::open(const string &filename, bool rd_only)
+{
+ if (!filename.empty()) {
+ initialized = true;
+ readonly = rd_only;
+ file = filename;
+
+ ios::openmode mode = ios::in | ios::binary;
+ if (!readonly)
+ mode |= ios::out;
+ stream.open(file.c_str(), mode);
+ if (!stream.is_open())
+ panic("Error opening %s", filename);
+ }
+}
+
+void
+RawDiskImage::close()
+{
+ stream.close();
+}
+
+std::streampos
+RawDiskImage::size() const
+{
+ if (disk_size == 0) {
+ if (!stream.is_open())
+ panic("file not open!\n");
+ stream.seekg(0, ios::end);
+ disk_size = stream.tellg();
+ }
+
+ return disk_size / SectorSize;
+}
+
+std::streampos
+RawDiskImage::read(uint8_t *data, std::streampos offset) const
+{
+ if (!initialized)
+ panic("RawDiskImage not initialized");
+
+ if (!stream.is_open())
+ panic("file not open!\n");
+
+ stream.seekg(offset * SectorSize, ios::beg);
+ if (!stream.good())
+ panic("Could not seek to location in file");
+
+ streampos pos = stream.tellg();
+ stream.read((char *)data, SectorSize);
+
+ DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
+ DDUMP(DiskImageRead, data, SectorSize);
+
+ return stream.tellg() - pos;
+}
+
+std::streampos
+RawDiskImage::write(const uint8_t *data, std::streampos offset)
+{
+ if (!initialized)
+ panic("RawDiskImage not initialized");
+
+ if (readonly)
+ panic("Cannot write to a read only disk image");
+
+ if (!stream.is_open())
+ panic("file not open!\n");
+
+ stream.seekp(offset * SectorSize, ios::beg);
+ if (!stream.good())
+ panic("Could not seek to location in file");
+
+ DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
+ DDUMP(DiskImageWrite, data, SectorSize);
+
+ streampos pos = stream.tellp();
+ stream.write((const char *)data, SectorSize);
+ return stream.tellp() - pos;
+}
+
+RawDiskImage *
+RawDiskImageParams::create()
+{
+ return new RawDiskImage(this);
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Copy on Write Disk image
+//
+const uint32_t CowDiskImage::VersionMajor = 1;
+const uint32_t CowDiskImage::VersionMinor = 0;
+
+class CowDiskCallback : public Callback
+{
+ private:
+ CowDiskImage *image;
+
+ public:
+ CowDiskCallback(CowDiskImage *i) : image(i) {}
+ void process() { image->save(); delete this; }
+};
+
+CowDiskImage::CowDiskImage(const Params *p)
+ : DiskImage(p), filename(p->image_file), child(p->child), table(NULL)
+{
+ if (filename.empty()) {
+ initSectorTable(p->table_size);
+ } else {
+ if (!open(filename)) {
+ if (p->read_only)
+ fatal("could not open read-only file");
+ initSectorTable(p->table_size);
+ }
+
+ if (!p->read_only)
+ registerExitCallback(new CowDiskCallback(this));
+ }
+}
+
+CowDiskImage::~CowDiskImage()
+{
+ SectorTable::iterator i = table->begin();
+ SectorTable::iterator end = table->end();
+
+ while (i != end) {
+ delete (*i).second;
+ ++i;
+ }
+}
+
+void
+SafeRead(ifstream &stream, void *data, int count)
+{
+ stream.read((char *)data, count);
+ if (!stream.is_open())
+ panic("file not open");
+
+ if (stream.eof())
+ panic("premature end-of-file");
+
+ if (stream.bad() || stream.fail())
+ panic("error reading cowdisk image");
+}
+
+template<class T>
+void
+SafeRead(ifstream &stream, T &data)
+{
+ SafeRead(stream, &data, sizeof(data));
+}
+
+template<class T>
+void
+SafeReadSwap(ifstream &stream, T &data)
+{
+ SafeRead(stream, &data, sizeof(data));
+ data = letoh(data); //is this the proper byte order conversion?
+}
+
+bool
+CowDiskImage::open(const string &file)
+{
+ ifstream stream(file.c_str());
+ if (!stream.is_open())
+ return false;
+
+ if (stream.fail() || stream.bad())
+ panic("Error opening %s", file);
+
+ uint64_t magic;
+ SafeRead(stream, magic);
+
+ if (memcmp(&magic, "COWDISK!", sizeof(magic)) != 0)
+ panic("Could not open %s: Invalid magic", file);
+
+ uint32_t major, minor;
+ SafeReadSwap(stream, major);
+ SafeReadSwap(stream, minor);
+
+ if (major != VersionMajor && minor != VersionMinor)
+ panic("Could not open %s: invalid version %d.%d != %d.%d",
+ file, major, minor, VersionMajor, VersionMinor);
+
+ uint64_t sector_count;
+ SafeReadSwap(stream, sector_count);
+ table = new SectorTable(sector_count);
+
+
+ for (uint64_t i = 0; i < sector_count; i++) {
+ uint64_t offset;
+ SafeReadSwap(stream, offset);
+
+ Sector *sector = new Sector;
+ SafeRead(stream, sector, sizeof(Sector));
+
+ assert(table->find(offset) == table->end());
+ (*table)[offset] = sector;
+ }
+
+ stream.close();
+
+ initialized = true;
+ return true;
+}
+
+void
+CowDiskImage::initSectorTable(int hash_size)
+{
+ table = new SectorTable(hash_size);
+
+ initialized = true;
+}
+
+void
+SafeWrite(ofstream &stream, const void *data, int count)
+{
+ stream.write((const char *)data, count);
+ if (!stream.is_open())
+ panic("file not open");
+
+ if (stream.eof())
+ panic("premature end-of-file");
+
+ if (stream.bad() || stream.fail())
+ panic("error reading cowdisk image");
+}
+
+template<class T>
+void
+SafeWrite(ofstream &stream, const T &data)
+{
+ SafeWrite(stream, &data, sizeof(data));
+}
+
+template<class T>
+void
+SafeWriteSwap(ofstream &stream, const T &data)
+{
+ T swappeddata = letoh(data); //is this the proper byte order conversion?
+ SafeWrite(stream, &swappeddata, sizeof(data));
+}
+void
+CowDiskImage::save() const
+{
+ save(filename);
+}
+
+void
+CowDiskImage::save(const string &file) const
+{
+ if (!initialized)
+ panic("RawDiskImage not initialized");
+
+ ofstream stream(file.c_str());
+ if (!stream.is_open() || stream.fail() || stream.bad())
+ panic("Error opening %s", file);
+
+ uint64_t magic;
+ memcpy(&magic, "COWDISK!", sizeof(magic));
+ SafeWrite(stream, magic);
+
+ SafeWriteSwap(stream, (uint32_t)VersionMajor);
+ SafeWriteSwap(stream, (uint32_t)VersionMinor);
+ SafeWriteSwap(stream, (uint64_t)table->size());
+
+ uint64_t size = table->size();
+ SectorTable::iterator iter = table->begin();
+ SectorTable::iterator end = table->end();
+
+ for (uint64_t i = 0; i < size; i++) {
+ if (iter == end)
+ panic("Incorrect Table Size during save of COW disk image");
+
+ SafeWriteSwap(stream, (uint64_t)(*iter).first);
+ SafeWrite(stream, (*iter).second->data, sizeof(Sector));
+ ++iter;
+ }
+
+ stream.close();
+}
+
+void
+CowDiskImage::writeback()
+{
+ SectorTable::iterator i = table->begin();
+ SectorTable::iterator end = table->end();
+
+ while (i != end) {
+ child->write((*i).second->data, (*i).first);
+ ++i;
+ }
+}
+
+std::streampos
+CowDiskImage::size() const
+{ return child->size(); }
+
+std::streampos
+CowDiskImage::read(uint8_t *data, std::streampos offset) const
+{
+ if (!initialized)
+ panic("CowDiskImage not initialized");
+
+ if (offset > size())
+ panic("access out of bounds");
+
+ SectorTable::const_iterator i = table->find(offset);
+ if (i == table->end())
+ return child->read(data, offset);
+ else {
+ memcpy(data, (*i).second->data, SectorSize);
+ DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset);
+ DDUMP(DiskImageRead, data, SectorSize);
+ return SectorSize;
+ }
+}
+
+std::streampos
+CowDiskImage::write(const uint8_t *data, std::streampos offset)
+{
+ if (!initialized)
+ panic("RawDiskImage not initialized");
+
+ if (offset > size())
+ panic("access out of bounds");
+
+ SectorTable::iterator i = table->find(offset);
+ if (i == table->end()) {
+ Sector *sector = new Sector;
+ memcpy(sector, data, SectorSize);
+ table->insert(make_pair(offset, sector));
+ } else {
+ memcpy((*i).second->data, data, SectorSize);
+ }
+
+ DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset);
+ DDUMP(DiskImageWrite, data, SectorSize);
+
+ return SectorSize;
+}
+
+void
+CowDiskImage::serialize(CheckpointOut &cp) const
+{
+ string cowFilename = name() + ".cow";
+ SERIALIZE_SCALAR(cowFilename);
+ save(CheckpointIn::dir() + "/" + cowFilename);
+}
+
+void
+CowDiskImage::unserialize(CheckpointIn &cp)
+{
+ string cowFilename;
+ UNSERIALIZE_SCALAR(cowFilename);
+ cowFilename = cp.cptDir + "/" + cowFilename;
+ open(cowFilename);
+}
+
+CowDiskImage *
+CowDiskImageParams::create()
+{
+ return new CowDiskImage(this);
+}
diff --git a/src/dev/storage/disk_image.hh b/src/dev/storage/disk_image.hh
new file mode 100644
index 000000000..43e6adf5e
--- /dev/null
+++ b/src/dev/storage/disk_image.hh
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2001-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
+ */
+
+/** @file
+ * Disk Image Interfaces
+ */
+
+#ifndef __DEV_STORAGE_DISK_IMAGE_HH__
+#define __DEV_STORAGE_DISK_IMAGE_HH__
+
+#include <fstream>
+#include <unordered_map>
+
+#include "params/CowDiskImage.hh"
+#include "params/DiskImage.hh"
+#include "params/RawDiskImage.hh"
+#include "sim/sim_object.hh"
+
+#define SectorSize (512)
+
+/**
+ * Basic interface for accessing a disk image.
+ */
+class DiskImage : public SimObject
+{
+ protected:
+ bool initialized;
+
+ public:
+ typedef DiskImageParams Params;
+ DiskImage(const Params *p) : SimObject(p), initialized(false) {}
+ virtual ~DiskImage() {}
+
+ virtual std::streampos size() const = 0;
+
+ virtual std::streampos read(uint8_t *data,
+ std::streampos offset) const = 0;
+ virtual std::streampos write(const uint8_t *data,
+ std::streampos offset) = 0;
+};
+
+/**
+ * Specialization for accessing a raw disk image
+ */
+class RawDiskImage : public DiskImage
+{
+ protected:
+ mutable std::fstream stream;
+ std::string file;
+ bool readonly;
+ mutable std::streampos disk_size;
+
+ public:
+ typedef RawDiskImageParams Params;
+ RawDiskImage(const Params *p);
+ ~RawDiskImage();
+
+ void close();
+ void open(const std::string &filename, bool rd_only = false);
+
+ virtual std::streampos size() const;
+
+ virtual std::streampos read(uint8_t *data, std::streampos offset) const;
+ virtual std::streampos write(const uint8_t *data, std::streampos offset);
+};
+
+/**
+ * Specialization for accessing a copy-on-write disk image layer.
+ * A copy-on-write(COW) layer must be stacked on top of another disk
+ * image layer this layer can be another CowDiskImage, or a
+ * RawDiskImage.
+ *
+ * This object is designed to provide a mechanism for persistant
+ * changes to a main disk image, or to provide a place for temporary
+ * changes to the image to take place that later may be thrown away.
+ */
+class CowDiskImage : public DiskImage
+{
+ public:
+ static const uint32_t VersionMajor;
+ static const uint32_t VersionMinor;
+
+ protected:
+ struct Sector {
+ uint8_t data[SectorSize];
+ };
+ typedef std::unordered_map<uint64_t, Sector *> SectorTable;
+
+ protected:
+ std::string filename;
+ DiskImage *child;
+ SectorTable *table;
+
+ public:
+ typedef CowDiskImageParams Params;
+ CowDiskImage(const Params *p);
+ ~CowDiskImage();
+
+ void initSectorTable(int hash_size);
+ bool open(const std::string &file);
+ void save() const;
+ void save(const std::string &file) const;
+ void writeback();
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+
+ std::streampos size() const override;
+
+ std::streampos read(uint8_t *data, std::streampos offset) const override;
+ std::streampos write(const uint8_t *data, std::streampos offset) override;
+};
+
+void SafeRead(std::ifstream &stream, void *data, int count);
+
+template<class T>
+void SafeRead(std::ifstream &stream, T &data);
+
+template<class T>
+void SafeReadSwap(std::ifstream &stream, T &data);
+
+void SafeWrite(std::ofstream &stream, const void *data, int count);
+
+template<class T>
+void SafeWrite(std::ofstream &stream, const T &data);
+
+template<class T>
+void SafeWriteSwap(std::ofstream &stream, const T &data);
+
+#endif // __DEV_STORAGE_DISK_IMAGE_HH__
diff --git a/src/dev/storage/ide_atareg.h b/src/dev/storage/ide_atareg.h
new file mode 100644
index 000000000..51c8aeccf
--- /dev/null
+++ b/src/dev/storage/ide_atareg.h
@@ -0,0 +1,293 @@
+/* $OpenBSD: atareg.h,v 1.12 2004/09/24 07:15:22 grange Exp $ */
+/* $NetBSD: atareg.h,v 1.5 1999/01/18 20:06:24 bouyer Exp $ */
+
+/*
+ * Copyright (c) 1998, 2001 Manuel Bouyer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DEV_ATA_ATAREG_H_
+#define _DEV_ATA_ATAREG_H_
+
+#if defined(__linux__)
+#include <endian.h>
+
+#elif defined(__sun)
+#include <sys/isa_defs.h>
+
+#else
+#include <machine/endian.h>
+
+#endif
+
+#ifdef LITTLE_ENDIAN
+#define ATA_BYTE_ORDER LITTLE_ENDIAN
+#elif defined(BIG_ENDIAN)
+#define ATA_BYTE_ORDER BIG_ENDIAN
+#elif defined(_LITTLE_ENDIAN)
+#define ATA_BYTE_ORDER 1
+#define LITTLE_ENDIAN 1
+#elif defined(_BIG_ENDIAN)
+#define ATA_BYTE_ORDER 0
+#define LITTLE_ENDIAN 1
+#else
+#error "No endianess defined"
+#endif
+
+/*
+ * Drive parameter structure for ATA/ATAPI.
+ * Bit fields: WDC_* : common to ATA/ATAPI
+ * ATA_* : ATA only
+ * ATAPI_* : ATAPI only.
+ */
+struct ataparams {
+ /* drive info */
+ uint16_t atap_config; /* 0: general configuration */
+#define WDC_CFG_ATAPI_MASK 0xc000
+#define WDC_CFG_ATAPI 0x8000
+#define ATA_CFG_REMOVABLE 0x0080
+#define ATA_CFG_FIXED 0x0040
+#define ATAPI_CFG_TYPE_MASK 0x1f00
+#define ATAPI_CFG_TYPE(x) (((x) & ATAPI_CFG_TYPE_MASK) >> 8)
+#define ATAPI_CFG_TYPE_DIRECT 0x00
+#define ATAPI_CFG_TYPE_SEQUENTIAL 0x01
+#define ATAPI_CFG_TYPE_CDROM 0x05
+#define ATAPI_CFG_TYPE_OPTICAL 0x07
+#define ATAPI_CFG_TYPE_NODEVICE 0x1F
+#define ATAPI_CFG_REMOV 0x0080
+#define ATAPI_CFG_DRQ_MASK 0x0060
+#define ATAPI_CFG_STD_DRQ 0x0000
+#define ATAPI_CFG_IRQ_DRQ 0x0020
+#define ATAPI_CFG_ACCEL_DRQ 0x0040
+#define ATAPI_CFG_CMD_MASK 0x0003
+#define ATAPI_CFG_CMD_12 0x0000
+#define ATAPI_CFG_CMD_16 0x0001
+/* words 1-9 are ATA only */
+ uint16_t atap_cylinders; /* 1: # of non-removable cylinders */
+ uint16_t __reserved1;
+ uint16_t atap_heads; /* 3: # of heads */
+ uint16_t __retired1[2]; /* 4-5: # of unform. bytes/track */
+ uint16_t atap_sectors; /* 6: # of sectors */
+ uint16_t __retired2[3];
+
+ uint8_t atap_serial[20]; /* 10-19: serial number */
+ uint16_t __retired3[2];
+ uint16_t __obsolete1;
+ uint8_t atap_revision[8]; /* 23-26: firmware revision */
+ uint8_t atap_model[40]; /* 27-46: model number */
+ uint16_t atap_multi; /* 47: maximum sectors per irq (ATA) */
+ uint16_t __reserved2;
+ uint8_t atap_vendor; /* 49: vendor */
+ uint8_t atap_capabilities1; /* 49: capability flags */
+#define WDC_CAP_IORDY 0x0800
+#define WDC_CAP_IORDY_DSBL 0x0400
+#define WDC_CAP_LBA 0x0200
+#define WDC_CAP_DMA 0x0100
+#define ATA_CAP_STBY 0x2000
+#define ATAPI_CAP_INTERL_DMA 0x8000
+#define ATAPI_CAP_CMD_QUEUE 0x4000
+#define ATAPI_CAP_OVERLP 0x2000
+#define ATAPI_CAP_ATA_RST 0x1000
+ uint16_t atap_capabilities2; /* 50: capability flags (ATA) */
+#if ATA_BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t __junk2;
+ uint8_t atap_oldpiotiming; /* 51: old PIO timing mode */
+ uint8_t __junk3;
+ uint8_t atap_olddmatiming; /* 52: old DMA timing mode (ATA) */
+#else
+ uint8_t atap_oldpiotiming; /* 51: old PIO timing mode */
+ uint8_t __junk2;
+ uint8_t atap_olddmatiming; /* 52: old DMA timing mode (ATA) */
+ uint8_t __junk3;
+#endif
+ uint16_t atap_extensions; /* 53: extensions supported */
+#define WDC_EXT_UDMA_MODES 0x0004
+#define WDC_EXT_MODES 0x0002
+#define WDC_EXT_GEOM 0x0001
+/* words 54-62 are ATA only */
+ uint16_t atap_curcylinders; /* 54: current logical cylinders */
+ uint16_t atap_curheads; /* 55: current logical heads */
+ uint16_t atap_cursectors; /* 56: current logical sectors/tracks */
+ uint16_t atap_curcapacity[2]; /* 57-58: current capacity */
+ uint8_t atap_curmulti; /* 59: current multi-sector setting */
+ uint8_t atap_curmulti_valid; /* 59: current multi-sector setting */
+#define WDC_MULTI_VALID 0x0100
+#define WDC_MULTI_MASK 0x00ff
+ uint32_t atap_capacity; /* 60-61: total capacity (LBA only) */
+ uint16_t __retired4;
+#if ATA_BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t atap_dmamode_supp; /* 63: multiword DMA mode supported */
+ uint8_t atap_dmamode_act; /* multiword DMA mode active */
+ uint8_t atap_piomode_supp; /* 64: PIO mode supported */
+ uint8_t __junk4;
+#else
+ uint8_t atap_dmamode_act; /* multiword DMA mode active */
+ uint8_t atap_dmamode_supp; /* 63: multiword DMA mode supported */
+ uint8_t __junk4;
+ uint8_t atap_piomode_supp; /* 64: PIO mode supported */
+#endif
+ uint16_t atap_dmatiming_mimi; /* 65: minimum DMA cycle time */
+ uint16_t atap_dmatiming_recom; /* 66: recommended DMA cycle time */
+ uint16_t atap_piotiming; /* 67: mini PIO cycle time without FC */
+ uint16_t atap_piotiming_iordy; /* 68: mini PIO cycle time with IORDY FC */
+ uint16_t __reserved3[2];
+/* words 71-72 are ATAPI only */
+ uint16_t atap_pkt_br; /* 71: time (ns) to bus release */
+ uint16_t atap_pkt_bsyclr; /* 72: tme to clear BSY after service */
+ uint16_t __reserved4[2];
+ uint16_t atap_queuedepth; /* 75: */
+#define WDC_QUEUE_DEPTH_MASK 0x1f
+ uint16_t atap_sata_caps; /* 76: SATA capabilities */
+#define SATA_SIGNAL_GEN1 0x0002 /* SATA Gen-1 signaling speed */
+#define SATA_SIGNAL_GEN2 0x0004 /* SATA Gen-2 signaling speed */
+#define SATA_NATIVE_CMDQ 0x0100 /* native command queuing */
+#define SATA_HOST_PWR_MGMT 0x0200 /* power management (host) */
+ uint16_t atap_sata_reserved; /* 77: reserved */
+ uint16_t atap_sata_features_supp;/* 78: SATA features supported */
+#define SATA_NONZERO_OFFSETS 0x0002 /* non-zero buffer offsets */
+#define SATA_DMA_SETUP_AUTO 0x0004 /* DMA setup auto-activate */
+#define SATA_DRIVE_PWR_MGMT 0x0008 /* power management (device) */
+ uint16_t atap_sata_features_en; /* 79: SATA features enabled */
+ uint16_t atap_ata_major; /* 80: Major version number */
+#define WDC_VER_ATA1 0x0002
+#define WDC_VER_ATA2 0x0004
+#define WDC_VER_ATA3 0x0008
+#define WDC_VER_ATA4 0x0010
+#define WDC_VER_ATA5 0x0020
+#define WDC_VER_ATA6 0x0040
+#define WDC_VER_ATA7 0x0080
+#define WDC_VER_ATA8 0x0100
+#define WDC_VER_ATA9 0x0200
+#define WDC_VER_ATA10 0x0400
+#define WDC_VER_ATA11 0x0800
+#define WDC_VER_ATA12 0x1000
+#define WDC_VER_ATA13 0x2000
+#define WDC_VER_ATA14 0x4000
+ uint16_t atap_ata_minor; /* 81: Minor version number */
+ uint16_t atap_cmd_set1; /* 82: command set supported */
+#define WDC_CMD1_NOP 0x4000
+#define WDC_CMD1_RB 0x2000
+#define WDC_CMD1_WB 0x1000
+#define WDC_CMD1_HPA 0x0400
+#define WDC_CMD1_DVRST 0x0200
+#define WDC_CMD1_SRV 0x0100
+#define WDC_CMD1_RLSE 0x0080
+#define WDC_CMD1_AHEAD 0x0040
+#define WDC_CMD1_CACHE 0x0020
+#define WDC_CMD1_PKT 0x0010
+#define WDC_CMD1_PM 0x0008
+#define WDC_CMD1_REMOV 0x0004
+#define WDC_CMD1_SEC 0x0002
+#define WDC_CMD1_SMART 0x0001
+ uint16_t atap_cmd_set2; /* 83: command set supported */
+#define ATAPI_CMD2_FCE 0x2000 /* Flush Cache Ext supported */
+#define ATAPI_CMD2_FC 0x1000 /* Flush Cache supported */
+#define ATAPI_CMD2_DCO 0x0800 /* Device Configuration Overlay supported */
+#define ATAPI_CMD2_48AD 0x0400 /* 48bit address supported */
+#define ATAPI_CMD2_AAM 0x0200 /* Automatic Acoustic Management supported */
+#define ATAPI_CMD2_SM 0x0100 /* Set Max security extension supported */
+#define ATAPI_CMD2_SF 0x0040 /* Set Features subcommand required */
+#define ATAPI_CMD2_PUIS 0x0020 /* Power up in standby supported */
+#define WDC_CMD2_RMSN 0x0010
+#define ATA_CMD2_APM 0x0008
+#define ATA_CMD2_CFA 0x0004
+#define ATA_CMD2_RWQ 0x0002
+#define WDC_CMD2_DM 0x0001 /* Download Microcode supported */
+ uint16_t atap_cmd_ext; /* 84: command/features supp. ext. */
+#define ATAPI_CMDE_MSER 0x0004 /* Media serial number supported */
+#define ATAPI_CMDE_TEST 0x0002 /* SMART self-test supported */
+#define ATAPI_CMDE_SLOG 0x0001 /* SMART error logging supported */
+ uint16_t atap_cmd1_en; /* 85: cmd/features enabled */
+/* bits are the same as atap_cmd_set1 */
+ uint16_t atap_cmd2_en; /* 86: cmd/features enabled */
+/* bits are the same as atap_cmd_set2 */
+ uint16_t atap_cmd_def; /* 87: cmd/features default */
+/* bits are NOT the same as atap_cmd_ext */
+#if ATA_BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t atap_udmamode_supp; /* 88: Ultra-DMA mode supported */
+ uint8_t atap_udmamode_act; /* Ultra-DMA mode active */
+#else
+ uint8_t atap_udmamode_act; /* Ultra-DMA mode active */
+ uint8_t atap_udmamode_supp; /* 88: Ultra-DMA mode supported */
+#endif
+/* 89-92 are ATA-only */
+ uint16_t atap_seu_time; /* 89: Sec. Erase Unit compl. time */
+ uint16_t atap_eseu_time; /* 90: Enhanced SEU compl. time */
+ uint16_t atap_apm_val; /* 91: current APM value */
+ uint16_t atap_mpasswd_rev; /* 92: Master Password revision */
+ uint16_t atap_hwreset_res; /* 93: Hardware reset value */
+#define ATA_HWRES_CBLID 0x2000 /* CBLID above Vih */
+#define ATA_HWRES_D1_PDIAG 0x0800 /* Device 1 PDIAG detect OK */
+#define ATA_HWRES_D1_CSEL 0x0400 /* Device 1 used CSEL for address */
+#define ATA_HWRES_D1_JUMP 0x0200 /* Device 1 jumpered to address */
+#define ATA_HWRES_D0_SEL 0x0040 /* Device 0 responds when Dev 1 selected */
+#define ATA_HWRES_D0_DASP 0x0020 /* Device 0 DASP detect OK */
+#define ATA_HWRES_D0_PDIAG 0x0010 /* Device 0 PDIAG detect OK */
+#define ATA_HWRES_D0_DIAG 0x0008 /* Device 0 diag OK */
+#define ATA_HWRES_D0_CSEL 0x0004 /* Device 0 used CSEL for address */
+#define ATA_HWRES_D0_JUMP 0x0002 /* Device 0 jumpered to address */
+#if ATA_BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t atap_acoustic_val; /* 94: Current acoustic level */
+ uint8_t atap_acoustic_def; /* recommended level */
+#else
+ uint8_t atap_acoustic_def; /* recommended level */
+ uint8_t atap_acoustic_val; /* 94: Current acoustic level */
+#endif
+ uint16_t __reserved6[5]; /* 95-99: reserved */
+ uint16_t atap_max_lba[4]; /* 100-103: Max. user LBA add */
+ uint16_t __reserved7[23]; /* 104-126: reserved */
+ uint16_t atap_rmsn_supp; /* 127: remov. media status notif. */
+#define WDC_RMSN_SUPP_MASK 0x0003
+#define WDC_RMSN_SUPP 0x0001
+ uint16_t atap_sec_st; /* 128: security status */
+#define WDC_SEC_LEV_MAX 0x0100
+#define WDC_SEC_ESE_SUPP 0x0020
+#define WDC_SEC_EXP 0x0010
+#define WDC_SEC_FROZEN 0x0008
+#define WDC_SEC_LOCKED 0x0004
+#define WDC_SEC_EN 0x0002
+#define WDC_SEC_SUPP 0x0001
+ uint16_t __reserved8[31]; /* 129-159: vendor specific */
+ uint16_t atap_cfa_power; /* 160: CFA powermode */
+#define ATAPI_CFA_MAX_MASK 0x0FFF
+#define ATAPI_CFA_MODE1_DIS 0x1000 /* CFA Mode 1 Disabled */
+#define ATAPI_CFA_MODE1_REQ 0x2000 /* CFA Mode 1 Required */
+#define ATAPI_CFA_WORD160 0x8000 /* Word 160 supported */
+ uint16_t __reserved9[15]; /* 161-175: reserved for CFA */
+ uint8_t atap_media_serial[60]; /* 176-205: media serial number */
+ uint16_t __reserved10[49]; /* 206-254: reserved */
+#if ATA_BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t atap_signature; /* 255: Signature */
+ uint8_t atap_checksum; /* Checksum */
+#else
+ uint8_t atap_checksum; /* Checksum */
+ uint8_t atap_signature; /* 255: Signature */
+#endif
+};
+
+#undef ATA_BYTE_ORDER
+#endif /* !_DEV_ATA_ATAREG_H_ */
diff --git a/src/dev/storage/ide_ctrl.cc b/src/dev/storage/ide_ctrl.cc
new file mode 100644
index 000000000..feed9cfd2
--- /dev/null
+++ b/src/dev/storage/ide_ctrl.cc
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 2013 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Schultz
+ * Ali Saidi
+ * Miguel Serrano
+ */
+
+#include "dev/storage/ide_ctrl.hh"
+
+#include <string>
+
+#include "cpu/intr_control.hh"
+#include "debug/IdeCtrl.hh"
+#include "dev/storage/ide_disk.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+#include "params/IdeController.hh"
+#include "sim/byteswap.hh"
+
+// clang complains about std::set being overloaded with Packet::set if
+// we open up the entire namespace std
+using std::string;
+
+// 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)
+{
+ memset(&bmiRegs, 0, sizeof(bmiRegs));
+ bmiRegs.status.dmaCap0 = 1;
+ bmiRegs.status.dmaCap1 = 1;
+}
+
+IdeController::Channel::~Channel()
+{
+}
+
+IdeController::IdeController(Params *p)
+ : PciDevice(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),
+ ioShift(p->io_shift), ctrlOffset(p->ctrl_offset)
+{
+ if (params()->disks.size() > 3)
+ panic("IDE controllers support a maximum of 4 devices attached!\n");
+
+ // 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];
+
+ for (int i = 0; i < params()->disks.size(); i++) {
+ params()->disks[i]->setController(this);
+ }
+ primary.select(false);
+ secondary.select(false);
+
+ if ((BARAddrs[0] & ~BAR_IO_MASK) && (!legacyIO[0] || ioShift)) {
+ primary.cmdAddr = BARAddrs[0]; primary.cmdSize = BARSize[0];
+ primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARSize[1];
+ }
+ if ((BARAddrs[2] & ~BAR_IO_MASK) && (!legacyIO[2] || ioShift)) {
+ secondary.cmdAddr = BARAddrs[2]; secondary.cmdSize = BARSize[2];
+ secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARSize[3];
+ }
+
+ ioEnabled = (config.command & htole(PCI_CMD_IOSE));
+ bmEnabled = (config.command & htole(PCI_CMD_BME));
+}
+
+bool
+IdeController::isDiskSelected(IdeDisk *diskPtr)
+{
+ return (primary.selected == diskPtr || secondary.selected == diskPtr);
+}
+
+void
+IdeController::intrPost()
+{
+ primary.bmiRegs.status.intStatus = 1;
+ PciDevice::intrPost();
+}
+
+void
+IdeController::setDmaComplete(IdeDisk *disk)
+{
+ Channel *channel;
+ if (disk == primary.master || disk == primary.slave) {
+ channel = &primary;
+ } else if (disk == secondary.master || disk == secondary.slave) {
+ channel = &secondary;
+ } else {
+ panic("Unable to find disk based on pointer %#x\n", disk);
+ }
+
+ channel->bmiRegs.command.startStop = 0;
+ channel->bmiRegs.status.active = 0;
+ channel->bmiRegs.status.intStatus = 1;
+}
+
+Tick
+IdeController::readConfig(PacketPtr pkt)
+{
+ int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
+ if (offset < PCI_DEVICE_SPECIFIC) {
+ return PciDevice::readConfig(pkt);
+ }
+
+ switch (pkt->getSize()) {
+ case sizeof(uint8_t):
+ switch (offset) {
+ case DeviceTiming:
+ pkt->set<uint8_t>(deviceTiming);
+ break;
+ case UDMAControl:
+ pkt->set<uint8_t>(udmaControl);
+ break;
+ case PrimaryTiming + 1:
+ pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8));
+ break;
+ case SecondaryTiming + 1:
+ pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8));
+ break;
+ case IDEConfig:
+ pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0));
+ break;
+ 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",
+ offset);
+ }
+ DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset,
+ (uint32_t)pkt->get<uint8_t>());
+ break;
+ case sizeof(uint16_t):
+ switch (offset) {
+ case UDMAControl:
+ pkt->set<uint16_t>(udmaControl);
+ break;
+ case PrimaryTiming:
+ pkt->set<uint16_t>(primaryTiming);
+ break;
+ case SecondaryTiming:
+ pkt->set<uint16_t>(secondaryTiming);
+ break;
+ case UDMATiming:
+ pkt->set<uint16_t>(udmaTiming);
+ break;
+ case IDEConfig:
+ pkt->set<uint16_t>(ideConfig);
+ break;
+ default:
+ panic("Invalid PCI configuration read for size 2 offset: %#x!\n",
+ offset);
+ }
+ DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset,
+ (uint32_t)pkt->get<uint16_t>());
+ break;
+ case sizeof(uint32_t):
+ switch (offset) {
+ case PrimaryTiming:
+ pkt->set<uint32_t>(primaryTiming);
+ break;
+ case IDEConfig:
+ pkt->set<uint32_t>(ideConfig);
+ break;
+ default:
+ panic("No 32bit reads implemented for this device.");
+ }
+ DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset,
+ (uint32_t)pkt->get<uint32_t>());
+ break;
+ default:
+ panic("invalid access size(?) for PCI configspace!\n");
+ }
+ pkt->makeAtomicResponse();
+ return configDelay;
+}
+
+
+Tick
+IdeController::writeConfig(PacketPtr pkt)
+{
+ int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
+ if (offset < PCI_DEVICE_SPECIFIC) {
+ PciDevice::writeConfig(pkt);
+ } else {
+ switch (pkt->getSize()) {
+ case sizeof(uint8_t):
+ switch (offset) {
+ case DeviceTiming:
+ deviceTiming = pkt->get<uint8_t>();
+ break;
+ case UDMAControl:
+ udmaControl = pkt->get<uint8_t>();
+ break;
+ case IDEConfig:
+ replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>());
+ break;
+ 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);
+ }
+ 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 UDMAControl:
+ udmaControl = pkt->get<uint16_t>();
+ break;
+ case PrimaryTiming:
+ primaryTiming = pkt->get<uint16_t>();
+ break;
+ case SecondaryTiming:
+ secondaryTiming = pkt->get<uint16_t>();
+ break;
+ case UDMATiming:
+ udmaTiming = pkt->get<uint16_t>();
+ break;
+ case IDEConfig:
+ ideConfig = pkt->get<uint16_t>();
+ break;
+ default:
+ panic("Invalid PCI configuration write "
+ "for size 2 offset: %#x!\n",
+ offset);
+ }
+ DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n",
+ offset, (uint32_t)pkt->get<uint16_t>());
+ break;
+ case sizeof(uint32_t):
+ switch (offset) {
+ case PrimaryTiming:
+ primaryTiming = pkt->get<uint32_t>();
+ break;
+ case IDEConfig:
+ ideConfig = pkt->get<uint32_t>();
+ break;
+ default:
+ panic("Write of unimplemented PCI config. register: %x\n", offset);
+ }
+ break;
+ default:
+ panic("invalid access size(?) for PCI configspace!\n");
+ }
+ pkt->makeAtomicResponse();
+ }
+
+ /* Trap command register writes and enable IO/BM as appropriate as well as
+ * BARs. */
+ switch(offset) {
+ case PCI0_BASE_ADDR0:
+ if (BARAddrs[0] != 0)
+ primary.cmdAddr = BARAddrs[0];
+ break;
+
+ case PCI0_BASE_ADDR1:
+ if (BARAddrs[1] != 0)
+ primary.ctrlAddr = BARAddrs[1];
+ break;
+
+ case PCI0_BASE_ADDR2:
+ if (BARAddrs[2] != 0)
+ secondary.cmdAddr = BARAddrs[2];
+ break;
+
+ case PCI0_BASE_ADDR3:
+ if (BARAddrs[3] != 0)
+ secondary.ctrlAddr = BARAddrs[3];
+ break;
+
+ case PCI0_BASE_ADDR4:
+ if (BARAddrs[4] != 0)
+ bmiAddr = BARAddrs[4];
+ break;
+
+ case PCI_COMMAND:
+ DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command);
+ ioEnabled = (config.command & htole(PCI_CMD_IOSE));
+ bmEnabled = (config.command & htole(PCI_CMD_BME));
+ break;
+ }
+ return configDelay;
+}
+
+void
+IdeController::Channel::accessCommand(Addr offset,
+ int size, uint8_t *data, bool read)
+{
+ const Addr SelectOffset = 6;
+ const uint8_t SelectDevBit = 0x10;
+
+ if (!read && offset == SelectOffset)
+ select(*data & SelectDevBit);
+
+ if (selected == NULL) {
+ assert(size == sizeof(uint8_t));
+ *data = 0;
+ } else if (read) {
+ selected->readCommand(offset, size, data);
+ } else {
+ selected->writeCommand(offset, size, data);
+ }
+}
+
+void
+IdeController::Channel::accessControl(Addr offset,
+ int size, uint8_t *data, bool read)
+{
+ if (selected == NULL) {
+ assert(size == sizeof(uint8_t));
+ *data = 0;
+ } else if (read) {
+ selected->readControl(offset, size, data);
+ } else {
+ selected->writeControl(offset, size, data);
+ }
+}
+
+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) {
+ case BMICommand:
+ {
+ if (size != sizeof(uint8_t))
+ panic("Invalid BMIC write size: %x\n", size);
+
+ BMICommandReg oldVal = bmiRegs.command;
+ BMICommandReg newVal = *data;
+
+ // if a DMA transfer is in progress, R/W control cannot change
+ if (oldVal.startStop && oldVal.rw != newVal.rw)
+ oldVal.rw = newVal.rw;
+
+ if (oldVal.startStop != newVal.startStop) {
+ if (selected == NULL)
+ panic("DMA start for disk which does not exist\n");
+
+ if (oldVal.startStop) {
+ DPRINTF(IdeCtrl, "Stopping DMA transfer\n");
+ bmiRegs.status.active = 0;
+
+ selected->abortDma();
+ } else {
+ DPRINTF(IdeCtrl, "Starting DMA transfer\n");
+ bmiRegs.status.active = 1;
+
+ selected->startDma(letoh(bmiRegs.bmidtp));
+ }
+ }
+
+ bmiRegs.command = newVal;
+ }
+ break;
+ case BMIStatus:
+ {
+ 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 == 1) && (newVal.intStatus == 1)) {
+ newVal.intStatus = 0; // clear the interrupt?
+ } else {
+ // Assigning two bitunion fields to each other does not
+ // work as intended, so we need to use this temporary variable
+ // to get around the bug.
+ uint8_t tmp = oldVal.intStatus;
+ newVal.intStatus = tmp;
+ }
+ if ((oldVal.dmaError == 1) && (newVal.dmaError == 1)) {
+ newVal.dmaError = 0;
+ } else {
+ uint8_t tmp = oldVal.dmaError;
+ newVal.dmaError = tmp;
+ }
+
+ 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 (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);
+ }
+ }
+}
+
+void
+IdeController::dispatchAccess(PacketPtr pkt, bool read)
+{
+ if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4)
+ panic("Bad IDE read size: %d\n", pkt->getSize());
+
+ if (!ioEnabled) {
+ pkt->makeAtomicResponse();
+ DPRINTF(IdeCtrl, "io not enabled\n");
+ return;
+ }
+
+ 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;
+ // linux may have shifted the address by ioShift,
+ // here we shift it back, similarly for ctrlOffset.
+ addr >>= ioShift;
+ primary.accessCommand(addr, size, dataPtr, read);
+ } else if (addr >= primary.ctrlAddr &&
+ addr < (primary.ctrlAddr + primary.ctrlSize)) {
+ addr -= primary.ctrlAddr;
+ addr += ctrlOffset;
+ 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);
+ }
+ } else {
+ panic("IDE controller access to invalid address: %#x\n", addr);
+ }
+
+#ifndef NDEBUG
+ uint32_t data;
+ if (pkt->getSize() == 1)
+ data = pkt->get<uint8_t>();
+ else if (pkt->getSize() == 2)
+ data = pkt->get<uint16_t>();
+ else
+ data = pkt->get<uint32_t>();
+ DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n",
+ read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data);
+#endif
+
+ pkt->makeAtomicResponse();
+}
+
+Tick
+IdeController::read(PacketPtr pkt)
+{
+ dispatchAccess(pkt, true);
+ return pioDelay;
+}
+
+Tick
+IdeController::write(PacketPtr pkt)
+{
+ dispatchAccess(pkt, false);
+ return pioDelay;
+}
+
+void
+IdeController::serialize(CheckpointOut &cp) const
+{
+ // Serialize the PciDevice base class
+ PciDevice::serialize(cp);
+
+ // Serialize channels
+ primary.serialize("primary", cp);
+ secondary.serialize("secondary", cp);
+
+ // 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(ioEnabled);
+ SERIALIZE_SCALAR(bmEnabled);
+ SERIALIZE_SCALAR(bmiAddr);
+ SERIALIZE_SCALAR(bmiSize);
+}
+
+void
+IdeController::Channel::serialize(const std::string &base,
+ CheckpointOut &cp) const
+{
+ paramOut(cp, base + ".cmdAddr", cmdAddr);
+ paramOut(cp, base + ".cmdSize", cmdSize);
+ paramOut(cp, base + ".ctrlAddr", ctrlAddr);
+ paramOut(cp, base + ".ctrlSize", ctrlSize);
+ uint8_t command = bmiRegs.command;
+ paramOut(cp, base + ".bmiRegs.command", command);
+ paramOut(cp, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
+ uint8_t status = bmiRegs.status;
+ paramOut(cp, base + ".bmiRegs.status", status);
+ paramOut(cp, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
+ paramOut(cp, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
+ paramOut(cp, base + ".selectBit", selectBit);
+}
+
+void
+IdeController::unserialize(CheckpointIn &cp)
+{
+ // Unserialize the PciDevice base class
+ PciDevice::unserialize(cp);
+
+ // Unserialize channels
+ primary.unserialize("primary", cp);
+ secondary.unserialize("secondary", cp);
+
+ // 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(ioEnabled);
+ UNSERIALIZE_SCALAR(bmEnabled);
+ UNSERIALIZE_SCALAR(bmiAddr);
+ UNSERIALIZE_SCALAR(bmiSize);
+}
+
+void
+IdeController::Channel::unserialize(const std::string &base, CheckpointIn &cp)
+{
+ paramIn(cp, base + ".cmdAddr", cmdAddr);
+ paramIn(cp, base + ".cmdSize", cmdSize);
+ paramIn(cp, base + ".ctrlAddr", ctrlAddr);
+ paramIn(cp, base + ".ctrlSize", ctrlSize);
+ uint8_t command;
+ paramIn(cp, base +".bmiRegs.command", command);
+ bmiRegs.command = command;
+ paramIn(cp, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
+ uint8_t status;
+ paramIn(cp, base + ".bmiRegs.status", status);
+ bmiRegs.status = status;
+ paramIn(cp, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
+ paramIn(cp, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
+ paramIn(cp, base + ".selectBit", selectBit);
+ select(selectBit);
+}
+
+IdeController *
+IdeControllerParams::create()
+{
+ return new IdeController(this);
+}
diff --git a/src/dev/storage/ide_ctrl.hh b/src/dev/storage/ide_ctrl.hh
new file mode 100644
index 000000000..94a9c65e5
--- /dev/null
+++ b/src/dev/storage/ide_ctrl.hh
@@ -0,0 +1,159 @@
+/*
+ * 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: Andrew Schultz
+ * Miguel Serrano
+ */
+
+/** @file
+ * Simple PCI IDE controller with bus mastering capability and UDMA
+ * modeled after controller in the Intel PIIX4 chip
+ */
+
+#ifndef __DEV_STORAGE_IDE_CTRL_HH__
+#define __DEV_STORAGE_IDE_CTRL_HH__
+
+#include "base/bitunion.hh"
+#include "dev/io_device.hh"
+#include "dev/pci/device.hh"
+#include "params/IdeController.hh"
+
+class IdeDisk;
+
+/**
+ * Device model for an Intel PIIX4 IDE controller
+ */
+
+class IdeController : public PciDevice
+{
+ 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;
+
+ /** IDE disks connected to this controller */
+ IdeDisk *master, *slave;
+
+ /** Currently selected disk */
+ IdeDisk *selected;
+
+ bool selectBit;
+
+ void
+ select(bool selSlave)
+ {
+ selectBit = selSlave;
+ selected = selectBit ? slave : master;
+ }
+
+ 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);
+
+ Channel(std::string newName, Addr _cmdSize, Addr _ctrlSize);
+ ~Channel();
+
+ void serialize(const std::string &base, std::ostream &os) const;
+ void unserialize(const std::string &base, CheckpointIn &cp);
+ };
+
+ Channel primary;
+ Channel secondary;
+
+ /** Bus master interface (BMI) registers */
+ Addr bmiAddr, bmiSize;
+
+ /** 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;
+
+ uint32_t ioShift, ctrlOffset;
+
+ void dispatchAccess(PacketPtr pkt, bool read);
+
+ public:
+ typedef IdeControllerParams Params;
+ const Params *params() const { return (const Params *)_params; }
+ IdeController(Params *p);
+
+ /** See if a disk is selected based on its pointer */
+ bool isDiskSelected(IdeDisk *diskPtr);
+
+ void intrPost();
+
+ Tick writeConfig(PacketPtr pkt) override;
+ Tick readConfig(PacketPtr pkt) override;
+
+ void setDmaComplete(IdeDisk *disk);
+
+ Tick read(PacketPtr pkt) override;
+ Tick write(PacketPtr pkt) override;
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+};
+#endif // __DEV_STORAGE_IDE_CTRL_HH_
diff --git a/src/dev/storage/ide_disk.cc b/src/dev/storage/ide_disk.cc
new file mode 100644
index 000000000..4eefdbbd7
--- /dev/null
+++ b/src/dev/storage/ide_disk.cc
@@ -0,0 +1,1203 @@
+/*
+ * Copyright (c) 2013 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Schultz
+ * Ali Saidi
+ */
+
+/** @file
+ * Device model implementation for an IDE disk
+ */
+
+#include "dev/storage/ide_disk.hh"
+
+#include <cerrno>
+#include <cstring>
+#include <deque>
+#include <string>
+
+#include "arch/isa_traits.hh"
+#include "base/chunk_generator.hh"
+#include "base/cprintf.hh" // csprintf
+#include "base/trace.hh"
+#include "config/the_isa.hh"
+#include "debug/IdeDisk.hh"
+#include "dev/storage/disk_image.hh"
+#include "dev/storage/ide_ctrl.hh"
+#include "sim/core.hh"
+#include "sim/sim_object.hh"
+
+using namespace std;
+using namespace TheISA;
+
+IdeDisk::IdeDisk(const Params *p)
+ : SimObject(p), ctrl(NULL), image(p->image), diskDelay(p->delay),
+ dmaTransferEvent(this), dmaReadCG(NULL), dmaReadWaitEvent(this),
+ dmaWriteCG(NULL), dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
+ dmaReadEvent(this), dmaWriteEvent(this)
+{
+ // Reset the device state
+ reset(p->driveID);
+
+ // fill out the drive ID structure
+ memset(&driveID, 0, sizeof(struct ataparams));
+
+ // Calculate LBA and C/H/S values
+ uint16_t cylinders;
+ uint8_t heads;
+ uint8_t sectors;
+
+ uint32_t lba_size = image->size();
+ if (lba_size >= 16383*16*63) {
+ cylinders = 16383;
+ heads = 16;
+ sectors = 63;
+ } else {
+ if (lba_size >= 63)
+ sectors = 63;
+ else if (lba_size == 0)
+ panic("Bad IDE image size: 0\n");
+ else
+ sectors = lba_size;
+
+ if ((lba_size / sectors) >= 16)
+ heads = 16;
+ else
+ heads = (lba_size / sectors);
+
+ cylinders = lba_size / (heads * sectors);
+ }
+
+ // Setup the model name
+ strncpy((char *)driveID.atap_model, "5MI EDD si k",
+ sizeof(driveID.atap_model));
+ // Set the maximum multisector transfer size
+ driveID.atap_multi = MAX_MULTSECT;
+ // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
+ driveID.atap_capabilities1 = 0x7;
+ // UDMA support, EIDE support
+ driveID.atap_extensions = 0x6;
+ // Setup default C/H/S settings
+ driveID.atap_cylinders = cylinders;
+ driveID.atap_sectors = sectors;
+ driveID.atap_heads = heads;
+ // Setup the current multisector transfer size
+ driveID.atap_curmulti = MAX_MULTSECT;
+ driveID.atap_curmulti_valid = 0x1;
+ // Number of sectors on disk
+ driveID.atap_capacity = lba_size;
+ // Multiword DMA mode 2 and below supported
+ driveID.atap_dmamode_supp = 0x4;
+ // Set PIO mode 4 and 3 supported
+ driveID.atap_piomode_supp = 0x3;
+ // Set DMA mode 4 and below supported
+ driveID.atap_udmamode_supp = 0x1f;
+ // Statically set hardware config word
+ driveID.atap_hwreset_res = 0x4001;
+
+ //arbitrary for now...
+ driveID.atap_ata_major = WDC_VER_ATA7;
+}
+
+IdeDisk::~IdeDisk()
+{
+ // destroy the data buffer
+ delete [] dataBuffer;
+}
+
+void
+IdeDisk::reset(int id)
+{
+ // initialize the data buffer and shadow registers
+ dataBuffer = new uint8_t[MAX_DMA_SIZE];
+
+ memset(dataBuffer, 0, MAX_DMA_SIZE);
+ memset(&cmdReg, 0, sizeof(CommandReg_t));
+ memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
+
+ curPrdAddr = 0;
+ curSector = 0;
+ cmdBytes = 0;
+ cmdBytesLeft = 0;
+ drqBytesLeft = 0;
+ dmaRead = false;
+ intrPending = false;
+ dmaAborted = false;
+
+ // set the device state to idle
+ dmaState = Dma_Idle;
+
+ if (id == DEV0) {
+ devState = Device_Idle_S;
+ devID = DEV0;
+ } else if (id == DEV1) {
+ devState = Device_Idle_NS;
+ devID = DEV1;
+ } else {
+ panic("Invalid device ID: %#x\n", id);
+ }
+
+ // set the device ready bit
+ status = STATUS_DRDY_BIT;
+
+ /* The error register must be set to 0x1 on start-up to
+ indicate that no diagnostic error was detected */
+ cmdReg.error = 0x1;
+}
+
+////
+// Utility functions
+////
+
+bool
+IdeDisk::isDEVSelect()
+{
+ return ctrl->isDiskSelected(this);
+}
+
+Addr
+IdeDisk::pciToDma(Addr pciAddr)
+{
+ if (ctrl)
+ return ctrl->pciToDma(pciAddr);
+ else
+ panic("Access to unset controller!\n");
+}
+
+////
+// Device registers read/write
+////
+
+void
+IdeDisk::readCommand(const Addr offset, int size, uint8_t *data)
+{
+ 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 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("Invalid IDE command register offset: %#x\n", offset);
+ }
+ DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data);
+}
+
+void
+IdeDisk::readControl(const Addr offset, int size, uint8_t *data)
+{
+ 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);
+}
+
+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 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("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);
+}
+
+////
+// Perform DMA transactions
+////
+
+void
+IdeDisk::doDmaTransfer()
+{
+ if (dmaAborted) {
+ DPRINTF(IdeDisk, "DMA Aborted before reading PRD entry\n");
+ updateState(ACT_CMD_ERROR);
+ return;
+ }
+
+ if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
+ panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
+ dmaState, devState);
+
+ if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
+ schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD);
+ return;
+ } else
+ ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent,
+ (uint8_t*)&curPrd.entry);
+}
+
+void
+IdeDisk::dmaPrdReadDone()
+{
+ if (dmaAborted) {
+ DPRINTF(IdeDisk, "DMA Aborted while reading PRD entry\n");
+ updateState(ACT_CMD_ERROR);
+ return;
+ }
+
+ DPRINTF(IdeDisk,
+ "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
+ curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
+ curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
+ curPrd.getEOT(), curSector);
+
+ // the prd pointer has already been translated, so just do an increment
+ curPrdAddr = curPrdAddr + sizeof(PrdEntry_t);
+
+ if (dmaRead)
+ doDmaDataRead();
+ else
+ doDmaDataWrite();
+}
+
+void
+IdeDisk::doDmaDataRead()
+{
+ /** @todo we need to figure out what the delay actually will be */
+ Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
+
+ DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n",
+ diskDelay, totalDiskDelay);
+
+ schedule(dmaReadWaitEvent, curTick() + totalDiskDelay);
+}
+
+void
+IdeDisk::regStats()
+{
+ using namespace Stats;
+ dmaReadFullPages
+ .name(name() + ".dma_read_full_pages")
+ .desc("Number of full page size DMA reads (not PRD).")
+ ;
+ dmaReadBytes
+ .name(name() + ".dma_read_bytes")
+ .desc("Number of bytes transfered via DMA reads (not PRD).")
+ ;
+ dmaReadTxs
+ .name(name() + ".dma_read_txs")
+ .desc("Number of DMA read transactions (not PRD).")
+ ;
+
+ dmaWriteFullPages
+ .name(name() + ".dma_write_full_pages")
+ .desc("Number of full page size DMA writes.")
+ ;
+ dmaWriteBytes
+ .name(name() + ".dma_write_bytes")
+ .desc("Number of bytes transfered via DMA writes.")
+ ;
+ dmaWriteTxs
+ .name(name() + ".dma_write_txs")
+ .desc("Number of DMA write transactions.")
+ ;
+}
+
+void
+IdeDisk::doDmaRead()
+{
+ if (dmaAborted) {
+ DPRINTF(IdeDisk, "DMA Aborted in middle of Dma Read\n");
+ if (dmaReadCG)
+ delete dmaReadCG;
+ dmaReadCG = NULL;
+ updateState(ACT_CMD_ERROR);
+ return;
+ }
+
+ if (!dmaReadCG) {
+ // clear out the data buffer
+ memset(dataBuffer, 0, MAX_DMA_SIZE);
+ dmaReadCG = new ChunkGenerator(curPrd.getBaseAddr(),
+ curPrd.getByteCount(), TheISA::PageBytes);
+
+ }
+ if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
+ schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
+ return;
+ } else if (!dmaReadCG->done()) {
+ assert(dmaReadCG->complete() < MAX_DMA_SIZE);
+ ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(),
+ &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete());
+ dmaReadBytes += dmaReadCG->size();
+ dmaReadTxs++;
+ if (dmaReadCG->size() == TheISA::PageBytes)
+ dmaReadFullPages++;
+ dmaReadCG->next();
+ } else {
+ assert(dmaReadCG->done());
+ delete dmaReadCG;
+ dmaReadCG = NULL;
+ dmaReadDone();
+ }
+}
+
+void
+IdeDisk::dmaReadDone()
+{
+ uint32_t bytesWritten = 0;
+
+ // write the data to the disk image
+ for (bytesWritten = 0; bytesWritten < curPrd.getByteCount();
+ bytesWritten += SectorSize) {
+
+ cmdBytesLeft -= SectorSize;
+ writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
+ }
+
+ // check for the EOT
+ if (curPrd.getEOT()) {
+ assert(cmdBytesLeft == 0);
+ dmaState = Dma_Idle;
+ updateState(ACT_DMA_DONE);
+ } else {
+ doDmaTransfer();
+ }
+}
+
+void
+IdeDisk::doDmaDataWrite()
+{
+ /** @todo we need to figure out what the delay actually will be */
+ Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
+ uint32_t bytesRead = 0;
+
+ DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
+ diskDelay, totalDiskDelay);
+
+ memset(dataBuffer, 0, MAX_DMA_SIZE);
+ assert(cmdBytesLeft <= MAX_DMA_SIZE);
+ while (bytesRead < curPrd.getByteCount()) {
+ readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
+ bytesRead += SectorSize;
+ cmdBytesLeft -= SectorSize;
+ }
+ DPRINTF(IdeDisk, "doDmaWrite, bytesRead: %d cmdBytesLeft: %d\n",
+ bytesRead, cmdBytesLeft);
+
+ schedule(dmaWriteWaitEvent, curTick() + totalDiskDelay);
+}
+
+void
+IdeDisk::doDmaWrite()
+{
+ if (dmaAborted) {
+ DPRINTF(IdeDisk, "DMA Aborted while doing DMA Write\n");
+ if (dmaWriteCG)
+ delete dmaWriteCG;
+ dmaWriteCG = NULL;
+ updateState(ACT_CMD_ERROR);
+ return;
+ }
+ if (!dmaWriteCG) {
+ // clear out the data buffer
+ dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
+ curPrd.getByteCount(), TheISA::PageBytes);
+ }
+ if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
+ schedule(dmaWriteWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
+ DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n");
+ return;
+ } else if (!dmaWriteCG->done()) {
+ assert(dmaWriteCG->complete() < MAX_DMA_SIZE);
+ ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(),
+ &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete());
+ DPRINTF(IdeDisk, "doDmaWrite: not done curPrd byte count %d, eot %#x\n",
+ curPrd.getByteCount(), curPrd.getEOT());
+ dmaWriteBytes += dmaWriteCG->size();
+ dmaWriteTxs++;
+ if (dmaWriteCG->size() == TheISA::PageBytes)
+ dmaWriteFullPages++;
+ dmaWriteCG->next();
+ } else {
+ DPRINTF(IdeDisk, "doDmaWrite: done curPrd byte count %d, eot %#x\n",
+ curPrd.getByteCount(), curPrd.getEOT());
+ assert(dmaWriteCG->done());
+ delete dmaWriteCG;
+ dmaWriteCG = NULL;
+ dmaWriteDone();
+ }
+}
+
+void
+IdeDisk::dmaWriteDone()
+{
+ DPRINTF(IdeDisk, "doWriteDone: curPrd byte count %d, eot %#x cmd bytes left:%d\n",
+ curPrd.getByteCount(), curPrd.getEOT(), cmdBytesLeft);
+ // check for the EOT
+ if (curPrd.getEOT()) {
+ assert(cmdBytesLeft == 0);
+ dmaState = Dma_Idle;
+ updateState(ACT_DMA_DONE);
+ } else {
+ doDmaTransfer();
+ }
+}
+
+////
+// Disk utility routines
+///
+
+void
+IdeDisk::readDisk(uint32_t sector, uint8_t *data)
+{
+ uint32_t bytesRead = image->read(data, sector);
+
+ if (bytesRead != SectorSize)
+ panic("Can't read from %s. Only %d of %d read. errno=%d\n",
+ name(), bytesRead, SectorSize, errno);
+}
+
+void
+IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
+{
+ uint32_t bytesWritten = image->write(data, sector);
+
+ if (bytesWritten != SectorSize)
+ panic("Can't write to %s. Only %d of %d written. errno=%d\n",
+ name(), bytesWritten, SectorSize, errno);
+}
+
+////
+// Setup and handle commands
+////
+
+void
+IdeDisk::startDma(const uint32_t &prdTableBase)
+{
+ if (dmaState != Dma_Start)
+ panic("Inconsistent DMA state, should be in Dma_Start!\n");
+
+ if (devState != Transfer_Data_Dma)
+ panic("Inconsistent device state for DMA start!\n");
+
+ // PRD base address is given by bits 31:2
+ curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
+
+ dmaState = Dma_Transfer;
+
+ // schedule dma transfer (doDmaTransfer)
+ schedule(dmaTransferEvent, curTick() + 1);
+}
+
+void
+IdeDisk::abortDma()
+{
+ if (dmaState == Dma_Idle)
+ panic("Inconsistent DMA state, should be Start or Transfer!");
+
+ if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
+ panic("Inconsistent device state, should be Transfer or Prepare!\n");
+
+ updateState(ACT_CMD_ERROR);
+}
+
+void
+IdeDisk::startCommand()
+{
+ DevAction_t action = ACT_NONE;
+ uint32_t size = 0;
+ dmaRead = false;
+
+ // Decode commands
+ switch (cmdReg.command) {
+ // Supported non-data commands
+ case WDSF_READ_NATIVE_MAX:
+ size = (uint32_t)image->size() - 1;
+ cmdReg.sec_num = (size & 0xff);
+ cmdReg.cyl_low = ((size & 0xff00) >> 8);
+ cmdReg.cyl_high = ((size & 0xff0000) >> 16);
+ cmdReg.head = ((size & 0xf000000) >> 24);
+
+ devState = Command_Execution;
+ action = ACT_CMD_COMPLETE;
+ break;
+
+ case WDCC_RECAL:
+ case WDCC_IDP:
+ case WDCC_STANDBY_IMMED:
+ case WDCC_FLUSHCACHE:
+ case WDSF_VERIFY:
+ case WDSF_SEEK:
+ case SET_FEATURES:
+ case WDCC_SETMULTI:
+ case WDCC_IDLE:
+ devState = Command_Execution;
+ action = ACT_CMD_COMPLETE;
+ break;
+
+ // Supported PIO data-in commands
+ case WDCC_IDENTIFY:
+ cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
+ devState = Prepare_Data_In;
+ action = ACT_DATA_READY;
+ break;
+
+ case WDCC_READMULTI:
+ case WDCC_READ:
+ if (!(cmdReg.drive & DRIVE_LBA_BIT))
+ panic("Attempt to perform CHS access, only supports LBA\n");
+
+ if (cmdReg.sec_count == 0)
+ cmdBytes = cmdBytesLeft = (256 * SectorSize);
+ else
+ cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
+
+ curSector = getLBABase();
+
+ /** @todo make this a scheduled event to simulate disk delay */
+ devState = Prepare_Data_In;
+ action = ACT_DATA_READY;
+ break;
+
+ // Supported PIO data-out commands
+ case WDCC_WRITEMULTI:
+ case WDCC_WRITE:
+ if (!(cmdReg.drive & DRIVE_LBA_BIT))
+ panic("Attempt to perform CHS access, only supports LBA\n");
+
+ if (cmdReg.sec_count == 0)
+ cmdBytes = cmdBytesLeft = (256 * SectorSize);
+ else
+ cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
+ DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d\n", cmdBytesLeft);
+ curSector = getLBABase();
+
+ devState = Prepare_Data_Out;
+ action = ACT_DATA_READY;
+ break;
+
+ // Supported DMA commands
+ case WDCC_WRITEDMA:
+ dmaRead = true; // a write to the disk is a DMA read from memory
+ case WDCC_READDMA:
+ if (!(cmdReg.drive & DRIVE_LBA_BIT))
+ panic("Attempt to perform CHS access, only supports LBA\n");
+
+ if (cmdReg.sec_count == 0)
+ cmdBytes = cmdBytesLeft = (256 * SectorSize);
+ else
+ cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
+ DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d in readdma\n", cmdBytesLeft);
+
+ curSector = getLBABase();
+
+ devState = Prepare_Data_Dma;
+ action = ACT_DMA_READY;
+ break;
+
+ default:
+ panic("Unsupported ATA command: %#x\n", cmdReg.command);
+ }
+
+ if (action != ACT_NONE) {
+ // set the BSY bit
+ status |= STATUS_BSY_BIT;
+ // clear the DRQ bit
+ status &= ~STATUS_DRQ_BIT;
+ // clear the DF bit
+ status &= ~STATUS_DF_BIT;
+
+ updateState(action);
+ }
+}
+
+////
+// Handle setting and clearing interrupts
+////
+
+void
+IdeDisk::intrPost()
+{
+ DPRINTF(IdeDisk, "Posting Interrupt\n");
+ if (intrPending)
+ panic("Attempt to post an interrupt with one pending\n");
+
+ intrPending = true;
+
+ // talk to controller to set interrupt
+ if (ctrl) {
+ ctrl->intrPost();
+ }
+}
+
+void
+IdeDisk::intrClear()
+{
+ DPRINTF(IdeDisk, "Clearing Interrupt\n");
+ if (!intrPending)
+ panic("Attempt to clear a non-pending interrupt\n");
+
+ intrPending = false;
+
+ // talk to controller to clear interrupt
+ if (ctrl)
+ ctrl->intrClear();
+}
+
+////
+// Manage the device internal state machine
+////
+
+void
+IdeDisk::updateState(DevAction_t action)
+{
+ switch (devState) {
+ case Device_Srst:
+ if (action == ACT_SRST_SET) {
+ // set the BSY bit
+ status |= STATUS_BSY_BIT;
+ } else if (action == ACT_SRST_CLEAR) {
+ // clear the BSY bit
+ status &= ~STATUS_BSY_BIT;
+
+ // reset the device state
+ reset(devID);
+ }
+ break;
+
+ case Device_Idle_S:
+ if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
+ devState = Device_Idle_NS;
+ } else if (action == ACT_CMD_WRITE) {
+ startCommand();
+ }
+
+ break;
+
+ case Device_Idle_SI:
+ if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
+ devState = Device_Idle_NS;
+ intrClear();
+ } else if (action == ACT_STAT_READ || isIENSet()) {
+ devState = Device_Idle_S;
+ intrClear();
+ } else if (action == ACT_CMD_WRITE) {
+ intrClear();
+ startCommand();
+ }
+
+ break;
+
+ case Device_Idle_NS:
+ if (action == ACT_SELECT_WRITE && isDEVSelect()) {
+ if (!isIENSet() && intrPending) {
+ devState = Device_Idle_SI;
+ intrPost();
+ }
+ if (isIENSet() || !intrPending) {
+ devState = Device_Idle_S;
+ }
+ }
+ break;
+
+ case Command_Execution:
+ if (action == ACT_CMD_COMPLETE) {
+ // clear the BSY bit
+ setComplete();
+
+ if (!isIENSet()) {
+ devState = Device_Idle_SI;
+ intrPost();
+ } else {
+ devState = Device_Idle_S;
+ }
+ }
+ break;
+
+ case Prepare_Data_In:
+ if (action == ACT_CMD_ERROR) {
+ // clear the BSY bit
+ setComplete();
+
+ if (!isIENSet()) {
+ devState = Device_Idle_SI;
+ intrPost();
+ } else {
+ devState = Device_Idle_S;
+ }
+ } else if (action == ACT_DATA_READY) {
+ // clear the BSY bit
+ status &= ~STATUS_BSY_BIT;
+ // set the DRQ bit
+ status |= STATUS_DRQ_BIT;
+
+ // copy the data into the data buffer
+ if (cmdReg.command == WDCC_IDENTIFY) {
+ // Reset the drqBytes for this block
+ drqBytesLeft = sizeof(struct ataparams);
+
+ memcpy((void *)dataBuffer, (void *)&driveID,
+ sizeof(struct ataparams));
+ } else {
+ // Reset the drqBytes for this block
+ drqBytesLeft = SectorSize;
+
+ readDisk(curSector++, dataBuffer);
+ }
+
+ // put the first two bytes into the data register
+ memcpy((void *)&cmdReg.data, (void *)dataBuffer,
+ sizeof(uint16_t));
+
+ if (!isIENSet()) {
+ devState = Data_Ready_INTRQ_In;
+ intrPost();
+ } else {
+ devState = Transfer_Data_In;
+ }
+ }
+ break;
+
+ case Data_Ready_INTRQ_In:
+ if (action == ACT_STAT_READ) {
+ devState = Transfer_Data_In;
+ intrClear();
+ }
+ break;
+
+ case Transfer_Data_In:
+ if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
+ if (action == ACT_DATA_READ_BYTE) {
+ panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
+ } else {
+ drqBytesLeft -= 2;
+ cmdBytesLeft -= 2;
+
+ // copy next short into data registers
+ if (drqBytesLeft)
+ memcpy((void *)&cmdReg.data,
+ (void *)&dataBuffer[SectorSize - drqBytesLeft],
+ sizeof(uint16_t));
+ }
+
+ if (drqBytesLeft == 0) {
+ if (cmdBytesLeft == 0) {
+ // Clear the BSY bit
+ setComplete();
+ devState = Device_Idle_S;
+ } else {
+ devState = Prepare_Data_In;
+ // set the BSY_BIT
+ status |= STATUS_BSY_BIT;
+ // clear the DRQ_BIT
+ status &= ~STATUS_DRQ_BIT;
+
+ /** @todo change this to a scheduled event to simulate
+ disk delay */
+ updateState(ACT_DATA_READY);
+ }
+ }
+ }
+ break;
+
+ case Prepare_Data_Out:
+ if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
+ // clear the BSY bit
+ setComplete();
+
+ if (!isIENSet()) {
+ devState = Device_Idle_SI;
+ intrPost();
+ } else {
+ devState = Device_Idle_S;
+ }
+ } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
+ // clear the BSY bit
+ status &= ~STATUS_BSY_BIT;
+ // set the DRQ bit
+ status |= STATUS_DRQ_BIT;
+
+ // clear the data buffer to get it ready for writes
+ memset(dataBuffer, 0, MAX_DMA_SIZE);
+
+ // reset the drqBytes for this block
+ drqBytesLeft = SectorSize;
+
+ if (cmdBytesLeft == cmdBytes || isIENSet()) {
+ devState = Transfer_Data_Out;
+ } else {
+ devState = Data_Ready_INTRQ_Out;
+ intrPost();
+ }
+ }
+ break;
+
+ case Data_Ready_INTRQ_Out:
+ if (action == ACT_STAT_READ) {
+ devState = Transfer_Data_Out;
+ intrClear();
+ }
+ break;
+
+ case Transfer_Data_Out:
+ if (action == ACT_DATA_WRITE_BYTE ||
+ action == ACT_DATA_WRITE_SHORT) {
+
+ if (action == ACT_DATA_READ_BYTE) {
+ panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
+ } else {
+ // copy the latest short into the data buffer
+ memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
+ (void *)&cmdReg.data,
+ sizeof(uint16_t));
+
+ drqBytesLeft -= 2;
+ cmdBytesLeft -= 2;
+ }
+
+ if (drqBytesLeft == 0) {
+ // copy the block to the disk
+ writeDisk(curSector++, dataBuffer);
+
+ // set the BSY bit
+ status |= STATUS_BSY_BIT;
+ // set the seek bit
+ status |= STATUS_SEEK_BIT;
+ // clear the DRQ bit
+ status &= ~STATUS_DRQ_BIT;
+
+ devState = Prepare_Data_Out;
+
+ /** @todo change this to a scheduled event to simulate
+ disk delay */
+ updateState(ACT_DATA_READY);
+ }
+ }
+ break;
+
+ case Prepare_Data_Dma:
+ if (action == ACT_CMD_ERROR) {
+ // clear the BSY bit
+ setComplete();
+
+ if (!isIENSet()) {
+ devState = Device_Idle_SI;
+ intrPost();
+ } else {
+ devState = Device_Idle_S;
+ }
+ } else if (action == ACT_DMA_READY) {
+ // clear the BSY bit
+ status &= ~STATUS_BSY_BIT;
+ // set the DRQ bit
+ status |= STATUS_DRQ_BIT;
+
+ devState = Transfer_Data_Dma;
+
+ if (dmaState != Dma_Idle)
+ panic("Inconsistent DMA state, should be Dma_Idle\n");
+
+ dmaState = Dma_Start;
+ // wait for the write to the DMA start bit
+ }
+ break;
+
+ case Transfer_Data_Dma:
+ if (action == ACT_CMD_ERROR) {
+ dmaAborted = true;
+ devState = Device_Dma_Abort;
+ } else if (action == ACT_DMA_DONE) {
+ // clear the BSY bit
+ setComplete();
+ // set the seek bit
+ status |= STATUS_SEEK_BIT;
+ // clear the controller state for DMA transfer
+ ctrl->setDmaComplete(this);
+
+ if (!isIENSet()) {
+ devState = Device_Idle_SI;
+ intrPost();
+ } else {
+ devState = Device_Idle_S;
+ }
+ }
+ break;
+
+ case Device_Dma_Abort:
+ if (action == ACT_CMD_ERROR) {
+ setComplete();
+ status |= STATUS_SEEK_BIT;
+ ctrl->setDmaComplete(this);
+ dmaAborted = false;
+ dmaState = Dma_Idle;
+
+ if (!isIENSet()) {
+ devState = Device_Idle_SI;
+ intrPost();
+ } else {
+ devState = Device_Idle_S;
+ }
+ } else {
+ DPRINTF(IdeDisk, "Disk still busy aborting previous DMA command\n");
+ }
+ break;
+
+ default:
+ panic("Unknown IDE device state: %#x\n", devState);
+ }
+}
+
+void
+IdeDisk::serialize(CheckpointOut &cp) const
+{
+ // Check all outstanding events to see if they are scheduled
+ // these are all mutually exclusive
+ Tick reschedule = 0;
+ Events_t event = None;
+
+ int eventCount = 0;
+
+ if (dmaTransferEvent.scheduled()) {
+ reschedule = dmaTransferEvent.when();
+ event = Transfer;
+ eventCount++;
+ }
+ if (dmaReadWaitEvent.scheduled()) {
+ reschedule = dmaReadWaitEvent.when();
+ event = ReadWait;
+ eventCount++;
+ }
+ if (dmaWriteWaitEvent.scheduled()) {
+ reschedule = dmaWriteWaitEvent.when();
+ event = WriteWait;
+ eventCount++;
+ }
+ if (dmaPrdReadEvent.scheduled()) {
+ reschedule = dmaPrdReadEvent.when();
+ event = PrdRead;
+ eventCount++;
+ }
+ if (dmaReadEvent.scheduled()) {
+ reschedule = dmaReadEvent.when();
+ event = DmaRead;
+ eventCount++;
+ }
+ if (dmaWriteEvent.scheduled()) {
+ reschedule = dmaWriteEvent.when();
+ event = DmaWrite;
+ eventCount++;
+ }
+
+ assert(eventCount <= 1);
+
+ SERIALIZE_SCALAR(reschedule);
+ SERIALIZE_ENUM(event);
+
+ // Serialize device registers
+ SERIALIZE_SCALAR(cmdReg.data);
+ SERIALIZE_SCALAR(cmdReg.sec_count);
+ SERIALIZE_SCALAR(cmdReg.sec_num);
+ SERIALIZE_SCALAR(cmdReg.cyl_low);
+ SERIALIZE_SCALAR(cmdReg.cyl_high);
+ SERIALIZE_SCALAR(cmdReg.drive);
+ SERIALIZE_SCALAR(cmdReg.command);
+ SERIALIZE_SCALAR(status);
+ SERIALIZE_SCALAR(nIENBit);
+ SERIALIZE_SCALAR(devID);
+
+ // Serialize the PRD related information
+ SERIALIZE_SCALAR(curPrd.entry.baseAddr);
+ SERIALIZE_SCALAR(curPrd.entry.byteCount);
+ SERIALIZE_SCALAR(curPrd.entry.endOfTable);
+ SERIALIZE_SCALAR(curPrdAddr);
+
+ /** @todo need to serialized chunk generator stuff!! */
+ // Serialize current transfer related information
+ SERIALIZE_SCALAR(cmdBytesLeft);
+ SERIALIZE_SCALAR(cmdBytes);
+ SERIALIZE_SCALAR(drqBytesLeft);
+ SERIALIZE_SCALAR(curSector);
+ SERIALIZE_SCALAR(dmaRead);
+ SERIALIZE_SCALAR(intrPending);
+ SERIALIZE_SCALAR(dmaAborted);
+ SERIALIZE_ENUM(devState);
+ SERIALIZE_ENUM(dmaState);
+ SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
+}
+
+void
+IdeDisk::unserialize(CheckpointIn &cp)
+{
+ // Reschedule events that were outstanding
+ // these are all mutually exclusive
+ Tick reschedule = 0;
+ Events_t event = None;
+
+ UNSERIALIZE_SCALAR(reschedule);
+ UNSERIALIZE_ENUM(event);
+
+ switch (event) {
+ case None : break;
+ case Transfer : 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
+ UNSERIALIZE_SCALAR(cmdReg.data);
+ UNSERIALIZE_SCALAR(cmdReg.sec_count);
+ UNSERIALIZE_SCALAR(cmdReg.sec_num);
+ UNSERIALIZE_SCALAR(cmdReg.cyl_low);
+ UNSERIALIZE_SCALAR(cmdReg.cyl_high);
+ UNSERIALIZE_SCALAR(cmdReg.drive);
+ UNSERIALIZE_SCALAR(cmdReg.command);
+ UNSERIALIZE_SCALAR(status);
+ UNSERIALIZE_SCALAR(nIENBit);
+ UNSERIALIZE_SCALAR(devID);
+
+ // Unserialize the PRD related information
+ UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
+ UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
+ UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
+ UNSERIALIZE_SCALAR(curPrdAddr);
+
+ /** @todo need to serialized chunk generator stuff!! */
+ // Unserialize current transfer related information
+ UNSERIALIZE_SCALAR(cmdBytes);
+ UNSERIALIZE_SCALAR(cmdBytesLeft);
+ UNSERIALIZE_SCALAR(drqBytesLeft);
+ UNSERIALIZE_SCALAR(curSector);
+ UNSERIALIZE_SCALAR(dmaRead);
+ UNSERIALIZE_SCALAR(intrPending);
+ UNSERIALIZE_SCALAR(dmaAborted);
+ UNSERIALIZE_ENUM(devState);
+ UNSERIALIZE_ENUM(dmaState);
+ UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
+}
+
+IdeDisk *
+IdeDiskParams::create()
+{
+ return new IdeDisk(this);
+}
diff --git a/src/dev/storage/ide_disk.hh b/src/dev/storage/ide_disk.hh
new file mode 100644
index 000000000..9214599e9
--- /dev/null
+++ b/src/dev/storage/ide_disk.hh
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2013 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Schultz
+ */
+
+/** @file
+ * Device model for an IDE disk
+ */
+
+#ifndef __DEV_STORAGE_IDE_DISK_HH__
+#define __DEV_STORAGE_IDE_DISK_HH__
+
+#include "base/statistics.hh"
+#include "dev/io_device.hh"
+#include "dev/storage/disk_image.hh"
+#include "dev/storage/ide_atareg.h"
+#include "dev/storage/ide_ctrl.hh"
+#include "dev/storage/ide_wdcreg.h"
+#include "params/IdeDisk.hh"
+#include "sim/eventq.hh"
+
+class ChunkGenerator;
+
+#define DMA_BACKOFF_PERIOD 200
+
+#define MAX_DMA_SIZE 0x20000 // 128K
+#define MAX_SINGLE_DMA_SIZE 0x10000
+#define MAX_MULTSECT (128)
+
+#define PRD_BASE_MASK 0xfffffffe
+#define PRD_COUNT_MASK 0xfffe
+#define PRD_EOT_MASK 0x8000
+
+typedef struct PrdEntry {
+ uint32_t baseAddr;
+ uint16_t byteCount;
+ uint16_t endOfTable;
+} PrdEntry_t;
+
+class PrdTableEntry {
+ public:
+ PrdEntry_t entry;
+
+ uint32_t getBaseAddr()
+ {
+ return (entry.baseAddr & PRD_BASE_MASK);
+ }
+
+ uint32_t getByteCount()
+ {
+ return ((entry.byteCount == 0) ? MAX_SINGLE_DMA_SIZE :
+ (entry.byteCount & PRD_COUNT_MASK));
+ }
+
+ uint16_t getEOT()
+ {
+ return (entry.endOfTable & PRD_EOT_MASK);
+ }
+};
+
+#define DATA_OFFSET (0)
+#define ERROR_OFFSET (1)
+#define FEATURES_OFFSET (1)
+#define NSECTOR_OFFSET (2)
+#define SECTOR_OFFSET (3)
+#define LCYL_OFFSET (4)
+#define HCYL_OFFSET (5)
+#define SELECT_OFFSET (6)
+#define DRIVE_OFFSET (6)
+#define STATUS_OFFSET (7)
+#define COMMAND_OFFSET (7)
+
+#define CONTROL_OFFSET (2)
+#define ALTSTAT_OFFSET (2)
+
+#define SELECT_DEV_BIT 0x10
+#define CONTROL_RST_BIT 0x04
+#define CONTROL_IEN_BIT 0x02
+#define STATUS_BSY_BIT 0x80
+#define STATUS_DRDY_BIT 0x40
+#define STATUS_DRQ_BIT 0x08
+#define STATUS_SEEK_BIT 0x10
+#define STATUS_DF_BIT 0x20
+#define DRIVE_LBA_BIT 0x40
+
+#define DEV0 (0)
+#define DEV1 (1)
+
+typedef struct CommandReg {
+ uint16_t data;
+ uint8_t error;
+ uint8_t sec_count;
+ uint8_t sec_num;
+ uint8_t cyl_low;
+ uint8_t cyl_high;
+ union {
+ uint8_t drive;
+ uint8_t head;
+ };
+ uint8_t command;
+} CommandReg_t;
+
+typedef enum Events {
+ None = 0,
+ Transfer,
+ ReadWait,
+ WriteWait,
+ PrdRead,
+ DmaRead,
+ DmaWrite
+} Events_t;
+
+typedef enum DevAction {
+ ACT_NONE = 0,
+ ACT_CMD_WRITE,
+ ACT_CMD_COMPLETE,
+ ACT_CMD_ERROR,
+ ACT_SELECT_WRITE,
+ ACT_STAT_READ,
+ ACT_DATA_READY,
+ ACT_DATA_READ_BYTE,
+ ACT_DATA_READ_SHORT,
+ ACT_DATA_WRITE_BYTE,
+ ACT_DATA_WRITE_SHORT,
+ ACT_DMA_READY,
+ ACT_DMA_DONE,
+ ACT_SRST_SET,
+ ACT_SRST_CLEAR
+} DevAction_t;
+
+typedef enum DevState {
+ // Device idle
+ Device_Idle_S = 0,
+ Device_Idle_SI,
+ Device_Idle_NS,
+
+ // Software reset
+ Device_Srst,
+
+ // Non-data commands
+ Command_Execution,
+
+ // PIO data-in (data to host)
+ Prepare_Data_In,
+ Data_Ready_INTRQ_In,
+ Transfer_Data_In,
+
+ // PIO data-out (data from host)
+ Prepare_Data_Out,
+ Data_Ready_INTRQ_Out,
+ Transfer_Data_Out,
+
+ // DMA protocol
+ Prepare_Data_Dma,
+ Transfer_Data_Dma,
+ Device_Dma_Abort
+} DevState_t;
+
+typedef enum DmaState {
+ Dma_Idle = 0,
+ Dma_Start,
+ Dma_Transfer
+} DmaState_t;
+
+class IdeController;
+
+/**
+ * IDE Disk device model
+ */
+class IdeDisk : public SimObject
+{
+ protected:
+ /** The IDE controller for this disk. */
+ IdeController *ctrl;
+ /** The image that contains the data of this disk. */
+ DiskImage *image;
+
+ protected:
+ /** The disk delay in microseconds. */
+ int diskDelay;
+
+ private:
+ /** Drive identification structure for this disk */
+ struct ataparams driveID;
+ /** Data buffer for transfers */
+ uint8_t *dataBuffer;
+ /** Number of bytes in command data transfer */
+ uint32_t cmdBytes;
+ /** Number of bytes left in command data transfer */
+ uint32_t cmdBytesLeft;
+ /** Number of bytes left in DRQ block */
+ uint32_t drqBytesLeft;
+ /** Current sector in access */
+ uint32_t curSector;
+ /** Command block registers */
+ CommandReg_t cmdReg;
+ /** Status register */
+ uint8_t status;
+ /** Interrupt enable bit */
+ bool nIENBit;
+ /** Device state */
+ DevState_t devState;
+ /** Dma state */
+ DmaState_t dmaState;
+ /** Dma transaction is a read */
+ bool dmaRead;
+ /** PRD table base address */
+ uint32_t curPrdAddr;
+ /** PRD entry */
+ PrdTableEntry curPrd;
+ /** Device ID (master=0/slave=1) */
+ int devID;
+ /** Interrupt pending */
+ bool intrPending;
+ /** DMA Aborted */
+ bool dmaAborted;
+
+ Stats::Scalar dmaReadFullPages;
+ Stats::Scalar dmaReadBytes;
+ Stats::Scalar dmaReadTxs;
+ Stats::Scalar dmaWriteFullPages;
+ Stats::Scalar dmaWriteBytes;
+ Stats::Scalar dmaWriteTxs;
+
+ public:
+ typedef IdeDiskParams Params;
+ IdeDisk(const Params *p);
+
+ /**
+ * Delete the data buffer.
+ */
+ ~IdeDisk();
+
+ /**
+ * Reset the device state
+ */
+ void reset(int id);
+
+ /**
+ * Register Statistics
+ */
+ void regStats() override;
+
+ /**
+ * Set the controller for this device
+ * @param c The IDE controller
+ */
+ void setController(IdeController *c) {
+ if (ctrl) panic("Cannot change the controller once set!\n");
+ ctrl = c;
+ }
+
+ // Device register read/write
+ 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);
+ void abortDma();
+
+ private:
+ void startCommand();
+
+ // Interrupt management
+ void intrPost();
+ void intrClear();
+
+ // DMA stuff
+ void doDmaTransfer();
+ friend class EventWrapper<IdeDisk, &IdeDisk::doDmaTransfer>;
+ EventWrapper<IdeDisk, &IdeDisk::doDmaTransfer> dmaTransferEvent;
+
+ void doDmaDataRead();
+
+ void doDmaRead();
+ ChunkGenerator *dmaReadCG;
+ friend class EventWrapper<IdeDisk, &IdeDisk::doDmaRead>;
+ EventWrapper<IdeDisk, &IdeDisk::doDmaRead> dmaReadWaitEvent;
+
+ void doDmaDataWrite();
+
+ void doDmaWrite();
+ ChunkGenerator *dmaWriteCG;
+ friend class EventWrapper<IdeDisk, &IdeDisk::doDmaWrite>;
+ EventWrapper<IdeDisk, &IdeDisk::doDmaWrite> dmaWriteWaitEvent;
+
+ void dmaPrdReadDone();
+ friend class EventWrapper<IdeDisk, &IdeDisk::dmaPrdReadDone>;
+ EventWrapper<IdeDisk, &IdeDisk::dmaPrdReadDone> dmaPrdReadEvent;
+
+ void dmaReadDone();
+ friend class EventWrapper<IdeDisk, &IdeDisk::dmaReadDone>;
+ EventWrapper<IdeDisk, &IdeDisk::dmaReadDone> dmaReadEvent;
+
+ void dmaWriteDone();
+ friend class EventWrapper<IdeDisk, &IdeDisk::dmaWriteDone>;
+ EventWrapper<IdeDisk, &IdeDisk::dmaWriteDone> dmaWriteEvent;
+
+ // Disk image read/write
+ void readDisk(uint32_t sector, uint8_t *data);
+ void writeDisk(uint32_t sector, uint8_t *data);
+
+ // State machine management
+ void updateState(DevAction_t action);
+
+ // Utility functions
+ bool isBSYSet() { return (status & STATUS_BSY_BIT); }
+ bool isIENSet() { return nIENBit; }
+ bool isDEVSelect();
+
+ void setComplete()
+ {
+ // clear out the status byte
+ status = 0;
+ // set the DRDY bit
+ status |= STATUS_DRDY_BIT;
+ // set the SEEK bit
+ status |= STATUS_SEEK_BIT;
+ }
+
+ uint32_t getLBABase()
+ {
+ return (Addr)(((cmdReg.head & 0xf) << 24) | (cmdReg.cyl_high << 16) |
+ (cmdReg.cyl_low << 8) | (cmdReg.sec_num));
+ }
+
+ inline Addr pciToDma(Addr pciAddr);
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+};
+
+
+#endif // __DEV_STORAGE_IDE_DISK_HH__
diff --git a/src/dev/storage/ide_wdcreg.h b/src/dev/storage/ide_wdcreg.h
new file mode 100644
index 000000000..f6a59c9f2
--- /dev/null
+++ b/src/dev/storage/ide_wdcreg.h
@@ -0,0 +1,197 @@
+/* $OpenBSD: wdcreg.h,v 1.13 2004/09/24 07:05:44 grange Exp $ */
+/* $NetBSD: wdcreg.h,v 1.22 1999/03/07 14:02:54 bouyer Exp $ */
+
+/*-
+ * Copyright (c) 1991 The Regents of the University of California
+ * All rights reserved
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)wdreg.h 7.1 (Berkeley) 5/9/91
+ */
+
+#ifndef _DEV_IC_WDCREG_H_
+#define _DEV_IC_WDCREG_H_
+
+/*
+ * Controller register (wdr_ctlr)
+ */
+#define WDCTL_4BIT 0x08 /* use four head bits (wd1003) */
+#define WDCTL_RST 0x04 /* reset the controller */
+#define WDCTL_IDS 0x02 /* disable controller interrupts */
+
+/*
+ * Status bits.
+ */
+#define WDCS_BSY 0x80 /* busy */
+#define WDCS_DRDY 0x40 /* drive ready */
+#define WDCS_DWF 0x20 /* drive write fault */
+#define WDCS_DSC 0x10 /* drive seek complete */
+#define WDCS_DRQ 0x08 /* data request */
+#define WDCS_CORR 0x04 /* corrected data */
+#define WDCS_IDX 0x02 /* index */
+#define WDCS_ERR 0x01 /* error */
+#define WDCS_BITS "\020\010BSY\007DRDY\006DWF\005DSC\004DRQ\003CORR\002IDX\001ERR"
+
+/*
+ * Error bits.
+ */
+#define WDCE_BBK 0x80 /* bad block detected */
+#define WDCE_CRC 0x80 /* CRC error (Ultra-DMA only) */
+#define WDCE_UNC 0x40 /* uncorrectable data error */
+#define WDCE_MC 0x20 /* media changed */
+#define WDCE_IDNF 0x10 /* id not found */
+#define WDCE_MCR 0x08 /* media change requested */
+#define WDCE_ABRT 0x04 /* aborted command */
+#define WDCE_TK0NF 0x02 /* track 0 not found */
+#define WDCE_AMNF 0x01 /* address mark not found */
+
+/*
+ * Commands for Disk Controller.
+ */
+#define WDCC_NOP 0x00 /* NOP - Always fail with "aborted command" */
+#define WDCC_RECAL 0x10 /* disk restore code -- resets cntlr */
+
+#define WDCC_READ 0x20 /* disk read code */
+#define WDCC_WRITE 0x30 /* disk write code */
+#define WDCC__LONG 0x02 /* modifier -- access ecc bytes */
+#define WDCC__NORETRY 0x01 /* modifier -- no retrys */
+
+#define WDCC_FORMAT 0x50 /* disk format code */
+#define WDCC_DIAGNOSE 0x90 /* controller diagnostic */
+#define WDCC_IDP 0x91 /* initialize drive parameters */
+
+#define WDCC_READMULTI 0xc4 /* read multiple */
+#define WDCC_WRITEMULTI 0xc5 /* write multiple */
+#define WDCC_SETMULTI 0xc6 /* set multiple mode */
+
+#define WDCC_READDMA 0xc8 /* read with DMA */
+#define WDCC_WRITEDMA 0xca /* write with DMA */
+
+#define WDCC_ACKMC 0xdb /* acknowledge media change */
+#define WDCC_LOCK 0xde /* lock drawer */
+#define WDCC_UNLOCK 0xdf /* unlock drawer */
+
+#define WDCC_FLUSHCACHE 0xe7 /* Flush cache */
+#define WDCC_IDENTIFY 0xec /* read parameters from controller */
+#define SET_FEATURES 0xef /* set features */
+
+#define WDCC_IDLE 0xe3 /* set idle timer & enter idle mode */
+#define WDCC_IDLE_IMMED 0xe1 /* enter idle mode */
+#define WDCC_SLEEP 0xe6 /* enter sleep mode */
+#define WDCC_STANDBY 0xe2 /* set standby timer & enter standby mode */
+#define WDCC_STANDBY_IMMED 0xe0 /* enter standby mode */
+#define WDCC_CHECK_PWR 0xe5 /* check power mode */
+
+#define WDCC_READ_EXT 0x24 /* read 48-bit addressing */
+#define WDCC_WRITE_EXT 0x34 /* write 48-bit addressing */
+
+#define WDCC_READMULTI_EXT 0x29 /* read multiple 48-bit addressing */
+#define WDCC_WRITEMULTI_EXT 0x39 /* write multiple 48-bit addressing */
+
+#define WDCC_READDMA_EXT 0x25 /* read 48-bit addressing with DMA */
+#define WDCC_WRITEDMA_EXT 0x35 /* write 48-bit addressing with DMA */
+
+#define WDCC_FLUSHCACHE_EXT 0xea /* 48-bit addressing flush cache */
+
+/* Subcommands for SET_FEATURES (features register ) */
+#define WDSF_8BIT_PIO_EN 0x01 /* Enable 8bit PIO (CFA featureset) */
+#define WDSF_EN_WR_CACHE 0x02
+#define WDSF_SET_MODE 0x03
+#define WDSF_REASSIGN_EN 0x04 /* Obsolete in ATA-6 */
+#define WDSF_APM_EN 0x05 /* Enable Adv. Power Management */
+#define WDSF_PUIS_EN 0x06 /* Enable Power-Up In Standby */
+#define WDSF_PUIS_SPINUP 0x07 /* Power-Up In Standby spin-up */
+#define WDSF_CFA_MODE1_EN 0x0A /* Enable CFA power mode 1 */
+#define WDSF_RMSN_DS 0x31 /* Disable Removable Media Status */
+#define WDSF_RETRY_DS 0x33 /* Obsolete in ATA-6 */
+#define WDSF_AAM_EN 0x42 /* Enable Autom. Acoustic Management */
+#define WDSF_SET_CACHE_SGMT 0x54 /* Obsolete in ATA-6 */
+#define WDSF_READAHEAD_DS 0x55 /* Disable read look-ahead */
+#define WDSF_RLSE_EN 0x5D /* Enable release interrupt */
+#define WDSF_SRV_EN 0x5E /* Enable SERVICE interrupt */
+#define WDSF_POD_DS 0x66
+#define WDSF_ECC_DS 0x77
+#define WDSF_8BIT_PIO_DS 0x81 /* Disable 8bit PIO (CFA featureset) */
+#define WDSF_WRITE_CACHE_DS 0x82
+#define WDSF_REASSIGN_DS 0x84
+#define WDSF_APM_DS 0x85 /* Disable Adv. Power Management */
+#define WDSF_PUIS_DS 0x86 /* Disable Power-Up In Standby */
+#define WDSF_ECC_EN 0x88
+#define WDSF_CFA_MODE1_DS 0x8A /* Disable CFA power mode 1 */
+#define WDSF_RMSN_EN 0x95 /* Enable Removable Media Status */
+#define WDSF_RETRY_EN 0x99 /* Obsolete in ATA-6 */
+#define WDSF_SET_CURRENT 0x9A /* Obsolete in ATA-6 */
+#define WDSF_READAHEAD_EN 0xAA
+#define WDSF_PREFETCH_SET 0xAB /* Obsolete in ATA-6 */
+#define WDSF_AAM_DS 0xC2 /* Disable Autom. Acoustic Management */
+#define WDSF_POD_EN 0xCC
+#define WDSF_RLSE_DS 0xDD /* Disable release interrupt */
+#define WDSF_SRV_DS 0xDE /* Disable SERVICE interrupt */
+#define WDSF_READ_NATIVE_MAX 0xF8
+#define WDSF_SEEK 0x70
+#define WDSF_VERIFY 0x40
+
+/* parameters uploaded to device/heads register */
+#define WDSD_IBM 0xa0 /* forced to 512 byte sector, ecc */
+#define WDSD_CHS 0x00 /* cylinder/head/sector addressing */
+#define WDSD_LBA 0x40 /* logical block addressing */
+
+/* Commands for ATAPI devices */
+#define ATAPI_CHECK_POWER_MODE 0xe5
+#define ATAPI_EXEC_DRIVE_DIAGS 0x90
+#define ATAPI_IDLE_IMMEDIATE 0xe1
+#define ATAPI_NOP 0x00
+#define ATAPI_PKT_CMD 0xa0
+#define ATAPI_IDENTIFY_DEVICE 0xa1
+#define ATAPI_SOFT_RESET 0x08
+#define ATAPI_DEVICE_RESET 0x08 /* ATA/ATAPI-5 name for soft reset */
+#define ATAPI_SLEEP 0xe6
+#define ATAPI_STANDBY_IMMEDIATE 0xe0
+#define ATAPI_SMART 0xB0 /* SMART operations */
+#define ATAPI_SETMAX 0xF9 /* Set Max Address */
+#define ATAPI_WRITEEXT 0x34 /* Write sectors Ext */
+#define ATAPI_SETMAXEXT 0x37 /* Set Max Address Ext */
+#define ATAPI_WRITEMULTIEXT 0x39 /* Write Multi Ext */
+
+/* Bytes used by ATAPI_PACKET_COMMAND ( feature register) */
+#define ATAPI_PKT_CMD_FTRE_DMA 0x01
+#define ATAPI_PKT_CMD_FTRE_OVL 0x02
+
+/* ireason */
+#define WDCI_CMD 0x01 /* command(1) or data(0) */
+#define WDCI_IN 0x02 /* transfer to(1) or from(0) the host */
+#define WDCI_RELEASE 0x04 /* bus released until completion */
+
+#define PHASE_CMDOUT (WDCS_DRQ | WDCI_CMD)
+#define PHASE_DATAIN (WDCS_DRQ | WDCI_IN)
+#define PHASE_DATAOUT WDCS_DRQ
+#define PHASE_COMPLETED (WDCI_IN | WDCI_CMD)
+#define PHASE_ABORTED 0
+
+#endif /* !_DEV_IC_WDCREG_H_ */
diff --git a/src/dev/storage/simple_disk.cc b/src/dev/storage/simple_disk.cc
new file mode 100644
index 000000000..49c001a00
--- /dev/null
+++ b/src/dev/storage/simple_disk.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2001-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
+ */
+
+/* @file
+ * Simple disk interface for the system console
+ */
+
+#include "dev/storage/simple_disk.hh"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <cstring>
+#include <string>
+
+#include "base/misc.hh"
+#include "base/trace.hh"
+#include "debug/SimpleDisk.hh"
+#include "debug/SimpleDiskData.hh"
+#include "dev/storage/disk_image.hh"
+#include "mem/port_proxy.hh"
+#include "sim/system.hh"
+
+using namespace std;
+
+SimpleDisk::SimpleDisk(const Params *p)
+ : SimObject(p), system(p->system), image(p->disk)
+{}
+
+SimpleDisk::~SimpleDisk()
+{}
+
+
+void
+SimpleDisk::read(Addr addr, baddr_t block, int count) const
+{
+ uint8_t *data = new uint8_t[SectorSize * count];
+
+ if (count & (SectorSize - 1))
+ panic("Not reading a multiple of a sector (count = %d)", count);
+
+ for (int i = 0, j = 0; i < count; i += SectorSize, j++)
+ image->read(data + i, block + j);
+
+ system->physProxy.writeBlob(addr, data, count);
+
+ DPRINTF(SimpleDisk, "read block=%#x len=%d\n", (uint64_t)block, count);
+ DDUMP(SimpleDiskData, data, count);
+
+ delete [] data;
+}
+
+void
+SimpleDisk::write(Addr addr, baddr_t block, int count)
+{
+ panic("unimplemented!\n");
+
+#if 0
+ uint8_t *data = physmem->dma_addr(addr, count);
+ if (!data)
+ panic("dma out of range! write addr=%#x count=%d\n", addr, count);
+
+ image->write(data, block, count);
+#endif
+}
+
+SimpleDisk *
+SimpleDiskParams::create()
+{
+ return new SimpleDisk(this);
+}
diff --git a/src/dev/storage/simple_disk.hh b/src/dev/storage/simple_disk.hh
new file mode 100644
index 000000000..2a3ff4986
--- /dev/null
+++ b/src/dev/storage/simple_disk.hh
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2001-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
+ */
+
+/* @file
+ * Simple disk interface for the system console
+ */
+
+#ifndef __DEV_STORAGE_SIMPLE_DISK_HH__
+#define __DEV_STORAGE_SIMPLE_DISK_HH__
+
+#include "params/SimpleDisk.hh"
+#include "sim/sim_object.hh"
+
+class DiskImage;
+class System;
+
+/*
+ * Trivial interface to a disk image used by the System Console
+ */
+class SimpleDisk : public SimObject
+{
+ public:
+ typedef uint64_t baddr_t;
+
+ protected:
+ System *system;
+ DiskImage *image;
+
+ public:
+ typedef SimpleDiskParams Params;
+ SimpleDisk(const Params *p);
+ ~SimpleDisk();
+
+ void read(Addr addr, baddr_t block, int count) const;
+ void write(Addr addr, baddr_t block, int count);
+};
+
+#endif // __DEV_STORAGE_SIMPLE_DISK_HH__