2001-10-03 17:10:38 +04:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
2006-01-08 15:01:25 +03:00
|
|
|
// $Id: ioapic.cc,v 1.23 2006-01-08 12:01:24 vruppert Exp $
|
2001-10-03 17:10:38 +04:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2001-05-23 11:48:11 +04:00
|
|
|
#include <stdio.h>
|
2004-06-19 19:20:15 +04:00
|
|
|
|
|
|
|
#include "iodev.h"
|
2002-11-19 08:47:45 +03:00
|
|
|
#if BX_SUPPORT_APIC
|
2001-05-23 11:48:11 +04:00
|
|
|
|
|
|
|
class bx_ioapic_c bx_ioapic;
|
|
|
|
#define LOG_THIS bx_ioapic.
|
|
|
|
|
2004-09-16 01:48:57 +04:00
|
|
|
void bx_io_redirect_entry_t::parse_value ()
|
2001-05-23 11:48:11 +04:00
|
|
|
{
|
2005-12-13 23:27:23 +03:00
|
|
|
dest = (Bit8u)((hi >> 24) & APIC_ID_MASK);
|
|
|
|
masked = (Bit8u)((lo >> 16) & 1);
|
|
|
|
trig_mode = (Bit8u)((lo >> 15) & 1);
|
|
|
|
remote_irr = (Bit8u)((lo >> 14) & 1);
|
|
|
|
polarity = (Bit8u)((lo >> 13) & 1);
|
|
|
|
//delivery_status = (lo >> 12) & 1;
|
2004-09-16 01:48:57 +04:00
|
|
|
delivery_status = 0; // we'll change this later...
|
2005-12-13 23:27:23 +03:00
|
|
|
dest_mode = (Bit8u)((lo >> 11) & 1);
|
|
|
|
delivery_mode = (Bit8u)((lo >> 8) & 7);
|
|
|
|
vector = (Bit8u)(lo & 0xff);
|
2001-05-23 11:48:11 +04:00
|
|
|
}
|
|
|
|
|
2004-09-16 01:48:57 +04:00
|
|
|
void bx_io_redirect_entry_t::sprintf_self (char *buf)
|
2001-05-23 11:48:11 +04:00
|
|
|
{
|
|
|
|
sprintf (buf, "dest=%02x, masked=%d, trig_mode=%d, remote_irr=%d, polarity=%d, delivery_status=%d, dest_mode=%d, delivery_mode=%d, vector=%02x", dest, masked, trig_mode, remote_irr, polarity, delivery_status, dest_mode, delivery_mode, vector);
|
|
|
|
}
|
|
|
|
|
|
|
|
bx_ioapic_c::bx_ioapic_c ()
|
|
|
|
: bx_generic_apic_c ()
|
|
|
|
{
|
2001-06-27 23:16:01 +04:00
|
|
|
put("IOAP");
|
2001-05-23 11:48:11 +04:00
|
|
|
settype(IOAPICLOG);
|
|
|
|
}
|
|
|
|
|
2004-09-16 01:48:57 +04:00
|
|
|
bx_ioapic_c::~bx_ioapic_c () {}
|
2001-05-23 11:48:11 +04:00
|
|
|
|
2004-09-16 01:48:57 +04:00
|
|
|
void bx_ioapic_c::init ()
|
2001-05-23 11:48:11 +04:00
|
|
|
{
|
|
|
|
bx_generic_apic_c::init ();
|
2005-04-27 22:09:27 +04:00
|
|
|
BX_INFO(("initializing I/O APIC"));
|
2001-05-23 11:48:11 +04:00
|
|
|
base_addr = 0xfec00000;
|
2004-09-16 01:48:57 +04:00
|
|
|
set_id(BX_IOAPIC_DEFAULT_ID);
|
2001-05-23 11:48:11 +04:00
|
|
|
ioregsel = 0;
|
|
|
|
// all interrupts masked
|
|
|
|
for (int i=0; i<BX_IOAPIC_NUM_PINS; i++) {
|
|
|
|
ioredtbl[i].set_even_word (0x00010000);
|
|
|
|
ioredtbl[i].set_odd_word (0x00000000);
|
|
|
|
}
|
2006-01-08 15:01:25 +03:00
|
|
|
intin = 0;
|
2001-05-23 11:48:11 +04:00
|
|
|
irr = 0;
|
|
|
|
}
|
|
|
|
|
2004-09-16 01:48:57 +04:00
|
|
|
void bx_ioapic_c::reset (unsigned type)
|
2002-08-27 23:54:46 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2004-09-16 01:48:57 +04:00
|
|
|
void bx_ioapic_c::read_aligned(Bit32u address, Bit32u *data, unsigned len)
|
2001-05-23 11:48:11 +04:00
|
|
|
{
|
2002-03-20 05:41:19 +03:00
|
|
|
BX_DEBUG( ("I/O APIC read_aligned addr=%08x, len=%d", address, len));
|
2001-05-23 11:48:11 +04:00
|
|
|
BX_ASSERT (len == 4);
|
|
|
|
address &= 0xff;
|
|
|
|
if (address == 0x00) {
|
|
|
|
// select register
|
|
|
|
*data = ioregsel;
|
|
|
|
return;
|
|
|
|
} else if (address != 0x10) {
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("IOAPIC: read from unsupported address"));
|
2001-05-23 11:48:11 +04:00
|
|
|
}
|
|
|
|
// only reached when reading data register
|
|
|
|
switch (ioregsel) {
|
2004-09-16 01:48:57 +04:00
|
|
|
case 0x00: // APIC ID, note this is 4bits, the upper 4 are reserved
|
|
|
|
*data = ((id & APIC_ID_MASK) << 24);
|
2001-05-23 11:48:11 +04:00
|
|
|
return;
|
|
|
|
case 0x01: // version
|
2005-12-13 23:27:23 +03:00
|
|
|
*data = BX_IOAPIC_VERSION_ID;
|
2001-05-23 11:48:11 +04:00
|
|
|
return;
|
|
|
|
case 0x02:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("IOAPIC: arbitration ID unsupported, returned 0"));
|
2001-05-23 11:48:11 +04:00
|
|
|
*data = 0;
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
int index = (ioregsel - 0x10) >> 1;
|
|
|
|
if (index >= 0 && index < BX_IOAPIC_NUM_PINS) {
|
|
|
|
bx_io_redirect_entry_t *entry = ioredtbl + index;
|
|
|
|
*data = (ioregsel&1) ? entry->get_odd_word() : entry->get_even_word ();
|
|
|
|
return;
|
|
|
|
}
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("IOAPIC: IOREGSEL points to undefined register %02x", ioregsel));
|
2001-05-23 11:48:11 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-16 01:48:57 +04:00
|
|
|
void bx_ioapic_c::write(Bit32u address, Bit32u *value, unsigned len)
|
2001-05-23 11:48:11 +04:00
|
|
|
{
|
2002-03-20 05:41:19 +03:00
|
|
|
BX_DEBUG(("IOAPIC: write addr=%08x, data=%08x, len=%d", address, *value, len));
|
2001-05-23 11:48:11 +04:00
|
|
|
address &= 0xff;
|
|
|
|
if (address == 0x00) {
|
|
|
|
ioregsel = *value;
|
|
|
|
return;
|
|
|
|
} else if (address != 0x10) {
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("IOAPIC: write to unsupported address"));
|
2001-05-23 11:48:11 +04:00
|
|
|
}
|
|
|
|
// only reached when writing data register
|
|
|
|
switch (ioregsel) {
|
|
|
|
case 0x00: // set APIC ID
|
|
|
|
{
|
2004-09-16 01:48:57 +04:00
|
|
|
Bit8u newid = (*value >> 24) & APIC_ID_MASK;
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("IOAPIC: setting id to 0x%x", newid));
|
2001-05-23 11:48:11 +04:00
|
|
|
set_id (newid);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case 0x01: // version
|
|
|
|
case 0x02: // arbitration id
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("IOAPIC: could not write, IOREGSEL=0x%02x", ioregsel));
|
2001-05-23 11:48:11 +04:00
|
|
|
return;
|
|
|
|
default:
|
|
|
|
int index = (ioregsel - 0x10) >> 1;
|
|
|
|
if (index >= 0 && index < BX_IOAPIC_NUM_PINS) {
|
|
|
|
bx_io_redirect_entry_t *entry = ioredtbl + index;
|
|
|
|
if (ioregsel&1)
|
|
|
|
entry->set_odd_word (*value);
|
|
|
|
else
|
|
|
|
entry->set_even_word (*value);
|
|
|
|
char buf[1024];
|
|
|
|
entry->sprintf_self (buf);
|
2002-03-20 05:41:19 +03:00
|
|
|
BX_DEBUG(("IOAPIC: now entry[%d] is %s", index, buf));
|
2001-05-23 11:48:11 +04:00
|
|
|
service_ioapic ();
|
|
|
|
return;
|
|
|
|
}
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("IOAPIC: IOREGSEL points to undefined register %02x", ioregsel));
|
2001-05-23 11:48:11 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-01-01 14:33:06 +03:00
|
|
|
void bx_ioapic_c::set_irq_level(Bit8u int_in, bx_bool level)
|
2001-05-23 11:48:11 +04:00
|
|
|
{
|
2006-01-01 14:33:06 +03:00
|
|
|
BX_DEBUG(("set_irq_level(): INTIN%d: level=%d", int_in, level));
|
|
|
|
if (int_in < BX_IOAPIC_NUM_PINS) {
|
|
|
|
Bit32u bit = 1<<int_in;
|
2006-01-08 15:01:25 +03:00
|
|
|
if ((level<<int_in) != (intin & bit)) {
|
|
|
|
bx_io_redirect_entry_t *entry = ioredtbl + int_in;
|
|
|
|
entry->parse_value();
|
|
|
|
if (entry->trig_mode) {
|
|
|
|
// level triggered
|
|
|
|
if (level) {
|
|
|
|
intin |= bit;
|
|
|
|
irr |= bit;
|
|
|
|
service_ioapic ();
|
|
|
|
} else {
|
|
|
|
intin &= ~bit;
|
|
|
|
irr &= ~bit;
|
|
|
|
}
|
2006-01-01 14:33:06 +03:00
|
|
|
} else {
|
2006-01-08 15:01:25 +03:00
|
|
|
// edge triggered
|
|
|
|
if (level) {
|
|
|
|
intin |= bit;
|
|
|
|
irr |= bit;
|
|
|
|
service_ioapic ();
|
|
|
|
} else {
|
|
|
|
intin &= ~bit;
|
|
|
|
}
|
2006-01-01 14:33:06 +03:00
|
|
|
}
|
2005-12-31 17:46:21 +03:00
|
|
|
}
|
|
|
|
}
|
2001-05-23 11:48:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void bx_ioapic_c::service_ioapic ()
|
|
|
|
{
|
2004-09-16 01:48:57 +04:00
|
|
|
static unsigned int stuck = 0;
|
2001-05-23 11:48:11 +04:00
|
|
|
// look in IRR and deliver any interrupts that are not masked.
|
2002-03-20 05:41:19 +03:00
|
|
|
BX_DEBUG(("IOAPIC: servicing"));
|
2001-05-23 11:48:11 +04:00
|
|
|
for (unsigned bit=0; bit < BX_IOAPIC_NUM_PINS; bit++) {
|
2006-01-01 14:33:06 +03:00
|
|
|
Bit32u mask = 1<<bit;
|
|
|
|
if (irr & mask) {
|
2001-05-23 11:48:11 +04:00
|
|
|
bx_io_redirect_entry_t *entry = ioredtbl + bit;
|
2004-09-16 01:48:57 +04:00
|
|
|
entry->parse_value();
|
2005-12-13 23:27:23 +03:00
|
|
|
if (! entry->masked) {
|
2006-01-01 14:33:06 +03:00
|
|
|
// clear irr bit and deliver
|
|
|
|
if (entry->delivery_mode == 7) {
|
|
|
|
BX_PANIC(("ExtINT not implemented yet"));
|
|
|
|
}
|
|
|
|
bx_bool done = deliver (entry->dest, entry->dest_mode, entry->delivery_mode, entry->vector, entry->polarity, entry->trig_mode);
|
|
|
|
if (done) {
|
|
|
|
if (! entry->trig_mode)
|
|
|
|
irr &= ~mask;
|
|
|
|
entry->delivery_status = 0;
|
|
|
|
stuck = 0;
|
|
|
|
} else {
|
|
|
|
entry->delivery_status = 1;
|
|
|
|
stuck++;
|
|
|
|
if (stuck > 5)
|
|
|
|
BX_INFO(("vector %#x stuck?\n", entry->vector));
|
|
|
|
}
|
2001-05-23 11:48:11 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-11-19 08:47:45 +03:00
|
|
|
|
|
|
|
#endif /* if BX_SUPPORT_APIC */
|