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