ide_ctrl.cc revision 10905
1/*
2 * Copyright (c) 2013 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2004-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Andrew Schultz
41 *          Ali Saidi
42 *          Miguel Serrano
43 */
44
45#include <string>
46
47#include "cpu/intr_control.hh"
48#include "debug/IdeCtrl.hh"
49#include "dev/ide_ctrl.hh"
50#include "dev/ide_disk.hh"
51#include "mem/packet.hh"
52#include "mem/packet_access.hh"
53#include "params/IdeController.hh"
54#include "sim/byteswap.hh"
55
56// clang complains about std::set being overloaded with Packet::set if
57// we open up the entire namespace std
58using std::string;
59
60// Bus master IDE registers
61enum BMIRegOffset {
62    BMICommand = 0x0,
63    BMIStatus = 0x2,
64    BMIDescTablePtr = 0x4
65};
66
67// PCI config space registers
68enum ConfRegOffset {
69    PrimaryTiming = 0x40,
70    SecondaryTiming = 0x42,
71    DeviceTiming = 0x44,
72    UDMAControl = 0x48,
73    UDMATiming = 0x4A,
74    IDEConfig = 0x54
75};
76
77static const uint16_t timeRegWithDecodeEn = 0x8000;
78
79IdeController::Channel::Channel(
80        string newName, Addr _cmdSize, Addr _ctrlSize) :
81    _name(newName),
82    cmdAddr(0), cmdSize(_cmdSize), ctrlAddr(0), ctrlSize(_ctrlSize),
83    master(NULL), slave(NULL), selected(NULL)
84{
85    memset(&bmiRegs, 0, sizeof(bmiRegs));
86    bmiRegs.status.dmaCap0 = 1;
87    bmiRegs.status.dmaCap1 = 1;
88}
89
90IdeController::Channel::~Channel()
91{
92}
93
94IdeController::IdeController(Params *p)
95    : PciDevice(p), primary(name() + ".primary", BARSize[0], BARSize[1]),
96    secondary(name() + ".secondary", BARSize[2], BARSize[3]),
97    bmiAddr(0), bmiSize(BARSize[4]),
98    primaryTiming(htole(timeRegWithDecodeEn)),
99    secondaryTiming(htole(timeRegWithDecodeEn)),
100    deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0),
101    ioEnabled(false), bmEnabled(false),
102    ioShift(p->io_shift), ctrlOffset(p->ctrl_offset)
103{
104    if (params()->disks.size() > 3)
105        panic("IDE controllers support a maximum of 4 devices attached!\n");
106
107    // Assign the disks to channels
108    int numDisks = params()->disks.size();
109    if (numDisks > 0)
110        primary.master = params()->disks[0];
111    if (numDisks > 1)
112        primary.slave = params()->disks[1];
113    if (numDisks > 2)
114        secondary.master = params()->disks[2];
115    if (numDisks > 3)
116        secondary.slave = params()->disks[3];
117
118    for (int i = 0; i < params()->disks.size(); i++) {
119        params()->disks[i]->setController(this);
120    }
121    primary.select(false);
122    secondary.select(false);
123
124    if ((BARAddrs[0] & ~BAR_IO_MASK) && (!legacyIO[0] || ioShift)) {
125        primary.cmdAddr = BARAddrs[0];  primary.cmdSize = BARSize[0];
126        primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARSize[1];
127    }
128    if ((BARAddrs[2] & ~BAR_IO_MASK) && (!legacyIO[2] || ioShift)) {
129        secondary.cmdAddr = BARAddrs[2];  secondary.cmdSize = BARSize[2];
130        secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARSize[3];
131    }
132
133    ioEnabled = (config.command & htole(PCI_CMD_IOSE));
134    bmEnabled = (config.command & htole(PCI_CMD_BME));
135}
136
137bool
138IdeController::isDiskSelected(IdeDisk *diskPtr)
139{
140    return (primary.selected == diskPtr || secondary.selected == diskPtr);
141}
142
143void
144IdeController::intrPost()
145{
146    primary.bmiRegs.status.intStatus = 1;
147    PciDevice::intrPost();
148}
149
150void
151IdeController::setDmaComplete(IdeDisk *disk)
152{
153    Channel *channel;
154    if (disk == primary.master || disk == primary.slave) {
155        channel = &primary;
156    } else if (disk == secondary.master || disk == secondary.slave) {
157        channel = &secondary;
158    } else {
159        panic("Unable to find disk based on pointer %#x\n", disk);
160    }
161
162    channel->bmiRegs.command.startStop = 0;
163    channel->bmiRegs.status.active = 0;
164    channel->bmiRegs.status.intStatus = 1;
165}
166
167Tick
168IdeController::readConfig(PacketPtr pkt)
169{
170    int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
171    if (offset < PCI_DEVICE_SPECIFIC) {
172        return PciDevice::readConfig(pkt);
173    }
174
175    switch (pkt->getSize()) {
176      case sizeof(uint8_t):
177        switch (offset) {
178          case DeviceTiming:
179            pkt->set<uint8_t>(deviceTiming);
180            break;
181          case UDMAControl:
182            pkt->set<uint8_t>(udmaControl);
183            break;
184          case PrimaryTiming + 1:
185            pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8));
186            break;
187          case SecondaryTiming + 1:
188            pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8));
189            break;
190          case IDEConfig:
191            pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0));
192            break;
193          case IDEConfig + 1:
194            pkt->set<uint8_t>(bits(htole(ideConfig), 15, 8));
195            break;
196          default:
197            panic("Invalid PCI configuration read for size 1 at offset: %#x!\n",
198                    offset);
199        }
200        DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset,
201                (uint32_t)pkt->get<uint8_t>());
202        break;
203      case sizeof(uint16_t):
204        switch (offset) {
205          case UDMAControl:
206            pkt->set<uint16_t>(udmaControl);
207            break;
208          case PrimaryTiming:
209            pkt->set<uint16_t>(primaryTiming);
210            break;
211          case SecondaryTiming:
212            pkt->set<uint16_t>(secondaryTiming);
213            break;
214          case UDMATiming:
215            pkt->set<uint16_t>(udmaTiming);
216            break;
217          case IDEConfig:
218            pkt->set<uint16_t>(ideConfig);
219            break;
220          default:
221            panic("Invalid PCI configuration read for size 2 offset: %#x!\n",
222                    offset);
223        }
224        DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset,
225                (uint32_t)pkt->get<uint16_t>());
226        break;
227      case sizeof(uint32_t):
228        switch (offset) {
229          case PrimaryTiming:
230            pkt->set<uint32_t>(primaryTiming);
231            break;
232          case IDEConfig:
233            pkt->set<uint32_t>(ideConfig);
234            break;
235          default:
236            panic("No 32bit reads implemented for this device.");
237        }
238        DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset,
239                (uint32_t)pkt->get<uint32_t>());
240        break;
241      default:
242        panic("invalid access size(?) for PCI configspace!\n");
243    }
244    pkt->makeAtomicResponse();
245    return configDelay;
246}
247
248
249Tick
250IdeController::writeConfig(PacketPtr pkt)
251{
252    int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
253    if (offset < PCI_DEVICE_SPECIFIC) {
254        PciDevice::writeConfig(pkt);
255    } else {
256        switch (pkt->getSize()) {
257          case sizeof(uint8_t):
258            switch (offset) {
259              case DeviceTiming:
260                deviceTiming = pkt->get<uint8_t>();
261                break;
262              case UDMAControl:
263                udmaControl = pkt->get<uint8_t>();
264                break;
265              case IDEConfig:
266                replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>());
267                break;
268              case IDEConfig + 1:
269                replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>());
270                break;
271              default:
272                panic("Invalid PCI configuration write "
273                        "for size 1 offset: %#x!\n", offset);
274            }
275            DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n",
276                    offset, (uint32_t)pkt->get<uint8_t>());
277            break;
278          case sizeof(uint16_t):
279            switch (offset) {
280              case UDMAControl:
281                udmaControl = pkt->get<uint16_t>();
282                break;
283              case PrimaryTiming:
284                primaryTiming = pkt->get<uint16_t>();
285                break;
286              case SecondaryTiming:
287                secondaryTiming = pkt->get<uint16_t>();
288                break;
289              case UDMATiming:
290                udmaTiming = pkt->get<uint16_t>();
291                break;
292              case IDEConfig:
293                ideConfig = pkt->get<uint16_t>();
294                break;
295              default:
296                panic("Invalid PCI configuration write "
297                        "for size 2 offset: %#x!\n",
298                        offset);
299            }
300            DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n",
301                    offset, (uint32_t)pkt->get<uint16_t>());
302            break;
303          case sizeof(uint32_t):
304            switch (offset) {
305              case PrimaryTiming:
306                primaryTiming = pkt->get<uint32_t>();
307                break;
308              case IDEConfig:
309                ideConfig = pkt->get<uint32_t>();
310                break;
311              default:
312                panic("Write of unimplemented PCI config. register: %x\n", offset);
313            }
314            break;
315          default:
316            panic("invalid access size(?) for PCI configspace!\n");
317        }
318        pkt->makeAtomicResponse();
319    }
320
321    /* Trap command register writes and enable IO/BM as appropriate as well as
322     * BARs. */
323    switch(offset) {
324      case PCI0_BASE_ADDR0:
325        if (BARAddrs[0] != 0)
326            primary.cmdAddr = BARAddrs[0];
327        break;
328
329      case PCI0_BASE_ADDR1:
330        if (BARAddrs[1] != 0)
331            primary.ctrlAddr = BARAddrs[1];
332        break;
333
334      case PCI0_BASE_ADDR2:
335        if (BARAddrs[2] != 0)
336            secondary.cmdAddr = BARAddrs[2];
337        break;
338
339      case PCI0_BASE_ADDR3:
340        if (BARAddrs[3] != 0)
341            secondary.ctrlAddr = BARAddrs[3];
342        break;
343
344      case PCI0_BASE_ADDR4:
345        if (BARAddrs[4] != 0)
346            bmiAddr = BARAddrs[4];
347        break;
348
349      case PCI_COMMAND:
350        DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command);
351        ioEnabled = (config.command & htole(PCI_CMD_IOSE));
352        bmEnabled = (config.command & htole(PCI_CMD_BME));
353        break;
354    }
355    return configDelay;
356}
357
358void
359IdeController::Channel::accessCommand(Addr offset,
360        int size, uint8_t *data, bool read)
361{
362    const Addr SelectOffset = 6;
363    const uint8_t SelectDevBit = 0x10;
364
365    if (!read && offset == SelectOffset)
366        select(*data & SelectDevBit);
367
368    if (selected == NULL) {
369        assert(size == sizeof(uint8_t));
370        *data = 0;
371    } else if (read) {
372        selected->readCommand(offset, size, data);
373    } else {
374        selected->writeCommand(offset, size, data);
375    }
376}
377
378void
379IdeController::Channel::accessControl(Addr offset,
380        int size, uint8_t *data, bool read)
381{
382    if (selected == NULL) {
383        assert(size == sizeof(uint8_t));
384        *data = 0;
385    } else if (read) {
386        selected->readControl(offset, size, data);
387    } else {
388        selected->writeControl(offset, size, data);
389    }
390}
391
392void
393IdeController::Channel::accessBMI(Addr offset,
394        int size, uint8_t *data, bool read)
395{
396    assert(offset + size <= sizeof(BMIRegs));
397    if (read) {
398        memcpy(data, (uint8_t *)&bmiRegs + offset, size);
399    } else {
400        switch (offset) {
401          case BMICommand:
402            {
403                if (size != sizeof(uint8_t))
404                    panic("Invalid BMIC write size: %x\n", size);
405
406                BMICommandReg oldVal = bmiRegs.command;
407                BMICommandReg newVal = *data;
408
409                // if a DMA transfer is in progress, R/W control cannot change
410                if (oldVal.startStop && oldVal.rw != newVal.rw)
411                    oldVal.rw = newVal.rw;
412
413                if (oldVal.startStop != newVal.startStop) {
414                    if (selected == NULL)
415                        panic("DMA start for disk which does not exist\n");
416
417                    if (oldVal.startStop) {
418                        DPRINTF(IdeCtrl, "Stopping DMA transfer\n");
419                        bmiRegs.status.active = 0;
420
421                        selected->abortDma();
422                    } else {
423                        DPRINTF(IdeCtrl, "Starting DMA transfer\n");
424                        bmiRegs.status.active = 1;
425
426                        selected->startDma(letoh(bmiRegs.bmidtp));
427                    }
428                }
429
430                bmiRegs.command = newVal;
431            }
432            break;
433          case BMIStatus:
434            {
435                if (size != sizeof(uint8_t))
436                    panic("Invalid BMIS write size: %x\n", size);
437
438                BMIStatusReg oldVal = bmiRegs.status;
439                BMIStatusReg newVal = *data;
440
441                // the BMIDEA bit is read only
442                newVal.active = oldVal.active;
443
444                // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each
445                if ((oldVal.intStatus == 1) && (newVal.intStatus == 1)) {
446                    newVal.intStatus = 0; // clear the interrupt?
447                } else {
448                    // Assigning two bitunion fields to each other does not
449                    // work as intended, so we need to use this temporary variable
450                    // to get around the bug.
451                    uint8_t tmp = oldVal.intStatus;
452                    newVal.intStatus = tmp;
453                }
454                if ((oldVal.dmaError == 1) && (newVal.dmaError == 1)) {
455                    newVal.dmaError = 0;
456                } else {
457                    uint8_t tmp = oldVal.dmaError;
458                    newVal.dmaError = tmp;
459                }
460
461                bmiRegs.status = newVal;
462            }
463            break;
464          case BMIDescTablePtr:
465            if (size != sizeof(uint32_t))
466                panic("Invalid BMIDTP write size: %x\n", size);
467            bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3);
468            break;
469          default:
470            if (size != sizeof(uint8_t) && size != sizeof(uint16_t) &&
471                    size != sizeof(uint32_t))
472                panic("IDE controller write of invalid write size: %x\n", size);
473            memcpy((uint8_t *)&bmiRegs + offset, data, size);
474        }
475    }
476}
477
478void
479IdeController::dispatchAccess(PacketPtr pkt, bool read)
480{
481    if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4)
482         panic("Bad IDE read size: %d\n", pkt->getSize());
483
484    if (!ioEnabled) {
485        pkt->makeAtomicResponse();
486        DPRINTF(IdeCtrl, "io not enabled\n");
487        return;
488    }
489
490    Addr addr = pkt->getAddr();
491    int size = pkt->getSize();
492    uint8_t *dataPtr = pkt->getPtr<uint8_t>();
493
494    if (addr >= primary.cmdAddr &&
495            addr < (primary.cmdAddr + primary.cmdSize)) {
496        addr -= primary.cmdAddr;
497        // linux may have shifted the address by ioShift,
498        // here we shift it back, similarly for ctrlOffset.
499        addr >>= ioShift;
500        primary.accessCommand(addr, size, dataPtr, read);
501    } else if (addr >= primary.ctrlAddr &&
502               addr < (primary.ctrlAddr + primary.ctrlSize)) {
503        addr -= primary.ctrlAddr;
504        addr += ctrlOffset;
505        primary.accessControl(addr, size, dataPtr, read);
506    } else if (addr >= secondary.cmdAddr &&
507               addr < (secondary.cmdAddr + secondary.cmdSize)) {
508        addr -= secondary.cmdAddr;
509        secondary.accessCommand(addr, size, dataPtr, read);
510    } else if (addr >= secondary.ctrlAddr &&
511               addr < (secondary.ctrlAddr + secondary.ctrlSize)) {
512        addr -= secondary.ctrlAddr;
513        secondary.accessControl(addr, size, dataPtr, read);
514    } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) {
515        if (!read && !bmEnabled)
516            return;
517        addr -= bmiAddr;
518        if (addr < sizeof(Channel::BMIRegs)) {
519            primary.accessBMI(addr, size, dataPtr, read);
520        } else {
521            addr -= sizeof(Channel::BMIRegs);
522            secondary.accessBMI(addr, size, dataPtr, read);
523        }
524    } else {
525        panic("IDE controller access to invalid address: %#x\n", addr);
526    }
527
528#ifndef NDEBUG
529    uint32_t data;
530    if (pkt->getSize() == 1)
531        data = pkt->get<uint8_t>();
532    else if (pkt->getSize() == 2)
533        data = pkt->get<uint16_t>();
534    else
535        data = pkt->get<uint32_t>();
536    DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n",
537            read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data);
538#endif
539
540    pkt->makeAtomicResponse();
541}
542
543Tick
544IdeController::read(PacketPtr pkt)
545{
546    dispatchAccess(pkt, true);
547    return pioDelay;
548}
549
550Tick
551IdeController::write(PacketPtr pkt)
552{
553    dispatchAccess(pkt, false);
554    return pioDelay;
555}
556
557void
558IdeController::serialize(CheckpointOut &cp) const
559{
560    // Serialize the PciDevice base class
561    PciDevice::serialize(cp);
562
563    // Serialize channels
564    primary.serialize("primary", cp);
565    secondary.serialize("secondary", cp);
566
567    // Serialize config registers
568    SERIALIZE_SCALAR(primaryTiming);
569    SERIALIZE_SCALAR(secondaryTiming);
570    SERIALIZE_SCALAR(deviceTiming);
571    SERIALIZE_SCALAR(udmaControl);
572    SERIALIZE_SCALAR(udmaTiming);
573    SERIALIZE_SCALAR(ideConfig);
574
575    // Serialize internal state
576    SERIALIZE_SCALAR(ioEnabled);
577    SERIALIZE_SCALAR(bmEnabled);
578    SERIALIZE_SCALAR(bmiAddr);
579    SERIALIZE_SCALAR(bmiSize);
580}
581
582void
583IdeController::Channel::serialize(const std::string &base,
584                                  CheckpointOut &cp) const
585{
586    paramOut(cp, base + ".cmdAddr", cmdAddr);
587    paramOut(cp, base + ".cmdSize", cmdSize);
588    paramOut(cp, base + ".ctrlAddr", ctrlAddr);
589    paramOut(cp, base + ".ctrlSize", ctrlSize);
590    uint8_t command = bmiRegs.command;
591    paramOut(cp, base + ".bmiRegs.command", command);
592    paramOut(cp, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
593    uint8_t status = bmiRegs.status;
594    paramOut(cp, base + ".bmiRegs.status", status);
595    paramOut(cp, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
596    paramOut(cp, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
597    paramOut(cp, base + ".selectBit", selectBit);
598}
599
600void
601IdeController::unserialize(CheckpointIn &cp)
602{
603    // Unserialize the PciDevice base class
604    PciDevice::unserialize(cp);
605
606    // Unserialize channels
607    primary.unserialize("primary", cp);
608    secondary.unserialize("secondary", cp);
609
610    // Unserialize config registers
611    UNSERIALIZE_SCALAR(primaryTiming);
612    UNSERIALIZE_SCALAR(secondaryTiming);
613    UNSERIALIZE_SCALAR(deviceTiming);
614    UNSERIALIZE_SCALAR(udmaControl);
615    UNSERIALIZE_SCALAR(udmaTiming);
616    UNSERIALIZE_SCALAR(ideConfig);
617
618    // Unserialize internal state
619    UNSERIALIZE_SCALAR(ioEnabled);
620    UNSERIALIZE_SCALAR(bmEnabled);
621    UNSERIALIZE_SCALAR(bmiAddr);
622    UNSERIALIZE_SCALAR(bmiSize);
623}
624
625void
626IdeController::Channel::unserialize(const std::string &base, CheckpointIn &cp)
627{
628    paramIn(cp, base + ".cmdAddr", cmdAddr);
629    paramIn(cp, base + ".cmdSize", cmdSize);
630    paramIn(cp, base + ".ctrlAddr", ctrlAddr);
631    paramIn(cp, base + ".ctrlSize", ctrlSize);
632    uint8_t command;
633    paramIn(cp, base +".bmiRegs.command", command);
634    bmiRegs.command = command;
635    paramIn(cp, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
636    uint8_t status;
637    paramIn(cp, base + ".bmiRegs.status", status);
638    bmiRegs.status = status;
639    paramIn(cp, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
640    paramIn(cp, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
641    paramIn(cp, base + ".selectBit", selectBit);
642    select(selectBit);
643}
644
645IdeController *
646IdeControllerParams::create()
647{
648    return new IdeController(this);
649}
650