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