| /* |
| * 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. |
| */ |
| |
| /** @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; |
| /** Size of OS pages. */ |
| Addr pageBytes; |
| /** 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, Addr page_bytes) |
| { |
| panic_if(ctrl, "Cannot change the controller once set!\n"); |
| ctrl = c; |
| pageBytes = page_bytes; |
| } |
| |
| // 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(); |
| EventFunctionWrapper dmaTransferEvent; |
| |
| void doDmaDataRead(); |
| |
| void doDmaRead(); |
| ChunkGenerator *dmaReadCG; |
| EventFunctionWrapper dmaReadWaitEvent; |
| |
| void doDmaDataWrite(); |
| |
| void doDmaWrite(); |
| ChunkGenerator *dmaWriteCG; |
| EventFunctionWrapper dmaWriteWaitEvent; |
| |
| void dmaPrdReadDone(); |
| EventFunctionWrapper dmaPrdReadEvent; |
| |
| void dmaReadDone(); |
| EventFunctionWrapper dmaReadEvent; |
| |
| void dmaWriteDone(); |
| EventFunctionWrapper 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__ |