ide_disk.cc revision 1763
112852Sgabeblack@google.com/*
212852Sgabeblack@google.com * Copyright (c) 2004-2005 The Regents of The University of Michigan
312852Sgabeblack@google.com * All rights reserved.
412852Sgabeblack@google.com *
512852Sgabeblack@google.com * Redistribution and use in source and binary forms, with or without
612852Sgabeblack@google.com * modification, are permitted provided that the following conditions are
712852Sgabeblack@google.com * met: redistributions of source code must retain the above copyright
812852Sgabeblack@google.com * notice, this list of conditions and the following disclaimer;
912852Sgabeblack@google.com * redistributions in binary form must reproduce the above copyright
1012852Sgabeblack@google.com * notice, this list of conditions and the following disclaimer in the
1112852Sgabeblack@google.com * documentation and/or other materials provided with the distribution;
1212852Sgabeblack@google.com * neither the name of the copyright holders nor the names of its
1312852Sgabeblack@google.com * contributors may be used to endorse or promote products derived from
1412852Sgabeblack@google.com * this software without specific prior written permission.
1512852Sgabeblack@google.com *
1612852Sgabeblack@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1712852Sgabeblack@google.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1812852Sgabeblack@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1912852Sgabeblack@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2012852Sgabeblack@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2112852Sgabeblack@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2212852Sgabeblack@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2312852Sgabeblack@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2412852Sgabeblack@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2512852Sgabeblack@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2612852Sgabeblack@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2712852Sgabeblack@google.com */
2812852Sgabeblack@google.com
2912852Sgabeblack@google.com/** @file
3012852Sgabeblack@google.com * Device model implementation for an IDE disk
3112852Sgabeblack@google.com */
3212852Sgabeblack@google.com
3312852Sgabeblack@google.com#include <cerrno>
3412852Sgabeblack@google.com#include <cstring>
3512852Sgabeblack@google.com#include <deque>
3612852Sgabeblack@google.com#include <string>
3712852Sgabeblack@google.com
3812852Sgabeblack@google.com#include "base/cprintf.hh" // csprintf
3912852Sgabeblack@google.com#include "base/trace.hh"
4012852Sgabeblack@google.com#include "dev/disk_image.hh"
4112852Sgabeblack@google.com#include "dev/ide_disk.hh"
4212852Sgabeblack@google.com#include "dev/ide_ctrl.hh"
4312852Sgabeblack@google.com#include "dev/tsunami.hh"
4412852Sgabeblack@google.com#include "dev/tsunami_pchip.hh"
4512852Sgabeblack@google.com#include "mem/functional/physical.hh"
4612852Sgabeblack@google.com#include "mem/bus/bus.hh"
4712852Sgabeblack@google.com#include "mem/bus/dma_interface.hh"
4812852Sgabeblack@google.com#include "mem/bus/pio_interface.hh"
4912852Sgabeblack@google.com#include "mem/bus/pio_interface_impl.hh"
5012852Sgabeblack@google.com#include "sim/builder.hh"
5112852Sgabeblack@google.com#include "sim/sim_object.hh"
5212852Sgabeblack@google.com#include "sim/root.hh"
5312852Sgabeblack@google.com#include "targetarch/isa_traits.hh"
5412852Sgabeblack@google.com
5512852Sgabeblack@google.comusing namespace std;
5612852Sgabeblack@google.com
5712852Sgabeblack@google.comIdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys,
5812852Sgabeblack@google.com                 int id, Tick delay)
5912852Sgabeblack@google.com    : SimObject(name), ctrl(NULL), image(img), physmem(phys), diskDelay(delay),
6012852Sgabeblack@google.com      dmaTransferEvent(this), dmaReadWaitEvent(this),
6112852Sgabeblack@google.com      dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
6212852Sgabeblack@google.com      dmaReadEvent(this), dmaWriteEvent(this)
6312852Sgabeblack@google.com{
6412852Sgabeblack@google.com    // Reset the device state
6512852Sgabeblack@google.com    reset(id);
6612852Sgabeblack@google.com
6712852Sgabeblack@google.com    // fill out the drive ID structure
6812852Sgabeblack@google.com    memset(&driveID, 0, sizeof(struct ataparams));
6912852Sgabeblack@google.com
7012852Sgabeblack@google.com    // Calculate LBA and C/H/S values
7112852Sgabeblack@google.com    uint16_t cylinders;
7212852Sgabeblack@google.com    uint8_t heads;
7312852Sgabeblack@google.com    uint8_t sectors;
7412852Sgabeblack@google.com
7512852Sgabeblack@google.com    uint32_t lba_size = image->size();
7612852Sgabeblack@google.com    if (lba_size >= 16383*16*63) {
7712852Sgabeblack@google.com        cylinders = 16383;
7812852Sgabeblack@google.com        heads = 16;
7912852Sgabeblack@google.com        sectors = 63;
8012852Sgabeblack@google.com    } else {
8112852Sgabeblack@google.com        if (lba_size >= 63)
8212852Sgabeblack@google.com            sectors = 63;
8312852Sgabeblack@google.com        else
8412852Sgabeblack@google.com            sectors = lba_size;
8512852Sgabeblack@google.com
8612852Sgabeblack@google.com        if ((lba_size / sectors) >= 16)
8712852Sgabeblack@google.com            heads = 16;
8812852Sgabeblack@google.com        else
8912852Sgabeblack@google.com            heads = (lba_size / sectors);
9012852Sgabeblack@google.com
9112852Sgabeblack@google.com        cylinders = lba_size / (heads * sectors);
9212852Sgabeblack@google.com    }
9312852Sgabeblack@google.com
9412852Sgabeblack@google.com    // Setup the model name
9512852Sgabeblack@google.com    sprintf((char *)driveID.atap_model, "5MI EDD si k");
9612852Sgabeblack@google.com    // Set the maximum multisector transfer size
9712852Sgabeblack@google.com    driveID.atap_multi = MAX_MULTSECT;
9812852Sgabeblack@google.com    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
9912852Sgabeblack@google.com    driveID.atap_capabilities1 = 0x7;
10012852Sgabeblack@google.com    // UDMA support, EIDE support
10112852Sgabeblack@google.com    driveID.atap_extensions = 0x6;
10212852Sgabeblack@google.com    // Setup default C/H/S settings
10312852Sgabeblack@google.com    driveID.atap_cylinders = cylinders;
10412852Sgabeblack@google.com    driveID.atap_sectors = sectors;
10512852Sgabeblack@google.com    driveID.atap_heads = heads;
10612852Sgabeblack@google.com    // Setup the current multisector transfer size
10712852Sgabeblack@google.com    driveID.atap_curmulti = MAX_MULTSECT;
10812852Sgabeblack@google.com    driveID.atap_curmulti_valid = 0x1;
10912852Sgabeblack@google.com    // Number of sectors on disk
11012852Sgabeblack@google.com    driveID.atap_capacity = lba_size;
11112852Sgabeblack@google.com    // Multiword DMA mode 2 and below supported
11212852Sgabeblack@google.com    driveID.atap_dmamode_supp = 0x400;
11312902Sgabeblack@google.com    // Set PIO mode 4 and 3 supported
11412902Sgabeblack@google.com    driveID.atap_piomode_supp = 0x3;
11512902Sgabeblack@google.com    // Set DMA mode 4 and below supported
11612902Sgabeblack@google.com    driveID.atap_udmamode_supp = 0x10;
11712902Sgabeblack@google.com    // Statically set hardware config word
11812902Sgabeblack@google.com    driveID.atap_hwreset_res = 0x4001;
11912902Sgabeblack@google.com}
12012902Sgabeblack@google.com
12112902Sgabeblack@google.comIdeDisk::~IdeDisk()
12212902Sgabeblack@google.com{
12312902Sgabeblack@google.com    // destroy the data buffer
12412902Sgabeblack@google.com    delete [] dataBuffer;
12512902Sgabeblack@google.com}
12612902Sgabeblack@google.com
12712902Sgabeblack@google.comvoid
12812902Sgabeblack@google.comIdeDisk::reset(int id)
12912902Sgabeblack@google.com{
13012902Sgabeblack@google.com    // initialize the data buffer and shadow registers
13112902Sgabeblack@google.com    dataBuffer = new uint8_t[MAX_DMA_SIZE];
13212902Sgabeblack@google.com
13312902Sgabeblack@google.com    memset(dataBuffer, 0, MAX_DMA_SIZE);
13412902Sgabeblack@google.com    memset(&cmdReg, 0, sizeof(CommandReg_t));
13512902Sgabeblack@google.com    memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
13612902Sgabeblack@google.com
13712902Sgabeblack@google.com    dmaInterfaceBytes = 0;
13812902Sgabeblack@google.com    curPrdAddr = 0;
13912902Sgabeblack@google.com    curSector = 0;
14012902Sgabeblack@google.com    cmdBytes = 0;
14112902Sgabeblack@google.com    cmdBytesLeft = 0;
14212902Sgabeblack@google.com    drqBytesLeft = 0;
14312902Sgabeblack@google.com    dmaRead = false;
14412902Sgabeblack@google.com    intrPending = false;
14512902Sgabeblack@google.com
14612902Sgabeblack@google.com    // set the device state to idle
14712902Sgabeblack@google.com    dmaState = Dma_Idle;
14812902Sgabeblack@google.com
14912902Sgabeblack@google.com    if (id == DEV0) {
15012902Sgabeblack@google.com        devState = Device_Idle_S;
15112902Sgabeblack@google.com        devID = DEV0;
15212902Sgabeblack@google.com    } else if (id == DEV1) {
15312902Sgabeblack@google.com        devState = Device_Idle_NS;
15412902Sgabeblack@google.com        devID = DEV1;
15512902Sgabeblack@google.com    } else {
15612902Sgabeblack@google.com        panic("Invalid device ID: %#x\n", id);
15712902Sgabeblack@google.com    }
15812902Sgabeblack@google.com
15912902Sgabeblack@google.com    // set the device ready bit
16012902Sgabeblack@google.com    status = STATUS_DRDY_BIT;
16112902Sgabeblack@google.com}
16212902Sgabeblack@google.com
16312902Sgabeblack@google.com////
16412852Sgabeblack@google.com// Utility functions
16512852Sgabeblack@google.com////
16612852Sgabeblack@google.com
16712852Sgabeblack@google.combool
16812852Sgabeblack@google.comIdeDisk::isDEVSelect()
16912852Sgabeblack@google.com{
17012852Sgabeblack@google.com    return ctrl->isDiskSelected(this);
171}
172
173Addr
174IdeDisk::pciToDma(Addr pciAddr)
175{
176    if (ctrl)
177        return ctrl->plat->pciToDma(pciAddr);
178    else
179        panic("Access to unset controller!\n");
180}
181
182uint32_t
183IdeDisk::bytesInDmaPage(Addr curAddr, uint32_t bytesLeft)
184{
185    uint32_t bytesInPage = 0;
186
187    // First calculate how many bytes could be in the page
188    if (bytesLeft > TheISA::PageBytes)
189        bytesInPage = TheISA::PageBytes;
190    else
191        bytesInPage = bytesLeft;
192
193    // Next, see if we have crossed a page boundary, and adjust
194    Addr upperBound = curAddr + bytesInPage;
195    Addr pageBound = TheISA::TruncPage(curAddr) + TheISA::PageBytes;
196
197    assert(upperBound >= curAddr && "DMA read wraps around address space!\n");
198
199    if (upperBound >= pageBound)
200        bytesInPage = pageBound - curAddr;
201
202    return bytesInPage;
203}
204
205////
206// Device registers read/write
207////
208
209void
210IdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data)
211{
212    DevAction_t action = ACT_NONE;
213
214    if (cmdBlk) {
215        if (offset < 0 || offset > sizeof(CommandReg_t))
216            panic("Invalid disk command register offset: %#x\n", offset);
217
218        if (!byte && offset != DATA_OFFSET)
219            panic("Invalid 16-bit read, only allowed on data reg\n");
220
221        if (!byte)
222            *(uint16_t *)data = *(uint16_t *)&cmdReg.data0;
223        else
224            *data = ((uint8_t *)&cmdReg)[offset];
225
226        // determine if an action needs to be taken on the state machine
227        if (offset == STATUS_OFFSET) {
228            action = ACT_STAT_READ;
229            *data = status; // status is in a shadow, explicity copy
230        } else if (offset == DATA_OFFSET) {
231            if (byte)
232                action = ACT_DATA_READ_BYTE;
233            else
234                action = ACT_DATA_READ_SHORT;
235        }
236
237    } else {
238        if (offset != ALTSTAT_OFFSET)
239            panic("Invalid disk control register offset: %#x\n", offset);
240
241        if (!byte)
242            panic("Invalid 16-bit read from control block\n");
243
244        *data = status;
245    }
246
247    if (action != ACT_NONE)
248        updateState(action);
249}
250
251void
252IdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data)
253{
254    DevAction_t action = ACT_NONE;
255
256    if (cmdBlk) {
257        if (offset < 0 || offset > sizeof(CommandReg_t))
258            panic("Invalid disk command register offset: %#x\n", offset);
259
260        if (!byte && offset != DATA_OFFSET)
261            panic("Invalid 16-bit write, only allowed on data reg\n");
262
263        if (!byte)
264            *((uint16_t *)&cmdReg.data0) = *(uint16_t *)data;
265        else
266            ((uint8_t *)&cmdReg)[offset] = *data;
267
268        // determine if an action needs to be taken on the state machine
269        if (offset == COMMAND_OFFSET) {
270            action = ACT_CMD_WRITE;
271        } else if (offset == DATA_OFFSET) {
272            if (byte)
273                action = ACT_DATA_WRITE_BYTE;
274            else
275                action = ACT_DATA_WRITE_SHORT;
276        } else if (offset == SELECT_OFFSET) {
277            action = ACT_SELECT_WRITE;
278        }
279
280    } else {
281        if (offset != CONTROL_OFFSET)
282            panic("Invalid disk control register offset: %#x\n", offset);
283
284        if (!byte)
285            panic("Invalid 16-bit write to control block\n");
286
287        if (*data & CONTROL_RST_BIT) {
288            // force the device into the reset state
289            devState = Device_Srst;
290            action = ACT_SRST_SET;
291        } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) {
292            action = ACT_SRST_CLEAR;
293        }
294
295        nIENBit = (*data & CONTROL_IEN_BIT) ? true : false;
296    }
297
298    if (action != ACT_NONE)
299        updateState(action);
300}
301
302////
303// Perform DMA transactions
304////
305
306void
307IdeDisk::doDmaTransfer()
308{
309    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
310        panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
311              dmaState, devState);
312
313    // first read the current PRD
314    if (dmaInterface) {
315        if (dmaInterface->busy()) {
316            // reschedule after waiting period
317            dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
318            return;
319        }
320
321        dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick,
322                            &dmaPrdReadEvent);
323    } else {
324        dmaPrdReadDone();
325    }
326}
327
328void
329IdeDisk::dmaPrdReadDone()
330{
331    // actually copy the PRD from physical memory
332    memcpy((void *)&curPrd.entry,
333           physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)),
334           sizeof(PrdEntry_t));
335
336    DPRINTF(IdeDisk,
337            "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
338            curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
339            curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
340            curPrd.getEOT(), curSector);
341
342    // the prd pointer has already been translated, so just do an increment
343    curPrdAddr = curPrdAddr + sizeof(PrdEntry_t);
344
345    if (dmaRead)
346        doDmaRead();
347    else
348        doDmaWrite();
349}
350
351void
352IdeDisk::doDmaRead()
353{
354    /** @todo we need to figure out what the delay actually will be */
355    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
356
357    DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n",
358            diskDelay, totalDiskDelay);
359    if (dmaInterface) {
360        if (dmaInterface->busy()) {
361            // reschedule after waiting period
362            dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
363            return;
364        }
365
366        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
367
368        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
369                                              (uint32_t)curPrd.getByteCount());
370
371        dmaInterfaceBytes = bytesInPage;
372
373        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
374                            curTick + totalDiskDelay, &dmaReadEvent);
375    } else {
376        // schedule dmaReadEvent with sectorDelay (dmaReadDone)
377        dmaReadEvent.schedule(curTick + totalDiskDelay);
378    }
379}
380
381void
382IdeDisk::dmaReadDone()
383{
384
385    Addr curAddr = 0, dmaAddr = 0;
386    uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0;
387
388    // continue to use the DMA interface until all pages are read
389    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
390        // see if the interface is busy
391        if (dmaInterface->busy()) {
392            // reschedule after waiting period
393            dmaReadEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
394            return;
395        }
396
397        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
398        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
399        dmaAddr = pciToDma(curAddr);
400
401        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
402        dmaInterfaceBytes += bytesInPage;
403
404        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
405                            curTick, &dmaReadEvent);
406
407        return;
408    }
409
410    // set initial address
411    curAddr = curPrd.getBaseAddr();
412
413    // clear out the data buffer
414    memset(dataBuffer, 0, MAX_DMA_SIZE);
415
416    // read the data from memory via DMA into a data buffer
417    while (bytesWritten < curPrd.getByteCount()) {
418        if (cmdBytesLeft <= 0)
419            panic("DMA data is larger than # of sectors specified\n");
420
421        dmaAddr = pciToDma(curAddr);
422
423        // calculate how many bytes are in the current page
424        bytesLeft = curPrd.getByteCount() - bytesWritten;
425        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
426
427        // copy the data from memory into the data buffer
428        memcpy((void *)(dataBuffer + bytesWritten),
429               physmem->dma_addr(dmaAddr, bytesInPage),
430               bytesInPage);
431
432        curAddr += bytesInPage;
433        bytesWritten += bytesInPage;
434        cmdBytesLeft -= bytesInPage;
435    }
436
437    // write the data to the disk image
438    for (bytesWritten = 0;
439         bytesWritten < curPrd.getByteCount();
440         bytesWritten += SectorSize) {
441
442        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
443    }
444
445    // check for the EOT
446    if (curPrd.getEOT()) {
447        assert(cmdBytesLeft == 0);
448        dmaState = Dma_Idle;
449        updateState(ACT_DMA_DONE);
450    } else {
451        doDmaTransfer();
452    }
453}
454
455void
456IdeDisk::doDmaWrite()
457{
458    /** @todo we need to figure out what the delay actually will be */
459    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
460
461    DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
462            diskDelay, totalDiskDelay);
463
464    if (dmaInterface) {
465        if (dmaInterface->busy()) {
466            // reschedule after waiting period
467            dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
468            return;
469        }
470
471        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
472
473        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
474                                              (uint32_t)curPrd.getByteCount());
475
476        dmaInterfaceBytes = bytesInPage;
477
478        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
479                            bytesInPage, curTick + totalDiskDelay,
480                            &dmaWriteEvent);
481    } else {
482        // schedule event with disk delay (dmaWriteDone)
483        dmaWriteEvent.schedule(curTick + totalDiskDelay);
484    }
485}
486
487void
488IdeDisk::dmaWriteDone()
489{
490    Addr curAddr = 0, pageAddr = 0, dmaAddr = 0;
491    uint32_t bytesRead = 0, bytesInPage = 0;
492
493    // continue to use the DMA interface until all pages are read
494    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
495        // see if the interface is busy
496        if (dmaInterface->busy()) {
497            // reschedule after waiting period
498            dmaWriteEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
499            return;
500        }
501
502        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
503        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
504        dmaAddr = pciToDma(curAddr);
505
506        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
507        dmaInterfaceBytes += bytesInPage;
508
509        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
510                            bytesInPage, curTick,
511                            &dmaWriteEvent);
512
513        return;
514    }
515
516    // setup the initial page and DMA address
517    curAddr = curPrd.getBaseAddr();
518    pageAddr = TheISA::TruncPage(curAddr);
519    dmaAddr = pciToDma(curAddr);
520
521    // clear out the data buffer
522    memset(dataBuffer, 0, MAX_DMA_SIZE);
523
524    while (bytesRead < curPrd.getByteCount()) {
525        // see if we have crossed into a new page
526        if (pageAddr != TheISA::TruncPage(curAddr)) {
527            // write the data to memory
528            memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
529                   (void *)(dataBuffer + (bytesRead - bytesInPage)),
530                   bytesInPage);
531
532            // update the DMA address and page address
533            pageAddr = TheISA::TruncPage(curAddr);
534            dmaAddr = pciToDma(curAddr);
535
536            bytesInPage = 0;
537        }
538
539        if (cmdBytesLeft <= 0)
540            panic("DMA requested data is larger than # sectors specified\n");
541
542        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
543
544        curAddr += SectorSize;
545        bytesRead += SectorSize;
546        bytesInPage += SectorSize;
547        cmdBytesLeft -= SectorSize;
548    }
549
550    // write the last page worth read to memory
551    if (bytesInPage != 0) {
552        memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
553               (void *)(dataBuffer + (bytesRead - bytesInPage)),
554               bytesInPage);
555    }
556
557    // check for the EOT
558    if (curPrd.getEOT()) {
559        assert(cmdBytesLeft == 0);
560        dmaState = Dma_Idle;
561        updateState(ACT_DMA_DONE);
562    } else {
563        doDmaTransfer();
564    }
565}
566
567////
568// Disk utility routines
569///
570
571void
572IdeDisk::readDisk(uint32_t sector, uint8_t *data)
573{
574    uint32_t bytesRead = image->read(data, sector);
575
576    if (bytesRead != SectorSize)
577        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
578              name(), bytesRead, SectorSize, errno);
579}
580
581void
582IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
583{
584    uint32_t bytesWritten = image->write(data, sector);
585
586    if (bytesWritten != SectorSize)
587        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
588              name(), bytesWritten, SectorSize, errno);
589}
590
591////
592// Setup and handle commands
593////
594
595void
596IdeDisk::startDma(const uint32_t &prdTableBase)
597{
598    if (dmaState != Dma_Start)
599        panic("Inconsistent DMA state, should be in Dma_Start!\n");
600
601    if (devState != Transfer_Data_Dma)
602        panic("Inconsistent device state for DMA start!\n");
603
604    // PRD base address is given by bits 31:2
605    curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
606
607    dmaState = Dma_Transfer;
608
609    // schedule dma transfer (doDmaTransfer)
610    dmaTransferEvent.schedule(curTick + 1);
611}
612
613void
614IdeDisk::abortDma()
615{
616    if (dmaState == Dma_Idle)
617        panic("Inconsistent DMA state, should be Start or Transfer!");
618
619    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
620        panic("Inconsistent device state, should be Transfer or Prepare!\n");
621
622    updateState(ACT_CMD_ERROR);
623}
624
625void
626IdeDisk::startCommand()
627{
628    DevAction_t action = ACT_NONE;
629    uint32_t size = 0;
630    dmaRead = false;
631
632    // Decode commands
633    switch (cmdReg.command) {
634        // Supported non-data commands
635      case WDSF_READ_NATIVE_MAX:
636        size = image->size() - 1;
637        cmdReg.sec_num = (size & 0xff);
638        cmdReg.cyl_low = ((size & 0xff00) >> 8);
639        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
640        cmdReg.head = ((size & 0xf000000) >> 24);
641
642        devState = Command_Execution;
643        action = ACT_CMD_COMPLETE;
644        break;
645
646      case WDCC_RECAL:
647      case WDCC_IDP:
648      case WDCC_STANDBY_IMMED:
649      case WDCC_FLUSHCACHE:
650      case WDSF_VERIFY:
651      case WDSF_SEEK:
652      case SET_FEATURES:
653      case WDCC_SETMULTI:
654        devState = Command_Execution;
655        action = ACT_CMD_COMPLETE;
656        break;
657
658        // Supported PIO data-in commands
659      case WDCC_IDENTIFY:
660        cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
661        devState = Prepare_Data_In;
662        action = ACT_DATA_READY;
663        break;
664
665      case WDCC_READMULTI:
666      case WDCC_READ:
667        if (!(cmdReg.drive & DRIVE_LBA_BIT))
668            panic("Attempt to perform CHS access, only supports LBA\n");
669
670        if (cmdReg.sec_count == 0)
671            cmdBytes = cmdBytesLeft = (256 * SectorSize);
672        else
673            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
674
675        curSector = getLBABase();
676
677        /** @todo make this a scheduled event to simulate disk delay */
678        devState = Prepare_Data_In;
679        action = ACT_DATA_READY;
680        break;
681
682        // Supported PIO data-out commands
683      case WDCC_WRITEMULTI:
684      case WDCC_WRITE:
685        if (!(cmdReg.drive & DRIVE_LBA_BIT))
686            panic("Attempt to perform CHS access, only supports LBA\n");
687
688        if (cmdReg.sec_count == 0)
689            cmdBytes = cmdBytesLeft = (256 * SectorSize);
690        else
691            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
692
693        curSector = getLBABase();
694
695        devState = Prepare_Data_Out;
696        action = ACT_DATA_READY;
697        break;
698
699        // Supported DMA commands
700      case WDCC_WRITEDMA:
701        dmaRead = true;  // a write to the disk is a DMA read from memory
702      case WDCC_READDMA:
703        if (!(cmdReg.drive & DRIVE_LBA_BIT))
704            panic("Attempt to perform CHS access, only supports LBA\n");
705
706        if (cmdReg.sec_count == 0)
707            cmdBytes = cmdBytesLeft = (256 * SectorSize);
708        else
709            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
710
711        curSector = getLBABase();
712
713        devState = Prepare_Data_Dma;
714        action = ACT_DMA_READY;
715        break;
716
717      default:
718        panic("Unsupported ATA command: %#x\n", cmdReg.command);
719    }
720
721    if (action != ACT_NONE) {
722        // set the BSY bit
723        status |= STATUS_BSY_BIT;
724        // clear the DRQ bit
725        status &= ~STATUS_DRQ_BIT;
726        // clear the DF bit
727        status &= ~STATUS_DF_BIT;
728
729        updateState(action);
730    }
731}
732
733////
734// Handle setting and clearing interrupts
735////
736
737void
738IdeDisk::intrPost()
739{
740    DPRINTF(IdeDisk, "Posting Interrupt\n");
741    if (intrPending)
742        panic("Attempt to post an interrupt with one pending\n");
743
744    intrPending = true;
745
746    // talk to controller to set interrupt
747    if (ctrl)
748        ctrl->intrPost();
749}
750
751void
752IdeDisk::intrClear()
753{
754    DPRINTF(IdeDisk, "Clearing Interrupt\n");
755    if (!intrPending)
756        panic("Attempt to clear a non-pending interrupt\n");
757
758    intrPending = false;
759
760    // talk to controller to clear interrupt
761    if (ctrl)
762        ctrl->intrClear();
763}
764
765////
766// Manage the device internal state machine
767////
768
769void
770IdeDisk::updateState(DevAction_t action)
771{
772    switch (devState) {
773      case Device_Srst:
774        if (action == ACT_SRST_SET) {
775            // set the BSY bit
776            status |= STATUS_BSY_BIT;
777        } else if (action == ACT_SRST_CLEAR) {
778            // clear the BSY bit
779            status &= ~STATUS_BSY_BIT;
780
781            // reset the device state
782            reset(devID);
783        }
784        break;
785
786      case Device_Idle_S:
787        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
788            devState = Device_Idle_NS;
789        } else if (action == ACT_CMD_WRITE) {
790            startCommand();
791        }
792
793        break;
794
795      case Device_Idle_SI:
796        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
797            devState = Device_Idle_NS;
798            intrClear();
799        } else if (action == ACT_STAT_READ || isIENSet()) {
800            devState = Device_Idle_S;
801            intrClear();
802        } else if (action == ACT_CMD_WRITE) {
803            intrClear();
804            startCommand();
805        }
806
807        break;
808
809      case Device_Idle_NS:
810        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
811            if (!isIENSet() && intrPending) {
812                devState = Device_Idle_SI;
813                intrPost();
814            }
815            if (isIENSet() || !intrPending) {
816                devState = Device_Idle_S;
817            }
818        }
819        break;
820
821      case Command_Execution:
822        if (action == ACT_CMD_COMPLETE) {
823            // clear the BSY bit
824            setComplete();
825
826            if (!isIENSet()) {
827                devState = Device_Idle_SI;
828                intrPost();
829            } else {
830                devState = Device_Idle_S;
831            }
832        }
833        break;
834
835      case Prepare_Data_In:
836        if (action == ACT_CMD_ERROR) {
837            // clear the BSY bit
838            setComplete();
839
840            if (!isIENSet()) {
841                devState = Device_Idle_SI;
842                intrPost();
843            } else {
844                devState = Device_Idle_S;
845            }
846        } else if (action == ACT_DATA_READY) {
847            // clear the BSY bit
848            status &= ~STATUS_BSY_BIT;
849            // set the DRQ bit
850            status |= STATUS_DRQ_BIT;
851
852            // copy the data into the data buffer
853            if (cmdReg.command == WDCC_IDENTIFY) {
854                // Reset the drqBytes for this block
855                drqBytesLeft = sizeof(struct ataparams);
856
857                memcpy((void *)dataBuffer, (void *)&driveID,
858                       sizeof(struct ataparams));
859            } else {
860                // Reset the drqBytes for this block
861                drqBytesLeft = SectorSize;
862
863                readDisk(curSector++, dataBuffer);
864            }
865
866            // put the first two bytes into the data register
867            memcpy((void *)&cmdReg.data0, (void *)dataBuffer,
868                   sizeof(uint16_t));
869
870            if (!isIENSet()) {
871                devState = Data_Ready_INTRQ_In;
872                intrPost();
873            } else {
874                devState = Transfer_Data_In;
875            }
876        }
877        break;
878
879      case Data_Ready_INTRQ_In:
880        if (action == ACT_STAT_READ) {
881            devState = Transfer_Data_In;
882            intrClear();
883        }
884        break;
885
886      case Transfer_Data_In:
887        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
888            if (action == ACT_DATA_READ_BYTE) {
889                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
890            } else {
891                drqBytesLeft -= 2;
892                cmdBytesLeft -= 2;
893
894                // copy next short into data registers
895                if (drqBytesLeft)
896                    memcpy((void *)&cmdReg.data0,
897                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
898                           sizeof(uint16_t));
899            }
900
901            if (drqBytesLeft == 0) {
902                if (cmdBytesLeft == 0) {
903                    // Clear the BSY bit
904                    setComplete();
905                    devState = Device_Idle_S;
906                } else {
907                    devState = Prepare_Data_In;
908                    // set the BSY_BIT
909                    status |= STATUS_BSY_BIT;
910                    // clear the DRQ_BIT
911                    status &= ~STATUS_DRQ_BIT;
912
913                    /** @todo change this to a scheduled event to simulate
914                        disk delay */
915                    updateState(ACT_DATA_READY);
916                }
917            }
918        }
919        break;
920
921      case Prepare_Data_Out:
922        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
923            // clear the BSY bit
924            setComplete();
925
926            if (!isIENSet()) {
927                devState = Device_Idle_SI;
928                intrPost();
929            } else {
930                devState = Device_Idle_S;
931            }
932        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
933            // clear the BSY bit
934            status &= ~STATUS_BSY_BIT;
935            // set the DRQ bit
936            status |= STATUS_DRQ_BIT;
937
938            // clear the data buffer to get it ready for writes
939            memset(dataBuffer, 0, MAX_DMA_SIZE);
940
941            // reset the drqBytes for this block
942            drqBytesLeft = SectorSize;
943
944            if (cmdBytesLeft == cmdBytes || isIENSet()) {
945                devState = Transfer_Data_Out;
946            } else {
947                devState = Data_Ready_INTRQ_Out;
948                intrPost();
949            }
950        }
951        break;
952
953      case Data_Ready_INTRQ_Out:
954        if (action == ACT_STAT_READ) {
955            devState = Transfer_Data_Out;
956            intrClear();
957        }
958        break;
959
960      case Transfer_Data_Out:
961        if (action == ACT_DATA_WRITE_BYTE ||
962            action == ACT_DATA_WRITE_SHORT) {
963
964            if (action == ACT_DATA_READ_BYTE) {
965                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
966            } else {
967                // copy the latest short into the data buffer
968                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
969                       (void *)&cmdReg.data0,
970                       sizeof(uint16_t));
971
972                drqBytesLeft -= 2;
973                cmdBytesLeft -= 2;
974            }
975
976            if (drqBytesLeft == 0) {
977                // copy the block to the disk
978                writeDisk(curSector++, dataBuffer);
979
980                // set the BSY bit
981                status |= STATUS_BSY_BIT;
982                // set the seek bit
983                status |= STATUS_SEEK_BIT;
984                // clear the DRQ bit
985                status &= ~STATUS_DRQ_BIT;
986
987                devState = Prepare_Data_Out;
988
989                /** @todo change this to a scheduled event to simulate
990                    disk delay */
991                updateState(ACT_DATA_READY);
992            }
993        }
994        break;
995
996      case Prepare_Data_Dma:
997        if (action == ACT_CMD_ERROR) {
998            // clear the BSY bit
999            setComplete();
1000
1001            if (!isIENSet()) {
1002                devState = Device_Idle_SI;
1003                intrPost();
1004            } else {
1005                devState = Device_Idle_S;
1006            }
1007        } else if (action == ACT_DMA_READY) {
1008            // clear the BSY bit
1009            status &= ~STATUS_BSY_BIT;
1010            // set the DRQ bit
1011            status |= STATUS_DRQ_BIT;
1012
1013            devState = Transfer_Data_Dma;
1014
1015            if (dmaState != Dma_Idle)
1016                panic("Inconsistent DMA state, should be Dma_Idle\n");
1017
1018            dmaState = Dma_Start;
1019            // wait for the write to the DMA start bit
1020        }
1021        break;
1022
1023      case Transfer_Data_Dma:
1024        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
1025            // clear the BSY bit
1026            setComplete();
1027            // set the seek bit
1028            status |= STATUS_SEEK_BIT;
1029            // clear the controller state for DMA transfer
1030            ctrl->setDmaComplete(this);
1031
1032            if (!isIENSet()) {
1033                devState = Device_Idle_SI;
1034                intrPost();
1035            } else {
1036                devState = Device_Idle_S;
1037            }
1038        }
1039        break;
1040
1041      default:
1042        panic("Unknown IDE device state: %#x\n", devState);
1043    }
1044}
1045
1046void
1047IdeDisk::serialize(ostream &os)
1048{
1049    // Check all outstanding events to see if they are scheduled
1050    // these are all mutually exclusive
1051    Tick reschedule = 0;
1052    Events_t event = None;
1053
1054    int eventCount = 0;
1055
1056    if (dmaTransferEvent.scheduled()) {
1057        reschedule = dmaTransferEvent.when();
1058        event = Transfer;
1059        eventCount++;
1060    }
1061    if (dmaReadWaitEvent.scheduled()) {
1062        reschedule = dmaReadWaitEvent.when();
1063        event = ReadWait;
1064        eventCount++;
1065    }
1066    if (dmaWriteWaitEvent.scheduled()) {
1067        reschedule = dmaWriteWaitEvent.when();
1068        event = WriteWait;
1069        eventCount++;
1070    }
1071    if (dmaPrdReadEvent.scheduled()) {
1072        reschedule = dmaPrdReadEvent.when();
1073        event = PrdRead;
1074        eventCount++;
1075    }
1076    if (dmaReadEvent.scheduled()) {
1077        reschedule = dmaReadEvent.when();
1078        event = DmaRead;
1079        eventCount++;
1080    }
1081    if (dmaWriteEvent.scheduled()) {
1082        reschedule = dmaWriteEvent.when();
1083        event = DmaWrite;
1084        eventCount++;
1085    }
1086
1087    assert(eventCount <= 1);
1088
1089    SERIALIZE_SCALAR(reschedule);
1090    SERIALIZE_ENUM(event);
1091
1092    // Serialize device registers
1093    SERIALIZE_SCALAR(cmdReg.data0);
1094    SERIALIZE_SCALAR(cmdReg.data1);
1095    SERIALIZE_SCALAR(cmdReg.sec_count);
1096    SERIALIZE_SCALAR(cmdReg.sec_num);
1097    SERIALIZE_SCALAR(cmdReg.cyl_low);
1098    SERIALIZE_SCALAR(cmdReg.cyl_high);
1099    SERIALIZE_SCALAR(cmdReg.drive);
1100    SERIALIZE_SCALAR(cmdReg.command);
1101    SERIALIZE_SCALAR(status);
1102    SERIALIZE_SCALAR(nIENBit);
1103    SERIALIZE_SCALAR(devID);
1104
1105    // Serialize the PRD related information
1106    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1107    SERIALIZE_SCALAR(curPrd.entry.byteCount);
1108    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1109    SERIALIZE_SCALAR(curPrdAddr);
1110
1111    // Serialize current transfer related information
1112    SERIALIZE_SCALAR(cmdBytesLeft);
1113    SERIALIZE_SCALAR(cmdBytes);
1114    SERIALIZE_SCALAR(drqBytesLeft);
1115    SERIALIZE_SCALAR(curSector);
1116    SERIALIZE_SCALAR(dmaRead);
1117    SERIALIZE_SCALAR(dmaInterfaceBytes);
1118    SERIALIZE_SCALAR(intrPending);
1119    SERIALIZE_ENUM(devState);
1120    SERIALIZE_ENUM(dmaState);
1121    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1122}
1123
1124void
1125IdeDisk::unserialize(Checkpoint *cp, const string &section)
1126{
1127    // Reschedule events that were outstanding
1128    // these are all mutually exclusive
1129    Tick reschedule = 0;
1130    Events_t event = None;
1131
1132    UNSERIALIZE_SCALAR(reschedule);
1133    UNSERIALIZE_ENUM(event);
1134
1135    switch (event) {
1136      case None : break;
1137      case Transfer : dmaTransferEvent.schedule(reschedule); break;
1138      case ReadWait : dmaReadWaitEvent.schedule(reschedule); break;
1139      case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break;
1140      case PrdRead : dmaPrdReadEvent.schedule(reschedule); break;
1141      case DmaRead : dmaReadEvent.schedule(reschedule); break;
1142      case DmaWrite : dmaWriteEvent.schedule(reschedule); break;
1143    }
1144
1145    // Unserialize device registers
1146    UNSERIALIZE_SCALAR(cmdReg.data0);
1147    UNSERIALIZE_SCALAR(cmdReg.data1);
1148    UNSERIALIZE_SCALAR(cmdReg.sec_count);
1149    UNSERIALIZE_SCALAR(cmdReg.sec_num);
1150    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1151    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1152    UNSERIALIZE_SCALAR(cmdReg.drive);
1153    UNSERIALIZE_SCALAR(cmdReg.command);
1154    UNSERIALIZE_SCALAR(status);
1155    UNSERIALIZE_SCALAR(nIENBit);
1156    UNSERIALIZE_SCALAR(devID);
1157
1158    // Unserialize the PRD related information
1159    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1160    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1161    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1162    UNSERIALIZE_SCALAR(curPrdAddr);
1163
1164    // Unserialize current transfer related information
1165    UNSERIALIZE_SCALAR(cmdBytes);
1166    UNSERIALIZE_SCALAR(cmdBytesLeft);
1167    UNSERIALIZE_SCALAR(drqBytesLeft);
1168    UNSERIALIZE_SCALAR(curSector);
1169    UNSERIALIZE_SCALAR(dmaRead);
1170    UNSERIALIZE_SCALAR(dmaInterfaceBytes);
1171    UNSERIALIZE_SCALAR(intrPending);
1172    UNSERIALIZE_ENUM(devState);
1173    UNSERIALIZE_ENUM(dmaState);
1174    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1175}
1176
1177#ifndef DOXYGEN_SHOULD_SKIP_THIS
1178
1179enum DriveID { master, slave };
1180static const char *DriveID_strings[] = { "master", "slave" };
1181BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1182
1183    SimObjectParam<DiskImage *> image;
1184    SimObjectParam<PhysicalMemory *> physmem;
1185    SimpleEnumParam<DriveID> driveID;
1186    Param<int> delay;
1187
1188END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1189
1190BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1191
1192    INIT_PARAM(image, "Disk image"),
1193    INIT_PARAM(physmem, "Physical memory"),
1194    INIT_ENUM_PARAM(driveID, "Drive ID (0=master 1=slave)", DriveID_strings),
1195    INIT_PARAM_DFLT(delay, "Fixed disk delay in microseconds", 1)
1196
1197END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1198
1199
1200CREATE_SIM_OBJECT(IdeDisk)
1201{
1202    return new IdeDisk(getInstanceName(), image, physmem, driveID, delay);
1203}
1204
1205REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
1206
1207#endif //DOXYGEN_SHOULD_SKIP_THIS
1208