ide_disk.cc revision 877
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 = 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        curSector = getLBABase();
674
675        /** @todo make this a scheduled event to simulate disk delay */
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        curSector = getLBABase();
692
693        devState = Prepare_Data_Out;
694        action = ACT_DATA_READY;
695        break;
696
697        // Supported DMA commands
698      case WIN_WRITEDMA:
699        dmaRead = true;  // a write to the disk is a DMA read from memory
700      case WIN_READDMA:
701        if (!(cmdReg.drive & DRIVE_LBA_BIT))
702            panic("Attempt to perform CHS access, only supports LBA\n");
703
704        if (cmdReg.sec_count == 0)
705            cmdBytesLeft = (256 * SectorSize);
706        else
707            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
708
709        curSector = getLBABase();
710
711        devState = Prepare_Data_Dma;
712        action = ACT_DMA_READY;
713        break;
714
715      default:
716        panic("Unsupported ATA command: %#x\n", cmdReg.command);
717    }
718
719    if (action != ACT_NONE) {
720        // set the BSY bit
721        cmdReg.status |= STATUS_BSY_BIT;
722        // clear the DRQ bit
723        cmdReg.status &= ~STATUS_DRQ_BIT;
724
725        updateState(action);
726    }
727}
728
729////
730// Handle setting and clearing interrupts
731////
732
733void
734IdeDisk::intrPost()
735{
736    if (intrPending)
737        panic("Attempt to post an interrupt with one pending\n");
738
739    intrPending = true;
740
741    // talk to controller to set interrupt
742    if (ctrl)
743        ctrl->intrPost();
744}
745
746void
747IdeDisk::intrClear()
748{
749    if (!intrPending)
750        panic("Attempt to clear a non-pending interrupt\n");
751
752    intrPending = false;
753
754    // talk to controller to clear interrupt
755    if (ctrl)
756        ctrl->intrClear();
757}
758
759////
760// Manage the device internal state machine
761////
762
763void
764IdeDisk::updateState(DevAction_t action)
765{
766    switch (devState) {
767      case Device_Idle_S:
768        if (!isDEVSelect())
769            devState = Device_Idle_NS;
770        else if (action == ACT_CMD_WRITE)
771            startCommand();
772
773        break;
774
775      case Device_Idle_SI:
776        if (!isDEVSelect()) {
777            devState = Device_Idle_NS;
778            intrClear();
779        } else if (action == ACT_STAT_READ || isIENSet()) {
780            devState = Device_Idle_S;
781            intrClear();
782        } else if (action == ACT_CMD_WRITE) {
783            intrClear();
784            startCommand();
785        }
786
787        break;
788
789      case Device_Idle_NS:
790        if (isDEVSelect()) {
791            if (!isIENSet() && intrPending) {
792                devState = Device_Idle_SI;
793                intrPost();
794            }
795            if (isIENSet() || !intrPending) {
796                devState = Device_Idle_S;
797            }
798        }
799        break;
800
801      case Command_Execution:
802        if (action == ACT_CMD_COMPLETE) {
803            // clear the BSY bit
804            setComplete();
805
806            if (!isIENSet()) {
807                devState = Device_Idle_SI;
808                intrPost();
809            } else {
810                devState = Device_Idle_S;
811            }
812        }
813        break;
814
815      case Prepare_Data_In:
816        if (action == ACT_CMD_ERROR) {
817            // clear the BSY bit
818            setComplete();
819
820            if (!isIENSet()) {
821                devState = Device_Idle_SI;
822                intrPost();
823            } else {
824                devState = Device_Idle_S;
825            }
826        } else if (action == ACT_DATA_READY) {
827            // clear the BSY bit
828            cmdReg.status &= ~STATUS_BSY_BIT;
829            // set the DRQ bit
830            cmdReg.status |= STATUS_DRQ_BIT;
831
832            // copy the data into the data buffer
833            if (curCommand == WIN_IDENTIFY) {
834                // Reset the drqBytes for this block
835                drqBytesLeft = sizeof(struct hd_driveid);
836
837                memcpy((void *)dataBuffer, (void *)&driveID,
838                       sizeof(struct hd_driveid));
839            } else {
840                // Reset the drqBytes for this block
841                drqBytesLeft = SectorSize;
842
843                readDisk(curSector++, dataBuffer);
844            }
845
846            // put the first two bytes into the data register
847            memcpy((void *)&cmdReg.data0, (void *)dataBuffer,
848                   sizeof(uint16_t));
849
850            if (!isIENSet()) {
851                devState = Data_Ready_INTRQ_In;
852                intrPost();
853            } else {
854                devState = Transfer_Data_In;
855            }
856        }
857        break;
858
859      case Data_Ready_INTRQ_In:
860        if (action == ACT_STAT_READ) {
861            devState = Transfer_Data_In;
862            intrClear();
863        }
864        break;
865
866      case Transfer_Data_In:
867        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
868            if (action == ACT_DATA_READ_BYTE) {
869                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
870            } else {
871                drqBytesLeft -= 2;
872                cmdBytesLeft -= 2;
873
874                // copy next short into data registers
875                if (drqBytesLeft)
876                    memcpy((void *)&cmdReg.data0,
877                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
878                           sizeof(uint16_t));
879            }
880
881            if (drqBytesLeft == 0) {
882                if (cmdBytesLeft == 0) {
883                    // Clear the BSY bit
884                    setComplete();
885                    devState = Device_Idle_S;
886                } else {
887                    devState = Prepare_Data_In;
888                    // set the BSY_BIT
889                    cmdReg.status |= STATUS_BSY_BIT;
890                    // clear the DRQ_BIT
891                    cmdReg.status &= ~STATUS_DRQ_BIT;
892
893                    /** @todo change this to a scheduled event to simulate
894                        disk delay */
895                    updateState(ACT_DATA_READY);
896                }
897            }
898        }
899        break;
900
901      case Prepare_Data_Out:
902        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
903            // clear the BSY bit
904            setComplete();
905
906            if (!isIENSet()) {
907                devState = Device_Idle_SI;
908                intrPost();
909            } else {
910                devState = Device_Idle_S;
911            }
912        } else if (cmdBytesLeft != 0) {
913            // clear the BSY bit
914            cmdReg.status &= ~STATUS_BSY_BIT;
915            // set the DRQ bit
916            cmdReg.status |= STATUS_DRQ_BIT;
917
918            // clear the data buffer to get it ready for writes
919            memset(dataBuffer, 0, MAX_DMA_SIZE);
920
921            if (!isIENSet()) {
922                devState = Data_Ready_INTRQ_Out;
923                intrPost();
924            } else {
925                devState = Transfer_Data_Out;
926            }
927        }
928        break;
929
930      case Data_Ready_INTRQ_Out:
931        if (action == ACT_STAT_READ) {
932            devState = Transfer_Data_Out;
933            intrClear();
934        }
935        break;
936
937      case Transfer_Data_Out:
938        if (action == ACT_DATA_WRITE_BYTE ||
939            action == ACT_DATA_WRITE_SHORT) {
940
941            if (action == ACT_DATA_READ_BYTE) {
942                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
943            } else {
944                // copy the latest short into the data buffer
945                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
946                       (void *)&cmdReg.data0,
947                       sizeof(uint16_t));
948
949                drqBytesLeft -= 2;
950                cmdBytesLeft -= 2;
951            }
952
953            if (drqBytesLeft == 0) {
954                // copy the block to the disk
955                writeDisk(curSector++, dataBuffer);
956
957                // set the BSY bit
958                cmdReg.status |= STATUS_BSY_BIT;
959                // clear the DRQ bit
960                cmdReg.status &= ~STATUS_DRQ_BIT;
961
962                devState = Prepare_Data_Out;
963            }
964        }
965        break;
966
967      case Prepare_Data_Dma:
968        if (action == ACT_CMD_ERROR) {
969            // clear the BSY bit
970            setComplete();
971
972            if (!isIENSet()) {
973                devState = Device_Idle_SI;
974                intrPost();
975            } else {
976                devState = Device_Idle_S;
977            }
978        } else if (action == ACT_DMA_READY) {
979            // clear the BSY bit
980            cmdReg.status &= ~STATUS_BSY_BIT;
981            // set the DRQ bit
982            cmdReg.status |= STATUS_DRQ_BIT;
983
984            devState = Transfer_Data_Dma;
985
986            if (dmaState != Dma_Idle)
987                panic("Inconsistent DMA state, should be Dma_Idle\n");
988
989            dmaState = Dma_Start;
990            // wait for the write to the DMA start bit
991        }
992        break;
993
994      case Transfer_Data_Dma:
995        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
996            // clear the BSY bit
997            setComplete();
998            // set the seek bit
999            cmdReg.status |= 0x10;
1000            // clear the controller state for DMA transfer
1001            ctrl->setDmaComplete(this);
1002
1003            if (!isIENSet()) {
1004                devState = Device_Idle_SI;
1005                intrPost();
1006            } else {
1007                devState = Device_Idle_S;
1008            }
1009        }
1010        break;
1011
1012      default:
1013        panic("Unknown IDE device state: %#x\n", devState);
1014    }
1015}
1016
1017void
1018IdeDisk::serialize(ostream &os)
1019{
1020    // Check all outstanding events to see if they are scheduled
1021    // these are all mutually exclusive
1022    Tick reschedule = 0;
1023    Events_t event = None;
1024
1025    if (dmaTransferEvent.scheduled()) {
1026        reschedule = dmaTransferEvent.when();
1027        event = Transfer;
1028    } else if (dmaReadWaitEvent.scheduled()) {
1029        reschedule = dmaReadWaitEvent.when();
1030        event = ReadWait;
1031    } else if (dmaWriteWaitEvent.scheduled()) {
1032        reschedule = dmaWriteWaitEvent.when();
1033        event = WriteWait;
1034    } else if (dmaPrdReadEvent.scheduled()) {
1035        reschedule = dmaPrdReadEvent.when();
1036        event = PrdRead;
1037    } else if (dmaReadEvent.scheduled()) {
1038        reschedule = dmaReadEvent.when();
1039        event = DmaRead;
1040    } else if (dmaWriteEvent.scheduled()) {
1041        reschedule = dmaWriteEvent.when();
1042        event = DmaWrite;
1043    }
1044
1045    SERIALIZE_SCALAR(reschedule);
1046    SERIALIZE_ENUM(event);
1047
1048    // Serialize device registers
1049    SERIALIZE_SCALAR(cmdReg.data0);
1050    SERIALIZE_SCALAR(cmdReg.data1);
1051    SERIALIZE_SCALAR(cmdReg.sec_count);
1052    SERIALIZE_SCALAR(cmdReg.sec_num);
1053    SERIALIZE_SCALAR(cmdReg.cyl_low);
1054    SERIALIZE_SCALAR(cmdReg.cyl_high);
1055    SERIALIZE_SCALAR(cmdReg.drive);
1056    SERIALIZE_SCALAR(cmdReg.status);
1057    SERIALIZE_SCALAR(nIENBit);
1058    SERIALIZE_SCALAR(devID);
1059
1060    // Serialize the PRD related information
1061    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1062    SERIALIZE_SCALAR(curPrd.entry.byteCount);
1063    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1064    SERIALIZE_SCALAR(curPrdAddr);
1065
1066    // Serialize current transfer related information
1067    SERIALIZE_SCALAR(cmdBytesLeft);
1068    SERIALIZE_SCALAR(drqBytesLeft);
1069    SERIALIZE_SCALAR(curSector);
1070    SERIALIZE_SCALAR(curCommand);
1071    SERIALIZE_SCALAR(dmaRead);
1072    SERIALIZE_SCALAR(dmaInterfaceBytes);
1073    SERIALIZE_SCALAR(intrPending);
1074    SERIALIZE_ENUM(devState);
1075    SERIALIZE_ENUM(dmaState);
1076    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1077}
1078
1079void
1080IdeDisk::unserialize(Checkpoint *cp, const string &section)
1081{
1082    // Reschedule events that were outstanding
1083    // these are all mutually exclusive
1084    Tick reschedule = 0;
1085    Events_t event = None;
1086
1087    UNSERIALIZE_SCALAR(reschedule);
1088    UNSERIALIZE_ENUM(event);
1089
1090    switch (event) {
1091      case None : break;
1092      case Transfer : dmaTransferEvent.schedule(reschedule); break;
1093      case ReadWait : dmaReadWaitEvent.schedule(reschedule); break;
1094      case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break;
1095      case PrdRead : dmaPrdReadEvent.schedule(reschedule); break;
1096      case DmaRead : dmaReadEvent.schedule(reschedule); break;
1097      case DmaWrite : dmaWriteEvent.schedule(reschedule); break;
1098    }
1099
1100    // Unserialize device registers
1101    UNSERIALIZE_SCALAR(cmdReg.data0);
1102    UNSERIALIZE_SCALAR(cmdReg.data1);
1103    UNSERIALIZE_SCALAR(cmdReg.sec_count);
1104    UNSERIALIZE_SCALAR(cmdReg.sec_num);
1105    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1106    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1107    UNSERIALIZE_SCALAR(cmdReg.drive);
1108    UNSERIALIZE_SCALAR(cmdReg.status);
1109    UNSERIALIZE_SCALAR(nIENBit);
1110    UNSERIALIZE_SCALAR(devID);
1111
1112    // Unserialize the PRD related information
1113    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1114    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1115    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1116    UNSERIALIZE_SCALAR(curPrdAddr);
1117
1118    // Unserialize current transfer related information
1119    UNSERIALIZE_SCALAR(cmdBytesLeft);
1120    UNSERIALIZE_SCALAR(drqBytesLeft);
1121    UNSERIALIZE_SCALAR(curSector);
1122    UNSERIALIZE_SCALAR(curCommand);
1123    UNSERIALIZE_SCALAR(dmaRead);
1124    UNSERIALIZE_SCALAR(dmaInterfaceBytes);
1125    UNSERIALIZE_SCALAR(intrPending);
1126    UNSERIALIZE_ENUM(devState);
1127    UNSERIALIZE_ENUM(dmaState);
1128    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1129}
1130
1131#ifndef DOXYGEN_SHOULD_SKIP_THIS
1132
1133BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1134
1135    SimObjectParam<DiskImage *> image;
1136    SimObjectParam<PhysicalMemory *> physmem;
1137    Param<int> driveID;
1138    Param<int> disk_delay;
1139
1140END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1141
1142BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1143
1144    INIT_PARAM(image, "Disk image"),
1145    INIT_PARAM(physmem, "Physical memory"),
1146    INIT_PARAM(driveID, "Drive ID (0=master 1=slave)"),
1147    INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in microseconds", 1)
1148
1149END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1150
1151
1152CREATE_SIM_OBJECT(IdeDisk)
1153{
1154    return new IdeDisk(getInstanceName(), image, physmem, driveID,
1155                       disk_delay);
1156}
1157
1158REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
1159
1160#endif //DOXYGEN_SHOULD_SKIP_THIS
1161