aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ioemu/iodev/ioapic.cc
blob: 8aaf67f848f8eade1008bce213cc910324fdbcf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/////////////////////////////////////////////////////////////////////////
// $Id: ioapic.cc,v 1.11 2002/11/19 05:47:45 bdenney Exp $
/////////////////////////////////////////////////////////////////////////
//
#include <stdio.h>
#include  "bochs.h"
#if BX_SUPPORT_APIC

class bx_ioapic_c bx_ioapic;
#define LOG_THIS  bx_ioapic.

void
bx_io_redirect_entry_t::parse_value ()
{
  dest = (value >> 56) & 0xff;
  masked = (value >> 16) & 1;
  trig_mode = (value >> 15) & 1;
  remote_irr = (value >> 14) & 1;
  polarity = (value >> 13) & 1;
  //delivery_status = (value >> 12) & 1;
  delivery_status = 0;  // always say the message has gone through
  dest_mode = (value >> 11) & 1;
  delivery_mode = (value >> 8) & 7;
  vector = (value >> 0) & 0xff;
}

void
bx_io_redirect_entry_t::sprintf_self (char *buf)
{
  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 ()
{
  put("IOAP");
  settype(IOAPICLOG);
}

bx_ioapic_c::~bx_ioapic_c () {
}

void 
bx_ioapic_c::init () 
{
  bx_generic_apic_c::init ();
  BX_DEBUG(("initializing I/O APIC"));
  base_addr = 0xfec00000;
  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);
  }
  irr = 0;
}

void 
bx_ioapic_c::reset (unsigned type) 
{
}

void 
bx_ioapic_c::read_aligned(Bit32u address, Bit32u *data, unsigned len)
{
  BX_DEBUG( ("I/O APIC read_aligned addr=%08x, len=%d", address, len));
  BX_ASSERT (len == 4);
  address &= 0xff;
  if (address == 0x00) {
    // select register
    *data = ioregsel;
    return;
  } else if (address != 0x10) {
      BX_PANIC(("IOAPIC: read from unsupported address"));
  }
  // only reached when reading data register
  switch (ioregsel) {
  case 0x00:  // APIC ID
    *data = ((id & 0xf) << 24);
    return;
  case 0x01:  // version
    *data = (((BX_IOAPIC_NUM_PINS-1) & 0xff) << 16) 
            | (BX_IOAPIC_VERSION_ID & 0x0f);
    return;
  case 0x02:
    BX_INFO(("IOAPIC: arbitration ID unsupported, returned 0"));
    *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;
    }
    BX_PANIC(("IOAPIC: IOREGSEL points to undefined register %02x", ioregsel));
  }
}

void 
bx_ioapic_c::write(Bit32u address, Bit32u *value, unsigned len)
{
  BX_DEBUG(("IOAPIC: write addr=%08x, data=%08x, len=%d", address, *value, len));
  address &= 0xff;
  if (address == 0x00)  {
    ioregsel = *value;
    return;
  } else if (address != 0x10) {
    BX_PANIC(("IOAPIC: write to unsupported address"));
  }
  // only reached when writing data register
  switch (ioregsel) {
    case 0x00: // set APIC ID
      {
	Bit8u newid = (*value >> 24) & 0xf;
	BX_INFO(("IOAPIC: setting id to 0x%x", newid));
	set_id (newid);
	return;
      }
    case 0x01: // version
    case 0x02: // arbitration id
      BX_INFO(("IOAPIC: could not write, IOREGSEL=0x%02x", ioregsel));
      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);
	BX_DEBUG(("IOAPIC: now entry[%d] is %s", index, buf));
	service_ioapic ();
	return;
      }
      BX_PANIC(("IOAPIC: IOREGSEL points to undefined register %02x", ioregsel));
  }
}

void bx_ioapic_c::trigger_irq (unsigned vector, unsigned from) 
{
  BX_DEBUG(("IOAPIC: received interrupt %d", vector));
  if (vector >= 0 && vector < BX_IOAPIC_NUM_PINS) {
    Bit32u bit = 1<<vector;
    if ((irr & bit) == 0) {
      irr |= bit;
      service_ioapic ();
    }
  } else BX_PANIC(("IOAPIC: vector %d out of range", vector));
}

void bx_ioapic_c::untrigger_irq (unsigned num, unsigned from) 
{
  BX_DEBUG(("IOAPIC: interrupt %d went away", num));
}

void bx_ioapic_c::service_ioapic ()
{
  // look in IRR and deliver any interrupts that are not masked.
  BX_DEBUG(("IOAPIC: servicing"));
  for (unsigned bit=0; bit < BX_IOAPIC_NUM_PINS; bit++) {
    if (irr & (1<<bit)) {
      bx_io_redirect_entry_t *entry = ioredtbl + bit;
      if (!entry->masked) {
	// clear irr bit and deliver
	bx_bool done = deliver (entry->dest, entry->dest_mode, entry->delivery_mode, entry->vector, entry->polarity, entry->trig_mode);
	if (done) irr &= ~(1<<bit);
      }
    }
  }
}

#endif /* if BX_SUPPORT_APIC */