Merge zizzer.eecs.umich.edu:/bk/linux
[gem5.git] / dev / ide_disk.cc
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
55 using namespace std;
56
57 IdeDisk::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 diskDelay = (delay * ticksPerSecond / 100000);
65
66 // initialize the data buffer and shadow registers
67 dataBuffer = new uint8_t[MAX_DMA_SIZE];
68
69 memset(dataBuffer, 0, MAX_DMA_SIZE);
70 memset(&cmdReg, 0, sizeof(CommandReg_t));
71 memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
72
73 curPrdAddr = 0;
74 curSector = 0;
75 curCommand = 0;
76 cmdBytesLeft = 0;
77 drqBytesLeft = 0;
78 dmaRead = false;
79 intrPending = false;
80
81 // fill out the drive ID structure
82 memset(&driveID, 0, sizeof(struct hd_driveid));
83
84 // Calculate LBA and C/H/S values
85 uint16_t cylinders;
86 uint8_t heads;
87 uint8_t sectors;
88
89 uint32_t lba_size = image->size();
90 if (lba_size >= 16383*16*63) {
91 cylinders = 16383;
92 heads = 16;
93 sectors = 63;
94 } else {
95 if (lba_size >= 63)
96 sectors = 63;
97 else
98 sectors = lba_size;
99
100 if ((lba_size / sectors) >= 16)
101 heads = 16;
102 else
103 heads = (lba_size / sectors);
104
105 cylinders = lba_size / (heads * sectors);
106 }
107
108 // Setup the model name
109 sprintf((char *)driveID.model, "5MI EDD si k");
110 // Set the maximum multisector transfer size
111 driveID.max_multsect = MAX_MULTSECT;
112 // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
113 driveID.capability = 0x7;
114 // UDMA support, EIDE support
115 driveID.field_valid = 0x6;
116 // Setup default C/H/S settings
117 driveID.cyls = cylinders;
118 driveID.sectors = sectors;
119 driveID.heads = heads;
120 // Setup the current multisector transfer size
121 driveID.multsect = MAX_MULTSECT;
122 driveID.multsect_valid = 0x1;
123 // Number of sectors on disk
124 driveID.lba_capacity = lba_size;
125 // Multiword DMA mode 2 and below supported
126 driveID.dma_mword = 0x400;
127 // Set PIO mode 4 and 3 supported
128 driveID.eide_pio_modes = 0x3;
129 // Set DMA mode 4 and below supported
130 driveID.dma_ultra = 0x10;
131 // Statically set hardware config word
132 driveID.hw_config = 0x4001;
133
134 // set the device state to idle
135 dmaState = Dma_Idle;
136
137 if (id == DEV0) {
138 devState = Device_Idle_S;
139 devID = DEV0;
140 } else if (id == DEV1) {
141 devState = Device_Idle_NS;
142 devID = DEV1;
143 } else {
144 panic("Invalid device ID: %#x\n", id);
145 }
146
147 // set the device ready bit
148 cmdReg.status |= STATUS_DRDY_BIT;
149 }
150
151 IdeDisk::~IdeDisk()
152 {
153 // destroy the data buffer
154 delete [] dataBuffer;
155 }
156
157 Addr
158 IdeDisk::pciToDma(Addr &pciAddr)
159 {
160 if (ctrl)
161 return ctrl->tsunami->pchip->translatePciToDma(pciAddr);
162 else
163 panic("Access to unset controller!\n");
164 }
165
166 ////
167 // Device registers read/write
168 ////
169
170 void
171 IdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data)
172 {
173 DevAction_t action = ACT_NONE;
174
175 if (cmdBlk) {
176 if (offset < 0 || offset > sizeof(CommandReg_t))
177 panic("Invalid disk command register offset: %#x\n", offset);
178
179 if (!byte && offset != DATA_OFFSET)
180 panic("Invalid 16-bit read, only allowed on data reg\n");
181
182 if (!byte)
183 *(uint16_t *)data = *(uint16_t *)&cmdReg.data0;
184 else
185 *data = ((uint8_t *)&cmdReg)[offset];
186
187 // determine if an action needs to be taken on the state machine
188 if (offset == STATUS_OFFSET) {
189 action = ACT_STAT_READ;
190 } else if (offset == DATA_OFFSET) {
191 if (byte)
192 action = ACT_DATA_READ_BYTE;
193 else
194 action = ACT_DATA_READ_SHORT;
195 }
196
197 } else {
198 if (offset != ALTSTAT_OFFSET)
199 panic("Invalid disk control register offset: %#x\n", offset);
200
201 if (!byte)
202 panic("Invalid 16-bit read from control block\n");
203
204 *data = ((uint8_t *)&cmdReg)[STATUS_OFFSET];
205 }
206
207 if (action != ACT_NONE)
208 updateState(action);
209 }
210
211 void
212 IdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data)
213 {
214 DevAction_t action = ACT_NONE;
215
216 if (cmdBlk) {
217 if (offset < 0 || offset > sizeof(CommandReg_t))
218 panic("Invalid disk command register offset: %#x\n", offset);
219
220 if (!byte && offset != DATA_OFFSET)
221 panic("Invalid 16-bit write, only allowed on data reg\n");
222
223 if (!byte)
224 *((uint16_t *)&cmdReg.data0) = *(uint16_t *)data;
225 else
226 ((uint8_t *)&cmdReg)[offset] = *data;
227
228 // determine if an action needs to be taken on the state machine
229 if (offset == COMMAND_OFFSET) {
230 action = ACT_CMD_WRITE;
231 } else if (offset == DATA_OFFSET) {
232 if (byte)
233 action = ACT_DATA_WRITE_BYTE;
234 else
235 action = ACT_DATA_WRITE_SHORT;
236 }
237
238 } else {
239 if (offset != CONTROL_OFFSET)
240 panic("Invalid disk control register offset: %#x\n", offset);
241
242 if (!byte)
243 panic("Invalid 16-bit write to control block\n");
244
245 if (*data & CONTROL_RST_BIT)
246 panic("Software reset not supported!\n");
247
248 nIENBit = (*data & CONTROL_IEN_BIT) ? true : false;
249 }
250
251 if (action != ACT_NONE)
252 updateState(action);
253 }
254
255 ////
256 // Perform DMA transactions
257 ////
258
259 void
260 IdeDisk::doDmaTransfer()
261 {
262 if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
263 panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
264 dmaState, devState);
265
266 // first read the current PRD
267 if (dmaInterface) {
268 if (dmaInterface->busy()) {
269 // reschedule after waiting period
270 dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
271 return;
272 }
273
274 dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick,
275 &dmaPrdReadEvent);
276 } else {
277 dmaPrdReadDone();
278 }
279 }
280
281 void
282 IdeDisk::dmaPrdReadDone()
283 {
284 // actually copy the PRD from physical memory
285 memcpy((void *)&curPrd.entry,
286 physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)),
287 sizeof(PrdEntry_t));
288
289 curPrdAddr += sizeof(PrdEntry_t);
290
291 if (dmaRead)
292 doDmaRead();
293 else
294 doDmaWrite();
295 }
296
297 void
298 IdeDisk::doDmaRead()
299 {
300 Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize);
301
302 if (dmaInterface) {
303 if (dmaInterface->busy()) {
304 // reschedule after waiting period
305 dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
306 return;
307 }
308
309 Addr dmaAddr =
310 ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
311 dmaInterface->doDMA(Read, dmaAddr, curPrd.getByteCount(),
312 curTick + totalDiskDelay, &dmaReadEvent);
313 } else {
314 // schedule dmaReadEvent with sectorDelay (dmaReadDone)
315 dmaReadEvent.schedule(curTick + totalDiskDelay);
316 }
317 }
318
319 void
320 IdeDisk::dmaReadDone()
321 {
322
323 Addr curAddr = 0, dmaAddr = 0;
324 uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0;
325
326 // set initial address
327 curAddr = curPrd.getBaseAddr();
328
329 // clear out the data buffer
330 memset(dataBuffer, 0, MAX_DMA_SIZE);
331
332 // read the data from memory via DMA into a data buffer
333 while (bytesWritten < curPrd.getByteCount()) {
334 if (cmdBytesLeft <= 0)
335 panic("DMA data is larger than # of sectors specified\n");
336
337 dmaAddr = pciToDma(curAddr);
338
339 // calculate how many bytes are in the current page
340 bytesLeft = curPrd.getByteCount() - bytesWritten;
341 bytesInPage = (bytesLeft > ALPHA_PGBYTES) ? ALPHA_PGBYTES : bytesLeft;
342 // check to make sure we don't cross a page boundary
343 if ((curAddr + bytesInPage) >
344 (alpha_trunc_page(curAddr) + ALPHA_PGBYTES))
345
346 bytesInPage = alpha_round_page(curAddr) - curAddr;
347
348 // copy the data from memory into the data buffer
349 /** @todo Use real DMA with interfaces here */
350 memcpy((void *)(dataBuffer + bytesWritten),
351 physmem->dma_addr(dmaAddr, bytesInPage),
352 bytesInPage);
353
354 curAddr += bytesInPage;
355 bytesWritten += bytesInPage;
356 cmdBytesLeft -= bytesInPage;
357 }
358
359 // write the data to the disk image
360 for (bytesWritten = 0;
361 bytesWritten < curPrd.getByteCount();
362 bytesWritten += SectorSize)
363
364 writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
365
366 #if 0
367 // actually copy the data from memory to data buffer
368 Addr dmaAddr =
369 ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
370 memcpy((void *)dataBuffer,
371 physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
372 curPrd.getByteCount());
373
374 uint32_t bytesWritten = 0;
375
376 while (bytesWritten < curPrd.getByteCount()) {
377 if (cmdBytesLeft <= 0)
378 panic("DMA data is larger than # sectors specified\n");
379
380 writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
381
382 bytesWritten += SectorSize;
383 cmdBytesLeft -= SectorSize;
384 }
385 #endif
386
387 // check for the EOT
388 if (curPrd.getEOT()){
389 assert(cmdBytesLeft == 0);
390 dmaState = Dma_Idle;
391 updateState(ACT_DMA_DONE);
392 } else {
393 doDmaTransfer();
394 }
395 }
396
397 void
398 IdeDisk::doDmaWrite()
399 {
400 Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize);
401
402 if (dmaInterface) {
403 if (dmaInterface->busy()) {
404 // reschedule after waiting period
405 dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
406 return;
407 }
408
409 Addr dmaAddr =
410 ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
411 dmaInterface->doDMA(WriteInvalidate, dmaAddr,
412 curPrd.getByteCount(), curTick + totalDiskDelay,
413 &dmaWriteEvent);
414 } else {
415 // schedule event with disk delay (dmaWriteDone)
416 dmaWriteEvent.schedule(curTick + totalDiskDelay);
417 }
418 }
419
420 void
421 IdeDisk::dmaWriteDone()
422 {
423 Addr curAddr = 0, pageAddr = 0, dmaAddr = 0;
424 uint32_t bytesRead = 0, bytesInPage = 0;
425
426 // setup the initial page and DMA address
427 curAddr = curPrd.getBaseAddr();
428 pageAddr = alpha_trunc_page(curAddr);
429 dmaAddr = pciToDma(curAddr);
430
431 // clear out the data buffer
432 memset(dataBuffer, 0, MAX_DMA_SIZE);
433
434 while (bytesRead < curPrd.getByteCount()) {
435 // see if we have crossed into a new page
436 if (pageAddr != alpha_trunc_page(curAddr)) {
437 // write the data to memory
438 /** @todo Do real DMA using interfaces here */
439 memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
440 (void *)(dataBuffer + (bytesRead - bytesInPage)),
441 bytesInPage);
442
443 // update the DMA address and page address
444 pageAddr = alpha_trunc_page(curAddr);
445 dmaAddr = pciToDma(curAddr);
446
447 bytesInPage = 0;
448 }
449
450 if (cmdBytesLeft <= 0)
451 panic("DMA requested data is larger than # sectors specified\n");
452
453 readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
454
455 curAddr += SectorSize;
456 bytesRead += SectorSize;
457 bytesInPage += SectorSize;
458 cmdBytesLeft -= SectorSize;
459 }
460
461 // write the last page worth read to memory
462 /** @todo Do real DMA using interfaces here */
463 if (bytesInPage != 0) {
464 memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
465 (void *)(dataBuffer + (bytesRead - bytesInPage)),
466 bytesInPage);
467 }
468
469 #if 0
470 Addr dmaAddr = ctrl->tsunami->pchip->
471 translatePciToDma(curPrd.getBaseAddr());
472
473 memcpy(physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
474 (void *)dataBuffer, curPrd.getByteCount());
475 #endif
476
477 // check for the EOT
478 if (curPrd.getEOT()) {
479 assert(cmdBytesLeft == 0);
480 dmaState = Dma_Idle;
481 updateState(ACT_DMA_DONE);
482 } else {
483 doDmaTransfer();
484 }
485 }
486
487 ////
488 // Disk utility routines
489 ///
490
491 void
492 IdeDisk::readDisk(uint32_t sector, uint8_t *data)
493 {
494 uint32_t bytesRead = image->read(data, sector);
495
496 if (bytesRead != SectorSize)
497 panic("Can't read from %s. Only %d of %d read. errno=%d\n",
498 name(), bytesRead, SectorSize, errno);
499 }
500
501 void
502 IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
503 {
504 uint32_t bytesWritten = image->write(data, sector);
505
506 if (bytesWritten != SectorSize)
507 panic("Can't write to %s. Only %d of %d written. errno=%d\n",
508 name(), bytesWritten, SectorSize, errno);
509 }
510
511 ////
512 // Setup and handle commands
513 ////
514
515 void
516 IdeDisk::startDma(const uint32_t &prdTableBase)
517 {
518 if (dmaState != Dma_Start)
519 panic("Inconsistent DMA state, should be in Dma_Start!\n");
520
521 if (devState != Transfer_Data_Dma)
522 panic("Inconsistent device state for DMA start!\n");
523
524 curPrdAddr = ctrl->tsunami->pchip->translatePciToDma(prdTableBase);
525
526 dmaState = Dma_Transfer;
527
528 // schedule dma transfer (doDmaTransfer)
529 dmaTransferEvent.schedule(curTick + 1);
530 }
531
532 void
533 IdeDisk::abortDma()
534 {
535 if (dmaState == Dma_Idle)
536 panic("Inconsistent DMA state, should be in Dma_Start or Dma_Transfer!\n");
537
538 if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
539 panic("Inconsistent device state, should be in Transfer or Prepare!\n");
540
541 updateState(ACT_CMD_ERROR);
542 }
543
544 void
545 IdeDisk::startCommand()
546 {
547 DevAction_t action = ACT_NONE;
548 uint32_t size = 0;
549 dmaRead = false;
550
551 // copy the command to the shadow
552 curCommand = cmdReg.command;
553
554 // Decode commands
555 switch (cmdReg.command) {
556 // Supported non-data commands
557 case WIN_READ_NATIVE_MAX:
558 size = image->size() - 1;
559 cmdReg.sec_num = (size & 0xff);
560 cmdReg.cyl_low = ((size & 0xff00) >> 8);
561 cmdReg.cyl_high = ((size & 0xff0000) >> 16);
562 cmdReg.head = ((size & 0xf000000) >> 24);
563
564 devState = Command_Execution;
565 action = ACT_CMD_COMPLETE;
566 break;
567
568 case WIN_RECAL:
569 case WIN_SPECIFY:
570 case WIN_FLUSH_CACHE:
571 case WIN_VERIFY:
572 case WIN_SEEK:
573 case WIN_SETFEATURES:
574 case WIN_SETMULT:
575 devState = Command_Execution;
576 action = ACT_CMD_COMPLETE;
577 break;
578
579 // Supported PIO data-in commands
580 case WIN_IDENTIFY:
581 cmdBytesLeft = drqBytesLeft = sizeof(struct hd_driveid);
582 devState = Prepare_Data_In;
583 action = ACT_DATA_READY;
584 break;
585
586 case WIN_MULTREAD:
587 case WIN_READ:
588 if (!(cmdReg.drive & DRIVE_LBA_BIT))
589 panic("Attempt to perform CHS access, only supports LBA\n");
590
591 if (cmdReg.sec_count == 0)
592 cmdBytesLeft = (256 * SectorSize);
593 else
594 cmdBytesLeft = (cmdReg.sec_count * SectorSize);
595
596 drqBytesLeft = SectorSize;
597 curSector = getLBABase();
598
599 devState = Prepare_Data_In;
600 action = ACT_DATA_READY;
601 break;
602
603 // Supported PIO data-out commands
604 case WIN_MULTWRITE:
605 case WIN_WRITE:
606 if (!(cmdReg.drive & DRIVE_LBA_BIT))
607 panic("Attempt to perform CHS access, only supports LBA\n");
608
609 if (cmdReg.sec_count == 0)
610 cmdBytesLeft = (256 * SectorSize);
611 else
612 cmdBytesLeft = (cmdReg.sec_count * SectorSize);
613
614 drqBytesLeft = SectorSize;
615 curSector = getLBABase();
616
617 devState = Prepare_Data_Out;
618 action = ACT_DATA_READY;
619 break;
620
621 // Supported DMA commands
622 case WIN_WRITEDMA:
623 dmaRead = true; // a write to the disk is a DMA read from memory
624 case WIN_READDMA:
625 if (!(cmdReg.drive & DRIVE_LBA_BIT))
626 panic("Attempt to perform CHS access, only supports LBA\n");
627
628 if (cmdReg.sec_count == 0)
629 cmdBytesLeft = (256 * SectorSize);
630 else
631 cmdBytesLeft = (cmdReg.sec_count * SectorSize);
632
633 drqBytesLeft = SectorSize;
634 curSector = getLBABase();
635
636 devState = Prepare_Data_Dma;
637 action = ACT_DMA_READY;
638 break;
639
640 default:
641 panic("Unsupported ATA command: %#x\n", cmdReg.command);
642 }
643
644 if (action != ACT_NONE) {
645 // set the BSY bit
646 cmdReg.status |= STATUS_BSY_BIT;
647 // clear the DRQ bit
648 cmdReg.status &= ~STATUS_DRQ_BIT;
649
650 updateState(action);
651 }
652 }
653
654 ////
655 // Handle setting and clearing interrupts
656 ////
657
658 void
659 IdeDisk::intrPost()
660 {
661 if (intrPending)
662 panic("Attempt to post an interrupt with one pending\n");
663
664 intrPending = true;
665
666 // talk to controller to set interrupt
667 if (ctrl)
668 ctrl->intrPost();
669 }
670
671 void
672 IdeDisk::intrClear()
673 {
674 if (!intrPending)
675 panic("Attempt to clear a non-pending interrupt\n");
676
677 intrPending = false;
678
679 // talk to controller to clear interrupt
680 if (ctrl)
681 ctrl->intrClear();
682 }
683
684 ////
685 // Manage the device internal state machine
686 ////
687
688 void
689 IdeDisk::updateState(DevAction_t action)
690 {
691 switch (devState) {
692 case Device_Idle_S:
693 if (!isDEVSelect())
694 devState = Device_Idle_NS;
695 else if (action == ACT_CMD_WRITE)
696 startCommand();
697
698 break;
699
700 case Device_Idle_SI:
701 if (!isDEVSelect()) {
702 devState = Device_Idle_NS;
703 intrClear();
704 } else if (action == ACT_STAT_READ || isIENSet()) {
705 devState = Device_Idle_S;
706 intrClear();
707 } else if (action == ACT_CMD_WRITE) {
708 intrClear();
709 startCommand();
710 }
711
712 break;
713
714 case Device_Idle_NS:
715 if (isDEVSelect()) {
716 if (!isIENSet() && intrPending) {
717 devState = Device_Idle_SI;
718 intrPost();
719 }
720 if (isIENSet() || !intrPending) {
721 devState = Device_Idle_S;
722 }
723 }
724 break;
725
726 case Command_Execution:
727 if (action == ACT_CMD_COMPLETE) {
728 // clear the BSY bit
729 setComplete();
730
731 if (!isIENSet()) {
732 devState = Device_Idle_SI;
733 intrPost();
734 } else {
735 devState = Device_Idle_S;
736 }
737 }
738 break;
739
740 case Prepare_Data_In:
741 if (action == ACT_CMD_ERROR) {
742 // clear the BSY bit
743 setComplete();
744
745 if (!isIENSet()) {
746 devState = Device_Idle_SI;
747 intrPost();
748 } else {
749 devState = Device_Idle_S;
750 }
751 } else if (action == ACT_DATA_READY) {
752 // clear the BSY bit
753 cmdReg.status &= ~STATUS_BSY_BIT;
754 // set the DRQ bit
755 cmdReg.status |= STATUS_DRQ_BIT;
756
757 // put the first two bytes into the data register
758 memcpy((void *)&cmdReg.data0, (void *)dataBuffer,
759 sizeof(uint16_t));
760
761 // copy the data into the data buffer
762 if (curCommand == WIN_IDENTIFY)
763 memcpy((void *)dataBuffer, (void *)&driveID,
764 sizeof(struct hd_driveid));
765 else
766 readDisk(curSector++, dataBuffer);
767
768 if (!isIENSet()) {
769 devState = Data_Ready_INTRQ_In;
770 intrPost();
771 } else {
772 devState = Transfer_Data_In;
773 }
774 }
775 break;
776
777 case Data_Ready_INTRQ_In:
778 if (action == ACT_STAT_READ) {
779 devState = Transfer_Data_In;
780 intrClear();
781 }
782 break;
783
784 case Transfer_Data_In:
785 if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
786 if (action == ACT_DATA_READ_BYTE) {
787 panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
788 } else {
789 drqBytesLeft -= 2;
790 cmdBytesLeft -= 2;
791
792 // copy next short into data registers
793 memcpy((void *)&cmdReg.data0,
794 (void *)&dataBuffer[SectorSize - drqBytesLeft],
795 sizeof(uint16_t));
796 }
797
798 if (drqBytesLeft == 0) {
799 if (cmdBytesLeft == 0) {
800 // Clear the BSY bit
801 setComplete();
802 devState = Device_Idle_S;
803 } else {
804 devState = Prepare_Data_In;
805 cmdReg.status |= STATUS_BSY_BIT;
806 }
807 }
808 }
809 break;
810
811 case Prepare_Data_Out:
812 if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
813 // clear the BSY bit
814 setComplete();
815
816 if (!isIENSet()) {
817 devState = Device_Idle_SI;
818 intrPost();
819 } else {
820 devState = Device_Idle_S;
821 }
822 } else if (cmdBytesLeft != 0) {
823 // clear the BSY bit
824 cmdReg.status &= ~STATUS_BSY_BIT;
825 // set the DRQ bit
826 cmdReg.status |= STATUS_DRQ_BIT;
827
828 // clear the data buffer to get it ready for writes
829 memset(dataBuffer, 0, MAX_DMA_SIZE);
830
831 if (!isIENSet()) {
832 devState = Data_Ready_INTRQ_Out;
833 intrPost();
834 } else {
835 devState = Transfer_Data_Out;
836 }
837 }
838 break;
839
840 case Data_Ready_INTRQ_Out:
841 if (action == ACT_STAT_READ) {
842 devState = Transfer_Data_Out;
843 intrClear();
844 }
845 break;
846
847 case Transfer_Data_Out:
848 if (action == ACT_DATA_WRITE_BYTE ||
849 action == ACT_DATA_WRITE_SHORT) {
850
851 if (action == ACT_DATA_READ_BYTE) {
852 panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
853 } else {
854 // copy the latest short into the data buffer
855 memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
856 (void *)&cmdReg.data0,
857 sizeof(uint16_t));
858
859 drqBytesLeft -= 2;
860 cmdBytesLeft -= 2;
861 }
862
863 if (drqBytesLeft == 0) {
864 // copy the block to the disk
865 writeDisk(curSector++, dataBuffer);
866
867 // set the BSY bit
868 cmdReg.status |= STATUS_BSY_BIT;
869 // clear the DRQ bit
870 cmdReg.status &= ~STATUS_DRQ_BIT;
871
872 devState = Prepare_Data_Out;
873 }
874 }
875 break;
876
877 case Prepare_Data_Dma:
878 if (action == ACT_CMD_ERROR) {
879 // clear the BSY bit
880 setComplete();
881
882 if (!isIENSet()) {
883 devState = Device_Idle_SI;
884 intrPost();
885 } else {
886 devState = Device_Idle_S;
887 }
888 } else if (action == ACT_DMA_READY) {
889 // clear the BSY bit
890 cmdReg.status &= ~STATUS_BSY_BIT;
891 // set the DRQ bit
892 cmdReg.status |= STATUS_DRQ_BIT;
893
894 devState = Transfer_Data_Dma;
895
896 if (dmaState != Dma_Idle)
897 panic("Inconsistent DMA state, should be Dma_Idle\n");
898
899 dmaState = Dma_Start;
900 // wait for the write to the DMA start bit
901 }
902 break;
903
904 case Transfer_Data_Dma:
905 if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
906 // clear the BSY bit
907 setComplete();
908 // set the seek bit
909 cmdReg.status |= 0x10;
910 // clear the controller state for DMA transfer
911 ctrl->setDmaComplete(this);
912
913 if (!isIENSet()) {
914 devState = Device_Idle_SI;
915 intrPost();
916 } else {
917 devState = Device_Idle_S;
918 }
919 }
920 break;
921
922 default:
923 panic("Unknown IDE device state: %#x\n", devState);
924 }
925 }
926
927 void
928 IdeDisk::serialize(ostream &os)
929 {
930 }
931
932 void
933 IdeDisk::unserialize(Checkpoint *cp, const string &section)
934 {
935 }
936
937 #ifndef DOXYGEN_SHOULD_SKIP_THIS
938
939 BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
940
941 SimObjectParam<DiskImage *> image;
942 SimObjectParam<PhysicalMemory *> physmem;
943 Param<int> driveID;
944 Param<int> disk_delay;
945
946 END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
947
948 BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
949
950 INIT_PARAM(image, "Disk image"),
951 INIT_PARAM(physmem, "Physical memory"),
952 INIT_PARAM(driveID, "Drive ID (0=master 1=slave)"),
953 INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in milliseconds", 0)
954
955 END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
956
957
958 CREATE_SIM_OBJECT(IdeDisk)
959 {
960 return new IdeDisk(getInstanceName(), image, physmem, driveID,
961 disk_delay);
962 }
963
964 REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
965
966 #endif //DOXYGEN_SHOULD_SKIP_THIS