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