ide_ctrl.cc revision 9956
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    pkt->allocate();
176
177    switch (pkt->getSize()) {
178      case sizeof(uint8_t):
179        switch (offset) {
180          case DeviceTiming:
181            pkt->set<uint8_t>(deviceTiming);
182            break;
183          case UDMAControl:
184            pkt->set<uint8_t>(udmaControl);
185            break;
186          case PrimaryTiming + 1:
187            pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8));
188            break;
189          case SecondaryTiming + 1:
190            pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8));
191            break;
192          case IDEConfig:
193            pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0));
194            break;
195          case IDEConfig + 1:
196            pkt->set<uint8_t>(bits(htole(ideConfig), 15, 8));
197            break;
198          default:
199            panic("Invalid PCI configuration read for size 1 at offset: %#x!\n",
200                    offset);
201        }
202        DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset,
203                (uint32_t)pkt->get<uint8_t>());
204        break;
205      case sizeof(uint16_t):
206        switch (offset) {
207          case PrimaryTiming:
208            pkt->set<uint16_t>(primaryTiming);
209            break;
210          case SecondaryTiming:
211            pkt->set<uint16_t>(secondaryTiming);
212            break;
213          case UDMATiming:
214            pkt->set<uint16_t>(udmaTiming);
215            break;
216          case IDEConfig:
217            pkt->set<uint16_t>(ideConfig);
218            break;
219          default:
220            panic("Invalid PCI configuration read for size 2 offset: %#x!\n",
221                    offset);
222        }
223        DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset,
224                (uint32_t)pkt->get<uint16_t>());
225        break;
226      case sizeof(uint32_t):
227        if (offset == IDEConfig)
228            pkt->set<uint32_t>(ideConfig);
229        else
230            panic("No 32bit reads implemented for this device.");
231        DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset,
232                (uint32_t)pkt->get<uint32_t>());
233        break;
234      default:
235        panic("invalid access size(?) for PCI configspace!\n");
236    }
237    pkt->makeAtomicResponse();
238    return configDelay;
239}
240
241
242Tick
243IdeController::writeConfig(PacketPtr pkt)
244{
245    int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
246    if (offset < PCI_DEVICE_SPECIFIC) {
247        PciDevice::writeConfig(pkt);
248    } else {
249        switch (pkt->getSize()) {
250          case sizeof(uint8_t):
251            switch (offset) {
252              case DeviceTiming:
253                deviceTiming = pkt->get<uint8_t>();
254                break;
255              case UDMAControl:
256                udmaControl = pkt->get<uint8_t>();
257                break;
258              case IDEConfig:
259                replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>());
260                break;
261              case IDEConfig + 1:
262                replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>());
263                break;
264              default:
265                panic("Invalid PCI configuration write "
266                        "for size 1 offset: %#x!\n", offset);
267            }
268            DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n",
269                    offset, (uint32_t)pkt->get<uint8_t>());
270            break;
271          case sizeof(uint16_t):
272            switch (offset) {
273              case PrimaryTiming:
274                primaryTiming = pkt->get<uint16_t>();
275                break;
276              case SecondaryTiming:
277                secondaryTiming = pkt->get<uint16_t>();
278                break;
279              case UDMATiming:
280                udmaTiming = pkt->get<uint16_t>();
281                break;
282              case IDEConfig:
283                ideConfig = pkt->get<uint16_t>();
284                break;
285              default:
286                panic("Invalid PCI configuration write "
287                        "for size 2 offset: %#x!\n",
288                        offset);
289            }
290            DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n",
291                    offset, (uint32_t)pkt->get<uint16_t>());
292            break;
293          case sizeof(uint32_t):
294            if (offset == IDEConfig)
295                ideConfig = pkt->get<uint32_t>();
296            else
297                panic("Write of unimplemented PCI config. register: %x\n", offset);
298            break;
299          default:
300            panic("invalid access size(?) for PCI configspace!\n");
301        }
302        pkt->makeAtomicResponse();
303    }
304
305    /* Trap command register writes and enable IO/BM as appropriate as well as
306     * BARs. */
307    switch(offset) {
308      case PCI0_BASE_ADDR0:
309        if (BARAddrs[0] != 0)
310            primary.cmdAddr = BARAddrs[0];
311        break;
312
313      case PCI0_BASE_ADDR1:
314        if (BARAddrs[1] != 0)
315            primary.ctrlAddr = BARAddrs[1];
316        break;
317
318      case PCI0_BASE_ADDR2:
319        if (BARAddrs[2] != 0)
320            secondary.cmdAddr = BARAddrs[2];
321        break;
322
323      case PCI0_BASE_ADDR3:
324        if (BARAddrs[3] != 0)
325            secondary.ctrlAddr = BARAddrs[3];
326        break;
327
328      case PCI0_BASE_ADDR4:
329        if (BARAddrs[4] != 0)
330            bmiAddr = BARAddrs[4];
331        break;
332
333      case PCI_COMMAND:
334        DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command);
335        ioEnabled = (config.command & htole(PCI_CMD_IOSE));
336        bmEnabled = (config.command & htole(PCI_CMD_BME));
337        break;
338    }
339    return configDelay;
340}
341
342void
343IdeController::Channel::accessCommand(Addr offset,
344        int size, uint8_t *data, bool read)
345{
346    const Addr SelectOffset = 6;
347    const uint8_t SelectDevBit = 0x10;
348
349    if (!read && offset == SelectOffset)
350        select(*data & SelectDevBit);
351
352    if (selected == NULL) {
353        assert(size == sizeof(uint8_t));
354        *data = 0;
355    } else if (read) {
356        selected->readCommand(offset, size, data);
357    } else {
358        selected->writeCommand(offset, size, data);
359    }
360}
361
362void
363IdeController::Channel::accessControl(Addr offset,
364        int size, uint8_t *data, bool read)
365{
366    if (selected == NULL) {
367        assert(size == sizeof(uint8_t));
368        *data = 0;
369    } else if (read) {
370        selected->readControl(offset, size, data);
371    } else {
372        selected->writeControl(offset, size, data);
373    }
374}
375
376void
377IdeController::Channel::accessBMI(Addr offset,
378        int size, uint8_t *data, bool read)
379{
380    assert(offset + size <= sizeof(BMIRegs));
381    if (read) {
382        memcpy(data, (uint8_t *)&bmiRegs + offset, size);
383    } else {
384        switch (offset) {
385          case BMICommand:
386            {
387                if (size != sizeof(uint8_t))
388                    panic("Invalid BMIC write size: %x\n", size);
389
390                BMICommandReg oldVal = bmiRegs.command;
391                BMICommandReg newVal = *data;
392
393                // if a DMA transfer is in progress, R/W control cannot change
394                if (oldVal.startStop && oldVal.rw != newVal.rw)
395                    oldVal.rw = newVal.rw;
396
397                if (oldVal.startStop != newVal.startStop) {
398                    if (selected == NULL)
399                        panic("DMA start for disk which does not exist\n");
400
401                    if (oldVal.startStop) {
402                        DPRINTF(IdeCtrl, "Stopping DMA transfer\n");
403                        bmiRegs.status.active = 0;
404
405                        selected->abortDma();
406                    } else {
407                        DPRINTF(IdeCtrl, "Starting DMA transfer\n");
408                        bmiRegs.status.active = 1;
409
410                        selected->startDma(letoh(bmiRegs.bmidtp));
411                    }
412                }
413
414                bmiRegs.command = newVal;
415            }
416            break;
417          case BMIStatus:
418            {
419                if (size != sizeof(uint8_t))
420                    panic("Invalid BMIS write size: %x\n", size);
421
422                BMIStatusReg oldVal = bmiRegs.status;
423                BMIStatusReg newVal = *data;
424
425                // the BMIDEA bit is read only
426                newVal.active = oldVal.active;
427
428                // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each
429                if ((oldVal.intStatus == 1) && (newVal.intStatus == 1)) {
430                    newVal.intStatus = 0; // clear the interrupt?
431                } else {
432                    // Assigning two bitunion fields to each other does not
433                    // work as intended, so we need to use this temporary variable
434                    // to get around the bug.
435                    uint8_t tmp = oldVal.intStatus;
436                    newVal.intStatus = tmp;
437                }
438                if ((oldVal.dmaError == 1) && (newVal.dmaError == 1)) {
439                    newVal.dmaError = 0;
440                } else {
441                    uint8_t tmp = oldVal.dmaError;
442                    newVal.dmaError = tmp;
443                }
444
445                bmiRegs.status = newVal;
446            }
447            break;
448          case BMIDescTablePtr:
449            if (size != sizeof(uint32_t))
450                panic("Invalid BMIDTP write size: %x\n", size);
451            bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3);
452            break;
453          default:
454            if (size != sizeof(uint8_t) && size != sizeof(uint16_t) &&
455                    size != sizeof(uint32_t))
456                panic("IDE controller write of invalid write size: %x\n", size);
457            memcpy((uint8_t *)&bmiRegs + offset, data, size);
458        }
459    }
460}
461
462void
463IdeController::dispatchAccess(PacketPtr pkt, bool read)
464{
465    pkt->allocate();
466    if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4)
467         panic("Bad IDE read size: %d\n", pkt->getSize());
468
469    if (!ioEnabled) {
470        pkt->makeAtomicResponse();
471        DPRINTF(IdeCtrl, "io not enabled\n");
472        return;
473    }
474
475    Addr addr = pkt->getAddr();
476    int size = pkt->getSize();
477    uint8_t *dataPtr = pkt->getPtr<uint8_t>();
478
479    if (addr >= primary.cmdAddr &&
480            addr < (primary.cmdAddr + primary.cmdSize)) {
481        addr -= primary.cmdAddr;
482        // linux may have shifted the address by ioShift,
483        // here we shift it back, similarly for ctrlOffset.
484        addr >>= ioShift;
485        primary.accessCommand(addr, size, dataPtr, read);
486    } else if (addr >= primary.ctrlAddr &&
487               addr < (primary.ctrlAddr + primary.ctrlSize)) {
488        addr -= primary.ctrlAddr;
489        addr += ctrlOffset;
490        primary.accessControl(addr, size, dataPtr, read);
491    } else if (addr >= secondary.cmdAddr &&
492               addr < (secondary.cmdAddr + secondary.cmdSize)) {
493        addr -= secondary.cmdAddr;
494        secondary.accessCommand(addr, size, dataPtr, read);
495    } else if (addr >= secondary.ctrlAddr &&
496               addr < (secondary.ctrlAddr + secondary.ctrlSize)) {
497        addr -= secondary.ctrlAddr;
498        secondary.accessControl(addr, size, dataPtr, read);
499    } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) {
500        if (!read && !bmEnabled)
501            return;
502        addr -= bmiAddr;
503        if (addr < sizeof(Channel::BMIRegs)) {
504            primary.accessBMI(addr, size, dataPtr, read);
505        } else {
506            addr -= sizeof(Channel::BMIRegs);
507            secondary.accessBMI(addr, size, dataPtr, read);
508        }
509    } else {
510        panic("IDE controller access to invalid address: %#x\n", addr);
511    }
512
513#ifndef NDEBUG
514    uint32_t data;
515    if (pkt->getSize() == 1)
516        data = pkt->get<uint8_t>();
517    else if (pkt->getSize() == 2)
518        data = pkt->get<uint16_t>();
519    else
520        data = pkt->get<uint32_t>();
521    DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n",
522            read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data);
523#endif
524
525    pkt->makeAtomicResponse();
526}
527
528Tick
529IdeController::read(PacketPtr pkt)
530{
531    dispatchAccess(pkt, true);
532    return pioDelay;
533}
534
535Tick
536IdeController::write(PacketPtr pkt)
537{
538    dispatchAccess(pkt, false);
539    return pioDelay;
540}
541
542void
543IdeController::serialize(std::ostream &os)
544{
545    // Serialize the PciDevice base class
546    PciDevice::serialize(os);
547
548    // Serialize channels
549    primary.serialize("primary", os);
550    secondary.serialize("secondary", os);
551
552    // Serialize config registers
553    SERIALIZE_SCALAR(primaryTiming);
554    SERIALIZE_SCALAR(secondaryTiming);
555    SERIALIZE_SCALAR(deviceTiming);
556    SERIALIZE_SCALAR(udmaControl);
557    SERIALIZE_SCALAR(udmaTiming);
558    SERIALIZE_SCALAR(ideConfig);
559
560    // Serialize internal state
561    SERIALIZE_SCALAR(ioEnabled);
562    SERIALIZE_SCALAR(bmEnabled);
563    SERIALIZE_SCALAR(bmiAddr);
564    SERIALIZE_SCALAR(bmiSize);
565}
566
567void
568IdeController::Channel::serialize(const std::string &base, std::ostream &os)
569{
570    paramOut(os, base + ".cmdAddr", cmdAddr);
571    paramOut(os, base + ".cmdSize", cmdSize);
572    paramOut(os, base + ".ctrlAddr", ctrlAddr);
573    paramOut(os, base + ".ctrlSize", ctrlSize);
574    uint8_t command = bmiRegs.command;
575    paramOut(os, base + ".bmiRegs.command", command);
576    paramOut(os, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
577    uint8_t status = bmiRegs.status;
578    paramOut(os, base + ".bmiRegs.status", status);
579    paramOut(os, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
580    paramOut(os, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
581    paramOut(os, base + ".selectBit", selectBit);
582}
583
584void
585IdeController::unserialize(Checkpoint *cp, const std::string &section)
586{
587    // Unserialize the PciDevice base class
588    PciDevice::unserialize(cp, section);
589
590    // Unserialize channels
591    primary.unserialize("primary", cp, section);
592    secondary.unserialize("secondary", cp, section);
593
594    // Unserialize config registers
595    UNSERIALIZE_SCALAR(primaryTiming);
596    UNSERIALIZE_SCALAR(secondaryTiming);
597    UNSERIALIZE_SCALAR(deviceTiming);
598    UNSERIALIZE_SCALAR(udmaControl);
599    UNSERIALIZE_SCALAR(udmaTiming);
600    UNSERIALIZE_SCALAR(ideConfig);
601
602    // Unserialize internal state
603    UNSERIALIZE_SCALAR(ioEnabled);
604    UNSERIALIZE_SCALAR(bmEnabled);
605    UNSERIALIZE_SCALAR(bmiAddr);
606    UNSERIALIZE_SCALAR(bmiSize);
607}
608
609void
610IdeController::Channel::unserialize(const std::string &base, Checkpoint *cp,
611    const std::string &section)
612{
613    paramIn(cp, section, base + ".cmdAddr", cmdAddr);
614    paramIn(cp, section, base + ".cmdSize", cmdSize);
615    paramIn(cp, section, base + ".ctrlAddr", ctrlAddr);
616    paramIn(cp, section, base + ".ctrlSize", ctrlSize);
617    uint8_t command;
618    paramIn(cp, section, base +".bmiRegs.command", command);
619    bmiRegs.command = command;
620    paramIn(cp, section, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
621    uint8_t status;
622    paramIn(cp, section, base + ".bmiRegs.status", status);
623    bmiRegs.status = status;
624    paramIn(cp, section, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
625    paramIn(cp, section, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
626    paramIn(cp, section, base + ".selectBit", selectBit);
627    select(selectBit);
628}
629
630IdeController *
631IdeControllerParams::create()
632{
633    return new IdeController(this);
634}
635