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