Revision 574bbf7b

b/hw/apic.c
1
/*
2
 *  APIC support
3
 * 
4
 *  Copyright (c) 2004-2005 Fabrice Bellard
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 */
20
#include "vl.h"
21

  
22
//#define DEBUG_APIC
23

  
24
/* APIC Local Vector Table */
25
#define APIC_LVT_TIMER   0
26
#define APIC_LVT_THERMAL 1
27
#define APIC_LVT_PERFORM 2
28
#define APIC_LVT_LINT0   3
29
#define APIC_LVT_LINT1   4
30
#define APIC_LVT_ERROR   5
31
#define APIC_LVT_NB      6
32

  
33
/* APIC delivery modes */
34
#define APIC_DM_FIXED	0
35
#define APIC_DM_LOWPRI	1
36
#define APIC_DM_SMI	2
37
#define APIC_DM_NMI	4
38
#define APIC_DM_INIT	5
39
#define APIC_DM_SIPI	6
40
#define APIC_DM_EXTINT	7
41

  
42
#define APIC_TRIGGER_EDGE  0
43
#define APIC_TRIGGER_LEVEL 1
44

  
45
#define	APIC_LVT_TIMER_PERIODIC		(1<<17)
46
#define	APIC_LVT_MASKED			(1<<16)
47
#define	APIC_LVT_LEVEL_TRIGGER		(1<<15)
48
#define	APIC_LVT_REMOTE_IRR		(1<<14)
49
#define	APIC_INPUT_POLARITY		(1<<13)
50
#define	APIC_SEND_PENDING		(1<<12)
51

  
52
#define ESR_ILLEGAL_ADDRESS (1 << 7)
53

  
54
#define APIC_SV_ENABLE (1 << 8)
55

  
56
typedef struct APICState {
57
    CPUState *cpu_env;
58
    uint32_t apicbase;
59
    uint8_t id;
60
    uint8_t tpr;
61
    uint32_t spurious_vec;
62
    uint32_t isr[8];  /* in service register */
63
    uint32_t tmr[8];  /* trigger mode register */
64
    uint32_t irr[8]; /* interrupt request register */
65
    uint32_t lvt[APIC_LVT_NB];
66
    uint32_t esr; /* error register */
67
    uint32_t icr[2];
68

  
69
    uint32_t divide_conf;
70
    int count_shift;
71
    uint32_t initial_count;
72
    int64_t initial_count_load_time, next_time;
73
    QEMUTimer *timer;
74
} APICState;
75

  
76
static int apic_io_memory;
77

  
78
void cpu_set_apic_base(CPUState *env, uint64_t val)
79
{
80
    APICState *s = env->apic_state;
81
#ifdef DEBUG_APIC
82
    printf("cpu_set_apic_base: %016llx\n", val);
83
#endif
84
    s->apicbase = (val & 0xfffff000) | 
85
        (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
86
    /* if disabled, cannot be enabled again */
87
    if (!(val & MSR_IA32_APICBASE_ENABLE)) {
88
        s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
89
        env->cpuid_features &= ~CPUID_APIC;
90
        s->spurious_vec &= ~APIC_SV_ENABLE;
91
    }
92
}
93

  
94
uint64_t cpu_get_apic_base(CPUState *env)
95
{
96
    APICState *s = env->apic_state;
97
#ifdef DEBUG_APIC
98
    printf("cpu_get_apic_base: %016llx\n", (uint64_t)s->apicbase);
99
#endif
100
    return s->apicbase;
101
}
102

  
103
/* return -1 if no bit is set */
104
static int get_highest_priority_int(uint32_t *tab)
105
{
106
    int i;
107
    for(i = 0;i < 8; i++) {
108
        if (tab[i] != 0) {
109
            return i * 32 + ffs(tab[i]) - 1;
110
        }
111
    }
112
    return -1;
113
}
114

  
115
static inline void set_bit(uint32_t *tab, int index)
116
{
117
    int i, mask;
118
    i = index >> 5;
119
    mask = 1 << (index & 0x1f);
120
    tab[i] |= mask;
121
}
122

  
123
static inline void reset_bit(uint32_t *tab, int index)
124
{
125
    int i, mask;
126
    i = index >> 5;
127
    mask = 1 << (index & 0x1f);
128
    tab[i] &= ~mask;
129
}
130

  
131
static int apic_get_ppr(APICState *s)
132
{
133
    int tpr, isrv, ppr;
134

  
135
    tpr = (s->tpr >> 4);
136
    isrv = get_highest_priority_int(s->isr);
137
    if (isrv < 0)
138
        isrv = 0;
139
    isrv >>= 4;
140
    if (tpr >= isrv)
141
        ppr = s->tpr;
142
    else
143
        ppr = isrv << 4;
144
    return ppr;
145
}
146

  
147
/* signal the CPU if an irq is pending */
148
static void apic_update_irq(APICState *s)
149
{
150
    int irrv, isrv;
151
    irrv = get_highest_priority_int(s->irr);
152
    if (irrv < 0)
153
        return;
154
    isrv = get_highest_priority_int(s->isr);
155
    /* if the pending irq has less priority, we do not make a new request */
156
    if (isrv >= 0 && irrv >= isrv)
157
        return;
158
    cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
159
}
160

  
161
static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
162
{
163
    set_bit(s->irr, vector_num);
164
    if (trigger_mode)
165
        set_bit(s->tmr, vector_num);
166
    else
167
        reset_bit(s->tmr, vector_num);
168
    apic_update_irq(s);
169
}
170

  
171
static void apic_eoi(APICState *s)
172
{
173
    int isrv;
174
    isrv = get_highest_priority_int(s->isr);
175
    if (isrv < 0)
176
        return;
177
    reset_bit(s->isr, isrv);
178
    apic_update_irq(s);
179
}
180

  
181
int apic_get_interrupt(CPUState *env)
182
{
183
    APICState *s = env->apic_state;
184
    int intno;
185

  
186
    /* if the APIC is installed or enabled, we let the 8259 handle the
187
       IRQs */
188
    if (!s)
189
        return -1;
190
    if (!(s->spurious_vec & APIC_SV_ENABLE))
191
        return -1;
192
    
193
    /* XXX: spurious IRQ handling */
194
    intno = get_highest_priority_int(s->irr);
195
    if (intno < 0)
196
        return -1;
197
    reset_bit(s->irr, intno);
198
    set_bit(s->isr, intno);
199
    apic_update_irq(s);
200
    return intno;
201
}
202

  
203
static uint32_t apic_get_current_count(APICState *s)
204
{
205
    int64_t d;
206
    uint32_t val;
207
    d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >> 
208
        s->count_shift;
209
    if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
210
        /* periodic */
211
        val = s->initial_count - (d % (s->initial_count + 1));
212
    } else {
213
        if (d >= s->initial_count)
214
            val = 0;
215
        else
216
            val = s->initial_count - d;
217
    }
218
    return val;
219
}
220

  
221
static void apic_timer_update(APICState *s, int64_t current_time)
222
{
223
    int64_t next_time, d;
224
    
225
    if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
226
        d = (current_time - s->initial_count_load_time) >> 
227
            s->count_shift;
228
        if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
229
            d = ((d / (s->initial_count + 1)) + 1) * (s->initial_count + 1);
230
        } else {
231
            if (d >= s->initial_count)
232
                goto no_timer;
233
            d = s->initial_count + 1;
234
        }
235
        next_time = s->initial_count_load_time + (d << s->count_shift);
236
        qemu_mod_timer(s->timer, next_time);
237
        s->next_time = next_time;
238
    } else {
239
    no_timer:
240
        qemu_del_timer(s->timer);
241
    }
242
}
243

  
244
static void apic_timer(void *opaque)
245
{
246
    APICState *s = opaque;
247

  
248
    if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
249
        apic_set_irq(s, s->lvt[APIC_LVT_TIMER] & 0xff, APIC_TRIGGER_EDGE);
250
    }
251
    apic_timer_update(s, s->next_time);
252
}
253

  
254
static uint32_t apic_mem_readb(void *opaque, target_phys_addr_t addr)
255
{
256
    return 0;
257
}
258

  
259
static uint32_t apic_mem_readw(void *opaque, target_phys_addr_t addr)
260
{
261
    return 0;
262
}
263

  
264
static void apic_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
265
{
266
}
267

  
268
static void apic_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
269
{
270
}
271

  
272
static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
273
{
274
    CPUState *env;
275
    APICState *s;
276
    uint32_t val;
277
    int index;
278

  
279
    env = cpu_single_env;
280
    if (!env)
281
        return 0;
282
    s = env->apic_state;
283

  
284
    index = (addr >> 4) & 0xff;
285
    switch(index) {
286
    case 0x02: /* id */
287
        val = s->id << 24;
288
        break;
289
    case 0x03: /* version */
290
        val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */
291
        break;
292
    case 0x08:
293
        val = s->tpr;
294
        break;
295
    case 0x0a:
296
        /* ppr */
297
        val = apic_get_ppr(s);
298
        break;
299
    case 0x0f:
300
        val = s->spurious_vec;
301
        break;
302
    case 0x10 ... 0x17:
303
        val = s->isr[index & 7];
304
        break;
305
    case 0x18 ... 0x1f:
306
        val = s->tmr[index & 7];
307
        break;
308
    case 0x20 ... 0x27:
309
        val = s->irr[index & 7];
310
        break;
311
    case 0x28:
312
        val = s->esr;
313
        break;
314
    case 0x32 ... 0x37:
315
        val = s->lvt[index - 0x32];
316
        break;
317
    case 0x30:
318
    case 0x31:
319
        val = s->icr[index & 1];
320
        break;
321
    case 0x38:
322
        val = s->initial_count;
323
        break;
324
    case 0x39:
325
        val = apic_get_current_count(s);
326
        break;
327
    case 0x3e:
328
        val = s->divide_conf;
329
        break;
330
    default:
331
        s->esr |= ESR_ILLEGAL_ADDRESS;
332
        val = 0;
333
        break;
334
    }
335
#ifdef DEBUG_APIC
336
    printf("APIC read: %08x = %08x\n", (uint32_t)addr, val);
337
#endif
338
    return val;
339
}
340

  
341
static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
342
{
343
    CPUState *env;
344
    APICState *s;
345
    int index;
346

  
347
    env = cpu_single_env;
348
    if (!env)
349
        return;
350
    s = env->apic_state;
351

  
352
#ifdef DEBUG_APIC
353
    printf("APIC write: %08x = %08x\n", (uint32_t)addr, val);
354
#endif
355

  
356
    index = (addr >> 4) & 0xff;
357
    switch(index) {
358
    case 0x02:
359
        s->id = (val >> 24);
360
        break;
361
    case 0x08:
362
        s->tpr = val;
363
        break;
364
    case 0x0b: /* EOI */
365
        apic_eoi(s);
366
        break;
367
    case 0x0f:
368
        s->spurious_vec = val & 0x1ff;
369
        break;
370
    case 0x30:
371
    case 0x31:
372
        s->icr[index & 1] = val;
373
        break;
374
    case 0x32 ... 0x37:
375
        {
376
            int n = index - 0x32;
377
            s->lvt[n] = val;
378
            if (n == APIC_LVT_TIMER)
379
                apic_timer_update(s, qemu_get_clock(vm_clock));
380
        }
381
        break;
382
    case 0x38:
383
        s->initial_count = val;
384
        s->initial_count_load_time = qemu_get_clock(vm_clock);
385
        apic_timer_update(s, s->initial_count_load_time);
386
        break;
387
    case 0x3e:
388
        {
389
            int v;
390
            s->divide_conf = val & 0xb;
391
            v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
392
            s->count_shift = (v + 1) & 7;
393
        }
394
        break;
395
    default:
396
        s->esr |= ESR_ILLEGAL_ADDRESS;
397
        break;
398
    }
399
}
400

  
401

  
402

  
403
static CPUReadMemoryFunc *apic_mem_read[3] = {
404
    apic_mem_readb,
405
    apic_mem_readw,
406
    apic_mem_readl,
407
};
408

  
409
static CPUWriteMemoryFunc *apic_mem_write[3] = {
410
    apic_mem_writeb,
411
    apic_mem_writew,
412
    apic_mem_writel,
413
};
414

  
415
int apic_init(CPUState *env)
416
{
417
    APICState *s;
418
    int i;
419

  
420
    s = malloc(sizeof(APICState));
421
    if (!s)
422
        return -1;
423
    memset(s, 0, sizeof(*s));
424
    env->apic_state = s;
425
    s->cpu_env = env;
426
    s->apicbase = 0xfee00000 | 
427
        MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE;
428
    for(i = 0; i < APIC_LVT_NB; i++)
429
        s->lvt[i] = 1 << 16; /* mask LVT */
430
    s->spurious_vec = 0xff;
431

  
432
    if (apic_io_memory == 0) {
433
        /* NOTE: the APIC is directly connected to the CPU - it is not
434
           on the global memory bus. */
435
        apic_io_memory = cpu_register_io_memory(0, apic_mem_read, 
436
                                                apic_mem_write, NULL);
437
        cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, apic_io_memory);
438
    }
439
    s->timer = qemu_new_timer(vm_clock, apic_timer, s);
440
    return 0;
441
}

Also available in: Unified diff