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