piix_pci.c 10.5 KB
Newer Older
P
pbrook 已提交
1 2 3 4
/*
 * QEMU i440FX/PIIX3 PCI Bridge Emulation
 *
 * Copyright (c) 2006 Fabrice Bellard
5
 *
P
pbrook 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

P
pbrook 已提交
25 26 27
#include "hw.h"
#include "pc.h"
#include "pci.h"
28
#include "isa.h"
G
Gerd Hoffmann 已提交
29
#include "sysbus.h"
P
pbrook 已提交
30

P
pbrook 已提交
31 32 33 34 35
typedef uint32_t pci_addr_t;
#include "pci_host.h"

typedef PCIHostState I440FXState;

36 37
struct PCII440FXState {
    PCIDevice dev;
38 39
    target_phys_addr_t isa_page_descs[384 / 4];
    uint8_t smm_enabled;
40 41
};

P
pbrook 已提交
42 43 44 45 46 47 48 49 50 51 52 53
static void i440fx_addr_writel(void* opaque, uint32_t addr, uint32_t val)
{
    I440FXState *s = opaque;
    s->config_reg = val;
}

static uint32_t i440fx_addr_readl(void* opaque, uint32_t addr)
{
    I440FXState *s = opaque;
    return s->config_reg;
}

P
pbrook 已提交
54
static void piix3_set_irq(qemu_irq *pic, int irq_num, int level);
55 56 57 58 59 60 61 62 63 64

/* return the global irq number corresponding to a given device irq
   pin. We could also use the bus number to have a more precise
   mapping. */
static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
{
    int slot_addend;
    slot_addend = (pci_dev->devfn >> 3) - 1;
    return (irq_num + slot_addend) & 3;
}
P
pbrook 已提交
65

66
static int pci_irq_levels[4];
67

68
static void update_pam(PCII440FXState *d, uint32_t start, uint32_t end, int r)
69 70 71 72 73 74 75
{
    uint32_t addr;

    //    printf("ISA mapping %08x-0x%08x: %d\n", start, end, r);
    switch(r) {
    case 3:
        /* RAM */
76
        cpu_register_physical_memory(start, end - start,
77 78 79 80
                                     start);
        break;
    case 1:
        /* ROM (XXX: not quite correct) */
81
        cpu_register_physical_memory(start, end - start,
82 83 84 85 86 87
                                     start | IO_MEM_ROM);
        break;
    case 2:
    case 0:
        /* XXX: should distinguish read/write cases */
        for(addr = start; addr < end; addr += 4096) {
88
            cpu_register_physical_memory(addr, 4096,
89
                                         d->isa_page_descs[(addr - 0xa0000) >> 12]);
90 91 92 93
        }
        break;
    }
}
94

95
static void i440fx_update_memory_mappings(PCII440FXState *d)
96 97
{
    int i, r;
98 99
    uint32_t smram, addr;

100
    update_pam(d, 0xf0000, 0x100000, (d->dev.config[0x59] >> 4) & 3);
101
    for(i = 0; i < 12; i++) {
102
        r = (d->dev.config[(i >> 1) + 0x5a] >> ((i & 1) * 4)) & 3;
103
        update_pam(d, 0xc0000 + 0x4000 * i, 0xc0000 + 0x4000 * (i + 1), r);
104
    }
105
    smram = d->dev.config[0x72];
106
    if ((d->smm_enabled && (smram & 0x08)) || (smram & 0x40)) {
107 108 109
        cpu_register_physical_memory(0xa0000, 0x20000, 0xa0000);
    } else {
        for(addr = 0xa0000; addr < 0xc0000; addr += 4096) {
110
            cpu_register_physical_memory(addr, 4096,
111
                                         d->isa_page_descs[(addr - 0xa0000) >> 12]);
112 113 114 115
        }
    }
}

116
void i440fx_set_smm(PCII440FXState *d, int val)
117 118
{
    val = (val != 0);
119 120
    if (d->smm_enabled != val) {
        d->smm_enabled = val;
121 122 123 124 125 126 127 128
        i440fx_update_memory_mappings(d);
    }
}


/* XXX: suppress when better memory API. We make the assumption that
   no device (in particular the VGA) changes the memory mappings in
   the 0xa0000-0x100000 range */
129
void i440fx_init_memory_mappings(PCII440FXState *d)
130 131 132
{
    int i;
    for(i = 0; i < 96; i++) {
133
        d->isa_page_descs[i] = cpu_get_physical_page_desc(0xa0000 + i * 0x1000);
134 135 136
    }
}

137
static void i440fx_write_config(PCIDevice *dev,
138 139
                                uint32_t address, uint32_t val, int len)
{
140 141
    PCII440FXState *d = DO_UPCAST(PCII440FXState, dev, dev);

142
    /* XXX: implement SMRAM.D_LOCK */
143
    pci_default_write_config(dev, address, val, len);
144
    if ((address >= 0x59 && address <= 0x5f) || address == 0x72)
145 146 147 148 149
        i440fx_update_memory_mappings(d);
}

static void i440fx_save(QEMUFile* f, void *opaque)
{
150
    PCII440FXState *d = opaque;
151 152
    int i;

153
    pci_device_save(&d->dev, f);
154
    qemu_put_8s(f, &d->smm_enabled);
155 156 157

    for (i = 0; i < 4; i++)
        qemu_put_be32(f, pci_irq_levels[i]);
158 159 160 161
}

static int i440fx_load(QEMUFile* f, void *opaque, int version_id)
{
162
    PCII440FXState *d = opaque;
163
    int ret, i;
164

165
    if (version_id > 2)
166
        return -EINVAL;
167
    ret = pci_device_load(&d->dev, f);
168 169 170
    if (ret < 0)
        return ret;
    i440fx_update_memory_mappings(d);
171
    qemu_get_8s(f, &d->smm_enabled);
172 173 174 175 176

    if (version_id >= 2)
        for (i = 0; i < 4; i++)
            pci_irq_levels[i] = qemu_get_be32(f);

177 178 179
    return 0;
}

180
static int i440fx_pcihost_initfn(SysBusDevice *dev)
P
pbrook 已提交
181
{
G
Gerd Hoffmann 已提交
182
    I440FXState *s = FROM_SYSBUS(I440FXState, dev);
P
pbrook 已提交
183 184 185 186 187 188 189 190 191 192

    register_ioport_write(0xcf8, 4, 4, i440fx_addr_writel, s);
    register_ioport_read(0xcf8, 4, 4, i440fx_addr_readl, s);

    register_ioport_write(0xcfc, 4, 1, pci_host_data_writeb, s);
    register_ioport_write(0xcfc, 4, 2, pci_host_data_writew, s);
    register_ioport_write(0xcfc, 4, 4, pci_host_data_writel, s);
    register_ioport_read(0xcfc, 4, 1, pci_host_data_readb, s);
    register_ioport_read(0xcfc, 4, 2, pci_host_data_readw, s);
    register_ioport_read(0xcfc, 4, 4, pci_host_data_readl, s);
193
    return 0;
G
Gerd Hoffmann 已提交
194
}
P
pbrook 已提交
195

196
static int i440fx_initfn(PCIDevice *dev)
G
Gerd Hoffmann 已提交
197
{
198
    PCII440FXState *d = DO_UPCAST(PCII440FXState, dev, dev);
199

200 201 202 203 204 205 206
    pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL);
    pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82441);
    d->dev.config[0x08] = 0x02; // revision
    pci_config_set_class(d->dev.config, PCI_CLASS_BRIDGE_HOST);
    d->dev.config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type

    d->dev.config[0x72] = 0x02; /* SMRAM */
207

208
    register_savevm("I440FX", 0, 2, i440fx_save, i440fx_load, d);
209
    return 0;
G
Gerd Hoffmann 已提交
210 211
}

212
PCIBus *i440fx_init(PCII440FXState **pi440fx_state, qemu_irq *pic)
G
Gerd Hoffmann 已提交
213 214 215 216 217 218 219 220 221 222 223 224 225 226
{
    DeviceState *dev;
    PCIBus *b;
    PCIDevice *d;
    I440FXState *s;

    dev = qdev_create(NULL, "i440FX-pcihost");
    s = FROM_SYSBUS(I440FXState, sysbus_from_qdev(dev));
    b = pci_register_bus(&s->busdev.qdev, "pci.0",
                         piix3_set_irq, pci_slot_get_pirq, pic, 0, 4);
    s->bus = b;
    qdev_init(dev);

    d = pci_create_simple(b, 0, "i440FX");
227
    *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d);
G
Gerd Hoffmann 已提交
228

P
pbrook 已提交
229 230 231 232 233
    return b;
}

/* PIIX3 PCI to ISA bridge */

234
static PCIDevice *piix3_dev;
P
pbrook 已提交
235

P
pbrook 已提交
236
static void piix3_set_irq(qemu_irq *pic, int irq_num, int level)
P
pbrook 已提交
237
{
238
    int i, pic_irq, pic_level;
P
pbrook 已提交
239

240
    pci_irq_levels[irq_num] = level;
P
pbrook 已提交
241 242 243 244 245

    /* now we change the pic irq level according to the piix irq mappings */
    /* XXX: optimize */
    pic_irq = piix3_dev->config[0x60 + irq_num];
    if (pic_irq < 16) {
246
        /* The pic level is the logical OR of all the PCI irqs mapped
P
pbrook 已提交
247 248
           to it */
        pic_level = 0;
249 250 251 252
        for (i = 0; i < 4; i++) {
            if (pic_irq == piix3_dev->config[0x60 + i])
                pic_level |= pci_irq_levels[i];
        }
P
pbrook 已提交
253
        qemu_set_irq(pic[pic_irq], pic_level);
P
pbrook 已提交
254 255 256
    }
}

257
static void piix3_reset(void *opaque)
P
pbrook 已提交
258
{
259
    PCIDevice *d = opaque;
P
pbrook 已提交
260 261 262 263 264 265 266 267 268 269
    uint8_t *pci_conf = d->config;

    pci_conf[0x04] = 0x07; // master, memory and I/O
    pci_conf[0x05] = 0x00;
    pci_conf[0x06] = 0x00;
    pci_conf[0x07] = 0x02; // PCI_status_devsel_medium
    pci_conf[0x4c] = 0x4d;
    pci_conf[0x4e] = 0x03;
    pci_conf[0x4f] = 0x00;
    pci_conf[0x60] = 0x80;
270 271 272
    pci_conf[0x61] = 0x80;
    pci_conf[0x62] = 0x80;
    pci_conf[0x63] = 0x80;
P
pbrook 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
    pci_conf[0x69] = 0x02;
    pci_conf[0x70] = 0x80;
    pci_conf[0x76] = 0x0c;
    pci_conf[0x77] = 0x0c;
    pci_conf[0x78] = 0x02;
    pci_conf[0x79] = 0x00;
    pci_conf[0x80] = 0x00;
    pci_conf[0x82] = 0x00;
    pci_conf[0xa0] = 0x08;
    pci_conf[0xa2] = 0x00;
    pci_conf[0xa3] = 0x00;
    pci_conf[0xa4] = 0x00;
    pci_conf[0xa5] = 0x00;
    pci_conf[0xa6] = 0x00;
    pci_conf[0xa7] = 0x00;
    pci_conf[0xa8] = 0x0f;
    pci_conf[0xaa] = 0x00;
    pci_conf[0xab] = 0x00;
    pci_conf[0xac] = 0x00;
    pci_conf[0xae] = 0x00;
293 294

    memset(pci_irq_levels, 0, sizeof(pci_irq_levels));
P
pbrook 已提交
295 296
}

B
bellard 已提交
297 298 299 300 301 302 303 304 305 306 307 308 309 310
static void piix_save(QEMUFile* f, void *opaque)
{
    PCIDevice *d = opaque;
    pci_device_save(d, f);
}

static int piix_load(QEMUFile* f, void *opaque, int version_id)
{
    PCIDevice *d = opaque;
    if (version_id != 2)
        return -EINVAL;
    return pci_device_load(d, f);
}

311
static int piix3_initfn(PCIDevice *d)
P
pbrook 已提交
312 313 314
{
    uint8_t *pci_conf;

315
    isa_bus_new(&d->qdev);
B
bellard 已提交
316
    register_savevm("PIIX3", 0, 2, piix_save, piix_load, d);
P
pbrook 已提交
317 318

    pci_conf = d->config;
319 320
    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371SB_0); // 82371SB PIIX3 PCI-to-ISA bridge (Step A1)
321
    pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
I
Isaku Yamahata 已提交
322 323
    pci_conf[PCI_HEADER_TYPE] =
        PCI_HEADER_TYPE_NORMAL | PCI_HEADER_TYPE_MULTI_FUNCTION; // header_type = PCI_multifunction, generic
P
pbrook 已提交
324

G
Gerd Hoffmann 已提交
325
    piix3_dev = d;
P
pbrook 已提交
326
    piix3_reset(d);
327
    qemu_register_reset(piix3_reset, d);
328
    return 0;
P
pbrook 已提交
329
}
T
ths 已提交
330

G
Gerd Hoffmann 已提交
331 332 333 334 335
int piix3_init(PCIBus *bus, int devfn)
{
    PCIDevice *d;

    d = pci_create_simple(bus, devfn, "PIIX3");
T
ths 已提交
336 337
    return d->devfn;
}
G
Gerd Hoffmann 已提交
338 339 340 341 342

static PCIDeviceInfo i440fx_info[] = {
    {
        .qdev.name    = "i440FX",
        .qdev.desc    = "Host bridge",
343
        .qdev.size    = sizeof(PCII440FXState),
G
Gerd Hoffmann 已提交
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
        .qdev.no_user = 1,
        .init         = i440fx_initfn,
        .config_write = i440fx_write_config,
    },{
        .qdev.name    = "PIIX3",
        .qdev.desc    = "ISA bridge",
        .qdev.size    = sizeof(PCIDevice),
        .qdev.no_user = 1,
        .init         = piix3_initfn,
    },{
        /* end of list */
    }
};

static SysBusDeviceInfo i440fx_pcihost_info = {
    .init         = i440fx_pcihost_initfn,
    .qdev.name    = "i440FX-pcihost",
    .qdev.size    = sizeof(I440FXState),
    .qdev.no_user = 1,
};

static void i440fx_register(void)
{
    sysbus_register_withprop(&i440fx_pcihost_info);
    pci_qdev_register_many(i440fx_info);
}
device_init(i440fx_register);