2004-03-23 23:10:07 +01:00
|
|
|
/*
|
2005-06-05 11:16:00 +02:00
|
|
|
* Copyright (c) 2004-2005 The Regents of The University of Michigan
|
2004-03-23 23:10:07 +01:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are
|
|
|
|
* met: redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer;
|
|
|
|
* redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution;
|
|
|
|
* neither the name of the copyright holders nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived from
|
|
|
|
* this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** @file
|
|
|
|
* Device model implementation for an IDE disk
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <cerrno>
|
|
|
|
#include <cstring>
|
|
|
|
#include <deque>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include "base/cprintf.hh" // csprintf
|
|
|
|
#include "base/trace.hh"
|
|
|
|
#include "dev/disk_image.hh"
|
|
|
|
#include "dev/ide_disk.hh"
|
2004-05-03 17:47:52 +02:00
|
|
|
#include "dev/ide_ctrl.hh"
|
|
|
|
#include "dev/tsunami.hh"
|
|
|
|
#include "dev/tsunami_pchip.hh"
|
2005-06-05 02:50:10 +02:00
|
|
|
#include "mem/functional/physical.hh"
|
2004-05-03 17:47:52 +02:00
|
|
|
#include "mem/bus/bus.hh"
|
|
|
|
#include "mem/bus/dma_interface.hh"
|
|
|
|
#include "mem/bus/pio_interface.hh"
|
|
|
|
#include "mem/bus/pio_interface_impl.hh"
|
2004-03-23 23:10:07 +01:00
|
|
|
#include "sim/builder.hh"
|
|
|
|
#include "sim/sim_object.hh"
|
2005-06-02 03:59:27 +02:00
|
|
|
#include "sim/root.hh"
|
2006-02-12 18:40:58 +01:00
|
|
|
#include "arch/isa_traits.hh"
|
2004-03-23 23:10:07 +01:00
|
|
|
|
|
|
|
using namespace std;
|
Changes to untemplate StaticInst and StaticInstPtr, change the isa to a namespace instead of a class, an improvement to the architecture specific header file selection system, and fixed up a few include paths.
arch/alpha/alpha_linux_process.cc:
Added using directive for AlphaISA namespace
arch/alpha/alpha_memory.hh:
arch/alpha/isa/branch.isa:
cpu/pc_event.hh:
Added typedefs for Addr
arch/alpha/alpha_tru64_process.cc:
arch/alpha/arguments.cc:
Added using directive for AlphaISA
arch/alpha/ev5.hh:
Added an include of arch/alpha/isa_traits.hh, and a using directive for the AlphaISA namespace.
arch/alpha/faults.hh:
Added a typedef for the Addr type, and changed the formatting of the faults slightly.
arch/alpha/isa/main.isa:
Untemplatized StaticInst, added a using for namespace AlphaISA to show up in decoder.cc and the exec.ccs, relocated makeNop to decoder.hh
arch/alpha/isa/mem.isa:
Untemplatized StaticInst and StaticInstPtr
arch/alpha/isa/pal.isa:
cpu/base_dyn_inst.cc:
Untemplatized StaticInstPtr
arch/alpha/isa_traits.hh:
Changed variables to be externs instead of static since they are part of a namespace and not a class.
arch/alpha/stacktrace.cc:
Untemplatized StaticInstPtr, and added a using directive for AlphaISA.
arch/alpha/stacktrace.hh:
Added some typedefs for Addr and MachInst, and untemplatized StaticInstPtr
arch/alpha/vtophys.cc:
Added a using directive for AlphaISA
arch/alpha/vtophys.hh:
Added the AlphaISA namespace specifier where needed
arch/isa_parser.py:
Changed the placement of the definition of the decodeInst function to be outside the namespaceInst namespace.
base/loader/object_file.hh:
cpu/o3/bpred_unit.hh:
Added a typedef for Addr
base/loader/symtab.hh:
Added a typedef for Addr, and added a TheISA to Addr in another typedef
base/remote_gdb.cc:
Added a using namespace TheISA, and untemplatized StaticInstPtr
base/remote_gdb.hh:
Added typedefs for Addr and MachInst
cpu/base.cc:
Added TheISA specifier to some variables exported from the isa.
cpu/base.hh:
Added a typedef for Addr, and TheISA to some variables from the ISA
cpu/base_dyn_inst.hh:
Untemplatized StaticInstPtr, and added TheISA specifier to some variables from the ISA.
cpu/exec_context.hh:
Added some typedefs for types from the isa, and added TheISA specifier to some variables from the isa
cpu/exetrace.hh:
Added typedefs for some types from the ISA, and untemplatized StaticInstPtr
cpu/memtest/memtest.cc:
cpu/o3/btb.cc:
dev/baddev.cc:
dev/ide_ctrl.cc:
dev/ide_disk.cc:
dev/isa_fake.cc:
dev/ns_gige.cc:
dev/pciconfigall.cc:
dev/platform.cc:
dev/sinic.cc:
dev/uart8250.cc:
kern/freebsd/freebsd_system.cc:
kern/linux/linux_system.cc:
kern/system_events.cc:
kern/tru64/dump_mbuf.cc:
kern/tru64/tru64_events.cc:
sim/process.cc:
sim/pseudo_inst.cc:
sim/system.cc:
Added using namespace TheISA
cpu/memtest/memtest.hh:
cpu/trace/opt_cpu.hh:
cpu/trace/reader/itx_reader.hh:
dev/ide_disk.hh:
dev/pcidev.hh:
dev/platform.hh:
dev/tsunami.hh:
sim/system.hh:
sim/vptr.hh:
Added typedef for Addr
cpu/o3/2bit_local_pred.hh:
Changed the include to use arch/isa_traits.hh instead of arch/alpha/isa_traits.hh. Added typedef for Addr
cpu/o3/alpha_cpu.hh:
Added typedefs for Addr and IntReg
cpu/o3/alpha_cpu_impl.hh:
Added this-> to setNextPC to fix a problem since it didn't depend on template parameters any more. Removed "typename" where it was no longer needed.
cpu/o3/alpha_dyn_inst.hh:
Cleaned up some typedefs, and untemplatized StaticInst
cpu/o3/alpha_dyn_inst_impl.hh:
untemplatized StaticInstPtr
cpu/o3/alpha_impl.hh:
Fixed up a typedef of MachInst
cpu/o3/bpred_unit_impl.hh:
Added a using TheISA::MachInst to a function
cpu/o3/btb.hh:
Changed an include from arch/alpha/isa_traits.hh to arch/isa_traits.hh, and added a typedef for Addr
cpu/o3/commit.hh:
Removed a typedef of Impl::ISA as ISA, since TheISA takes care of this now.
cpu/o3/cpu.cc:
Cleaned up namespace issues
cpu/o3/cpu.hh:
Cleaned up namespace usage
cpu/o3/decode.hh:
Removed typedef of ISA, and changed it to TheISA
cpu/o3/fetch.hh:
Fized up typedefs, and changed ISA to TheISA
cpu/o3/free_list.hh:
Changed include of arch/alpha/isa_traits.hh to arch/isa_traits.hh
cpu/o3/iew.hh:
Removed typedef of ISA
cpu/o3/iew_impl.hh:
Added TheISA namespace specifier to MachInst
cpu/o3/ras.hh:
Changed include from arch/alpha/isa_traits.hh to arch/isa_traits.hh, and added a typedef for Addr.
cpu/o3/regfile.hh:
Changed ISA to TheISA, and added some typedefs for Addr, IntReg, FloatReg, and MiscRegFile
cpu/o3/rename.hh:
Changed ISA to TheISA, and added a typedef for RegIndex
cpu/o3/rename_map.hh:
Added an include for arch/isa_traits.hh, and a typedef for RegIndex
cpu/o3/rob.hh:
Added a typedef for RegIndex
cpu/o3/store_set.hh:
cpu/o3/tournament_pred.hh:
Changed an include of arch/alpha/isa_traits.hh to arch/isa_traits.hh, and added a typedef of Addr
cpu/ozone/cpu.hh:
Changed ISA into TheISA, and untemplatized StaticInst
cpu/pc_event.cc:
Added namespace specifier TheISA to Addr types
cpu/profile.hh:
kern/kernel_stats.hh:
Added typedef for Addr, and untemplatized StaticInstPtr
cpu/simple/cpu.cc:
Changed using directive from LittleEndianGuest to AlphaISA, which will contain both namespaces. Added TheISA where needed, and untemplatized StaticInst
cpu/simple/cpu.hh:
Added a typedef for MachInst, and untemplatized StaticInst
cpu/static_inst.cc:
Untemplatized StaticInst
cpu/static_inst.hh:
Untemplatized StaticInst by using the TheISA namespace
dev/alpha_console.cc:
Added using namespace AlphaISA
dev/simple_disk.hh:
Added typedef for Addr and fixed up some formatting
dev/sinicreg.hh:
Added TheISA namespace specifier where needed
dev/tsunami.cc:
dev/tsunami_io.cc:
dev/tsunami_pchip.cc:
Added using namespace TheISA. It might be better for it to be AlphaISA
dev/tsunami_cchip.cc:
Added typedef for TheISA. It might be better for it to be AlphaISA
kern/linux/aligned.hh:
sim/pseudo_inst.hh:
Added TheISA namespace specifier to Addr
kern/linux/linux_threadinfo.hh:
Added typedef for Addr, and TheISA namespace specifier to StackPointerReg
kern/tru64/mbuf.hh:
Added TheISA to Addr type in structs
sim/process.hh:
Added typedefs of Addr, RegFile, and MachInst
sim/syscall_emul.cc:
Added using namespace TheISA, and a cast of VMPageSize to the int type
sim/syscall_emul.hh:
Added typecast for Addr, and TheISA namespace specifier for where needed
--HG--
extra : convert_revision : 91d4f6ca33a73b21c1f1771d74bfdea3b80eff45
2006-02-19 08:34:37 +01:00
|
|
|
using namespace TheISA;
|
2004-03-23 23:10:07 +01:00
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys,
|
Make the notion of a global event tick independent of the actual
CPU cycle ticks. This allows the user to have CPUs of different
frequencies, and also allows frequencies and latencies that are
not evenly divisible by the CPU frequency. For now, the CPU
frequency is still set to the global frequency, but soon, we'll
hopefully make the global frequency fixed at something like 1THz
and set all other frequencies independently.
arch/alpha/ev5.cc:
The cycles counter is based on the current cpu cycle.
cpu/base_cpu.cc:
frequency isn't the cpu parameter anymore, cycleTime is.
cpu/base_cpu.hh:
frequency isn't the cpu parameter anymore, cycleTime is.
create several public functions for getting the cpu frequency
and the numbers of ticks for a given number of cycles, etc.
cpu/memtest/memtest.cc:
cpu/simple_cpu/simple_cpu.cc:
cpu/simple_cpu/simple_cpu.hh:
cpu/trace/trace_cpu.cc:
Now that ticks aren't cpu cycles, fixup code to advance
by the proper number of ticks.
cpu/memtest/memtest.hh:
cpu/trace/trace_cpu.hh:
Provide a function to get the number of ticks for a given
number of cycles.
dev/alpha_console.cc:
Update for changes in the way that frequencies and latencies are
accessed. Move some stuff to init()
dev/alpha_console.hh:
Need a pointer to the system and the cpu to get the frequency
so we can pass the info to the console code.
dev/etherbus.cc:
dev/etherbus.hh:
dev/etherlink.cc:
dev/etherlink.hh:
dev/ethertap.cc:
dev/ide_disk.hh:
dev/ns_gige.cc:
dev/ns_gige.hh:
update for changes in the way bandwidths are passed from
python to C++ to accomidate the new way that ticks works.
dev/ide_disk.cc:
update for changes in the way bandwidths are passed from
python to C++ to accomidate the new way that ticks works.
Add some extra debugging printfs
dev/platform.cc:
dev/sinic.cc:
dev/sinic.hh:
outline the constructor and destructor
dev/platform.hh:
outline the constructor and destructor.
don't keep track of the interrupt frequency. Only provide the
accessor function.
dev/tsunami.cc:
dev/tsunami.hh:
outline the constructor and destructor
Don't set the interrupt frequency here. Get it from the actual device
that does the interrupting.
dev/tsunami_io.cc:
dev/tsunami_io.hh:
Make the interrupt interval a configuration parameter. (And convert
the interval to the new latency/frequency stuff in the python)
kern/linux/linux_system.cc:
update for changes in the way bandwidths are passed from
python to C++ to accomidate the new way that ticks works.
For now, we must get the boot cpu's frequency as a parameter
since allowing the system to have a pointer to the boot cpu would
cause a cycle.
kern/tru64/tru64_system.cc:
For now, we must get the boot cpu's frequency as a parameter
since allowing the system to have a pointer to the boot cpu would
cause a cycle.
python/m5/config.py:
Fix support for cycle_time relative latencies and frequencies.
Add support for getting a NetworkBandwidth or a MemoryBandwidth.
python/m5/objects/BaseCPU.mpy:
All CPUs now have a cycle_time. The default is the global frequency,
but it is now possible to set the global frequency to some large value
(like 1THz) and set each CPU frequency independently.
python/m5/objects/BaseCache.mpy:
python/m5/objects/Ide.mpy:
Make this a Latency parameter
python/m5/objects/BaseSystem.mpy:
We need to pass the boot CPU's frequency to the system
python/m5/objects/Ethernet.mpy:
Update parameter types to use latency and bandwidth types
python/m5/objects/Platform.mpy:
this frequency isn't needed. We get it from the clock interrupt.
python/m5/objects/Tsunami.mpy:
The clock generator should hold the frequency
sim/eventq.hh:
Need to remove this assertion because the writeback event
queue is different from the CPU's event queue which can cause
this assertion to fail.
sim/process.cc:
Fix comment.
sim/system.hh:
Struct member to hold the boot CPU's frequency.
sim/universe.cc:
remove unneeded variable.
--HG--
extra : convert_revision : 51efe4041095234bf458d9b3b0d417f4cae16fdc
2005-04-11 21:32:06 +02:00
|
|
|
int id, Tick delay)
|
|
|
|
: SimObject(name), ctrl(NULL), image(img), physmem(phys), diskDelay(delay),
|
2004-05-06 21:21:07 +02:00
|
|
|
dmaTransferEvent(this), dmaReadWaitEvent(this),
|
|
|
|
dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
|
2004-05-03 17:47:52 +02:00
|
|
|
dmaReadEvent(this), dmaWriteEvent(this)
|
2004-03-23 23:10:07 +01:00
|
|
|
{
|
2004-06-01 23:19:47 +02:00
|
|
|
// Reset the device state
|
|
|
|
reset(id);
|
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
// fill out the drive ID structure
|
2005-06-05 05:56:53 +02:00
|
|
|
memset(&driveID, 0, sizeof(struct ataparams));
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
// Calculate LBA and C/H/S values
|
|
|
|
uint16_t cylinders;
|
|
|
|
uint8_t heads;
|
|
|
|
uint8_t sectors;
|
|
|
|
|
|
|
|
uint32_t lba_size = image->size();
|
|
|
|
if (lba_size >= 16383*16*63) {
|
|
|
|
cylinders = 16383;
|
|
|
|
heads = 16;
|
|
|
|
sectors = 63;
|
|
|
|
} else {
|
|
|
|
if (lba_size >= 63)
|
|
|
|
sectors = 63;
|
|
|
|
else
|
|
|
|
sectors = lba_size;
|
|
|
|
|
|
|
|
if ((lba_size / sectors) >= 16)
|
|
|
|
heads = 16;
|
|
|
|
else
|
|
|
|
heads = (lba_size / sectors);
|
|
|
|
|
|
|
|
cylinders = lba_size / (heads * sectors);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the model name
|
2005-08-23 17:38:27 +02:00
|
|
|
strncpy((char *)driveID.atap_model, "5MI EDD si k",
|
|
|
|
sizeof(driveID.atap_model));
|
2004-05-03 17:47:52 +02:00
|
|
|
// Set the maximum multisector transfer size
|
2005-06-05 05:56:53 +02:00
|
|
|
driveID.atap_multi = MAX_MULTSECT;
|
2004-05-03 17:47:52 +02:00
|
|
|
// IORDY supported, IORDY disabled, LBA enabled, DMA enabled
|
2005-06-05 05:56:53 +02:00
|
|
|
driveID.atap_capabilities1 = 0x7;
|
2004-05-03 17:47:52 +02:00
|
|
|
// UDMA support, EIDE support
|
2005-06-05 05:56:53 +02:00
|
|
|
driveID.atap_extensions = 0x6;
|
2004-05-03 17:47:52 +02:00
|
|
|
// Setup default C/H/S settings
|
2005-06-05 05:56:53 +02:00
|
|
|
driveID.atap_cylinders = cylinders;
|
|
|
|
driveID.atap_sectors = sectors;
|
|
|
|
driveID.atap_heads = heads;
|
2004-05-03 17:47:52 +02:00
|
|
|
// Setup the current multisector transfer size
|
2005-06-05 05:56:53 +02:00
|
|
|
driveID.atap_curmulti = MAX_MULTSECT;
|
|
|
|
driveID.atap_curmulti_valid = 0x1;
|
2004-05-03 17:47:52 +02:00
|
|
|
// Number of sectors on disk
|
2005-06-05 05:56:53 +02:00
|
|
|
driveID.atap_capacity = lba_size;
|
2004-05-03 17:47:52 +02:00
|
|
|
// Multiword DMA mode 2 and below supported
|
2005-06-05 05:56:53 +02:00
|
|
|
driveID.atap_dmamode_supp = 0x400;
|
2004-05-03 17:47:52 +02:00
|
|
|
// Set PIO mode 4 and 3 supported
|
2005-06-05 05:56:53 +02:00
|
|
|
driveID.atap_piomode_supp = 0x3;
|
2004-05-03 17:47:52 +02:00
|
|
|
// Set DMA mode 4 and below supported
|
2005-09-24 21:22:28 +02:00
|
|
|
driveID.atap_udmamode_supp = 0x1f;
|
2004-05-03 17:47:52 +02:00
|
|
|
// Statically set hardware config word
|
2005-06-05 05:56:53 +02:00
|
|
|
driveID.atap_hwreset_res = 0x4001;
|
2005-08-15 22:59:58 +02:00
|
|
|
|
|
|
|
//arbitrary for now...
|
|
|
|
driveID.atap_ata_major = WDC_VER_ATA7;
|
2004-06-01 23:19:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
IdeDisk::~IdeDisk()
|
|
|
|
{
|
|
|
|
// destroy the data buffer
|
|
|
|
delete [] dataBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::reset(int id)
|
|
|
|
{
|
|
|
|
// initialize the data buffer and shadow registers
|
|
|
|
dataBuffer = new uint8_t[MAX_DMA_SIZE];
|
|
|
|
|
|
|
|
memset(dataBuffer, 0, MAX_DMA_SIZE);
|
|
|
|
memset(&cmdReg, 0, sizeof(CommandReg_t));
|
|
|
|
memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
|
|
|
|
|
|
|
|
dmaInterfaceBytes = 0;
|
|
|
|
curPrdAddr = 0;
|
|
|
|
curSector = 0;
|
|
|
|
cmdBytes = 0;
|
|
|
|
cmdBytesLeft = 0;
|
|
|
|
drqBytesLeft = 0;
|
|
|
|
dmaRead = false;
|
|
|
|
intrPending = false;
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
// 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
|
2004-06-01 23:19:47 +02:00
|
|
|
status = STATUS_DRDY_BIT;
|
2005-08-15 22:59:58 +02:00
|
|
|
|
|
|
|
/* The error register must be set to 0x1 on start-up to
|
|
|
|
indicate that no diagnostic error was detected */
|
|
|
|
cmdReg.error = 0x1;
|
2004-05-03 17:47:52 +02:00
|
|
|
}
|
|
|
|
|
2004-05-12 22:55:49 +02:00
|
|
|
////
|
|
|
|
// Utility functions
|
|
|
|
////
|
|
|
|
|
2004-06-23 21:37:05 +02:00
|
|
|
bool
|
|
|
|
IdeDisk::isDEVSelect()
|
|
|
|
{
|
|
|
|
return ctrl->isDiskSelected(this);
|
|
|
|
}
|
|
|
|
|
2004-05-12 00:06:50 +02:00
|
|
|
Addr
|
2004-05-12 22:55:49 +02:00
|
|
|
IdeDisk::pciToDma(Addr pciAddr)
|
2004-05-12 00:06:50 +02:00
|
|
|
{
|
|
|
|
if (ctrl)
|
2004-11-13 21:45:22 +01:00
|
|
|
return ctrl->plat->pciToDma(pciAddr);
|
2004-05-12 00:06:50 +02:00
|
|
|
else
|
|
|
|
panic("Access to unset controller!\n");
|
|
|
|
}
|
|
|
|
|
2004-05-12 22:55:49 +02:00
|
|
|
uint32_t
|
|
|
|
IdeDisk::bytesInDmaPage(Addr curAddr, uint32_t bytesLeft)
|
|
|
|
{
|
|
|
|
uint32_t bytesInPage = 0;
|
|
|
|
|
|
|
|
// First calculate how many bytes could be in the page
|
2004-10-23 16:41:35 +02:00
|
|
|
if (bytesLeft > TheISA::PageBytes)
|
|
|
|
bytesInPage = TheISA::PageBytes;
|
2004-05-12 22:55:49 +02:00
|
|
|
else
|
|
|
|
bytesInPage = bytesLeft;
|
|
|
|
|
|
|
|
// Next, see if we have crossed a page boundary, and adjust
|
|
|
|
Addr upperBound = curAddr + bytesInPage;
|
2004-10-23 16:41:35 +02:00
|
|
|
Addr pageBound = TheISA::TruncPage(curAddr) + TheISA::PageBytes;
|
2004-05-12 22:55:49 +02:00
|
|
|
|
|
|
|
assert(upperBound >= curAddr && "DMA read wraps around address space!\n");
|
|
|
|
|
|
|
|
if (upperBound >= pageBound)
|
|
|
|
bytesInPage = pageBound - curAddr;
|
|
|
|
|
|
|
|
return bytesInPage;
|
|
|
|
}
|
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
////
|
|
|
|
// Device registers read/write
|
|
|
|
////
|
|
|
|
|
|
|
|
void
|
2005-08-15 22:59:58 +02:00
|
|
|
IdeDisk::read(const Addr &offset, IdeRegType reg_type, uint8_t *data)
|
2004-05-03 17:47:52 +02:00
|
|
|
{
|
|
|
|
DevAction_t action = ACT_NONE;
|
|
|
|
|
2005-08-15 22:59:58 +02:00
|
|
|
switch (reg_type) {
|
|
|
|
case COMMAND_BLOCK:
|
|
|
|
switch (offset) {
|
|
|
|
// Data transfers occur two bytes at a time
|
|
|
|
case DATA_OFFSET:
|
|
|
|
*(uint16_t*)data = cmdReg.data;
|
|
|
|
action = ACT_DATA_READ_SHORT;
|
|
|
|
break;
|
|
|
|
case ERROR_OFFSET:
|
|
|
|
*data = cmdReg.error;
|
|
|
|
break;
|
|
|
|
case NSECTOR_OFFSET:
|
|
|
|
*data = cmdReg.sec_count;
|
|
|
|
break;
|
|
|
|
case SECTOR_OFFSET:
|
|
|
|
*data = cmdReg.sec_num;
|
|
|
|
break;
|
|
|
|
case LCYL_OFFSET:
|
|
|
|
*data = cmdReg.cyl_low;
|
|
|
|
break;
|
|
|
|
case HCYL_OFFSET:
|
|
|
|
*data = cmdReg.cyl_high;
|
|
|
|
break;
|
|
|
|
case DRIVE_OFFSET:
|
|
|
|
*data = cmdReg.drive;
|
|
|
|
break;
|
|
|
|
case STATUS_OFFSET:
|
|
|
|
*data = status;
|
2004-05-03 17:47:52 +02:00
|
|
|
action = ACT_STAT_READ;
|
2005-08-15 22:59:58 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("Invalid IDE command register offset: %#x\n", offset);
|
2004-05-03 17:47:52 +02:00
|
|
|
}
|
2005-08-15 22:59:58 +02:00
|
|
|
break;
|
|
|
|
case CONTROL_BLOCK:
|
|
|
|
if (offset == ALTSTAT_OFFSET)
|
|
|
|
*data = status;
|
|
|
|
else
|
|
|
|
panic("Invalid IDE control register offset: %#x\n", offset);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("Unknown register block!\n");
|
2004-05-03 17:47:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (action != ACT_NONE)
|
|
|
|
updateState(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2005-08-15 22:59:58 +02:00
|
|
|
IdeDisk::write(const Addr &offset, IdeRegType reg_type, const uint8_t *data)
|
2004-05-03 17:47:52 +02:00
|
|
|
{
|
|
|
|
DevAction_t action = ACT_NONE;
|
|
|
|
|
2005-08-15 22:59:58 +02:00
|
|
|
switch (reg_type) {
|
|
|
|
case COMMAND_BLOCK:
|
|
|
|
switch (offset) {
|
|
|
|
case DATA_OFFSET:
|
|
|
|
cmdReg.data = *(uint16_t*)data;
|
|
|
|
action = ACT_DATA_WRITE_SHORT;
|
|
|
|
break;
|
|
|
|
case FEATURES_OFFSET:
|
|
|
|
break;
|
|
|
|
case NSECTOR_OFFSET:
|
|
|
|
cmdReg.sec_count = *data;
|
|
|
|
break;
|
|
|
|
case SECTOR_OFFSET:
|
|
|
|
cmdReg.sec_num = *data;
|
|
|
|
break;
|
|
|
|
case LCYL_OFFSET:
|
|
|
|
cmdReg.cyl_low = *data;
|
|
|
|
break;
|
|
|
|
case HCYL_OFFSET:
|
|
|
|
cmdReg.cyl_high = *data;
|
|
|
|
break;
|
|
|
|
case DRIVE_OFFSET:
|
|
|
|
cmdReg.drive = *data;
|
2004-06-01 23:19:47 +02:00
|
|
|
action = ACT_SELECT_WRITE;
|
2005-08-15 22:59:58 +02:00
|
|
|
break;
|
|
|
|
case COMMAND_OFFSET:
|
|
|
|
cmdReg.command = *data;
|
|
|
|
action = ACT_CMD_WRITE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("Invalid IDE command register offset: %#x\n", offset);
|
2004-05-03 17:47:52 +02:00
|
|
|
}
|
2005-08-15 22:59:58 +02:00
|
|
|
break;
|
|
|
|
case CONTROL_BLOCK:
|
|
|
|
if (offset == CONTROL_OFFSET) {
|
|
|
|
if (*data & CONTROL_RST_BIT) {
|
|
|
|
// force the device into the reset state
|
|
|
|
devState = Device_Srst;
|
|
|
|
action = ACT_SRST_SET;
|
|
|
|
} else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT))
|
|
|
|
action = ACT_SRST_CLEAR;
|
|
|
|
|
|
|
|
nIENBit = (*data & CONTROL_IEN_BIT) ? true : false;
|
2004-06-01 23:19:47 +02:00
|
|
|
}
|
2005-08-15 22:59:58 +02:00
|
|
|
else
|
|
|
|
panic("Invalid IDE control register offset: %#x\n", offset);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("Unknown register block!\n");
|
2004-05-03 17:47:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (action != ACT_NONE)
|
|
|
|
updateState(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
////
|
|
|
|
// Perform DMA transactions
|
|
|
|
////
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::doDmaTransfer()
|
|
|
|
{
|
|
|
|
if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
|
|
|
|
panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
|
|
|
|
dmaState, devState);
|
|
|
|
|
|
|
|
// first read the current PRD
|
|
|
|
if (dmaInterface) {
|
|
|
|
if (dmaInterface->busy()) {
|
|
|
|
// reschedule after waiting period
|
|
|
|
dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick,
|
|
|
|
&dmaPrdReadEvent);
|
|
|
|
} else {
|
|
|
|
dmaPrdReadDone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::dmaPrdReadDone()
|
|
|
|
{
|
|
|
|
// actually copy the PRD from physical memory
|
|
|
|
memcpy((void *)&curPrd.entry,
|
|
|
|
physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)),
|
|
|
|
sizeof(PrdEntry_t));
|
|
|
|
|
2005-04-06 23:47:32 +02:00
|
|
|
DPRINTF(IdeDisk,
|
|
|
|
"PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
|
2004-06-03 23:48:05 +02:00
|
|
|
curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
|
|
|
|
curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
|
|
|
|
curPrd.getEOT(), curSector);
|
|
|
|
|
2004-07-10 04:32:27 +02:00
|
|
|
// the prd pointer has already been translated, so just do an increment
|
|
|
|
curPrdAddr = curPrdAddr + sizeof(PrdEntry_t);
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
if (dmaRead)
|
|
|
|
doDmaRead();
|
|
|
|
else
|
|
|
|
doDmaWrite();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::doDmaRead()
|
|
|
|
{
|
2005-06-05 14:08:29 +02:00
|
|
|
/** @todo we need to figure out what the delay actually will be */
|
2004-05-12 22:55:49 +02:00
|
|
|
Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
|
2004-05-03 17:47:52 +02:00
|
|
|
|
Make the notion of a global event tick independent of the actual
CPU cycle ticks. This allows the user to have CPUs of different
frequencies, and also allows frequencies and latencies that are
not evenly divisible by the CPU frequency. For now, the CPU
frequency is still set to the global frequency, but soon, we'll
hopefully make the global frequency fixed at something like 1THz
and set all other frequencies independently.
arch/alpha/ev5.cc:
The cycles counter is based on the current cpu cycle.
cpu/base_cpu.cc:
frequency isn't the cpu parameter anymore, cycleTime is.
cpu/base_cpu.hh:
frequency isn't the cpu parameter anymore, cycleTime is.
create several public functions for getting the cpu frequency
and the numbers of ticks for a given number of cycles, etc.
cpu/memtest/memtest.cc:
cpu/simple_cpu/simple_cpu.cc:
cpu/simple_cpu/simple_cpu.hh:
cpu/trace/trace_cpu.cc:
Now that ticks aren't cpu cycles, fixup code to advance
by the proper number of ticks.
cpu/memtest/memtest.hh:
cpu/trace/trace_cpu.hh:
Provide a function to get the number of ticks for a given
number of cycles.
dev/alpha_console.cc:
Update for changes in the way that frequencies and latencies are
accessed. Move some stuff to init()
dev/alpha_console.hh:
Need a pointer to the system and the cpu to get the frequency
so we can pass the info to the console code.
dev/etherbus.cc:
dev/etherbus.hh:
dev/etherlink.cc:
dev/etherlink.hh:
dev/ethertap.cc:
dev/ide_disk.hh:
dev/ns_gige.cc:
dev/ns_gige.hh:
update for changes in the way bandwidths are passed from
python to C++ to accomidate the new way that ticks works.
dev/ide_disk.cc:
update for changes in the way bandwidths are passed from
python to C++ to accomidate the new way that ticks works.
Add some extra debugging printfs
dev/platform.cc:
dev/sinic.cc:
dev/sinic.hh:
outline the constructor and destructor
dev/platform.hh:
outline the constructor and destructor.
don't keep track of the interrupt frequency. Only provide the
accessor function.
dev/tsunami.cc:
dev/tsunami.hh:
outline the constructor and destructor
Don't set the interrupt frequency here. Get it from the actual device
that does the interrupting.
dev/tsunami_io.cc:
dev/tsunami_io.hh:
Make the interrupt interval a configuration parameter. (And convert
the interval to the new latency/frequency stuff in the python)
kern/linux/linux_system.cc:
update for changes in the way bandwidths are passed from
python to C++ to accomidate the new way that ticks works.
For now, we must get the boot cpu's frequency as a parameter
since allowing the system to have a pointer to the boot cpu would
cause a cycle.
kern/tru64/tru64_system.cc:
For now, we must get the boot cpu's frequency as a parameter
since allowing the system to have a pointer to the boot cpu would
cause a cycle.
python/m5/config.py:
Fix support for cycle_time relative latencies and frequencies.
Add support for getting a NetworkBandwidth or a MemoryBandwidth.
python/m5/objects/BaseCPU.mpy:
All CPUs now have a cycle_time. The default is the global frequency,
but it is now possible to set the global frequency to some large value
(like 1THz) and set each CPU frequency independently.
python/m5/objects/BaseCache.mpy:
python/m5/objects/Ide.mpy:
Make this a Latency parameter
python/m5/objects/BaseSystem.mpy:
We need to pass the boot CPU's frequency to the system
python/m5/objects/Ethernet.mpy:
Update parameter types to use latency and bandwidth types
python/m5/objects/Platform.mpy:
this frequency isn't needed. We get it from the clock interrupt.
python/m5/objects/Tsunami.mpy:
The clock generator should hold the frequency
sim/eventq.hh:
Need to remove this assertion because the writeback event
queue is different from the CPU's event queue which can cause
this assertion to fail.
sim/process.cc:
Fix comment.
sim/system.hh:
Struct member to hold the boot CPU's frequency.
sim/universe.cc:
remove unneeded variable.
--HG--
extra : convert_revision : 51efe4041095234bf458d9b3b0d417f4cae16fdc
2005-04-11 21:32:06 +02:00
|
|
|
DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n",
|
|
|
|
diskDelay, totalDiskDelay);
|
2004-05-03 17:47:52 +02:00
|
|
|
if (dmaInterface) {
|
|
|
|
if (dmaInterface->busy()) {
|
|
|
|
// reschedule after waiting period
|
|
|
|
dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-05-12 22:55:49 +02:00
|
|
|
Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
|
|
|
|
|
|
|
|
uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
|
|
|
|
(uint32_t)curPrd.getByteCount());
|
|
|
|
|
|
|
|
dmaInterfaceBytes = bytesInPage;
|
|
|
|
|
|
|
|
dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
|
2004-05-03 17:47:52 +02:00
|
|
|
curTick + totalDiskDelay, &dmaReadEvent);
|
|
|
|
} else {
|
|
|
|
// schedule dmaReadEvent with sectorDelay (dmaReadDone)
|
|
|
|
dmaReadEvent.schedule(curTick + totalDiskDelay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::dmaReadDone()
|
|
|
|
{
|
2004-05-12 00:06:50 +02:00
|
|
|
|
|
|
|
Addr curAddr = 0, dmaAddr = 0;
|
|
|
|
uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0;
|
|
|
|
|
2004-05-12 22:55:49 +02:00
|
|
|
// continue to use the DMA interface until all pages are read
|
|
|
|
if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
|
|
|
|
// see if the interface is busy
|
|
|
|
if (dmaInterface->busy()) {
|
|
|
|
// reschedule after waiting period
|
|
|
|
dmaReadEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
|
|
|
|
curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
|
|
|
|
dmaAddr = pciToDma(curAddr);
|
|
|
|
|
|
|
|
bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
|
|
|
|
dmaInterfaceBytes += bytesInPage;
|
|
|
|
|
|
|
|
dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
|
|
|
|
curTick, &dmaReadEvent);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-05-12 00:06:50 +02:00
|
|
|
// set initial address
|
|
|
|
curAddr = curPrd.getBaseAddr();
|
|
|
|
|
|
|
|
// clear out the data buffer
|
|
|
|
memset(dataBuffer, 0, MAX_DMA_SIZE);
|
|
|
|
|
|
|
|
// read the data from memory via DMA into a data buffer
|
|
|
|
while (bytesWritten < curPrd.getByteCount()) {
|
|
|
|
if (cmdBytesLeft <= 0)
|
|
|
|
panic("DMA data is larger than # of sectors specified\n");
|
|
|
|
|
|
|
|
dmaAddr = pciToDma(curAddr);
|
|
|
|
|
|
|
|
// calculate how many bytes are in the current page
|
|
|
|
bytesLeft = curPrd.getByteCount() - bytesWritten;
|
2004-05-12 22:55:49 +02:00
|
|
|
bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
|
2004-05-12 00:06:50 +02:00
|
|
|
|
|
|
|
// copy the data from memory into the data buffer
|
|
|
|
memcpy((void *)(dataBuffer + bytesWritten),
|
|
|
|
physmem->dma_addr(dmaAddr, bytesInPage),
|
|
|
|
bytesInPage);
|
|
|
|
|
|
|
|
curAddr += bytesInPage;
|
|
|
|
bytesWritten += bytesInPage;
|
|
|
|
cmdBytesLeft -= bytesInPage;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write the data to the disk image
|
|
|
|
for (bytesWritten = 0;
|
|
|
|
bytesWritten < curPrd.getByteCount();
|
2004-05-12 22:55:49 +02:00
|
|
|
bytesWritten += SectorSize) {
|
2004-05-12 00:06:50 +02:00
|
|
|
|
|
|
|
writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
|
2004-05-12 22:55:49 +02:00
|
|
|
}
|
2004-05-12 00:06:50 +02:00
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
// check for the EOT
|
2004-06-17 17:24:14 +02:00
|
|
|
if (curPrd.getEOT()) {
|
2004-05-03 17:47:52 +02:00
|
|
|
assert(cmdBytesLeft == 0);
|
|
|
|
dmaState = Dma_Idle;
|
|
|
|
updateState(ACT_DMA_DONE);
|
|
|
|
} else {
|
|
|
|
doDmaTransfer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::doDmaWrite()
|
|
|
|
{
|
2005-06-05 14:08:29 +02:00
|
|
|
/** @todo we need to figure out what the delay actually will be */
|
2004-05-12 22:55:49 +02:00
|
|
|
Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
|
2004-05-03 17:47:52 +02:00
|
|
|
|
Make the notion of a global event tick independent of the actual
CPU cycle ticks. This allows the user to have CPUs of different
frequencies, and also allows frequencies and latencies that are
not evenly divisible by the CPU frequency. For now, the CPU
frequency is still set to the global frequency, but soon, we'll
hopefully make the global frequency fixed at something like 1THz
and set all other frequencies independently.
arch/alpha/ev5.cc:
The cycles counter is based on the current cpu cycle.
cpu/base_cpu.cc:
frequency isn't the cpu parameter anymore, cycleTime is.
cpu/base_cpu.hh:
frequency isn't the cpu parameter anymore, cycleTime is.
create several public functions for getting the cpu frequency
and the numbers of ticks for a given number of cycles, etc.
cpu/memtest/memtest.cc:
cpu/simple_cpu/simple_cpu.cc:
cpu/simple_cpu/simple_cpu.hh:
cpu/trace/trace_cpu.cc:
Now that ticks aren't cpu cycles, fixup code to advance
by the proper number of ticks.
cpu/memtest/memtest.hh:
cpu/trace/trace_cpu.hh:
Provide a function to get the number of ticks for a given
number of cycles.
dev/alpha_console.cc:
Update for changes in the way that frequencies and latencies are
accessed. Move some stuff to init()
dev/alpha_console.hh:
Need a pointer to the system and the cpu to get the frequency
so we can pass the info to the console code.
dev/etherbus.cc:
dev/etherbus.hh:
dev/etherlink.cc:
dev/etherlink.hh:
dev/ethertap.cc:
dev/ide_disk.hh:
dev/ns_gige.cc:
dev/ns_gige.hh:
update for changes in the way bandwidths are passed from
python to C++ to accomidate the new way that ticks works.
dev/ide_disk.cc:
update for changes in the way bandwidths are passed from
python to C++ to accomidate the new way that ticks works.
Add some extra debugging printfs
dev/platform.cc:
dev/sinic.cc:
dev/sinic.hh:
outline the constructor and destructor
dev/platform.hh:
outline the constructor and destructor.
don't keep track of the interrupt frequency. Only provide the
accessor function.
dev/tsunami.cc:
dev/tsunami.hh:
outline the constructor and destructor
Don't set the interrupt frequency here. Get it from the actual device
that does the interrupting.
dev/tsunami_io.cc:
dev/tsunami_io.hh:
Make the interrupt interval a configuration parameter. (And convert
the interval to the new latency/frequency stuff in the python)
kern/linux/linux_system.cc:
update for changes in the way bandwidths are passed from
python to C++ to accomidate the new way that ticks works.
For now, we must get the boot cpu's frequency as a parameter
since allowing the system to have a pointer to the boot cpu would
cause a cycle.
kern/tru64/tru64_system.cc:
For now, we must get the boot cpu's frequency as a parameter
since allowing the system to have a pointer to the boot cpu would
cause a cycle.
python/m5/config.py:
Fix support for cycle_time relative latencies and frequencies.
Add support for getting a NetworkBandwidth or a MemoryBandwidth.
python/m5/objects/BaseCPU.mpy:
All CPUs now have a cycle_time. The default is the global frequency,
but it is now possible to set the global frequency to some large value
(like 1THz) and set each CPU frequency independently.
python/m5/objects/BaseCache.mpy:
python/m5/objects/Ide.mpy:
Make this a Latency parameter
python/m5/objects/BaseSystem.mpy:
We need to pass the boot CPU's frequency to the system
python/m5/objects/Ethernet.mpy:
Update parameter types to use latency and bandwidth types
python/m5/objects/Platform.mpy:
this frequency isn't needed. We get it from the clock interrupt.
python/m5/objects/Tsunami.mpy:
The clock generator should hold the frequency
sim/eventq.hh:
Need to remove this assertion because the writeback event
queue is different from the CPU's event queue which can cause
this assertion to fail.
sim/process.cc:
Fix comment.
sim/system.hh:
Struct member to hold the boot CPU's frequency.
sim/universe.cc:
remove unneeded variable.
--HG--
extra : convert_revision : 51efe4041095234bf458d9b3b0d417f4cae16fdc
2005-04-11 21:32:06 +02:00
|
|
|
DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
|
|
|
|
diskDelay, totalDiskDelay);
|
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
if (dmaInterface) {
|
|
|
|
if (dmaInterface->busy()) {
|
|
|
|
// reschedule after waiting period
|
|
|
|
dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-05-12 22:55:49 +02:00
|
|
|
Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
|
|
|
|
|
|
|
|
uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
|
|
|
|
(uint32_t)curPrd.getByteCount());
|
|
|
|
|
|
|
|
dmaInterfaceBytes = bytesInPage;
|
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
dmaInterface->doDMA(WriteInvalidate, dmaAddr,
|
2004-05-12 22:55:49 +02:00
|
|
|
bytesInPage, curTick + totalDiskDelay,
|
2004-05-03 17:47:52 +02:00
|
|
|
&dmaWriteEvent);
|
|
|
|
} else {
|
|
|
|
// schedule event with disk delay (dmaWriteDone)
|
|
|
|
dmaWriteEvent.schedule(curTick + totalDiskDelay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::dmaWriteDone()
|
|
|
|
{
|
2004-05-12 00:06:50 +02:00
|
|
|
Addr curAddr = 0, pageAddr = 0, dmaAddr = 0;
|
|
|
|
uint32_t bytesRead = 0, bytesInPage = 0;
|
|
|
|
|
2004-05-12 22:55:49 +02:00
|
|
|
// continue to use the DMA interface until all pages are read
|
|
|
|
if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
|
|
|
|
// see if the interface is busy
|
|
|
|
if (dmaInterface->busy()) {
|
|
|
|
// reschedule after waiting period
|
|
|
|
dmaWriteEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
|
|
|
|
curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
|
|
|
|
dmaAddr = pciToDma(curAddr);
|
|
|
|
|
|
|
|
bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
|
|
|
|
dmaInterfaceBytes += bytesInPage;
|
|
|
|
|
|
|
|
dmaInterface->doDMA(WriteInvalidate, dmaAddr,
|
|
|
|
bytesInPage, curTick,
|
|
|
|
&dmaWriteEvent);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-05-12 00:06:50 +02:00
|
|
|
// setup the initial page and DMA address
|
|
|
|
curAddr = curPrd.getBaseAddr();
|
2004-10-23 16:41:35 +02:00
|
|
|
pageAddr = TheISA::TruncPage(curAddr);
|
2004-05-12 00:06:50 +02:00
|
|
|
dmaAddr = pciToDma(curAddr);
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
// clear out the data buffer
|
|
|
|
memset(dataBuffer, 0, MAX_DMA_SIZE);
|
|
|
|
|
|
|
|
while (bytesRead < curPrd.getByteCount()) {
|
2004-05-12 00:06:50 +02:00
|
|
|
// see if we have crossed into a new page
|
2004-10-23 16:41:35 +02:00
|
|
|
if (pageAddr != TheISA::TruncPage(curAddr)) {
|
2004-05-12 00:06:50 +02:00
|
|
|
// write the data to memory
|
|
|
|
memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
|
|
|
|
(void *)(dataBuffer + (bytesRead - bytesInPage)),
|
|
|
|
bytesInPage);
|
|
|
|
|
|
|
|
// update the DMA address and page address
|
2004-10-23 16:41:35 +02:00
|
|
|
pageAddr = TheISA::TruncPage(curAddr);
|
2004-05-12 00:06:50 +02:00
|
|
|
dmaAddr = pciToDma(curAddr);
|
|
|
|
|
|
|
|
bytesInPage = 0;
|
|
|
|
}
|
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
if (cmdBytesLeft <= 0)
|
|
|
|
panic("DMA requested data is larger than # sectors specified\n");
|
|
|
|
|
|
|
|
readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
|
|
|
|
|
2004-05-12 00:06:50 +02:00
|
|
|
curAddr += SectorSize;
|
2004-05-03 17:47:52 +02:00
|
|
|
bytesRead += SectorSize;
|
2004-05-12 00:06:50 +02:00
|
|
|
bytesInPage += SectorSize;
|
2004-05-03 17:47:52 +02:00
|
|
|
cmdBytesLeft -= SectorSize;
|
|
|
|
}
|
|
|
|
|
2004-05-12 00:06:50 +02:00
|
|
|
// write the last page worth read to memory
|
|
|
|
if (bytesInPage != 0) {
|
|
|
|
memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
|
|
|
|
(void *)(dataBuffer + (bytesRead - bytesInPage)),
|
|
|
|
bytesInPage);
|
|
|
|
}
|
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
// 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");
|
|
|
|
|
2004-06-03 23:48:05 +02:00
|
|
|
// PRD base address is given by bits 31:2
|
|
|
|
curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
dmaState = Dma_Transfer;
|
|
|
|
|
|
|
|
// schedule dma transfer (doDmaTransfer)
|
|
|
|
dmaTransferEvent.schedule(curTick + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::abortDma()
|
|
|
|
{
|
|
|
|
if (dmaState == Dma_Idle)
|
2005-04-06 23:47:32 +02:00
|
|
|
panic("Inconsistent DMA state, should be Start or Transfer!");
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
|
2005-04-06 23:47:32 +02:00
|
|
|
panic("Inconsistent device state, should be Transfer or Prepare!\n");
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
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
|
2005-06-05 05:56:53 +02:00
|
|
|
case WDSF_READ_NATIVE_MAX:
|
2004-05-03 17:47:52 +02:00
|
|
|
size = image->size() - 1;
|
|
|
|
cmdReg.sec_num = (size & 0xff);
|
|
|
|
cmdReg.cyl_low = ((size & 0xff00) >> 8);
|
|
|
|
cmdReg.cyl_high = ((size & 0xff0000) >> 16);
|
|
|
|
cmdReg.head = ((size & 0xf000000) >> 24);
|
|
|
|
|
|
|
|
devState = Command_Execution;
|
|
|
|
action = ACT_CMD_COMPLETE;
|
|
|
|
break;
|
|
|
|
|
2005-06-05 05:56:53 +02:00
|
|
|
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:
|
2004-05-03 17:47:52 +02:00
|
|
|
devState = Command_Execution;
|
|
|
|
action = ACT_CMD_COMPLETE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Supported PIO data-in commands
|
2005-06-05 05:56:53 +02:00
|
|
|
case WDCC_IDENTIFY:
|
|
|
|
cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
|
2004-05-03 17:47:52 +02:00
|
|
|
devState = Prepare_Data_In;
|
|
|
|
action = ACT_DATA_READY;
|
|
|
|
break;
|
|
|
|
|
2005-06-05 05:56:53 +02:00
|
|
|
case WDCC_READMULTI:
|
|
|
|
case WDCC_READ:
|
2004-05-03 17:47:52 +02:00
|
|
|
if (!(cmdReg.drive & DRIVE_LBA_BIT))
|
|
|
|
panic("Attempt to perform CHS access, only supports LBA\n");
|
|
|
|
|
|
|
|
if (cmdReg.sec_count == 0)
|
2004-06-01 23:19:47 +02:00
|
|
|
cmdBytes = cmdBytesLeft = (256 * SectorSize);
|
2004-05-03 17:47:52 +02:00
|
|
|
else
|
2004-06-01 23:19:47 +02:00
|
|
|
cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
curSector = getLBABase();
|
|
|
|
|
2004-05-25 00:58:27 +02:00
|
|
|
/** @todo make this a scheduled event to simulate disk delay */
|
2004-05-03 17:47:52 +02:00
|
|
|
devState = Prepare_Data_In;
|
|
|
|
action = ACT_DATA_READY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Supported PIO data-out commands
|
2005-06-05 05:56:53 +02:00
|
|
|
case WDCC_WRITEMULTI:
|
|
|
|
case WDCC_WRITE:
|
2004-05-03 17:47:52 +02:00
|
|
|
if (!(cmdReg.drive & DRIVE_LBA_BIT))
|
|
|
|
panic("Attempt to perform CHS access, only supports LBA\n");
|
|
|
|
|
|
|
|
if (cmdReg.sec_count == 0)
|
2004-06-01 23:19:47 +02:00
|
|
|
cmdBytes = cmdBytesLeft = (256 * SectorSize);
|
2004-05-03 17:47:52 +02:00
|
|
|
else
|
2004-06-01 23:19:47 +02:00
|
|
|
cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
curSector = getLBABase();
|
|
|
|
|
|
|
|
devState = Prepare_Data_Out;
|
|
|
|
action = ACT_DATA_READY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Supported DMA commands
|
2005-06-05 05:56:53 +02:00
|
|
|
case WDCC_WRITEDMA:
|
2004-05-03 17:47:52 +02:00
|
|
|
dmaRead = true; // a write to the disk is a DMA read from memory
|
2005-06-05 05:56:53 +02:00
|
|
|
case WDCC_READDMA:
|
2004-05-03 17:47:52 +02:00
|
|
|
if (!(cmdReg.drive & DRIVE_LBA_BIT))
|
|
|
|
panic("Attempt to perform CHS access, only supports LBA\n");
|
|
|
|
|
|
|
|
if (cmdReg.sec_count == 0)
|
2004-06-01 23:19:47 +02:00
|
|
|
cmdBytes = cmdBytesLeft = (256 * SectorSize);
|
2004-05-03 17:47:52 +02:00
|
|
|
else
|
2004-06-01 23:19:47 +02:00
|
|
|
cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
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
|
2004-06-01 23:19:47 +02:00
|
|
|
status |= STATUS_BSY_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
// clear the DRQ bit
|
2004-06-01 23:19:47 +02:00
|
|
|
status &= ~STATUS_DRQ_BIT;
|
|
|
|
// clear the DF bit
|
|
|
|
status &= ~STATUS_DF_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
updateState(action);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////
|
|
|
|
// Handle setting and clearing interrupts
|
|
|
|
////
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::intrPost()
|
|
|
|
{
|
2005-04-06 23:47:32 +02:00
|
|
|
DPRINTF(IdeDisk, "Posting Interrupt\n");
|
2004-05-03 17:47:52 +02:00
|
|
|
if (intrPending)
|
|
|
|
panic("Attempt to post an interrupt with one pending\n");
|
|
|
|
|
|
|
|
intrPending = true;
|
|
|
|
|
|
|
|
// talk to controller to set interrupt
|
2005-08-15 22:59:58 +02:00
|
|
|
if (ctrl) {
|
|
|
|
ctrl->bmi_regs.bmis0 |= IDEINTS;
|
2004-05-03 17:47:52 +02:00
|
|
|
ctrl->intrPost();
|
2005-08-15 22:59:58 +02:00
|
|
|
}
|
2004-05-03 17:47:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::intrClear()
|
|
|
|
{
|
2005-04-06 23:47:32 +02:00
|
|
|
DPRINTF(IdeDisk, "Clearing Interrupt\n");
|
2004-05-03 17:47:52 +02:00
|
|
|
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) {
|
2004-06-01 23:19:47 +02:00
|
|
|
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;
|
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
case Device_Idle_S:
|
2004-06-01 23:19:47 +02:00
|
|
|
if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
|
2004-05-03 17:47:52 +02:00
|
|
|
devState = Device_Idle_NS;
|
2004-06-01 23:19:47 +02:00
|
|
|
} else if (action == ACT_CMD_WRITE) {
|
2004-05-03 17:47:52 +02:00
|
|
|
startCommand();
|
2004-06-01 23:19:47 +02:00
|
|
|
}
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Device_Idle_SI:
|
2004-06-01 23:19:47 +02:00
|
|
|
if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
|
2004-05-03 17:47:52 +02:00
|
|
|
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:
|
2004-06-01 23:19:47 +02:00
|
|
|
if (action == ACT_SELECT_WRITE && isDEVSelect()) {
|
2004-05-03 17:47:52 +02:00
|
|
|
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
|
2004-06-01 23:19:47 +02:00
|
|
|
status &= ~STATUS_BSY_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
// set the DRQ bit
|
2004-06-01 23:19:47 +02:00
|
|
|
status |= STATUS_DRQ_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
// copy the data into the data buffer
|
2005-06-05 05:56:53 +02:00
|
|
|
if (cmdReg.command == WDCC_IDENTIFY) {
|
2004-05-25 00:58:27 +02:00
|
|
|
// Reset the drqBytes for this block
|
2005-06-05 05:56:53 +02:00
|
|
|
drqBytesLeft = sizeof(struct ataparams);
|
2004-05-25 00:58:27 +02:00
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
memcpy((void *)dataBuffer, (void *)&driveID,
|
2005-06-05 05:56:53 +02:00
|
|
|
sizeof(struct ataparams));
|
2004-05-25 00:58:27 +02:00
|
|
|
} else {
|
|
|
|
// Reset the drqBytes for this block
|
|
|
|
drqBytesLeft = SectorSize;
|
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
readDisk(curSector++, dataBuffer);
|
2004-05-25 00:58:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// put the first two bytes into the data register
|
2005-08-15 22:59:58 +02:00
|
|
|
memcpy((void *)&cmdReg.data, (void *)dataBuffer,
|
2004-05-25 00:58:27 +02:00
|
|
|
sizeof(uint16_t));
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
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
|
2004-05-25 00:58:27 +02:00
|
|
|
if (drqBytesLeft)
|
2005-08-15 22:59:58 +02:00
|
|
|
memcpy((void *)&cmdReg.data,
|
2004-05-25 00:58:27 +02:00
|
|
|
(void *)&dataBuffer[SectorSize - drqBytesLeft],
|
|
|
|
sizeof(uint16_t));
|
2004-05-03 17:47:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (drqBytesLeft == 0) {
|
|
|
|
if (cmdBytesLeft == 0) {
|
|
|
|
// Clear the BSY bit
|
|
|
|
setComplete();
|
|
|
|
devState = Device_Idle_S;
|
|
|
|
} else {
|
|
|
|
devState = Prepare_Data_In;
|
2004-05-25 00:58:27 +02:00
|
|
|
// set the BSY_BIT
|
2004-06-01 23:19:47 +02:00
|
|
|
status |= STATUS_BSY_BIT;
|
2004-05-25 00:58:27 +02:00
|
|
|
// clear the DRQ_BIT
|
2004-06-01 23:19:47 +02:00
|
|
|
status &= ~STATUS_DRQ_BIT;
|
2004-05-25 00:58:27 +02:00
|
|
|
|
|
|
|
/** @todo change this to a scheduled event to simulate
|
|
|
|
disk delay */
|
|
|
|
updateState(ACT_DATA_READY);
|
2004-05-03 17:47:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2004-06-01 23:19:47 +02:00
|
|
|
} else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
|
2004-05-03 17:47:52 +02:00
|
|
|
// clear the BSY bit
|
2004-06-01 23:19:47 +02:00
|
|
|
status &= ~STATUS_BSY_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
// set the DRQ bit
|
2004-06-01 23:19:47 +02:00
|
|
|
status |= STATUS_DRQ_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
// clear the data buffer to get it ready for writes
|
|
|
|
memset(dataBuffer, 0, MAX_DMA_SIZE);
|
|
|
|
|
2004-06-01 23:19:47 +02:00
|
|
|
// reset the drqBytes for this block
|
|
|
|
drqBytesLeft = SectorSize;
|
|
|
|
|
|
|
|
if (cmdBytesLeft == cmdBytes || isIENSet()) {
|
|
|
|
devState = Transfer_Data_Out;
|
|
|
|
} else {
|
2004-05-03 17:47:52 +02:00
|
|
|
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:
|
2004-05-06 21:21:07 +02:00
|
|
|
if (action == ACT_DATA_WRITE_BYTE ||
|
|
|
|
action == ACT_DATA_WRITE_SHORT) {
|
|
|
|
|
2004-05-03 17:47:52 +02:00
|
|
|
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],
|
2005-08-15 22:59:58 +02:00
|
|
|
(void *)&cmdReg.data,
|
2004-05-03 17:47:52 +02:00
|
|
|
sizeof(uint16_t));
|
|
|
|
|
|
|
|
drqBytesLeft -= 2;
|
|
|
|
cmdBytesLeft -= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (drqBytesLeft == 0) {
|
|
|
|
// copy the block to the disk
|
|
|
|
writeDisk(curSector++, dataBuffer);
|
|
|
|
|
|
|
|
// set the BSY bit
|
2004-06-01 23:19:47 +02:00
|
|
|
status |= STATUS_BSY_BIT;
|
|
|
|
// set the seek bit
|
|
|
|
status |= STATUS_SEEK_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
// clear the DRQ bit
|
2004-06-01 23:19:47 +02:00
|
|
|
status &= ~STATUS_DRQ_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
devState = Prepare_Data_Out;
|
2004-05-25 22:35:18 +02:00
|
|
|
|
|
|
|
/** @todo change this to a scheduled event to simulate
|
|
|
|
disk delay */
|
|
|
|
updateState(ACT_DATA_READY);
|
2004-05-03 17:47:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
2004-06-01 23:19:47 +02:00
|
|
|
status &= ~STATUS_BSY_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
// set the DRQ bit
|
2004-06-01 23:19:47 +02:00
|
|
|
status |= STATUS_DRQ_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
|
|
|
|
devState = Transfer_Data_Dma;
|
|
|
|
|
|
|
|
if (dmaState != Dma_Idle)
|
|
|
|
panic("Inconsistent DMA state, should be Dma_Idle\n");
|
|
|
|
|
|
|
|
dmaState = Dma_Start;
|
|
|
|
// wait for the write to the DMA start bit
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Transfer_Data_Dma:
|
|
|
|
if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
|
|
|
|
// clear the BSY bit
|
|
|
|
setComplete();
|
|
|
|
// set the seek bit
|
2004-06-01 23:19:47 +02:00
|
|
|
status |= STATUS_SEEK_BIT;
|
2004-05-03 17:47:52 +02:00
|
|
|
// clear the controller state for DMA transfer
|
|
|
|
ctrl->setDmaComplete(this);
|
|
|
|
|
|
|
|
if (!isIENSet()) {
|
|
|
|
devState = Device_Idle_SI;
|
|
|
|
intrPost();
|
|
|
|
} else {
|
|
|
|
devState = Device_Idle_S;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic("Unknown IDE device state: %#x\n", devState);
|
|
|
|
}
|
2004-03-23 23:10:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::serialize(ostream &os)
|
|
|
|
{
|
2004-05-12 22:55:49 +02:00
|
|
|
// Check all outstanding events to see if they are scheduled
|
|
|
|
// these are all mutually exclusive
|
|
|
|
Tick reschedule = 0;
|
|
|
|
Events_t event = None;
|
|
|
|
|
2004-06-17 17:24:14 +02:00
|
|
|
int eventCount = 0;
|
|
|
|
|
2004-05-12 22:55:49 +02:00
|
|
|
if (dmaTransferEvent.scheduled()) {
|
|
|
|
reschedule = dmaTransferEvent.when();
|
|
|
|
event = Transfer;
|
2004-06-17 17:24:14 +02:00
|
|
|
eventCount++;
|
|
|
|
}
|
|
|
|
if (dmaReadWaitEvent.scheduled()) {
|
2004-05-12 22:55:49 +02:00
|
|
|
reschedule = dmaReadWaitEvent.when();
|
|
|
|
event = ReadWait;
|
2004-06-17 17:24:14 +02:00
|
|
|
eventCount++;
|
|
|
|
}
|
|
|
|
if (dmaWriteWaitEvent.scheduled()) {
|
2004-05-12 22:55:49 +02:00
|
|
|
reschedule = dmaWriteWaitEvent.when();
|
|
|
|
event = WriteWait;
|
2004-06-17 17:24:14 +02:00
|
|
|
eventCount++;
|
|
|
|
}
|
|
|
|
if (dmaPrdReadEvent.scheduled()) {
|
2004-05-12 22:55:49 +02:00
|
|
|
reschedule = dmaPrdReadEvent.when();
|
|
|
|
event = PrdRead;
|
2004-06-17 17:24:14 +02:00
|
|
|
eventCount++;
|
|
|
|
}
|
|
|
|
if (dmaReadEvent.scheduled()) {
|
2004-05-12 22:55:49 +02:00
|
|
|
reschedule = dmaReadEvent.when();
|
|
|
|
event = DmaRead;
|
2004-06-17 17:24:14 +02:00
|
|
|
eventCount++;
|
|
|
|
}
|
|
|
|
if (dmaWriteEvent.scheduled()) {
|
2004-05-12 22:55:49 +02:00
|
|
|
reschedule = dmaWriteEvent.when();
|
|
|
|
event = DmaWrite;
|
2004-06-17 17:24:14 +02:00
|
|
|
eventCount++;
|
2004-05-12 22:55:49 +02:00
|
|
|
}
|
|
|
|
|
2004-06-17 17:24:14 +02:00
|
|
|
assert(eventCount <= 1);
|
|
|
|
|
2004-05-12 22:55:49 +02:00
|
|
|
SERIALIZE_SCALAR(reschedule);
|
|
|
|
SERIALIZE_ENUM(event);
|
|
|
|
|
|
|
|
// Serialize device registers
|
2005-08-15 22:59:58 +02:00
|
|
|
SERIALIZE_SCALAR(cmdReg.data);
|
2004-05-12 22:55:49 +02:00
|
|
|
SERIALIZE_SCALAR(cmdReg.sec_count);
|
|
|
|
SERIALIZE_SCALAR(cmdReg.sec_num);
|
|
|
|
SERIALIZE_SCALAR(cmdReg.cyl_low);
|
|
|
|
SERIALIZE_SCALAR(cmdReg.cyl_high);
|
|
|
|
SERIALIZE_SCALAR(cmdReg.drive);
|
2004-06-17 17:24:14 +02:00
|
|
|
SERIALIZE_SCALAR(cmdReg.command);
|
2004-06-01 23:19:47 +02:00
|
|
|
SERIALIZE_SCALAR(status);
|
2004-05-12 22:55:49 +02:00
|
|
|
SERIALIZE_SCALAR(nIENBit);
|
|
|
|
SERIALIZE_SCALAR(devID);
|
|
|
|
|
|
|
|
// Serialize the PRD related information
|
|
|
|
SERIALIZE_SCALAR(curPrd.entry.baseAddr);
|
|
|
|
SERIALIZE_SCALAR(curPrd.entry.byteCount);
|
|
|
|
SERIALIZE_SCALAR(curPrd.entry.endOfTable);
|
|
|
|
SERIALIZE_SCALAR(curPrdAddr);
|
|
|
|
|
|
|
|
// Serialize current transfer related information
|
|
|
|
SERIALIZE_SCALAR(cmdBytesLeft);
|
2004-06-01 23:19:47 +02:00
|
|
|
SERIALIZE_SCALAR(cmdBytes);
|
2004-05-12 22:55:49 +02:00
|
|
|
SERIALIZE_SCALAR(drqBytesLeft);
|
|
|
|
SERIALIZE_SCALAR(curSector);
|
|
|
|
SERIALIZE_SCALAR(dmaRead);
|
|
|
|
SERIALIZE_SCALAR(dmaInterfaceBytes);
|
|
|
|
SERIALIZE_SCALAR(intrPending);
|
|
|
|
SERIALIZE_ENUM(devState);
|
|
|
|
SERIALIZE_ENUM(dmaState);
|
|
|
|
SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
|
2004-03-23 23:10:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdeDisk::unserialize(Checkpoint *cp, const string §ion)
|
|
|
|
{
|
2004-05-12 22:55:49 +02:00
|
|
|
// Reschedule events that were outstanding
|
|
|
|
// these are all mutually exclusive
|
|
|
|
Tick reschedule = 0;
|
|
|
|
Events_t event = None;
|
|
|
|
|
|
|
|
UNSERIALIZE_SCALAR(reschedule);
|
|
|
|
UNSERIALIZE_ENUM(event);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case None : break;
|
|
|
|
case Transfer : dmaTransferEvent.schedule(reschedule); break;
|
|
|
|
case ReadWait : dmaReadWaitEvent.schedule(reschedule); break;
|
|
|
|
case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break;
|
|
|
|
case PrdRead : dmaPrdReadEvent.schedule(reschedule); break;
|
|
|
|
case DmaRead : dmaReadEvent.schedule(reschedule); break;
|
|
|
|
case DmaWrite : dmaWriteEvent.schedule(reschedule); break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unserialize device registers
|
2005-08-15 22:59:58 +02:00
|
|
|
UNSERIALIZE_SCALAR(cmdReg.data);
|
2004-05-12 22:55:49 +02:00
|
|
|
UNSERIALIZE_SCALAR(cmdReg.sec_count);
|
|
|
|
UNSERIALIZE_SCALAR(cmdReg.sec_num);
|
|
|
|
UNSERIALIZE_SCALAR(cmdReg.cyl_low);
|
|
|
|
UNSERIALIZE_SCALAR(cmdReg.cyl_high);
|
|
|
|
UNSERIALIZE_SCALAR(cmdReg.drive);
|
2004-06-17 17:24:14 +02:00
|
|
|
UNSERIALIZE_SCALAR(cmdReg.command);
|
2004-06-01 23:19:47 +02:00
|
|
|
UNSERIALIZE_SCALAR(status);
|
2004-05-12 22:55:49 +02:00
|
|
|
UNSERIALIZE_SCALAR(nIENBit);
|
|
|
|
UNSERIALIZE_SCALAR(devID);
|
|
|
|
|
|
|
|
// Unserialize the PRD related information
|
|
|
|
UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
|
|
|
|
UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
|
|
|
|
UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
|
|
|
|
UNSERIALIZE_SCALAR(curPrdAddr);
|
|
|
|
|
|
|
|
// Unserialize current transfer related information
|
2004-06-01 23:19:47 +02:00
|
|
|
UNSERIALIZE_SCALAR(cmdBytes);
|
2004-05-12 22:55:49 +02:00
|
|
|
UNSERIALIZE_SCALAR(cmdBytesLeft);
|
|
|
|
UNSERIALIZE_SCALAR(drqBytesLeft);
|
|
|
|
UNSERIALIZE_SCALAR(curSector);
|
|
|
|
UNSERIALIZE_SCALAR(dmaRead);
|
|
|
|
UNSERIALIZE_SCALAR(dmaInterfaceBytes);
|
|
|
|
UNSERIALIZE_SCALAR(intrPending);
|
|
|
|
UNSERIALIZE_ENUM(devState);
|
|
|
|
UNSERIALIZE_ENUM(dmaState);
|
|
|
|
UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
|
2004-03-23 23:10:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
|
|
|
|
2005-01-15 10:12:25 +01:00
|
|
|
enum DriveID { master, slave };
|
|
|
|
static const char *DriveID_strings[] = { "master", "slave" };
|
2004-03-23 23:10:07 +01:00
|
|
|
BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
|
|
|
|
|
|
|
|
SimObjectParam<DiskImage *> image;
|
2004-05-03 17:47:52 +02:00
|
|
|
SimObjectParam<PhysicalMemory *> physmem;
|
2005-01-15 10:12:25 +01:00
|
|
|
SimpleEnumParam<DriveID> driveID;
|
|
|
|
Param<int> delay;
|
2004-03-23 23:10:07 +01:00
|
|
|
|
|
|
|
END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
|
|
|
|
|
|
|
|
BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
|
|
|
|
|
|
|
|
INIT_PARAM(image, "Disk image"),
|
2004-05-03 17:47:52 +02:00
|
|
|
INIT_PARAM(physmem, "Physical memory"),
|
2005-01-15 10:12:25 +01:00
|
|
|
INIT_ENUM_PARAM(driveID, "Drive ID (0=master 1=slave)", DriveID_strings),
|
|
|
|
INIT_PARAM_DFLT(delay, "Fixed disk delay in microseconds", 1)
|
2004-03-23 23:10:07 +01:00
|
|
|
|
|
|
|
END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
|
|
|
|
|
|
|
|
|
|
|
|
CREATE_SIM_OBJECT(IdeDisk)
|
|
|
|
{
|
2005-01-15 10:12:25 +01:00
|
|
|
return new IdeDisk(getInstanceName(), image, physmem, driveID, delay);
|
2004-03-23 23:10:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
|
|
|
|
|
|
|
|
#endif //DOXYGEN_SHOULD_SKIP_THIS
|