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