ide_ctrl.cc revision 8198
1/*
2 * Copyright (c) 2004-2005 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 * Authors: Andrew Schultz
29 *          Ali Saidi
30 *          Miguel Serrano
31 */
32
33#include <string>
34
35#include "base/trace.hh"
36#include "cpu/intr_control.hh"
37#include "dev/ide_ctrl.hh"
38#include "dev/ide_disk.hh"
39#include "mem/packet.hh"
40#include "mem/packet_access.hh"
41#include "params/IdeController.hh"
42#include "sim/byteswap.hh"
43
44using namespace std;
45
46// Bus master IDE registers
47enum BMIRegOffset {
48    BMICommand = 0x0,
49    BMIStatus = 0x2,
50    BMIDescTablePtr = 0x4
51};
52
53// PCI config space registers
54enum ConfRegOffset {
55    PrimaryTiming = 0x40,
56    SecondaryTiming = 0x42,
57    DeviceTiming = 0x44,
58    UDMAControl = 0x48,
59    UDMATiming = 0x4A,
60    IDEConfig = 0x54
61};
62
63static const uint16_t timeRegWithDecodeEn = 0x8000;
64
65IdeController::Channel::Channel(
66        string newName, Addr _cmdSize, Addr _ctrlSize) :
67    _name(newName),
68    cmdAddr(0), cmdSize(_cmdSize), ctrlAddr(0), ctrlSize(_ctrlSize),
69    master(NULL), slave(NULL), selected(NULL)
70{
71    memset(&bmiRegs, 0, sizeof(bmiRegs));
72    bmiRegs.status.dmaCap0 = 1;
73    bmiRegs.status.dmaCap1 = 1;
74}
75
76IdeController::Channel::~Channel()
77{
78}
79
80IdeController::IdeController(Params *p)
81    : PciDev(p), primary(name() + ".primary", BARSize[0], BARSize[1]),
82    secondary(name() + ".secondary", BARSize[2], BARSize[3]),
83    bmiAddr(0), bmiSize(BARSize[4]),
84    primaryTiming(htole(timeRegWithDecodeEn)),
85    secondaryTiming(htole(timeRegWithDecodeEn)),
86    deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0),
87    ioEnabled(false), bmEnabled(false),
88    ioShift(p->io_shift), ctrlOffset(p->ctrl_offset)
89{
90    if (params()->disks.size() > 3)
91        panic("IDE controllers support a maximum of 4 devices attached!\n");
92
93    // Assign the disks to channels
94    int numDisks = params()->disks.size();
95    if (numDisks > 0)
96        primary.master = params()->disks[0];
97    if (numDisks > 1)
98        primary.slave = params()->disks[1];
99    if (numDisks > 2)
100        secondary.master = params()->disks[2];
101    if (numDisks > 3)
102        secondary.slave = params()->disks[3];
103
104    for (int i = 0; i < params()->disks.size(); i++) {
105        params()->disks[i]->setController(this);
106    }
107    primary.select(false);
108    secondary.select(false);
109
110    if ((BARAddrs[0] & ~BAR_IO_MASK) && (!legacyIO[0] || ioShift)) {
111        primary.cmdAddr = BARAddrs[0];  primary.cmdSize = BARSize[0];
112        primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARAddrs[1];
113    }
114    if ((BARAddrs[2] & ~BAR_IO_MASK) && (!legacyIO[2] || ioShift)) {
115        secondary.cmdAddr = BARAddrs[2];  secondary.cmdSize = BARSize[2];
116        secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARAddrs[3];
117    }
118
119    ioEnabled = (config.command & htole(PCI_CMD_IOSE));
120    bmEnabled = (config.command & htole(PCI_CMD_BME));
121}
122
123bool
124IdeController::isDiskSelected(IdeDisk *diskPtr)
125{
126    return (primary.selected == diskPtr || secondary.selected == diskPtr);
127}
128
129void
130IdeController::intrPost()
131{
132    primary.bmiRegs.status.intStatus = 1;
133    PciDev::intrPost();
134}
135
136void
137IdeController::setDmaComplete(IdeDisk *disk)
138{
139    Channel *channel;
140    if (disk == primary.master || disk == primary.slave) {
141        channel = &primary;
142    } else if (disk == secondary.master || disk == secondary.slave) {
143        channel = &secondary;
144    } else {
145        panic("Unable to find disk based on pointer %#x\n", disk);
146    }
147
148    channel->bmiRegs.command.startStop = 0;
149    channel->bmiRegs.status.active = 0;
150    channel->bmiRegs.status.intStatus = 1;
151}
152
153Tick
154IdeController::readConfig(PacketPtr pkt)
155{
156    int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
157    if (offset < PCI_DEVICE_SPECIFIC) {
158        return PciDev::readConfig(pkt);
159    }
160
161    pkt->allocate();
162
163    switch (pkt->getSize()) {
164      case sizeof(uint8_t):
165        switch (offset) {
166          case DeviceTiming:
167            pkt->set<uint8_t>(deviceTiming);
168            break;
169          case UDMAControl:
170            pkt->set<uint8_t>(udmaControl);
171            break;
172          case PrimaryTiming + 1:
173            pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8));
174            break;
175          case SecondaryTiming + 1:
176            pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8));
177            break;
178          case IDEConfig:
179            pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0));
180            break;
181          case IDEConfig + 1:
182            pkt->set<uint8_t>(bits(htole(ideConfig), 15, 8));
183            break;
184          default:
185            panic("Invalid PCI configuration read for size 1 at offset: %#x!\n",
186                    offset);
187        }
188        DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset,
189                (uint32_t)pkt->get<uint8_t>());
190        break;
191      case sizeof(uint16_t):
192        switch (offset) {
193          case PrimaryTiming:
194            pkt->set<uint16_t>(primaryTiming);
195            break;
196          case SecondaryTiming:
197            pkt->set<uint16_t>(secondaryTiming);
198            break;
199          case UDMATiming:
200            pkt->set<uint16_t>(udmaTiming);
201            break;
202          case IDEConfig:
203            pkt->set<uint16_t>(ideConfig);
204            break;
205          default:
206            panic("Invalid PCI configuration read for size 2 offset: %#x!\n",
207                    offset);
208        }
209        DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset,
210                (uint32_t)pkt->get<uint16_t>());
211        break;
212      case sizeof(uint32_t):
213        panic("No 32bit reads implemented for this device.");
214        DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset,
215                (uint32_t)pkt->get<uint32_t>());
216        break;
217      default:
218        panic("invalid access size(?) for PCI configspace!\n");
219    }
220    pkt->makeAtomicResponse();
221    return configDelay;
222}
223
224
225Tick
226IdeController::writeConfig(PacketPtr pkt)
227{
228    int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
229    if (offset < PCI_DEVICE_SPECIFIC) {
230        PciDev::writeConfig(pkt);
231    } else {
232        switch (pkt->getSize()) {
233          case sizeof(uint8_t):
234            switch (offset) {
235              case DeviceTiming:
236                deviceTiming = pkt->get<uint8_t>();
237                break;
238              case UDMAControl:
239                udmaControl = pkt->get<uint8_t>();
240                break;
241              case IDEConfig:
242                replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>());
243                break;
244              case IDEConfig + 1:
245                replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>());
246                break;
247              default:
248                panic("Invalid PCI configuration write "
249                        "for size 1 offset: %#x!\n", offset);
250            }
251            DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n",
252                    offset, (uint32_t)pkt->get<uint8_t>());
253            break;
254          case sizeof(uint16_t):
255            switch (offset) {
256              case PrimaryTiming:
257                primaryTiming = pkt->get<uint16_t>();
258                break;
259              case SecondaryTiming:
260                secondaryTiming = pkt->get<uint16_t>();
261                break;
262              case UDMATiming:
263                udmaTiming = pkt->get<uint16_t>();
264                break;
265              case IDEConfig:
266                ideConfig = pkt->get<uint16_t>();
267                break;
268              default:
269                panic("Invalid PCI configuration write "
270                        "for size 2 offset: %#x!\n",
271                        offset);
272            }
273            DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n",
274                    offset, (uint32_t)pkt->get<uint16_t>());
275            break;
276          case sizeof(uint32_t):
277            panic("Write of unimplemented PCI config. register: %x\n", offset);
278            break;
279          default:
280            panic("invalid access size(?) for PCI configspace!\n");
281        }
282        pkt->makeAtomicResponse();
283    }
284
285    /* Trap command register writes and enable IO/BM as appropriate as well as
286     * BARs. */
287    switch(offset) {
288      case PCI0_BASE_ADDR0:
289        if (BARAddrs[0] != 0)
290            primary.cmdAddr = BARAddrs[0];
291        break;
292
293      case PCI0_BASE_ADDR1:
294        if (BARAddrs[1] != 0)
295            primary.ctrlAddr = BARAddrs[1];
296        break;
297
298      case PCI0_BASE_ADDR2:
299        if (BARAddrs[2] != 0)
300            secondary.cmdAddr = BARAddrs[2];
301        break;
302
303      case PCI0_BASE_ADDR3:
304        if (BARAddrs[3] != 0)
305            secondary.ctrlAddr = BARAddrs[3];
306        break;
307
308      case PCI0_BASE_ADDR4:
309        if (BARAddrs[4] != 0)
310            bmiAddr = BARAddrs[4];
311        break;
312
313      case PCI_COMMAND:
314        ioEnabled = (config.command & htole(PCI_CMD_IOSE));
315        bmEnabled = (config.command & htole(PCI_CMD_BME));
316        break;
317    }
318    return configDelay;
319}
320
321void
322IdeController::Channel::accessCommand(Addr offset,
323        int size, uint8_t *data, bool read)
324{
325    const Addr SelectOffset = 6;
326    const uint8_t SelectDevBit = 0x10;
327
328    if (!read && offset == SelectOffset)
329        select(*data & SelectDevBit);
330
331    if (selected == NULL) {
332        assert(size == sizeof(uint8_t));
333        *data = 0;
334    } else if (read) {
335        selected->readCommand(offset, size, data);
336    } else {
337        selected->writeCommand(offset, size, data);
338    }
339}
340
341void
342IdeController::Channel::accessControl(Addr offset,
343        int size, uint8_t *data, bool read)
344{
345    if (selected == NULL) {
346        assert(size == sizeof(uint8_t));
347        *data = 0;
348    } else if (read) {
349        selected->readControl(offset, size, data);
350    } else {
351        selected->writeControl(offset, size, data);
352    }
353}
354
355void
356IdeController::Channel::accessBMI(Addr offset,
357        int size, uint8_t *data, bool read)
358{
359    assert(offset + size <= sizeof(BMIRegs));
360    if (read) {
361        memcpy(data, (uint8_t *)&bmiRegs + offset, size);
362    } else {
363        switch (offset) {
364          case BMICommand:
365            {
366                if (size != sizeof(uint8_t))
367                    panic("Invalid BMIC write size: %x\n", size);
368
369                BMICommandReg oldVal = bmiRegs.command;
370                BMICommandReg newVal = *data;
371
372                // if a DMA transfer is in progress, R/W control cannot change
373                if (oldVal.startStop && oldVal.rw != newVal.rw)
374                    oldVal.rw = newVal.rw;
375
376                if (oldVal.startStop != newVal.startStop) {
377                    if (selected == NULL)
378                        panic("DMA start for disk which does not exist\n");
379
380                    if (oldVal.startStop) {
381                        DPRINTF(IdeCtrl, "Stopping DMA transfer\n");
382                        bmiRegs.status.active = 0;
383
384                        selected->abortDma();
385                    } else {
386                        DPRINTF(IdeCtrl, "Starting DMA transfer\n");
387                        bmiRegs.status.active = 1;
388
389                        selected->startDma(letoh(bmiRegs.bmidtp));
390                    }
391                }
392
393                bmiRegs.command = newVal;
394            }
395            break;
396          case BMIStatus:
397            {
398                if (size != sizeof(uint8_t))
399                    panic("Invalid BMIS write size: %x\n", size);
400
401                BMIStatusReg oldVal = bmiRegs.status;
402                BMIStatusReg newVal = *data;
403
404                // the BMIDEA bit is read only
405                newVal.active = oldVal.active;
406
407                // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each
408                if (oldVal.intStatus && newVal.intStatus)
409                    newVal.intStatus = 0; // clear the interrupt?
410                else
411                    newVal.intStatus = oldVal.intStatus;
412                if (oldVal.dmaError && newVal.dmaError)
413                    newVal.dmaError = 0;
414                else
415                    newVal.dmaError = oldVal.dmaError;
416
417                bmiRegs.status = newVal;
418            }
419            break;
420          case BMIDescTablePtr:
421            if (size != sizeof(uint32_t))
422                panic("Invalid BMIDTP write size: %x\n", size);
423            bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3);
424            break;
425          default:
426            if (size != sizeof(uint8_t) && size != sizeof(uint16_t) &&
427                    size != sizeof(uint32_t))
428                panic("IDE controller write of invalid write size: %x\n", size);
429            memcpy((uint8_t *)&bmiRegs + offset, data, size);
430        }
431    }
432}
433
434void
435IdeController::dispatchAccess(PacketPtr pkt, bool read)
436{
437    pkt->allocate();
438    if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4)
439         panic("Bad IDE read size: %d\n", pkt->getSize());
440
441    if (!ioEnabled) {
442        pkt->makeAtomicResponse();
443        DPRINTF(IdeCtrl, "io not enabled\n");
444        return;
445    }
446
447    Addr addr = pkt->getAddr();
448    int size = pkt->getSize();
449    uint8_t *dataPtr = pkt->getPtr<uint8_t>();
450
451    if (addr >= primary.cmdAddr &&
452            addr < (primary.cmdAddr + primary.cmdSize)) {
453        addr -= primary.cmdAddr;
454        // linux may have shifted the address by ioShift,
455        // here we shift it back, similarly for ctrlOffset.
456        addr >>= ioShift;
457        primary.accessCommand(addr, size, dataPtr, read);
458    } else if (addr >= primary.ctrlAddr &&
459               addr < (primary.ctrlAddr + primary.ctrlSize)) {
460        addr -= primary.ctrlAddr;
461        addr += ctrlOffset;
462        primary.accessControl(addr, size, dataPtr, read);
463    } else if (addr >= secondary.cmdAddr &&
464               addr < (secondary.cmdAddr + secondary.cmdSize)) {
465        addr -= secondary.cmdAddr;
466        secondary.accessCommand(addr, size, dataPtr, read);
467    } else if (addr >= secondary.ctrlAddr &&
468               addr < (secondary.ctrlAddr + secondary.ctrlSize)) {
469        addr -= secondary.ctrlAddr;
470        secondary.accessControl(addr, size, dataPtr, read);
471    } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) {
472        if (!read && !bmEnabled)
473            return;
474        addr -= bmiAddr;
475        if (addr < sizeof(Channel::BMIRegs)) {
476            primary.accessBMI(addr, size, dataPtr, read);
477        } else {
478            addr -= sizeof(Channel::BMIRegs);
479            secondary.accessBMI(addr, size, dataPtr, read);
480        }
481    } else {
482        panic("IDE controller access to invalid address: %#x\n", addr);
483    }
484
485    uint32_t data;
486    if (pkt->getSize() == 1)
487        data = pkt->get<uint8_t>();
488    else if (pkt->getSize() == 2)
489        data = pkt->get<uint16_t>();
490    else
491        data = pkt->get<uint32_t>();
492    DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n",
493            read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data);
494
495    pkt->makeAtomicResponse();
496}
497
498Tick
499IdeController::read(PacketPtr pkt)
500{
501    dispatchAccess(pkt, true);
502    return pioDelay;
503}
504
505Tick
506IdeController::write(PacketPtr pkt)
507{
508    dispatchAccess(pkt, false);
509    return pioDelay;
510}
511
512void
513IdeController::serialize(std::ostream &os)
514{
515    // Serialize the PciDev base class
516    PciDev::serialize(os);
517
518    // Serialize channels
519    primary.serialize("primary", os);
520    secondary.serialize("secondary", os);
521
522    // Serialize config registers
523    SERIALIZE_SCALAR(primaryTiming);
524    SERIALIZE_SCALAR(secondaryTiming);
525    SERIALIZE_SCALAR(deviceTiming);
526    SERIALIZE_SCALAR(udmaControl);
527    SERIALIZE_SCALAR(udmaTiming);
528    SERIALIZE_SCALAR(ideConfig);
529
530    // Serialize internal state
531    SERIALIZE_SCALAR(ioEnabled);
532    SERIALIZE_SCALAR(bmEnabled);
533    SERIALIZE_SCALAR(bmiAddr);
534    SERIALIZE_SCALAR(bmiSize);
535}
536
537void
538IdeController::Channel::serialize(const std::string &base, std::ostream &os)
539{
540    paramOut(os, base + ".cmdAddr", cmdAddr);
541    paramOut(os, base + ".cmdSize", cmdSize);
542    paramOut(os, base + ".ctrlAddr", ctrlAddr);
543    paramOut(os, base + ".ctrlSize", ctrlSize);
544    uint8_t command = bmiRegs.command;
545    paramOut(os, base + ".bmiRegs.command", command);
546    paramOut(os, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
547    uint8_t status = bmiRegs.status;
548    paramOut(os, base + ".bmiRegs.status", status);
549    paramOut(os, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
550    paramOut(os, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
551    paramOut(os, base + ".selectBit", selectBit);
552}
553
554void
555IdeController::unserialize(Checkpoint *cp, const std::string &section)
556{
557    // Unserialize the PciDev base class
558    PciDev::unserialize(cp, section);
559
560    // Unserialize channels
561    primary.unserialize("primary", cp, section);
562    secondary.unserialize("secondary", cp, section);
563
564    // Unserialize config registers
565    UNSERIALIZE_SCALAR(primaryTiming);
566    UNSERIALIZE_SCALAR(secondaryTiming);
567    UNSERIALIZE_SCALAR(deviceTiming);
568    UNSERIALIZE_SCALAR(udmaControl);
569    UNSERIALIZE_SCALAR(udmaTiming);
570    UNSERIALIZE_SCALAR(ideConfig);
571
572    // Unserialize internal state
573    UNSERIALIZE_SCALAR(ioEnabled);
574    UNSERIALIZE_SCALAR(bmEnabled);
575    UNSERIALIZE_SCALAR(bmiAddr);
576    UNSERIALIZE_SCALAR(bmiSize);
577}
578
579void
580IdeController::Channel::unserialize(const std::string &base, Checkpoint *cp,
581    const std::string &section)
582{
583    paramIn(cp, section, base + ".cmdAddr", cmdAddr);
584    paramIn(cp, section, base + ".cmdSize", cmdSize);
585    paramIn(cp, section, base + ".ctrlAddr", ctrlAddr);
586    paramIn(cp, section, base + ".ctrlSize", ctrlSize);
587    uint8_t command;
588    paramIn(cp, section, base +".bmiRegs.command", command);
589    bmiRegs.command = command;
590    paramIn(cp, section, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
591    uint8_t status;
592    paramIn(cp, section, base + ".bmiRegs.status", status);
593    bmiRegs.status = status;
594    paramIn(cp, section, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
595    paramIn(cp, section, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
596    paramIn(cp, section, base + ".selectBit", selectBit);
597    select(selectBit);
598}
599
600IdeController *
601IdeControllerParams::create()
602{
603    return new IdeController(this);
604}
605