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