debuggers.hg

view tools/ioemu/hw/piix4acpi.c @ 0:7d21f7218375

Exact replica of unstable on 051908 + README-this
author Mukesh Rathor
date Mon May 19 15:34:57 2008 -0700 (2008-05-19)
parents
children 5c0bf00e371d
line source
1 /*
2 * PIIX4 ACPI controller emulation
3 *
4 * Winston liwen Wang, winston.l.wang@intel.com
5 * Copyright (c) 2006 , Intel Corporation.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
26 #include "vl.h"
27 #include <xen/hvm/ioreq.h>
29 /* PM1a_CNT bits, as defined in the ACPI specification. */
30 #define SCI_EN (1 << 0)
31 #define GBL_RLS (1 << 2)
32 #define SLP_TYP_Sx (7 << 10)
33 #define SLP_EN (1 << 13)
35 /* Sleep state type codes as defined by the \_Sx objects in the DSDT. */
36 /* These must be kept in sync with the DSDT (hvmloader/acpi/dsdt.asl) */
37 #define SLP_TYP_S4 (6 << 10)
38 #define SLP_TYP_S5 (7 << 10)
40 #define ACPI_DBG_IO_ADDR 0xb044
41 #define ACPI_PHP_IO_ADDR 0x10c0
43 #define PHP_EVT_ADD 0x0
44 #define PHP_EVT_REMOVE 0x3
46 #define ACPI_SCI_IRQ 9
48 /* The bit in GPE0_STS/EN to notify the pci hotplug event */
49 #define ACPI_PHP_GPE_BIT 3
51 #define ACPI_PHP_SLOT_NUM PHP_SLOT_LEN
53 typedef struct AcpiDeviceState AcpiDeviceState;
54 AcpiDeviceState *acpi_device_table;
56 typedef struct PCIAcpiState {
57 PCIDevice dev;
58 uint16_t pm1_control; /* pm1a_ECNT_BLK */
59 } PCIAcpiState;
61 typedef struct GPEState {
62 /* GPE0 block */
63 uint8_t gpe0_sts[ACPI_GPE0_BLK_LEN / 2];
64 uint8_t gpe0_en[ACPI_GPE0_BLK_LEN / 2];
66 /* SCI IRQ level */
67 uint8_t sci_asserted;
69 } GPEState;
71 GPEState gpe_state;
73 typedef struct PHPSlots {
74 struct {
75 uint8_t status; /* Apaptor stats */
76 } slot[ACPI_PHP_SLOT_NUM];
77 uint8_t plug_evt; /* slot|event slot:0-no event;1-1st. event:0-remove;1-add */
78 } PHPSlots;
80 PHPSlots php_slots;
82 static void piix4acpi_save(QEMUFile *f, void *opaque)
83 {
84 PCIAcpiState *s = opaque;
85 pci_device_save(&s->dev, f);
86 qemu_put_be16s(f, &s->pm1_control);
87 }
89 static int piix4acpi_load(QEMUFile *f, void *opaque, int version_id)
90 {
91 PCIAcpiState *s = opaque;
92 int ret;
93 if (version_id > 1)
94 return -EINVAL;
95 ret = pci_device_load(&s->dev, f);
96 if (ret < 0)
97 return ret;
98 qemu_get_be16s(f, &s->pm1_control);
99 return 0;
100 }
102 static void acpiPm1Control_writeb(void *opaque, uint32_t addr, uint32_t val)
103 {
104 PCIAcpiState *s = opaque;
105 s->pm1_control = (s->pm1_control & 0xff00) | (val & 0xff);
106 }
108 static uint32_t acpiPm1Control_readb(void *opaque, uint32_t addr)
109 {
110 PCIAcpiState *s = opaque;
111 /* Mask out the write-only bits */
112 return (uint8_t)(s->pm1_control & ~(GBL_RLS|SLP_EN));
113 }
115 static void acpi_shutdown(uint32_t val)
116 {
117 if (!(val & SLP_EN))
118 return;
120 switch (val & SLP_TYP_Sx) {
121 case SLP_TYP_S4:
122 case SLP_TYP_S5:
123 qemu_system_shutdown_request();
124 break;
125 default:
126 break;
127 }
128 }
130 static void acpiPm1ControlP1_writeb(void *opaque, uint32_t addr, uint32_t val)
131 {
132 PCIAcpiState *s = opaque;
134 val <<= 8;
135 s->pm1_control = ((s->pm1_control & 0xff) | val) & ~SLP_EN;
137 acpi_shutdown(val);
138 }
140 static uint32_t acpiPm1ControlP1_readb(void *opaque, uint32_t addr)
141 {
142 PCIAcpiState *s = opaque;
143 /* Mask out the write-only bits */
144 return (uint8_t)((s->pm1_control & ~(GBL_RLS|SLP_EN)) >> 8);
145 }
147 static void acpiPm1Control_writew(void *opaque, uint32_t addr, uint32_t val)
148 {
149 PCIAcpiState *s = opaque;
151 s->pm1_control = val & ~SLP_EN;
153 acpi_shutdown(val);
154 }
156 static uint32_t acpiPm1Control_readw(void *opaque, uint32_t addr)
157 {
158 PCIAcpiState *s = opaque;
159 /* Mask out the write-only bits */
160 return (s->pm1_control & ~(GBL_RLS|SLP_EN));
161 }
163 static void acpi_map(PCIDevice *pci_dev, int region_num,
164 uint32_t addr, uint32_t size, int type)
165 {
166 PCIAcpiState *d = (PCIAcpiState *)pci_dev;
168 /* Byte access */
169 register_ioport_write(addr + 4, 1, 1, acpiPm1Control_writeb, d);
170 register_ioport_read(addr + 4, 1, 1, acpiPm1Control_readb, d);
171 register_ioport_write(addr + 4 + 1, 1, 1, acpiPm1ControlP1_writeb, d);
172 register_ioport_read(addr + 4 +1, 1, 1, acpiPm1ControlP1_readb, d);
174 /* Word access */
175 register_ioport_write(addr + 4, 2, 2, acpiPm1Control_writew, d);
176 register_ioport_read(addr + 4, 2, 2, acpiPm1Control_readw, d);
177 }
179 #ifdef CONFIG_PASSTHROUGH
181 static inline int test_bit(uint8_t *map, int bit)
182 {
183 return ( map[bit / 8] & (1 << (bit % 8)) );
184 }
186 static inline void set_bit(uint8_t *map, int bit)
187 {
188 map[bit / 8] |= (1 << (bit % 8));
189 }
191 static inline void clear_bit(uint8_t *map, int bit)
192 {
193 map[bit / 8] &= ~(1 << (bit % 8));
194 }
196 extern FILE *logfile;
197 static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val)
198 {
199 #if defined(DEBUG)
200 printf("ACPI: DBG: 0x%08x\n", val);
201 #endif
202 fprintf(logfile, "ACPI:debug: write addr=0x%x, val=0x%x.\n", addr, val);
203 }
205 /*
206 * simple PCI hotplug controller IO
207 * ACPI_PHP_IO_ADDR + :
208 * 0 - the hotplug description: slot(|event(remove/add);
209 * 1 - 1st php slot ctr/sts reg
210 * 2 - 2nd php slot ctr/sts reg
211 * ......
212 */
213 static uint32_t acpi_php_readb(void *opaque, uint32_t addr)
214 {
215 PHPSlots *hotplug_slots = opaque;
216 int num;
217 uint32_t val;
219 switch (addr)
220 {
221 case ACPI_PHP_IO_ADDR:
222 val = hotplug_slots->plug_evt;
223 break;
224 default:
225 num = addr - ACPI_PHP_IO_ADDR - 1;
226 val = hotplug_slots->slot[num].status;
227 }
229 fprintf(logfile, "ACPI PCI hotplug: read addr=0x%x, val=0x%x.\n",
230 addr, val);
232 return val;
233 }
235 static void acpi_php_writeb(void *opaque, uint32_t addr, uint32_t val)
236 {
237 PHPSlots *hotplug_slots = opaque;
238 int php_slot;
240 fprintf(logfile, "ACPI PCI hotplug: write addr=0x%x, val=0x%x.\n",
241 addr, val);
243 switch (addr)
244 {
245 case ACPI_PHP_IO_ADDR:
246 break;
247 default:
248 php_slot = addr - ACPI_PHP_IO_ADDR - 1;
249 if ( val == 0x1 ) { /* Eject command */
250 /* make _STA of the slot 0 */
251 hotplug_slots->slot[php_slot].status = 0;
253 /* clear the hotplug event */
254 hotplug_slots->plug_evt = 0;
256 /* power off the slot */
257 power_off_php_slot(php_slot);
259 /* signal the CP ACPI hot remove done. */
260 xenstore_record_dm_state("pci-removed");
261 }
262 }
263 }
265 static void pcislots_save(QEMUFile* f, void* opaque)
266 {
267 PHPSlots *s = (PHPSlots*)opaque;
268 int i;
269 for ( i = 0; i < ACPI_PHP_SLOT_NUM; i++ ) {
270 qemu_put_8s( f, &s->slot[i].status);
271 }
272 qemu_put_8s(f, &s->plug_evt);
273 }
275 static int pcislots_load(QEMUFile* f, void* opaque, int version_id)
276 {
277 PHPSlots *s = (PHPSlots*)opaque;
278 int i;
279 if (version_id != 1)
280 return -EINVAL;
281 for ( i = 0; i < ACPI_PHP_SLOT_NUM; i++ ) {
282 qemu_get_8s( f, &s->slot[i].status);
283 }
284 qemu_get_8s(f, &s->plug_evt);
285 return 0;
286 }
288 static void php_slots_init(void)
289 {
290 PHPSlots *slots = &php_slots;
291 int i;
292 memset(slots, 0, sizeof(PHPSlots));
294 /* update the pci slot status */
295 for ( i = 0; i < PHP_SLOT_LEN; i++ ) {
296 if ( test_pci_slot( PHP_TO_PCI_SLOT(i) ) == 1 )
297 slots->slot[i].status = 0xf;
298 }
301 /* ACPI PCI hotplug controller */
302 register_ioport_read(ACPI_PHP_IO_ADDR, ACPI_PHP_SLOT_NUM + 1, 1, acpi_php_readb, slots);
303 register_ioport_write(ACPI_PHP_IO_ADDR, ACPI_PHP_SLOT_NUM + 1, 1, acpi_php_writeb, slots);
304 register_savevm("pcislots", 0, 1, pcislots_save, pcislots_load, slots);
305 }
307 /* GPEx_STS occupy 1st half of the block, while GPEx_EN 2nd half */
308 static uint32_t gpe_sts_read(void *opaque, uint32_t addr)
309 {
310 GPEState *s = opaque;
312 return s->gpe0_sts[addr - ACPI_GPE0_BLK_ADDRESS];
313 }
315 /* write 1 to clear specific GPE bits */
316 static void gpe_sts_write(void *opaque, uint32_t addr, uint32_t val)
317 {
318 GPEState *s = opaque;
319 int hotplugged = 0;
321 fprintf(logfile, "gpe_sts_write: addr=0x%x, val=0x%x.\n", addr, val);
323 hotplugged = test_bit(&s->gpe0_sts[0], ACPI_PHP_GPE_BIT);
324 s->gpe0_sts[addr - ACPI_GPE0_BLK_ADDRESS] &= ~val;
325 if ( s->sci_asserted &&
326 hotplugged &&
327 !test_bit(&s->gpe0_sts[0], ACPI_PHP_GPE_BIT)) {
328 fprintf(logfile, "Clear the GPE0_STS bit for ACPI hotplug & deassert the IRQ.\n");
329 pic_set_irq(ACPI_SCI_IRQ, 0);
330 }
332 }
334 static uint32_t gpe_en_read(void *opaque, uint32_t addr)
335 {
336 GPEState *s = opaque;
338 return s->gpe0_en[addr - (ACPI_GPE0_BLK_ADDRESS + ACPI_GPE0_BLK_LEN / 2)];
339 }
341 /* write 0 to clear en bit */
342 static void gpe_en_write(void *opaque, uint32_t addr, uint32_t val)
343 {
344 GPEState *s = opaque;
345 int reg_count;
347 fprintf(logfile, "gpe_en_write: addr=0x%x, val=0x%x.\n", addr, val);
348 reg_count = addr - (ACPI_GPE0_BLK_ADDRESS + ACPI_GPE0_BLK_LEN / 2);
349 s->gpe0_en[reg_count] = val;
350 /* If disable GPE bit right after generating SCI on it,
351 * need deassert the intr to avoid redundant intrs
352 */
353 if ( s->sci_asserted &&
354 reg_count == (ACPI_PHP_GPE_BIT / 8) &&
355 !(val & (1 << (ACPI_PHP_GPE_BIT % 8))) ) {
356 fprintf(logfile, "deassert due to disable GPE bit.\n");
357 s->sci_asserted = 0;
358 pic_set_irq(ACPI_SCI_IRQ, 0);
359 }
361 }
363 static void gpe_save(QEMUFile* f, void* opaque)
364 {
365 GPEState *s = (GPEState*)opaque;
366 int i;
368 for ( i = 0; i < ACPI_GPE0_BLK_LEN / 2; i++ ) {
369 qemu_put_8s(f, &s->gpe0_sts[i]);
370 qemu_put_8s(f, &s->gpe0_en[i]);
371 }
373 qemu_put_8s(f, &s->sci_asserted);
374 if ( s->sci_asserted ) {
375 fprintf(logfile, "gpe_save with sci asserted!\n");
376 }
377 }
379 static int gpe_load(QEMUFile* f, void* opaque, int version_id)
380 {
381 GPEState *s = (GPEState*)opaque;
382 int i;
383 if (version_id != 1)
384 return -EINVAL;
386 for ( i = 0; i < ACPI_GPE0_BLK_LEN / 2; i++ ) {
387 qemu_get_8s(f, &s->gpe0_sts[i]);
388 qemu_get_8s(f, &s->gpe0_en[i]);
389 }
391 qemu_get_8s(f, &s->sci_asserted);
392 return 0;
393 }
395 static void gpe_acpi_init(void)
396 {
397 GPEState *s = &gpe_state;
398 memset(s, 0, sizeof(GPEState));
400 register_ioport_read(ACPI_GPE0_BLK_ADDRESS,
401 ACPI_GPE0_BLK_LEN / 2,
402 1,
403 gpe_sts_read,
404 s);
405 register_ioport_read(ACPI_GPE0_BLK_ADDRESS + ACPI_GPE0_BLK_LEN / 2,
406 ACPI_GPE0_BLK_LEN / 2,
407 1,
408 gpe_en_read,
409 s);
411 register_ioport_write(ACPI_GPE0_BLK_ADDRESS,
412 ACPI_GPE0_BLK_LEN / 2,
413 1,
414 gpe_sts_write,
415 s);
416 register_ioport_write(ACPI_GPE0_BLK_ADDRESS + ACPI_GPE0_BLK_LEN / 2,
417 ACPI_GPE0_BLK_LEN / 2,
418 1,
419 gpe_en_write,
420 s);
422 register_savevm("gpe", 0, 1, gpe_save, gpe_load, s);
423 }
425 static void acpi_sci_intr(GPEState *s)
426 {
427 if ( !test_bit(&s->gpe0_sts[0], ACPI_PHP_GPE_BIT) &&
428 test_bit(&s->gpe0_en[0], ACPI_PHP_GPE_BIT) ) {
430 set_bit(&s->gpe0_sts[0], ACPI_PHP_GPE_BIT);
431 s->sci_asserted = 1;
432 pic_set_irq(ACPI_SCI_IRQ, 1);
433 fprintf(logfile, "generate a sci for PHP.\n");
434 }
435 }
437 void acpi_php_del(int pci_slot)
438 {
439 GPEState *s = &gpe_state;
440 PHPSlots *hotplug_slots = &php_slots;
441 int php_slot = PCI_TO_PHP_SLOT(pci_slot);
443 if ( pci_slot < PHP_SLOT_START || pci_slot >= PHP_SLOT_END ) {
444 fprintf(logfile, "not find the pci slot %d when hot remove.\n", pci_slot);
446 return;
447 }
449 /* update the php controller status */
450 hotplug_slots->plug_evt = (((php_slot+1) << 4) | PHP_EVT_REMOVE);
452 /* generate a SCI interrupt */
453 acpi_sci_intr(s);
454 }
456 void acpi_php_add(int pci_slot)
457 {
458 GPEState *s = &gpe_state;
459 PHPSlots *hotplug_slots = &php_slots;
460 int php_slot = PCI_TO_PHP_SLOT(pci_slot);
461 char ret_str[30];
463 if ( pci_slot < PHP_SLOT_START || pci_slot >= PHP_SLOT_END ) {
464 fprintf(logfile, "hot add pci slot %d exceed.\n", pci_slot);
466 if ( pci_slot == 0 )
467 sprintf(ret_str, "no free hotplug slots");
468 else if ( pci_slot == -1 )
469 sprintf(ret_str, "wrong bdf or vslot");
471 if ( strlen(ret_str) > 0 )
472 xenstore_record_dm("parameter", ret_str);
474 return;
475 }
477 /* update the php controller status */
478 hotplug_slots->plug_evt = (((php_slot+1) << 4) | PHP_EVT_ADD);
480 /* update the slot status as present */
481 hotplug_slots->slot[php_slot].status = 0xf;
483 /* power on the slot */
484 power_on_php_slot(php_slot);
486 /* tell Control panel which slot for the new pass-throgh dev */
487 sprintf(ret_str, "0x%x", pci_slot);
488 xenstore_record_dm("parameter", ret_str);
490 /* signal the CP ACPI hot insert done */
491 xenstore_record_dm_state("pci-inserted");
493 /* generate a SCI interrupt */
494 acpi_sci_intr(s);
495 }
497 #endif /* CONFIG_PASSTHROUGH */
499 /* PIIX4 acpi pci configuration space, func 2 */
500 void pci_piix4_acpi_init(PCIBus *bus, int devfn)
501 {
502 PCIAcpiState *d;
503 uint8_t *pci_conf;
505 /* register a function 2 of PIIX4 */
506 d = (PCIAcpiState *)pci_register_device(
507 bus, "PIIX4 ACPI", sizeof(PCIAcpiState),
508 devfn, NULL, NULL);
510 pci_conf = d->dev.config;
511 pci_conf[0x00] = 0x86; /* Intel */
512 pci_conf[0x01] = 0x80;
513 pci_conf[0x02] = 0x13;
514 pci_conf[0x03] = 0x71;
515 pci_conf[0x08] = 0x01; /* B0 stepping */
516 pci_conf[0x09] = 0x00; /* base class */
517 pci_conf[0x0a] = 0x80; /* Sub class */
518 pci_conf[0x0b] = 0x06;
519 pci_conf[0x0e] = 0x00;
520 pci_conf[0x3d] = 0x01; /* Hardwired to PIRQA is used */
523 /* PMBA POWER MANAGEMENT BASE ADDRESS, hardcoded to 0x1f40
524 * to make shutdown work for IPF, due to IPF Guest Firmware
525 * will enumerate pci devices.
526 *
527 * TODO: if Guest Firmware or Guest OS will change this PMBA,
528 * More logic will be added.
529 */
530 pci_conf[0x40] = 0x41; /* Special device-specific BAR at 0x40 */
531 pci_conf[0x41] = 0x1f;
532 pci_conf[0x42] = 0x00;
533 pci_conf[0x43] = 0x00;
534 d->pm1_control = SCI_EN;
536 acpi_map((PCIDevice *)d, 0, 0x1f40, 0x10, PCI_ADDRESS_SPACE_IO);
538 #ifdef CONFIG_PASSTHROUGH
539 gpe_acpi_init();
540 php_slots_init();
541 register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, d);
542 #endif
544 register_savevm("piix4acpi", 0, 1, piix4acpi_save, piix4acpi_load, d);
545 }