Commit 0ac32c837507260009d170af15f4ae9652a9ea97
1 parent
4a9c9687
PCI interrupt support - PCI BIOS interrupt remapping - more accurate memory mapp…
…ing - 'info pci' monitor command git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@823 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
469 additions
and
74 deletions
hw/pci.c
... | ... | @@ -25,6 +25,21 @@ |
25 | 25 | |
26 | 26 | //#define DEBUG_PCI |
27 | 27 | |
28 | +#define PCI_VENDOR_ID 0x00 /* 16 bits */ | |
29 | +#define PCI_DEVICE_ID 0x02 /* 16 bits */ | |
30 | +#define PCI_COMMAND 0x04 /* 16 bits */ | |
31 | +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ | |
32 | +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ | |
33 | +#define PCI_CLASS_DEVICE 0x0a /* Device class */ | |
34 | +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ | |
35 | +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ | |
36 | +#define PCI_MIN_GNT 0x3e /* 8 bits */ | |
37 | +#define PCI_MAX_LAT 0x3f /* 8 bits */ | |
38 | + | |
39 | +/* just used for simpler irq handling. */ | |
40 | +#define PCI_DEVICES_MAX 64 | |
41 | +#define PCI_IRQ_WORDS ((PCI_DEVICES_MAX + 31) / 32) | |
42 | + | |
28 | 43 | typedef struct PCIBridge { |
29 | 44 | uint32_t config_reg; |
30 | 45 | PCIDevice **pci_bus[256]; |
... | ... | @@ -32,6 +47,8 @@ typedef struct PCIBridge { |
32 | 47 | |
33 | 48 | static PCIBridge pci_bridge; |
34 | 49 | target_phys_addr_t pci_mem_base; |
50 | +static int pci_irq_index; | |
51 | +static uint32_t pci_irq_levels[4][PCI_IRQ_WORDS]; | |
35 | 52 | |
36 | 53 | /* -1 for devfn means auto assign */ |
37 | 54 | PCIDevice *pci_register_device(const char *name, int instance_size, |
... | ... | @@ -42,6 +59,9 @@ PCIDevice *pci_register_device(const char *name, int instance_size, |
42 | 59 | PCIBridge *s = &pci_bridge; |
43 | 60 | PCIDevice *pci_dev, **bus; |
44 | 61 | |
62 | + if (pci_irq_index >= PCI_DEVICES_MAX) | |
63 | + return NULL; | |
64 | + | |
45 | 65 | if (!s->pci_bus[bus_num]) { |
46 | 66 | s->pci_bus[bus_num] = qemu_mallocz(256 * sizeof(PCIDevice *)); |
47 | 67 | if (!s->pci_bus[bus_num]) |
... | ... | @@ -62,8 +82,14 @@ PCIDevice *pci_register_device(const char *name, int instance_size, |
62 | 82 | pci_dev->bus_num = bus_num; |
63 | 83 | pci_dev->devfn = devfn; |
64 | 84 | pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); |
85 | + | |
86 | + if (!config_read) | |
87 | + config_read = pci_default_read_config; | |
88 | + if (!config_write) | |
89 | + config_write = pci_default_write_config; | |
65 | 90 | pci_dev->config_read = config_read; |
66 | 91 | pci_dev->config_write = config_write; |
92 | + pci_dev->irq_index = pci_irq_index++; | |
67 | 93 | bus[devfn] = pci_dev; |
68 | 94 | return pci_dev; |
69 | 95 | } |
... | ... | @@ -83,30 +109,160 @@ void pci_register_io_region(PCIDevice *pci_dev, int region_num, |
83 | 109 | r->map_func = map_func; |
84 | 110 | } |
85 | 111 | |
86 | -static void pci_config_writel(void* opaque, uint32_t addr, uint32_t val) | |
112 | +static void pci_addr_writel(void* opaque, uint32_t addr, uint32_t val) | |
87 | 113 | { |
88 | 114 | PCIBridge *s = opaque; |
89 | 115 | s->config_reg = val; |
90 | 116 | } |
91 | 117 | |
92 | -static uint32_t pci_config_readl(void* opaque, uint32_t addr) | |
118 | +static uint32_t pci_addr_readl(void* opaque, uint32_t addr) | |
93 | 119 | { |
94 | 120 | PCIBridge *s = opaque; |
95 | 121 | return s->config_reg; |
96 | 122 | } |
97 | 123 | |
98 | -static void unmap_region(PCIIORegion *r) | |
124 | +static void pci_update_mappings(PCIDevice *d) | |
125 | +{ | |
126 | + PCIIORegion *r; | |
127 | + int cmd, i; | |
128 | + uint32_t last_addr, new_addr; | |
129 | + | |
130 | + cmd = le16_to_cpu(*(uint16_t *)(d->config + PCI_COMMAND)); | |
131 | + for(i = 0; i < 6; i++) { | |
132 | + r = &d->io_regions[i]; | |
133 | + if (r->size != 0) { | |
134 | + if (r->type & PCI_ADDRESS_SPACE_IO) { | |
135 | + if (cmd & PCI_COMMAND_IO) { | |
136 | + new_addr = le32_to_cpu(*(uint32_t *)(d->config + | |
137 | + 0x10 + i * 4)); | |
138 | + new_addr = new_addr & ~(r->size - 1); | |
139 | + last_addr = new_addr + r->size - 1; | |
140 | + /* NOTE: we have only 64K ioports on PC */ | |
141 | + if (last_addr <= new_addr || new_addr == 0 || | |
142 | + last_addr >= 0x10000) { | |
143 | + new_addr = -1; | |
144 | + } | |
145 | + } else { | |
146 | + new_addr = -1; | |
147 | + } | |
148 | + } else { | |
149 | + if (cmd & PCI_COMMAND_MEMORY) { | |
150 | + new_addr = le32_to_cpu(*(uint32_t *)(d->config + | |
151 | + 0x10 + i * 4)); | |
152 | + new_addr = new_addr & ~(r->size - 1); | |
153 | + last_addr = new_addr + r->size - 1; | |
154 | + /* NOTE: we do not support wrapping */ | |
155 | + /* XXX: as we cannot support really dynamic | |
156 | + mappings, we handle specific values as invalid | |
157 | + mappings. */ | |
158 | + if (last_addr <= new_addr || new_addr == 0 || | |
159 | + last_addr == -1) { | |
160 | + new_addr = -1; | |
161 | + } | |
162 | + } else { | |
163 | + new_addr = -1; | |
164 | + } | |
165 | + } | |
166 | + /* now do the real mapping */ | |
167 | + if (new_addr != r->addr) { | |
168 | + if (r->addr != -1) { | |
169 | + if (r->type & PCI_ADDRESS_SPACE_IO) { | |
170 | + int class; | |
171 | + /* NOTE: specific hack for IDE in PC case: | |
172 | + only one byte must be mapped. */ | |
173 | + class = d->config[0x0a] | (d->config[0x0b] << 8); | |
174 | + if (class == 0x0101 && r->size == 4) { | |
175 | + isa_unassign_ioport(r->addr + 2, 1); | |
176 | + } else { | |
177 | + isa_unassign_ioport(r->addr, r->size); | |
178 | + } | |
179 | + } else { | |
180 | + cpu_register_physical_memory(r->addr + pci_mem_base, | |
181 | + r->size, | |
182 | + IO_MEM_UNASSIGNED); | |
183 | + } | |
184 | + } | |
185 | + r->addr = new_addr; | |
186 | + if (r->addr != -1) { | |
187 | + r->map_func(d, i, r->addr, r->size, r->type); | |
188 | + } | |
189 | + } | |
190 | + } | |
191 | + } | |
192 | +} | |
193 | + | |
194 | +uint32_t pci_default_read_config(PCIDevice *d, | |
195 | + uint32_t address, int len) | |
99 | 196 | { |
100 | - if (r->addr == -1) | |
197 | + uint32_t val; | |
198 | + switch(len) { | |
199 | + case 1: | |
200 | + val = d->config[address]; | |
201 | + break; | |
202 | + case 2: | |
203 | + val = le16_to_cpu(*(uint16_t *)(d->config + address)); | |
204 | + break; | |
205 | + default: | |
206 | + case 4: | |
207 | + val = le32_to_cpu(*(uint32_t *)(d->config + address)); | |
208 | + break; | |
209 | + } | |
210 | + return val; | |
211 | +} | |
212 | + | |
213 | +void pci_default_write_config(PCIDevice *d, | |
214 | + uint32_t address, uint32_t val, int len) | |
215 | +{ | |
216 | + int can_write, i; | |
217 | + uint32_t end; | |
218 | + | |
219 | + if (len == 4 && (address >= 0x10 && address < 0x10 + 4 * 6)) { | |
220 | + PCIIORegion *r; | |
221 | + int reg; | |
222 | + | |
223 | + reg = (address - 0x10) >> 2; | |
224 | + r = &d->io_regions[reg]; | |
225 | + if (r->size == 0) | |
226 | + goto default_config; | |
227 | + /* compute the stored value */ | |
228 | + val &= ~(r->size - 1); | |
229 | + val |= r->type; | |
230 | + *(uint32_t *)(d->config + 0x10 + reg * 4) = cpu_to_le32(val); | |
231 | + pci_update_mappings(d); | |
101 | 232 | return; |
102 | -#ifdef DEBUG_PCI | |
103 | - printf("unmap addr=%08x size=%08x\n", r->addr, r->size); | |
104 | -#endif | |
105 | - if (r->type & PCI_ADDRESS_SPACE_IO) { | |
106 | - isa_unassign_ioport(r->addr, r->size); | |
107 | - } else { | |
108 | - cpu_register_physical_memory(r->addr + pci_mem_base, r->size, | |
109 | - IO_MEM_UNASSIGNED); | |
233 | + } | |
234 | + default_config: | |
235 | + /* not efficient, but simple */ | |
236 | + for(i = 0; i < len; i++) { | |
237 | + /* default read/write accesses */ | |
238 | + switch(address) { | |
239 | + case 0x00: | |
240 | + case 0x01: | |
241 | + case 0x02: | |
242 | + case 0x03: | |
243 | + case 0x08: | |
244 | + case 0x09: | |
245 | + case 0x0a: | |
246 | + case 0x0b: | |
247 | + case 0x0e: | |
248 | + case 0x3d: | |
249 | + can_write = 0; | |
250 | + break; | |
251 | + default: | |
252 | + can_write = 1; | |
253 | + break; | |
254 | + } | |
255 | + if (can_write) { | |
256 | + d->config[address] = val; | |
257 | + } | |
258 | + address++; | |
259 | + val >>= 8; | |
260 | + } | |
261 | + | |
262 | + end = address + len; | |
263 | + if (end > PCI_COMMAND && address < (PCI_COMMAND + 2)) { | |
264 | + /* if the command register is modified, we must modify the mappings */ | |
265 | + pci_update_mappings(d); | |
110 | 266 | } |
111 | 267 | } |
112 | 268 | |
... | ... | @@ -115,7 +271,7 @@ static void pci_data_write(void *opaque, uint32_t addr, |
115 | 271 | { |
116 | 272 | PCIBridge *s = opaque; |
117 | 273 | PCIDevice **bus, *pci_dev; |
118 | - int config_addr, reg; | |
274 | + int config_addr; | |
119 | 275 | |
120 | 276 | #if defined(DEBUG_PCI) && 0 |
121 | 277 | printf("pci_data_write: addr=%08x val=%08x len=%d\n", |
... | ... | @@ -134,41 +290,11 @@ static void pci_data_write(void *opaque, uint32_t addr, |
134 | 290 | if (!pci_dev) |
135 | 291 | return; |
136 | 292 | config_addr = (s->config_reg & 0xfc) | (addr & 3); |
137 | - | |
138 | 293 | #if defined(DEBUG_PCI) |
139 | 294 | printf("pci_config_write: %s: addr=%02x val=%08x len=%d\n", |
140 | 295 | pci_dev->name, config_addr, val, len); |
141 | 296 | #endif |
142 | - if (len == 4 && (config_addr >= 0x10 && config_addr < 0x10 + 4 * 6)) { | |
143 | - PCIIORegion *r; | |
144 | - reg = (config_addr - 0x10) >> 2; | |
145 | - r = &pci_dev->io_regions[reg]; | |
146 | - if (r->size == 0) | |
147 | - goto default_config; | |
148 | - if (val != 0xffffffff && val != 0) { | |
149 | - /* XXX: the memory assignment should be global to handle | |
150 | - overlaps, but it is not needed at this stage */ | |
151 | - /* first unmap the old region */ | |
152 | - unmap_region(r); | |
153 | - /* change the address */ | |
154 | - if (r->type & PCI_ADDRESS_SPACE_IO) | |
155 | - r->addr = val & ~0x3; | |
156 | - else | |
157 | - r->addr = val & ~0xf; | |
158 | -#ifdef DEBUG_PCI | |
159 | - printf("map addr=%08x size=%08x type=%d\n", | |
160 | - r->addr, r->size, r->type); | |
161 | -#endif | |
162 | - r->map_func(pci_dev, reg, r->addr, r->size, r->type); | |
163 | - } | |
164 | - /* now compute the stored value */ | |
165 | - val &= ~(r->size - 1); | |
166 | - val |= r->type; | |
167 | - *(uint32_t *)(pci_dev->config + 0x10 + reg * 4) = cpu_to_le32(val); | |
168 | - } else { | |
169 | - default_config: | |
170 | - pci_dev->config_write(pci_dev, config_addr, val, len); | |
171 | - } | |
297 | + pci_dev->config_write(pci_dev, config_addr, val, len); | |
172 | 298 | } |
173 | 299 | |
174 | 300 | static uint32_t pci_data_read(void *opaque, uint32_t addr, |
... | ... | @@ -238,28 +364,13 @@ static uint32_t pci_data_readl(void* opaque, uint32_t addr) |
238 | 364 | |
239 | 365 | /* i440FX PCI bridge */ |
240 | 366 | |
241 | -static uint32_t i440_read_config(PCIDevice *d, | |
242 | - uint32_t address, int len) | |
243 | -{ | |
244 | - uint32_t val; | |
245 | - val = 0; | |
246 | - memcpy(&val, d->config + address, len); | |
247 | - return val; | |
248 | -} | |
249 | - | |
250 | -static void i440_write_config(PCIDevice *d, | |
251 | - uint32_t address, uint32_t val, int len) | |
252 | -{ | |
253 | - memcpy(d->config + address, &val, len); | |
254 | -} | |
255 | - | |
256 | 367 | void i440fx_init(void) |
257 | 368 | { |
258 | 369 | PCIBridge *s = &pci_bridge; |
259 | 370 | PCIDevice *d; |
260 | 371 | |
261 | - register_ioport_write(0xcf8, 4, 4, pci_config_writel, s); | |
262 | - register_ioport_read(0xcf8, 4, 4, pci_config_readl, s); | |
372 | + register_ioport_write(0xcf8, 4, 4, pci_addr_writel, s); | |
373 | + register_ioport_read(0xcf8, 4, 4, pci_addr_readl, s); | |
263 | 374 | |
264 | 375 | register_ioport_write(0xcfc, 4, 1, pci_data_writeb, s); |
265 | 376 | register_ioport_write(0xcfc, 4, 2, pci_data_writew, s); |
... | ... | @@ -269,7 +380,7 @@ void i440fx_init(void) |
269 | 380 | register_ioport_read(0xcfc, 4, 4, pci_data_readl, s); |
270 | 381 | |
271 | 382 | d = pci_register_device("i440FX", sizeof(PCIDevice), 0, 0, |
272 | - i440_read_config, i440_write_config); | |
383 | + NULL, NULL); | |
273 | 384 | |
274 | 385 | d->config[0x00] = 0x86; // vendor_id |
275 | 386 | d->config[0x01] = 0x80; |
... | ... | @@ -282,35 +393,295 @@ void i440fx_init(void) |
282 | 393 | d->config[0x0e] = 0x01; // header_type |
283 | 394 | } |
284 | 395 | |
285 | -/* NOTE: the following should be done by the BIOS */ | |
396 | +/* PIIX3 PCI to ISA bridge */ | |
397 | + | |
398 | +typedef struct PIIX3State { | |
399 | + PCIDevice dev; | |
400 | +} PIIX3State; | |
401 | + | |
402 | +PIIX3State *piix3_state; | |
403 | + | |
404 | +static void piix3_reset(PIIX3State *d) | |
405 | +{ | |
406 | + uint8_t *pci_conf = d->dev.config; | |
407 | + | |
408 | + pci_conf[0x04] = 0x07; // master, memory and I/O | |
409 | + pci_conf[0x05] = 0x00; | |
410 | + pci_conf[0x06] = 0x00; | |
411 | + pci_conf[0x07] = 0x02; // PCI_status_devsel_medium | |
412 | + pci_conf[0x4c] = 0x4d; | |
413 | + pci_conf[0x4e] = 0x03; | |
414 | + pci_conf[0x4f] = 0x00; | |
415 | + pci_conf[0x60] = 0x80; | |
416 | + pci_conf[0x69] = 0x02; | |
417 | + pci_conf[0x70] = 0x80; | |
418 | + pci_conf[0x76] = 0x0c; | |
419 | + pci_conf[0x77] = 0x0c; | |
420 | + pci_conf[0x78] = 0x02; | |
421 | + pci_conf[0x79] = 0x00; | |
422 | + pci_conf[0x80] = 0x00; | |
423 | + pci_conf[0x82] = 0x00; | |
424 | + pci_conf[0xa0] = 0x08; | |
425 | + pci_conf[0xa0] = 0x08; | |
426 | + pci_conf[0xa2] = 0x00; | |
427 | + pci_conf[0xa3] = 0x00; | |
428 | + pci_conf[0xa4] = 0x00; | |
429 | + pci_conf[0xa5] = 0x00; | |
430 | + pci_conf[0xa6] = 0x00; | |
431 | + pci_conf[0xa7] = 0x00; | |
432 | + pci_conf[0xa8] = 0x0f; | |
433 | + pci_conf[0xaa] = 0x00; | |
434 | + pci_conf[0xab] = 0x00; | |
435 | + pci_conf[0xac] = 0x00; | |
436 | + pci_conf[0xae] = 0x00; | |
437 | +} | |
438 | + | |
439 | +void piix3_init(void) | |
440 | +{ | |
441 | + PIIX3State *d; | |
442 | + uint8_t *pci_conf; | |
443 | + | |
444 | + d = (PIIX3State *)pci_register_device("PIIX3", sizeof(PIIX3State), | |
445 | + 0, -1, | |
446 | + NULL, NULL); | |
447 | + piix3_state = d; | |
448 | + pci_conf = d->dev.config; | |
449 | + | |
450 | + pci_conf[0x00] = 0x86; // Intel | |
451 | + pci_conf[0x01] = 0x80; | |
452 | + pci_conf[0x02] = 0x00; // 82371SB PIIX3 PCI-to-ISA bridge (Step A1) | |
453 | + pci_conf[0x03] = 0x70; | |
454 | + pci_conf[0x0a] = 0x01; // class_sub = PCI_ISA | |
455 | + pci_conf[0x0b] = 0x06; // class_base = PCI_bridge | |
456 | + pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic | |
457 | + | |
458 | + piix3_reset(d); | |
459 | +} | |
460 | + | |
461 | +/***********************************************************/ | |
462 | +/* generic PCI irq support */ | |
463 | + | |
464 | +/* return the global irq number corresponding to a given device irq | |
465 | + pin. We could also use the bus number to have a more precise | |
466 | + mapping. */ | |
467 | +static inline int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) | |
468 | +{ | |
469 | + int slot_addend; | |
470 | + slot_addend = (pci_dev->devfn >> 3); | |
471 | + return (irq_num + slot_addend) & 3; | |
472 | +} | |
473 | + | |
474 | +/* 0 <= irq_num <= 3. level must be 0 or 1 */ | |
475 | +void pci_set_irq(PCIDevice *pci_dev, int irq_num, int level) | |
476 | +{ | |
477 | + int irq_index, shift, pic_irq, pic_level; | |
478 | + uint32_t *p; | |
479 | + | |
480 | + irq_num = pci_slot_get_pirq(pci_dev, irq_num); | |
481 | + irq_index = pci_dev->irq_index; | |
482 | + p = &pci_irq_levels[irq_num][irq_index >> 5]; | |
483 | + shift = (irq_index & 0x1f); | |
484 | + *p = (*p & ~(1 << shift)) | (level << shift); | |
485 | + | |
486 | + /* now we change the pic irq level according to the piix irq mappings */ | |
487 | + pic_irq = piix3_state->dev.config[0x60 + irq_num]; | |
488 | + if (pic_irq < 16) { | |
489 | + /* the pic level is the logical OR of all the PCI irqs mapped | |
490 | + to it */ | |
491 | + pic_level = 0; | |
492 | +#if (PCI_IRQ_WORDS == 2) | |
493 | + pic_level = ((pci_irq_levels[irq_num][0] | | |
494 | + pci_irq_levels[irq_num][1]) != 0); | |
495 | +#else | |
496 | + { | |
497 | + int i; | |
498 | + pic_level = 0; | |
499 | + for(i = 0; i < PCI_IRQ_WORDS; i++) { | |
500 | + if (pci_irq_levels[irq_num][i]) { | |
501 | + pic_level = 1; | |
502 | + break; | |
503 | + } | |
504 | + } | |
505 | + } | |
506 | +#endif | |
507 | + pic_set_irq(pic_irq, pic_level); | |
508 | + } | |
509 | +} | |
510 | + | |
511 | +/***********************************************************/ | |
512 | +/* monitor info on PCI */ | |
513 | + | |
514 | +static void pci_info_device(PCIDevice *d) | |
515 | +{ | |
516 | + int i, class; | |
517 | + PCIIORegion *r; | |
518 | + | |
519 | + printf(" Bus %2d, device %3d, function %d:\n", | |
520 | + d->bus_num, d->devfn >> 3, d->devfn & 7); | |
521 | + class = le16_to_cpu(*((uint16_t *)(d->config + PCI_CLASS_DEVICE))); | |
522 | + printf(" "); | |
523 | + switch(class) { | |
524 | + case 0x0101: | |
525 | + printf("IDE controller"); | |
526 | + break; | |
527 | + case 0x0200: | |
528 | + printf("Ethernet controller"); | |
529 | + break; | |
530 | + case 0x0300: | |
531 | + printf("VGA controller"); | |
532 | + break; | |
533 | + default: | |
534 | + printf("Class %04x", class); | |
535 | + break; | |
536 | + } | |
537 | + printf(": PCI device %04x:%04x\n", | |
538 | + le16_to_cpu(*((uint16_t *)(d->config + PCI_VENDOR_ID))), | |
539 | + le16_to_cpu(*((uint16_t *)(d->config + PCI_DEVICE_ID)))); | |
540 | + | |
541 | + if (d->config[PCI_INTERRUPT_PIN] != 0) { | |
542 | + printf(" IRQ %d.\n", d->config[PCI_INTERRUPT_LINE]); | |
543 | + } | |
544 | + for(i = 0;i < 6; i++) { | |
545 | + r = &d->io_regions[i]; | |
546 | + if (r->size != 0) { | |
547 | + printf(" BAR%d: ", i); | |
548 | + if (r->type & PCI_ADDRESS_SPACE_IO) { | |
549 | + printf("I/O at 0x%04x [0x%04x].\n", | |
550 | + r->addr, r->addr + r->size - 1); | |
551 | + } else { | |
552 | + printf("32 bit memory at 0x%08x [0x%08x].\n", | |
553 | + r->addr, r->addr + r->size - 1); | |
554 | + } | |
555 | + } | |
556 | + } | |
557 | +} | |
558 | + | |
559 | +void pci_info(void) | |
560 | +{ | |
561 | + PCIBridge *s = &pci_bridge; | |
562 | + PCIDevice **bus; | |
563 | + int bus_num, devfn; | |
564 | + | |
565 | + for(bus_num = 0; bus_num < 256; bus_num++) { | |
566 | + bus = s->pci_bus[bus_num]; | |
567 | + if (bus) { | |
568 | + for(devfn = 0; devfn < 256; devfn++) { | |
569 | + if (bus[devfn]) | |
570 | + pci_info_device(bus[devfn]); | |
571 | + } | |
572 | + } | |
573 | + } | |
574 | +} | |
575 | + | |
576 | +/***********************************************************/ | |
577 | +/* XXX: the following should be moved to the PC BIOS */ | |
578 | + | |
579 | +static uint32_t isa_inb(uint32_t addr) | |
580 | +{ | |
581 | + return cpu_inb(cpu_single_env, addr); | |
582 | +} | |
583 | + | |
584 | +static void isa_outb(uint32_t val, uint32_t addr) | |
585 | +{ | |
586 | + cpu_outb(cpu_single_env, addr, val); | |
587 | +} | |
588 | + | |
589 | +static uint32_t isa_inw(uint32_t addr) | |
590 | +{ | |
591 | + return cpu_inw(cpu_single_env, addr); | |
592 | +} | |
593 | + | |
594 | +static void isa_outw(uint32_t val, uint32_t addr) | |
595 | +{ | |
596 | + cpu_outw(cpu_single_env, addr, val); | |
597 | +} | |
598 | + | |
599 | +static uint32_t isa_inl(uint32_t addr) | |
600 | +{ | |
601 | + return cpu_inl(cpu_single_env, addr); | |
602 | +} | |
603 | + | |
604 | +static void isa_outl(uint32_t val, uint32_t addr) | |
605 | +{ | |
606 | + cpu_outl(cpu_single_env, addr, val); | |
607 | +} | |
608 | + | |
609 | +static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val) | |
610 | +{ | |
611 | + PCIBridge *s = &pci_bridge; | |
612 | + s->config_reg = 0x80000000 | (d->bus_num << 16) | | |
613 | + (d->devfn << 8) | addr; | |
614 | + pci_data_write(s, 0, val, 4); | |
615 | +} | |
616 | + | |
617 | +static void pci_config_writew(PCIDevice *d, uint32_t addr, uint32_t val) | |
618 | +{ | |
619 | + PCIBridge *s = &pci_bridge; | |
620 | + s->config_reg = 0x80000000 | (d->bus_num << 16) | | |
621 | + (d->devfn << 8) | (addr & ~3); | |
622 | + pci_data_write(s, addr & 3, val, 2); | |
623 | +} | |
624 | + | |
625 | +static void pci_config_writeb(PCIDevice *d, uint32_t addr, uint32_t val) | |
626 | +{ | |
627 | + PCIBridge *s = &pci_bridge; | |
628 | + s->config_reg = 0x80000000 | (d->bus_num << 16) | | |
629 | + (d->devfn << 8) | (addr & ~3); | |
630 | + pci_data_write(s, addr & 3, val, 1); | |
631 | +} | |
632 | + | |
633 | +static uint32_t pci_config_readl(PCIDevice *d, uint32_t addr) | |
634 | +{ | |
635 | + PCIBridge *s = &pci_bridge; | |
636 | + s->config_reg = 0x80000000 | (d->bus_num << 16) | | |
637 | + (d->devfn << 8) | addr; | |
638 | + return pci_data_read(s, 0, 4); | |
639 | +} | |
640 | + | |
641 | +static uint32_t pci_config_readw(PCIDevice *d, uint32_t addr) | |
642 | +{ | |
643 | + PCIBridge *s = &pci_bridge; | |
644 | + s->config_reg = 0x80000000 | (d->bus_num << 16) | | |
645 | + (d->devfn << 8) | (addr & ~3); | |
646 | + return pci_data_read(s, addr & 3, 2); | |
647 | +} | |
648 | + | |
649 | +static uint32_t pci_config_readb(PCIDevice *d, uint32_t addr) | |
650 | +{ | |
651 | + PCIBridge *s = &pci_bridge; | |
652 | + s->config_reg = 0x80000000 | (d->bus_num << 16) | | |
653 | + (d->devfn << 8) | (addr & ~3); | |
654 | + return pci_data_read(s, addr & 3, 1); | |
655 | +} | |
286 | 656 | |
287 | 657 | static uint32_t pci_bios_io_addr; |
288 | 658 | static uint32_t pci_bios_mem_addr; |
659 | +/* host irqs corresponding to PCI irqs A-D */ | |
660 | +static uint8_t pci_irqs[4] = { 11, 9, 11, 9 }; | |
289 | 661 | |
290 | 662 | static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr) |
291 | 663 | { |
292 | - PCIBridge *s = &pci_bridge; | |
293 | 664 | PCIIORegion *r; |
665 | + uint16_t cmd; | |
294 | 666 | |
295 | - s->config_reg = 0x80000000 | (d->bus_num << 16) | | |
296 | - (d->devfn << 8) | (0x10 + region_num * 4); | |
297 | - pci_data_write(s, 0, addr, 4); | |
667 | + pci_config_writel(d, 0x10 + region_num * 4, addr); | |
298 | 668 | r = &d->io_regions[region_num]; |
299 | 669 | |
300 | 670 | /* enable memory mappings */ |
671 | + cmd = pci_config_readw(d, PCI_COMMAND); | |
301 | 672 | if (r->type & PCI_ADDRESS_SPACE_IO) |
302 | - d->config[0x04] |= 1; | |
673 | + cmd |= 1; | |
303 | 674 | else |
304 | - d->config[0x04] |= 2; | |
675 | + cmd |= 2; | |
676 | + pci_config_writew(d, PCI_COMMAND, cmd); | |
305 | 677 | } |
306 | 678 | |
307 | - | |
308 | 679 | static void pci_bios_init_device(PCIDevice *d) |
309 | 680 | { |
310 | 681 | int class; |
311 | 682 | PCIIORegion *r; |
312 | 683 | uint32_t *paddr; |
313 | - int i; | |
684 | + int i, pin, pic_irq; | |
314 | 685 | |
315 | 686 | class = d->config[0x0a] | (d->config[0x0b] << 8); |
316 | 687 | switch(class) { |
... | ... | @@ -321,6 +692,10 @@ static void pci_bios_init_device(PCIDevice *d) |
321 | 692 | pci_set_io_region_addr(d, 2, 0x170); |
322 | 693 | pci_set_io_region_addr(d, 3, 0x374); |
323 | 694 | break; |
695 | + case 0x0300: | |
696 | + /* VGA: map frame buffer to default Bochs VBE address */ | |
697 | + pci_set_io_region_addr(d, 0, 0xE0000000); | |
698 | + break; | |
324 | 699 | default: |
325 | 700 | /* default memory mappings */ |
326 | 701 | for(i = 0; i < 6; i++) { |
... | ... | @@ -337,6 +712,14 @@ static void pci_bios_init_device(PCIDevice *d) |
337 | 712 | } |
338 | 713 | break; |
339 | 714 | } |
715 | + | |
716 | + /* map the interrupt */ | |
717 | + pin = pci_config_readb(d, PCI_INTERRUPT_PIN); | |
718 | + if (pin != 0) { | |
719 | + pin = pci_slot_get_pirq(d, pin - 1); | |
720 | + pic_irq = pci_irqs[pin]; | |
721 | + pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq); | |
722 | + } | |
340 | 723 | } |
341 | 724 | |
342 | 725 | /* |
... | ... | @@ -348,11 +731,25 @@ void pci_bios_init(void) |
348 | 731 | { |
349 | 732 | PCIBridge *s = &pci_bridge; |
350 | 733 | PCIDevice **bus; |
351 | - int bus_num, devfn; | |
734 | + int bus_num, devfn, i, irq; | |
735 | + uint8_t elcr[2]; | |
352 | 736 | |
353 | 737 | pci_bios_io_addr = 0xc000; |
354 | 738 | pci_bios_mem_addr = 0xf0000000; |
355 | 739 | |
740 | + /* activate IRQ mappings */ | |
741 | + elcr[0] = 0x00; | |
742 | + elcr[1] = 0x00; | |
743 | + for(i = 0; i < 4; i++) { | |
744 | + irq = pci_irqs[i]; | |
745 | + /* set to trigger level */ | |
746 | + elcr[irq >> 3] |= (1 << (irq & 7)); | |
747 | + /* activate irq remapping in PIIX */ | |
748 | + pci_config_writeb((PCIDevice *)piix3_state, 0x60 + i, irq); | |
749 | + } | |
750 | + isa_outb(elcr[0], 0x4d0); | |
751 | + isa_outb(elcr[1], 0x4d1); | |
752 | + | |
356 | 753 | for(bus_num = 0; bus_num < 256; bus_num++) { |
357 | 754 | bus = s->pci_bus[bus_num]; |
358 | 755 | if (bus) { |
... | ... | @@ -363,5 +760,3 @@ void pci_bios_init(void) |
363 | 760 | } |
364 | 761 | } |
365 | 762 | } |
366 | - | |
367 | - | ... | ... |