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