ide_disk.cc revision 864
1/*
2 * Copyright (c) 2003 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @file
30 * Device model implementation for an IDE disk
31 */
32
33#include <cerrno>
34#include <cstring>
35#include <deque>
36#include <string>
37
38#include "arch/alpha/pmap.h"
39#include "base/cprintf.hh" // csprintf
40#include "base/trace.hh"
41#include "dev/disk_image.hh"
42#include "dev/ide_disk.hh"
43#include "dev/ide_ctrl.hh"
44#include "dev/tsunami.hh"
45#include "dev/tsunami_pchip.hh"
46#include "mem/functional_mem/physical_memory.hh"
47#include "mem/bus/bus.hh"
48#include "mem/bus/dma_interface.hh"
49#include "mem/bus/pio_interface.hh"
50#include "mem/bus/pio_interface_impl.hh"
51#include "sim/builder.hh"
52#include "sim/sim_object.hh"
53#include "sim/universe.hh"
54
55using namespace std;
56
57IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys,
58                 int id, int delay)
59    : SimObject(name), ctrl(NULL), image(img), physmem(phys),
60      dmaTransferEvent(this), dmaReadWaitEvent(this),
61      dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
62      dmaReadEvent(this), dmaWriteEvent(this)
63{
64    // calculate disk delay in microseconds
65    diskDelay = (delay * ticksPerSecond / 100000);
66
67    // initialize the data buffer and shadow registers
68    dataBuffer = new uint8_t[MAX_DMA_SIZE];
69
70    memset(dataBuffer, 0, MAX_DMA_SIZE);
71    memset(&cmdReg, 0, sizeof(CommandReg_t));
72    memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
73
74    dmaInterfaceBytes = 0;
75    curPrdAddr = 0;
76    curSector = 0;
77    curCommand = 0;
78    cmdBytesLeft = 0;
79    drqBytesLeft = 0;
80    dmaRead = false;
81    intrPending = false;
82
83    // fill out the drive ID structure
84    memset(&driveID, 0, sizeof(struct hd_driveid));
85
86    // Calculate LBA and C/H/S values
87    uint16_t cylinders;
88    uint8_t heads;
89    uint8_t sectors;
90
91    uint32_t lba_size = image->size();
92    if (lba_size >= 16383*16*63) {
93        cylinders = 16383;
94        heads = 16;
95        sectors = 63;
96    } else {
97        if (lba_size >= 63)
98            sectors = 63;
99        else
100            sectors = lba_size;
101
102        if ((lba_size / sectors) >= 16)
103            heads = 16;
104        else
105            heads = (lba_size / sectors);
106
107        cylinders = lba_size / (heads * sectors);
108    }
109
110    // Setup the model name
111    sprintf((char *)driveID.model, "5MI EDD si k");
112    // Set the maximum multisector transfer size
113    driveID.max_multsect = MAX_MULTSECT;
114    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
115    driveID.capability = 0x7;
116    // UDMA support, EIDE support
117    driveID.field_valid = 0x6;
118    // Setup default C/H/S settings
119    driveID.cyls = cylinders;
120    driveID.sectors = sectors;
121    driveID.heads = heads;
122    // Setup the current multisector transfer size
123    driveID.multsect = MAX_MULTSECT;
124    driveID.multsect_valid = 0x1;
125    // Number of sectors on disk
126    driveID.lba_capacity = lba_size;
127    // Multiword DMA mode 2 and below supported
128    driveID.dma_mword = 0x400;
129    // Set PIO mode 4 and 3 supported
130    driveID.eide_pio_modes = 0x3;
131    // Set DMA mode 4 and below supported
132    driveID.dma_ultra = 0x10;
133    // Statically set hardware config word
134    driveID.hw_config = 0x4001;
135
136    // set the device state to idle
137    dmaState = Dma_Idle;
138
139    if (id == DEV0) {
140        devState = Device_Idle_S;
141        devID = DEV0;
142    } else if (id == DEV1) {
143        devState = Device_Idle_NS;
144        devID = DEV1;
145    } else {
146        panic("Invalid device ID: %#x\n", id);
147    }
148
149    // set the device ready bit
150    cmdReg.status |= STATUS_DRDY_BIT;
151}
152
153IdeDisk::~IdeDisk()
154{
155    // destroy the data buffer
156    delete [] dataBuffer;
157}
158
159////
160// Utility functions
161////
162
163Addr
164IdeDisk::pciToDma(Addr pciAddr)
165{
166    if (ctrl)
167        return ctrl->tsunami->pchip->translatePciToDma(pciAddr);
168    else
169        panic("Access to unset controller!\n");
170}
171
172uint32_t
173IdeDisk::bytesInDmaPage(Addr curAddr, uint32_t bytesLeft)
174{
175    uint32_t bytesInPage = 0;
176
177    // First calculate how many bytes could be in the page
178    if (bytesLeft > ALPHA_PGBYTES)
179        bytesInPage = ALPHA_PGBYTES;
180    else
181        bytesInPage = bytesLeft;
182
183    // Next, see if we have crossed a page boundary, and adjust
184    Addr upperBound = curAddr + bytesInPage;
185    Addr pageBound = alpha_trunc_page(curAddr) + ALPHA_PGBYTES;
186
187    assert(upperBound >= curAddr && "DMA read wraps around address space!\n");
188
189    if (upperBound >= pageBound)
190        bytesInPage = pageBound - curAddr;
191
192    return bytesInPage;
193}
194
195////
196// Device registers read/write
197////
198
199void
200IdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data)
201{
202    DevAction_t action = ACT_NONE;
203
204    if (cmdBlk) {
205        if (offset < 0 || offset > sizeof(CommandReg_t))
206            panic("Invalid disk command register offset: %#x\n", offset);
207
208        if (!byte && offset != DATA_OFFSET)
209            panic("Invalid 16-bit read, only allowed on data reg\n");
210
211        if (!byte)
212            *(uint16_t *)data = *(uint16_t *)&cmdReg.data0;
213        else
214            *data = ((uint8_t *)&cmdReg)[offset];
215
216        // determine if an action needs to be taken on the state machine
217        if (offset == STATUS_OFFSET) {
218            action = ACT_STAT_READ;
219        } else if (offset == DATA_OFFSET) {
220            if (byte)
221                action = ACT_DATA_READ_BYTE;
222            else
223                action = ACT_DATA_READ_SHORT;
224        }
225
226    } else {
227        if (offset != ALTSTAT_OFFSET)
228            panic("Invalid disk control register offset: %#x\n", offset);
229
230        if (!byte)
231            panic("Invalid 16-bit read from control block\n");
232
233        *data = ((uint8_t *)&cmdReg)[STATUS_OFFSET];
234    }
235
236    if (action != ACT_NONE)
237        updateState(action);
238}
239
240void
241IdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data)
242{
243    DevAction_t action = ACT_NONE;
244
245    if (cmdBlk) {
246        if (offset < 0 || offset > sizeof(CommandReg_t))
247            panic("Invalid disk command register offset: %#x\n", offset);
248
249        if (!byte && offset != DATA_OFFSET)
250            panic("Invalid 16-bit write, only allowed on data reg\n");
251
252        if (!byte)
253            *((uint16_t *)&cmdReg.data0) = *(uint16_t *)data;
254        else
255            ((uint8_t *)&cmdReg)[offset] = *data;
256
257        // determine if an action needs to be taken on the state machine
258        if (offset == COMMAND_OFFSET) {
259            action = ACT_CMD_WRITE;
260        } else if (offset == DATA_OFFSET) {
261            if (byte)
262                action = ACT_DATA_WRITE_BYTE;
263            else
264                action = ACT_DATA_WRITE_SHORT;
265        }
266
267    } else {
268        if (offset != CONTROL_OFFSET)
269            panic("Invalid disk control register offset: %#x\n", offset);
270
271        if (!byte)
272            panic("Invalid 16-bit write to control block\n");
273
274        if (*data & CONTROL_RST_BIT)
275            panic("Software reset not supported!\n");
276
277        nIENBit = (*data & CONTROL_IEN_BIT) ? true : false;
278    }
279
280    if (action != ACT_NONE)
281        updateState(action);
282}
283
284////
285// Perform DMA transactions
286////
287
288void
289IdeDisk::doDmaTransfer()
290{
291    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
292        panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
293              dmaState, devState);
294
295    // first read the current PRD
296    if (dmaInterface) {
297        if (dmaInterface->busy()) {
298            // reschedule after waiting period
299            dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
300            return;
301        }
302
303        dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick,
304                            &dmaPrdReadEvent);
305    } else {
306        dmaPrdReadDone();
307    }
308}
309
310void
311IdeDisk::dmaPrdReadDone()
312{
313    // actually copy the PRD from physical memory
314    memcpy((void *)&curPrd.entry,
315           physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)),
316           sizeof(PrdEntry_t));
317
318    curPrdAddr += sizeof(PrdEntry_t);
319
320    if (dmaRead)
321        doDmaRead();
322    else
323        doDmaWrite();
324}
325
326void
327IdeDisk::doDmaRead()
328{
329    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
330
331    if (dmaInterface) {
332        if (dmaInterface->busy()) {
333            // reschedule after waiting period
334            dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
335            return;
336        }
337
338        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
339
340        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
341                                              (uint32_t)curPrd.getByteCount());
342
343        dmaInterfaceBytes = bytesInPage;
344
345        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
346                            curTick + totalDiskDelay, &dmaReadEvent);
347    } else {
348        // schedule dmaReadEvent with sectorDelay (dmaReadDone)
349        dmaReadEvent.schedule(curTick + totalDiskDelay);
350    }
351}
352
353void
354IdeDisk::dmaReadDone()
355{
356
357    Addr curAddr = 0, dmaAddr = 0;
358    uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0;
359
360    // continue to use the DMA interface until all pages are read
361    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
362        // see if the interface is busy
363        if (dmaInterface->busy()) {
364            // reschedule after waiting period
365            dmaReadEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
366            return;
367        }
368
369        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
370        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
371        dmaAddr = pciToDma(curAddr);
372
373        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
374        dmaInterfaceBytes += bytesInPage;
375
376        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
377                            curTick, &dmaReadEvent);
378
379        return;
380    }
381
382    // set initial address
383    curAddr = curPrd.getBaseAddr();
384
385    // clear out the data buffer
386    memset(dataBuffer, 0, MAX_DMA_SIZE);
387
388    // read the data from memory via DMA into a data buffer
389    while (bytesWritten < curPrd.getByteCount()) {
390        if (cmdBytesLeft <= 0)
391            panic("DMA data is larger than # of sectors specified\n");
392
393        dmaAddr = pciToDma(curAddr);
394
395        // calculate how many bytes are in the current page
396        bytesLeft = curPrd.getByteCount() - bytesWritten;
397        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
398
399        // copy the data from memory into the data buffer
400        memcpy((void *)(dataBuffer + bytesWritten),
401               physmem->dma_addr(dmaAddr, bytesInPage),
402               bytesInPage);
403
404        curAddr += bytesInPage;
405        bytesWritten += bytesInPage;
406        cmdBytesLeft -= bytesInPage;
407    }
408
409    // write the data to the disk image
410    for (bytesWritten = 0;
411         bytesWritten < curPrd.getByteCount();
412         bytesWritten += SectorSize) {
413
414        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
415    }
416
417#if 0
418    // actually copy the data from memory to data buffer
419    Addr dmaAddr =
420        ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
421    memcpy((void *)dataBuffer,
422           physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
423           curPrd.getByteCount());
424
425    uint32_t bytesWritten = 0;
426
427    while (bytesWritten < curPrd.getByteCount()) {
428        if (cmdBytesLeft <= 0)
429            panic("DMA data is larger than # sectors specified\n");
430
431        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
432
433        bytesWritten += SectorSize;
434        cmdBytesLeft -= SectorSize;
435    }
436#endif
437
438    // check for the EOT
439    if (curPrd.getEOT()){
440        assert(cmdBytesLeft == 0);
441        dmaState = Dma_Idle;
442        updateState(ACT_DMA_DONE);
443    } else {
444        doDmaTransfer();
445    }
446}
447
448void
449IdeDisk::doDmaWrite()
450{
451    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
452
453    if (dmaInterface) {
454        if (dmaInterface->busy()) {
455            // reschedule after waiting period
456            dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
457            return;
458        }
459
460        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
461
462        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
463                                              (uint32_t)curPrd.getByteCount());
464
465        dmaInterfaceBytes = bytesInPage;
466
467        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
468                            bytesInPage, curTick + totalDiskDelay,
469                            &dmaWriteEvent);
470    } else {
471        // schedule event with disk delay (dmaWriteDone)
472        dmaWriteEvent.schedule(curTick + totalDiskDelay);
473    }
474}
475
476void
477IdeDisk::dmaWriteDone()
478{
479    Addr curAddr = 0, pageAddr = 0, dmaAddr = 0;
480    uint32_t bytesRead = 0, bytesInPage = 0;
481
482    // continue to use the DMA interface until all pages are read
483    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
484        // see if the interface is busy
485        if (dmaInterface->busy()) {
486            // reschedule after waiting period
487            dmaWriteEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
488            return;
489        }
490
491        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
492        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
493        dmaAddr = pciToDma(curAddr);
494
495        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
496        dmaInterfaceBytes += bytesInPage;
497
498        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
499                            bytesInPage, curTick,
500                            &dmaWriteEvent);
501
502        return;
503    }
504
505    // setup the initial page and DMA address
506    curAddr = curPrd.getBaseAddr();
507    pageAddr = alpha_trunc_page(curAddr);
508    dmaAddr = pciToDma(curAddr);
509
510    // clear out the data buffer
511    memset(dataBuffer, 0, MAX_DMA_SIZE);
512
513    while (bytesRead < curPrd.getByteCount()) {
514        // see if we have crossed into a new page
515        if (pageAddr != alpha_trunc_page(curAddr)) {
516            // write the data to memory
517            memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
518                   (void *)(dataBuffer + (bytesRead - bytesInPage)),
519                   bytesInPage);
520
521            // update the DMA address and page address
522            pageAddr = alpha_trunc_page(curAddr);
523            dmaAddr = pciToDma(curAddr);
524
525            bytesInPage = 0;
526        }
527
528        if (cmdBytesLeft <= 0)
529            panic("DMA requested data is larger than # sectors specified\n");
530
531        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
532
533        curAddr += SectorSize;
534        bytesRead += SectorSize;
535        bytesInPage += SectorSize;
536        cmdBytesLeft -= SectorSize;
537    }
538
539    // write the last page worth read to memory
540    if (bytesInPage != 0) {
541        memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
542               (void *)(dataBuffer + (bytesRead - bytesInPage)),
543               bytesInPage);
544    }
545
546#if 0
547    Addr dmaAddr = ctrl->tsunami->pchip->
548        translatePciToDma(curPrd.getBaseAddr());
549
550    memcpy(physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
551           (void *)dataBuffer, curPrd.getByteCount());
552#endif
553
554    // check for the EOT
555    if (curPrd.getEOT()) {
556        assert(cmdBytesLeft == 0);
557        dmaState = Dma_Idle;
558        updateState(ACT_DMA_DONE);
559    } else {
560        doDmaTransfer();
561    }
562}
563
564////
565// Disk utility routines
566///
567
568void
569IdeDisk::readDisk(uint32_t sector, uint8_t *data)
570{
571    uint32_t bytesRead = image->read(data, sector);
572
573    if (bytesRead != SectorSize)
574        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
575              name(), bytesRead, SectorSize, errno);
576}
577
578void
579IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
580{
581    uint32_t bytesWritten = image->write(data, sector);
582
583    if (bytesWritten != SectorSize)
584        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
585              name(), bytesWritten, SectorSize, errno);
586}
587
588////
589// Setup and handle commands
590////
591
592void
593IdeDisk::startDma(const uint32_t &prdTableBase)
594{
595    if (dmaState != Dma_Start)
596        panic("Inconsistent DMA state, should be in Dma_Start!\n");
597
598    if (devState != Transfer_Data_Dma)
599        panic("Inconsistent device state for DMA start!\n");
600
601    curPrdAddr = pciToDma((Addr)prdTableBase);
602
603    dmaState = Dma_Transfer;
604
605    // schedule dma transfer (doDmaTransfer)
606    dmaTransferEvent.schedule(curTick + 1);
607}
608
609void
610IdeDisk::abortDma()
611{
612    if (dmaState == Dma_Idle)
613        panic("Inconsistent DMA state, should be in Dma_Start or Dma_Transfer!\n");
614
615    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
616        panic("Inconsistent device state, should be in Transfer or Prepare!\n");
617
618    updateState(ACT_CMD_ERROR);
619}
620
621void
622IdeDisk::startCommand()
623{
624    DevAction_t action = ACT_NONE;
625    uint32_t size = 0;
626    dmaRead = false;
627
628    // copy the command to the shadow
629    curCommand = cmdReg.command;
630
631    // Decode commands
632    switch (cmdReg.command) {
633        // Supported non-data commands
634      case WIN_READ_NATIVE_MAX:
635        size = image->size() - 1;
636        cmdReg.sec_num = (size & 0xff);
637        cmdReg.cyl_low = ((size & 0xff00) >> 8);
638        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
639        cmdReg.head = ((size & 0xf000000) >> 24);
640
641        devState = Command_Execution;
642        action = ACT_CMD_COMPLETE;
643        break;
644
645      case WIN_RECAL:
646      case WIN_SPECIFY:
647      case WIN_FLUSH_CACHE:
648      case WIN_VERIFY:
649      case WIN_SEEK:
650      case WIN_SETFEATURES:
651      case WIN_SETMULT:
652        devState = Command_Execution;
653        action = ACT_CMD_COMPLETE;
654        break;
655
656        // Supported PIO data-in commands
657      case WIN_IDENTIFY:
658        cmdBytesLeft = drqBytesLeft = sizeof(struct hd_driveid);
659        devState = Prepare_Data_In;
660        action = ACT_DATA_READY;
661        break;
662
663      case WIN_MULTREAD:
664      case WIN_READ:
665        if (!(cmdReg.drive & DRIVE_LBA_BIT))
666            panic("Attempt to perform CHS access, only supports LBA\n");
667
668        if (cmdReg.sec_count == 0)
669            cmdBytesLeft = (256 * SectorSize);
670        else
671            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
672
673        drqBytesLeft = SectorSize;
674        curSector = getLBABase();
675
676        devState = Prepare_Data_In;
677        action = ACT_DATA_READY;
678        break;
679
680        // Supported PIO data-out commands
681      case WIN_MULTWRITE:
682      case WIN_WRITE:
683        if (!(cmdReg.drive & DRIVE_LBA_BIT))
684            panic("Attempt to perform CHS access, only supports LBA\n");
685
686        if (cmdReg.sec_count == 0)
687            cmdBytesLeft = (256 * SectorSize);
688        else
689            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
690
691        drqBytesLeft = SectorSize;
692        curSector = getLBABase();
693
694        devState = Prepare_Data_Out;
695        action = ACT_DATA_READY;
696        break;
697
698        // Supported DMA commands
699      case WIN_WRITEDMA:
700        dmaRead = true;  // a write to the disk is a DMA read from memory
701      case WIN_READDMA:
702        if (!(cmdReg.drive & DRIVE_LBA_BIT))
703            panic("Attempt to perform CHS access, only supports LBA\n");
704
705        if (cmdReg.sec_count == 0)
706            cmdBytesLeft = (256 * SectorSize);
707        else
708            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
709
710        drqBytesLeft = SectorSize;
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        cmdReg.status |= STATUS_BSY_BIT;
724        // clear the DRQ bit
725        cmdReg.status &= ~STATUS_DRQ_BIT;
726
727        updateState(action);
728    }
729}
730
731////
732// Handle setting and clearing interrupts
733////
734
735void
736IdeDisk::intrPost()
737{
738    if (intrPending)
739        panic("Attempt to post an interrupt with one pending\n");
740
741    intrPending = true;
742
743    // talk to controller to set interrupt
744    if (ctrl)
745        ctrl->intrPost();
746}
747
748void
749IdeDisk::intrClear()
750{
751    if (!intrPending)
752        panic("Attempt to clear a non-pending interrupt\n");
753
754    intrPending = false;
755
756    // talk to controller to clear interrupt
757    if (ctrl)
758        ctrl->intrClear();
759}
760
761////
762// Manage the device internal state machine
763////
764
765void
766IdeDisk::updateState(DevAction_t action)
767{
768    switch (devState) {
769      case Device_Idle_S:
770        if (!isDEVSelect())
771            devState = Device_Idle_NS;
772        else if (action == ACT_CMD_WRITE)
773            startCommand();
774
775        break;
776
777      case Device_Idle_SI:
778        if (!isDEVSelect()) {
779            devState = Device_Idle_NS;
780            intrClear();
781        } else if (action == ACT_STAT_READ || isIENSet()) {
782            devState = Device_Idle_S;
783            intrClear();
784        } else if (action == ACT_CMD_WRITE) {
785            intrClear();
786            startCommand();
787        }
788
789        break;
790
791      case Device_Idle_NS:
792        if (isDEVSelect()) {
793            if (!isIENSet() && intrPending) {
794                devState = Device_Idle_SI;
795                intrPost();
796            }
797            if (isIENSet() || !intrPending) {
798                devState = Device_Idle_S;
799            }
800        }
801        break;
802
803      case Command_Execution:
804        if (action == ACT_CMD_COMPLETE) {
805            // clear the BSY bit
806            setComplete();
807
808            if (!isIENSet()) {
809                devState = Device_Idle_SI;
810                intrPost();
811            } else {
812                devState = Device_Idle_S;
813            }
814        }
815        break;
816
817      case Prepare_Data_In:
818        if (action == ACT_CMD_ERROR) {
819            // clear the BSY bit
820            setComplete();
821
822            if (!isIENSet()) {
823                devState = Device_Idle_SI;
824                intrPost();
825            } else {
826                devState = Device_Idle_S;
827            }
828        } else if (action == ACT_DATA_READY) {
829            // clear the BSY bit
830            cmdReg.status &= ~STATUS_BSY_BIT;
831            // set the DRQ bit
832            cmdReg.status |= STATUS_DRQ_BIT;
833
834            // put the first two bytes into the data register
835            memcpy((void *)&cmdReg.data0, (void *)dataBuffer,
836                   sizeof(uint16_t));
837
838            // copy the data into the data buffer
839            if (curCommand == WIN_IDENTIFY)
840                memcpy((void *)dataBuffer, (void *)&driveID,
841                       sizeof(struct hd_driveid));
842            else
843                readDisk(curSector++, dataBuffer);
844
845            if (!isIENSet()) {
846                devState = Data_Ready_INTRQ_In;
847                intrPost();
848            } else {
849                devState = Transfer_Data_In;
850            }
851        }
852        break;
853
854      case Data_Ready_INTRQ_In:
855        if (action == ACT_STAT_READ) {
856            devState = Transfer_Data_In;
857            intrClear();
858        }
859        break;
860
861      case Transfer_Data_In:
862        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
863            if (action == ACT_DATA_READ_BYTE) {
864                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
865            } else {
866                drqBytesLeft -= 2;
867                cmdBytesLeft -= 2;
868
869                // copy next short into data registers
870                memcpy((void *)&cmdReg.data0,
871                       (void *)&dataBuffer[SectorSize - drqBytesLeft],
872                       sizeof(uint16_t));
873            }
874
875            if (drqBytesLeft == 0) {
876                if (cmdBytesLeft == 0) {
877                    // Clear the BSY bit
878                    setComplete();
879                    devState = Device_Idle_S;
880                } else {
881                    devState = Prepare_Data_In;
882                    cmdReg.status |= STATUS_BSY_BIT;
883                }
884            }
885        }
886        break;
887
888      case Prepare_Data_Out:
889        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
890            // clear the BSY bit
891            setComplete();
892
893            if (!isIENSet()) {
894                devState = Device_Idle_SI;
895                intrPost();
896            } else {
897                devState = Device_Idle_S;
898            }
899        } else if (cmdBytesLeft != 0) {
900            // clear the BSY bit
901            cmdReg.status &= ~STATUS_BSY_BIT;
902            // set the DRQ bit
903            cmdReg.status |= STATUS_DRQ_BIT;
904
905            // clear the data buffer to get it ready for writes
906            memset(dataBuffer, 0, MAX_DMA_SIZE);
907
908            if (!isIENSet()) {
909                devState = Data_Ready_INTRQ_Out;
910                intrPost();
911            } else {
912                devState = Transfer_Data_Out;
913            }
914        }
915        break;
916
917      case Data_Ready_INTRQ_Out:
918        if (action == ACT_STAT_READ) {
919            devState = Transfer_Data_Out;
920            intrClear();
921        }
922        break;
923
924      case Transfer_Data_Out:
925        if (action == ACT_DATA_WRITE_BYTE ||
926            action == ACT_DATA_WRITE_SHORT) {
927
928            if (action == ACT_DATA_READ_BYTE) {
929                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
930            } else {
931                // copy the latest short into the data buffer
932                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
933                       (void *)&cmdReg.data0,
934                       sizeof(uint16_t));
935
936                drqBytesLeft -= 2;
937                cmdBytesLeft -= 2;
938            }
939
940            if (drqBytesLeft == 0) {
941                // copy the block to the disk
942                writeDisk(curSector++, dataBuffer);
943
944                // set the BSY bit
945                cmdReg.status |= STATUS_BSY_BIT;
946                // clear the DRQ bit
947                cmdReg.status &= ~STATUS_DRQ_BIT;
948
949                devState = Prepare_Data_Out;
950            }
951        }
952        break;
953
954      case Prepare_Data_Dma:
955        if (action == ACT_CMD_ERROR) {
956            // clear the BSY bit
957            setComplete();
958
959            if (!isIENSet()) {
960                devState = Device_Idle_SI;
961                intrPost();
962            } else {
963                devState = Device_Idle_S;
964            }
965        } else if (action == ACT_DMA_READY) {
966            // clear the BSY bit
967            cmdReg.status &= ~STATUS_BSY_BIT;
968            // set the DRQ bit
969            cmdReg.status |= STATUS_DRQ_BIT;
970
971            devState = Transfer_Data_Dma;
972
973            if (dmaState != Dma_Idle)
974                panic("Inconsistent DMA state, should be Dma_Idle\n");
975
976            dmaState = Dma_Start;
977            // wait for the write to the DMA start bit
978        }
979        break;
980
981      case Transfer_Data_Dma:
982        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
983            // clear the BSY bit
984            setComplete();
985            // set the seek bit
986            cmdReg.status |= 0x10;
987            // clear the controller state for DMA transfer
988            ctrl->setDmaComplete(this);
989
990            if (!isIENSet()) {
991                devState = Device_Idle_SI;
992                intrPost();
993            } else {
994                devState = Device_Idle_S;
995            }
996        }
997        break;
998
999      default:
1000        panic("Unknown IDE device state: %#x\n", devState);
1001    }
1002}
1003
1004void
1005IdeDisk::serialize(ostream &os)
1006{
1007    // Check all outstanding events to see if they are scheduled
1008    // these are all mutually exclusive
1009    Tick reschedule = 0;
1010    Events_t event = None;
1011
1012    if (dmaTransferEvent.scheduled()) {
1013        reschedule = dmaTransferEvent.when();
1014        event = Transfer;
1015    } else if (dmaReadWaitEvent.scheduled()) {
1016        reschedule = dmaReadWaitEvent.when();
1017        event = ReadWait;
1018    } else if (dmaWriteWaitEvent.scheduled()) {
1019        reschedule = dmaWriteWaitEvent.when();
1020        event = WriteWait;
1021    } else if (dmaPrdReadEvent.scheduled()) {
1022        reschedule = dmaPrdReadEvent.when();
1023        event = PrdRead;
1024    } else if (dmaReadEvent.scheduled()) {
1025        reschedule = dmaReadEvent.when();
1026        event = DmaRead;
1027    } else if (dmaWriteEvent.scheduled()) {
1028        reschedule = dmaWriteEvent.when();
1029        event = DmaWrite;
1030    }
1031
1032    SERIALIZE_SCALAR(reschedule);
1033    SERIALIZE_ENUM(event);
1034
1035    // Serialize device registers
1036    SERIALIZE_SCALAR(cmdReg.data0);
1037    SERIALIZE_SCALAR(cmdReg.data1);
1038    SERIALIZE_SCALAR(cmdReg.sec_count);
1039    SERIALIZE_SCALAR(cmdReg.sec_num);
1040    SERIALIZE_SCALAR(cmdReg.cyl_low);
1041    SERIALIZE_SCALAR(cmdReg.cyl_high);
1042    SERIALIZE_SCALAR(cmdReg.drive);
1043    SERIALIZE_SCALAR(cmdReg.status);
1044    SERIALIZE_SCALAR(nIENBit);
1045    SERIALIZE_SCALAR(devID);
1046
1047    // Serialize the PRD related information
1048    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1049    SERIALIZE_SCALAR(curPrd.entry.byteCount);
1050    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1051    SERIALIZE_SCALAR(curPrdAddr);
1052
1053    // Serialize current transfer related information
1054    SERIALIZE_SCALAR(cmdBytesLeft);
1055    SERIALIZE_SCALAR(drqBytesLeft);
1056    SERIALIZE_SCALAR(curSector);
1057    SERIALIZE_SCALAR(curCommand);
1058    SERIALIZE_SCALAR(dmaRead);
1059    SERIALIZE_SCALAR(dmaInterfaceBytes);
1060    SERIALIZE_SCALAR(intrPending);
1061    SERIALIZE_ENUM(devState);
1062    SERIALIZE_ENUM(dmaState);
1063    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1064}
1065
1066void
1067IdeDisk::unserialize(Checkpoint *cp, const string &section)
1068{
1069    // Reschedule events that were outstanding
1070    // these are all mutually exclusive
1071    Tick reschedule = 0;
1072    Events_t event = None;
1073
1074    UNSERIALIZE_SCALAR(reschedule);
1075    UNSERIALIZE_ENUM(event);
1076
1077    switch (event) {
1078      case None : break;
1079      case Transfer : dmaTransferEvent.schedule(reschedule); break;
1080      case ReadWait : dmaReadWaitEvent.schedule(reschedule); break;
1081      case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break;
1082      case PrdRead : dmaPrdReadEvent.schedule(reschedule); break;
1083      case DmaRead : dmaReadEvent.schedule(reschedule); break;
1084      case DmaWrite : dmaWriteEvent.schedule(reschedule); break;
1085    }
1086
1087    // Unserialize device registers
1088    UNSERIALIZE_SCALAR(cmdReg.data0);
1089    UNSERIALIZE_SCALAR(cmdReg.data1);
1090    UNSERIALIZE_SCALAR(cmdReg.sec_count);
1091    UNSERIALIZE_SCALAR(cmdReg.sec_num);
1092    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1093    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1094    UNSERIALIZE_SCALAR(cmdReg.drive);
1095    UNSERIALIZE_SCALAR(cmdReg.status);
1096    UNSERIALIZE_SCALAR(nIENBit);
1097    UNSERIALIZE_SCALAR(devID);
1098
1099    // Unserialize the PRD related information
1100    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1101    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1102    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1103    UNSERIALIZE_SCALAR(curPrdAddr);
1104
1105    // Unserialize current transfer related information
1106    UNSERIALIZE_SCALAR(cmdBytesLeft);
1107    UNSERIALIZE_SCALAR(drqBytesLeft);
1108    UNSERIALIZE_SCALAR(curSector);
1109    UNSERIALIZE_SCALAR(curCommand);
1110    UNSERIALIZE_SCALAR(dmaRead);
1111    UNSERIALIZE_SCALAR(dmaInterfaceBytes);
1112    UNSERIALIZE_SCALAR(intrPending);
1113    UNSERIALIZE_ENUM(devState);
1114    UNSERIALIZE_ENUM(dmaState);
1115    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1116}
1117
1118#ifndef DOXYGEN_SHOULD_SKIP_THIS
1119
1120BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1121
1122    SimObjectParam<DiskImage *> image;
1123    SimObjectParam<PhysicalMemory *> physmem;
1124    Param<int> driveID;
1125    Param<int> disk_delay;
1126
1127END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1128
1129BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1130
1131    INIT_PARAM(image, "Disk image"),
1132    INIT_PARAM(physmem, "Physical memory"),
1133    INIT_PARAM(driveID, "Drive ID (0=master 1=slave)"),
1134    INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in microseconds", 1)
1135
1136END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1137
1138
1139CREATE_SIM_OBJECT(IdeDisk)
1140{
1141    return new IdeDisk(getInstanceName(), image, physmem, driveID,
1142                       disk_delay);
1143}
1144
1145REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
1146
1147#endif //DOXYGEN_SHOULD_SKIP_THIS
1148