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