Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/arch/x86/hvm/vpic.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * i8259 interrupt controller emulation
3
 * 
4
 * Copyright (c) 2003-2004 Fabrice Bellard
5
 * Copyright (c) 2005 Intel Corperation
6
 * Copyright (c) 2006 Keir Fraser, XenSource Inc.
7
 * 
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 */
26
27
#include <xen/types.h>
28
#include <xen/event.h>
29
#include <xen/lib.h>
30
#include <xen/errno.h>
31
#include <xen/sched.h>
32
#include <xen/trace.h>
33
#include <asm/hvm/hvm.h>
34
#include <asm/hvm/io.h>
35
#include <asm/hvm/support.h>
36
37
0
#define vpic_domain(v) (container_of((v), struct domain, \
38
0
                        arch.hvm_domain.vpic[!vpic->is_master]))
39
#define __vpic_lock(v) &container_of((v), struct hvm_domain, \
40
                                        vpic[!(v)->is_master])->irq_lock
41
0
#define vpic_lock(v)   spin_lock(__vpic_lock(v))
42
0
#define vpic_unlock(v) spin_unlock(__vpic_lock(v))
43
#define vpic_is_locked(v) spin_is_locked(__vpic_lock(v))
44
0
#define vpic_elcr_mask(v) (vpic->is_master ? (uint8_t)0xf8 : (uint8_t)0xde);
45
46
/* Return the highest priority found in mask. Return 8 if none. */
47
0
#define VPIC_PRIO_NONE 8
48
static int vpic_get_priority(struct hvm_hw_vpic *vpic, uint8_t mask)
49
0
{
50
0
    int prio;
51
0
52
0
    ASSERT(vpic_is_locked(vpic));
53
0
54
0
    if ( mask == 0 )
55
0
        return VPIC_PRIO_NONE;
56
0
57
0
    /* prio = ffs(mask ROR vpic->priority_add); */
58
0
    asm ( "ror %%cl,%b1 ; rep; bsf %1,%0"
59
0
          : "=r" (prio) : "q" ((uint32_t)mask), "c" (vpic->priority_add) );
60
0
    return prio;
61
0
}
62
63
/* Return the PIC's highest priority pending interrupt. Return -1 if none. */
64
static int vpic_get_highest_priority_irq(struct hvm_hw_vpic *vpic)
65
0
{
66
0
    int cur_priority, priority, irq;
67
0
    uint8_t mask;
68
0
69
0
    ASSERT(vpic_is_locked(vpic));
70
0
71
0
    mask = vpic->irr & ~vpic->imr;
72
0
    priority = vpic_get_priority(vpic, mask);
73
0
    if ( priority == VPIC_PRIO_NONE )
74
0
        return -1;
75
0
76
0
    irq = (priority + vpic->priority_add) & 7;
77
0
78
0
    /*
79
0
     * Compute current priority. If special fully nested mode on the master,
80
0
     * the IRQ coming from the slave is not taken into account for the
81
0
     * priority computation. In special mask mode, masked interrupts do not
82
0
     * block lower-priority interrupts even if their IS bit is set.
83
0
     */
84
0
    mask = vpic->isr;
85
0
    if ( vpic->special_fully_nested_mode && vpic->is_master && (irq == 2) )
86
0
        mask &= ~(1 << 2);
87
0
    if ( vpic->special_mask_mode )
88
0
        mask &= ~vpic->imr;
89
0
    cur_priority = vpic_get_priority(vpic, mask);
90
0
91
0
    /* If a higher priority is found then an irq should be generated. */
92
0
    return (priority < cur_priority) ? irq : -1;
93
0
}
94
95
static void vpic_update_int_output(struct hvm_hw_vpic *vpic)
96
0
{
97
0
    int irq;
98
0
99
0
    ASSERT(vpic_is_locked(vpic));
100
0
101
0
    irq = vpic_get_highest_priority_irq(vpic);
102
0
    TRACE_3D(TRC_HVM_EMUL_PIC_INT_OUTPUT, vpic->int_output, vpic->is_master,
103
0
             irq);
104
0
    if ( vpic->int_output == (irq >= 0) )
105
0
        return;
106
0
107
0
    /* INT line transition L->H or H->L. */
108
0
    vpic->int_output = !vpic->int_output;
109
0
110
0
    if ( vpic->int_output )
111
0
    {
112
0
        if ( vpic->is_master )
113
0
        {
114
0
            /* Master INT line is connected in Virtual Wire Mode. */
115
0
            struct vcpu *v = vpic_domain(vpic)->arch.hvm_domain.i8259_target;
116
0
            if ( v != NULL )
117
0
            {
118
0
                TRACE_1D(TRC_HVM_EMUL_PIC_KICK, irq);
119
0
                vcpu_kick(v);
120
0
            }
121
0
        }
122
0
        else
123
0
        {
124
0
            /* Assert slave line in master PIC. */
125
0
            (--vpic)->irr |= 1 << 2;
126
0
            vpic_update_int_output(vpic);
127
0
        }
128
0
    }
129
0
    else if ( !vpic->is_master )
130
0
    {
131
0
        /* Clear slave line in master PIC. */
132
0
        (--vpic)->irr &= ~(1 << 2);
133
0
        vpic_update_int_output(vpic);
134
0
    }
135
0
}
136
137
static void __vpic_intack(struct hvm_hw_vpic *vpic, int irq)
138
0
{
139
0
    uint8_t mask = 1 << irq;
140
0
141
0
    ASSERT(vpic_is_locked(vpic));
142
0
143
0
    TRACE_2D(TRC_HVM_EMUL_PIC_INTACK, vpic->is_master, irq);
144
0
    /* Edge-triggered: clear the IRR (forget the edge). */
145
0
    if ( !(vpic->elcr & mask) )
146
0
        vpic->irr &= ~mask;
147
0
148
0
    if ( !vpic->auto_eoi )
149
0
        vpic->isr |= mask;
150
0
    else if ( vpic->rotate_on_auto_eoi )
151
0
        vpic->priority_add = (irq + 1) & 7;
152
0
153
0
    vpic_update_int_output(vpic);
154
0
}
155
156
static int vpic_intack(struct hvm_hw_vpic *vpic)
157
0
{
158
0
    int irq = -1;
159
0
160
0
    vpic_lock(vpic);
161
0
162
0
    if ( !vpic->int_output )
163
0
        goto out;
164
0
165
0
    irq = vpic_get_highest_priority_irq(vpic);
166
0
    BUG_ON(irq < 0);
167
0
    __vpic_intack(vpic, irq);
168
0
169
0
    if ( (irq == 2) && vpic->is_master )
170
0
    {
171
0
        vpic++; /* Slave PIC */
172
0
        irq = vpic_get_highest_priority_irq(vpic);
173
0
        BUG_ON(irq < 0);
174
0
        __vpic_intack(vpic, irq);
175
0
        irq += 8;
176
0
    }
177
0
178
0
 out:
179
0
    vpic_unlock(vpic);
180
0
    return irq;
181
0
}
182
183
static void vpic_ioport_write(
184
    struct hvm_hw_vpic *vpic, uint32_t addr, uint32_t val)
185
0
{
186
0
    int priority, cmd, irq;
187
0
    uint8_t mask, unmasked = 0;
188
0
189
0
    vpic_lock(vpic);
190
0
191
0
    if ( (addr & 1) == 0 )
192
0
    {
193
0
        if ( val & 0x10 )
194
0
        {
195
0
            /* ICW1 */
196
0
            /* Clear edge-sensing logic. */
197
0
            vpic->irr &= vpic->elcr;
198
0
199
0
            unmasked = vpic->imr;
200
0
            /* No interrupts masked or in service. */
201
0
            vpic->imr = vpic->isr = 0;
202
0
203
0
            /* IR7 is lowest priority. */
204
0
            vpic->priority_add = 0;
205
0
            vpic->rotate_on_auto_eoi = 0;
206
0
207
0
            vpic->special_mask_mode = 0;
208
0
            vpic->readsel_isr = 0;
209
0
            vpic->poll = 0;
210
0
211
0
            if ( !(val & 1) )
212
0
            {
213
0
                /* NO ICW4: ICW4 features are cleared. */
214
0
                vpic->auto_eoi = 0;
215
0
                vpic->special_fully_nested_mode = 0;
216
0
            }
217
0
218
0
            vpic->init_state = ((val & 3) << 2) | 1;
219
0
        }
220
0
        else if ( val & 0x08 )
221
0
        {
222
0
            /* OCW3 */
223
0
            if ( val & 0x04 )
224
0
                vpic->poll = 1;
225
0
            if ( val & 0x02 )
226
0
                vpic->readsel_isr = val & 1;
227
0
            if ( val & 0x40 )
228
0
                vpic->special_mask_mode = (val >> 5) & 1;
229
0
        }
230
0
        else
231
0
        {
232
0
            /* OCW2 */
233
0
            cmd = val >> 5;
234
0
            switch ( cmd )
235
0
            {
236
0
            case 0: /* Rotate in AEOI Mode (Clear) */
237
0
            case 4: /* Rotate in AEOI Mode (Set)   */
238
0
                vpic->rotate_on_auto_eoi = cmd >> 2;
239
0
                break;
240
0
            case 1: /* Non-Specific EOI            */
241
0
            case 5: /* Non-Specific EOI & Rotate   */
242
0
                mask = vpic->isr;
243
0
                if ( vpic->special_mask_mode )
244
0
                    mask &= ~vpic->imr; /* SMM: ignore masked IRs. */
245
0
                priority = vpic_get_priority(vpic, mask);
246
0
                if ( priority == VPIC_PRIO_NONE )
247
0
                    break;
248
0
                irq = (priority + vpic->priority_add) & 7;
249
0
                vpic->isr &= ~(1 << irq);
250
0
                if ( cmd == 5 )
251
0
                    vpic->priority_add = (irq + 1) & 7;
252
0
                break;
253
0
            case 3: /* Specific EOI                */
254
0
            case 7: /* Specific EOI & Rotate       */
255
0
                irq = val & 7;
256
0
                vpic->isr &= ~(1 << irq);
257
0
                if ( cmd == 7 )
258
0
                    vpic->priority_add = (irq + 1) & 7;
259
0
                /* Release lock and EOI the physical interrupt (if any). */
260
0
                vpic_update_int_output(vpic);
261
0
                vpic_unlock(vpic);
262
0
                hvm_dpci_eoi(current->domain,
263
0
                             hvm_isa_irq_to_gsi((addr >> 7) ? (irq|8) : irq),
264
0
                             NULL);
265
0
                return; /* bail immediately */
266
0
            case 6: /* Set Priority                */
267
0
                vpic->priority_add = (val + 1) & 7;
268
0
                break;
269
0
            }
270
0
        }
271
0
    }
272
0
    else
273
0
    {
274
0
        switch ( vpic->init_state & 3 )
275
0
        {
276
0
        case 0:
277
0
            /* OCW1 */
278
0
            unmasked = vpic->imr & (~val);
279
0
            vpic->imr = val;
280
0
            break;
281
0
        case 1:
282
0
            /* ICW2 */
283
0
            vpic->irq_base = val & 0xf8;
284
0
            vpic->init_state++;
285
0
            if ( !(vpic->init_state & 8) )
286
0
                break; /* CASCADE mode: wait for write to ICW3. */
287
0
            /* SNGL mode: fall through (no ICW3). */
288
0
        case 2:
289
0
            /* ICW3 */
290
0
            vpic->init_state++;
291
0
            if ( !(vpic->init_state & 4) )
292
0
                vpic->init_state = 0; /* No ICW4: init done */
293
0
            break;
294
0
        case 3:
295
0
            /* ICW4 */
296
0
            vpic->special_fully_nested_mode = (val >> 4) & 1;
297
0
            vpic->auto_eoi = (val >> 1) & 1;
298
0
            vpic->init_state = 0;
299
0
            break;
300
0
        }
301
0
    }
302
0
303
0
    vpic_update_int_output(vpic);
304
0
305
0
    vpic_unlock(vpic);
306
0
307
0
    if ( unmasked )
308
0
        pt_may_unmask_irq(vpic_domain(vpic), NULL);
309
0
}
310
311
static uint32_t vpic_ioport_read(struct hvm_hw_vpic *vpic, uint32_t addr)
312
0
{
313
0
    if ( vpic->poll )
314
0
    {
315
0
        vpic->poll = 0;
316
0
        return vpic_intack(vpic);
317
0
    }
318
0
319
0
    if ( (addr & 1) == 0 )
320
0
        return (vpic->readsel_isr ? vpic->isr : vpic->irr);
321
0
322
0
    return vpic->imr;
323
0
}
324
325
static int vpic_intercept_pic_io(
326
    int dir, unsigned int port, unsigned int bytes, uint32_t *val)
327
0
{
328
0
    struct hvm_hw_vpic *vpic;
329
0
330
0
    if ( bytes != 1 )
331
0
    {
332
0
        gdprintk(XENLOG_WARNING, "PIC_IO bad access size %d\n", bytes);
333
0
        *val = ~0;
334
0
        return X86EMUL_OKAY;
335
0
    }
336
0
337
0
    vpic = &current->domain->arch.hvm_domain.vpic[port >> 7];
338
0
339
0
    if ( dir == IOREQ_WRITE )
340
0
        vpic_ioport_write(vpic, port, (uint8_t)*val);
341
0
    else
342
0
        *val = (uint8_t)vpic_ioport_read(vpic, port);
343
0
344
0
    return X86EMUL_OKAY;
345
0
}
346
347
static int vpic_intercept_elcr_io(
348
    int dir, unsigned int port, unsigned int bytes, uint32_t *val)
349
0
{
350
0
    struct hvm_hw_vpic *vpic;
351
0
    uint32_t data;
352
0
353
0
    BUG_ON(bytes != 1);
354
0
355
0
    vpic = &current->domain->arch.hvm_domain.vpic[port & 1];
356
0
357
0
    if ( dir == IOREQ_WRITE )
358
0
    {
359
0
        /* Some IRs are always edge trig. Slave IR is always level trig. */
360
0
        data = *val & vpic_elcr_mask(vpic);
361
0
        if ( vpic->is_master )
362
0
            data |= 1 << 2;
363
0
        vpic->elcr = data;
364
0
    }
365
0
    else
366
0
    {
367
0
        /* Reader should not see hardcoded level-triggered slave IR. */
368
0
        *val = vpic->elcr & vpic_elcr_mask(vpic);
369
0
    }
370
0
371
0
    return X86EMUL_OKAY;
372
0
}
373
374
static int vpic_save(struct domain *d, hvm_domain_context_t *h)
375
0
{
376
0
    struct hvm_hw_vpic *s;
377
0
    int i;
378
0
379
0
    if ( !has_vpic(d) )
380
0
        return 0;
381
0
382
0
    /* Save the state of both PICs */
383
0
    for ( i = 0; i < 2 ; i++ )
384
0
    {
385
0
        s = &d->arch.hvm_domain.vpic[i];
386
0
        if ( hvm_save_entry(PIC, i, h, s) )
387
0
            return 1;
388
0
    }
389
0
390
0
    return 0;
391
0
}
392
393
static int vpic_load(struct domain *d, hvm_domain_context_t *h)
394
0
{
395
0
    struct hvm_hw_vpic *s;
396
0
    uint16_t inst;
397
0
398
0
    if ( !has_vpic(d) )
399
0
        return -ENODEV;
400
0
401
0
    /* Which PIC is this? */
402
0
    inst = hvm_load_instance(h);
403
0
    if ( inst > 1 )
404
0
        return -EINVAL;
405
0
    s = &d->arch.hvm_domain.vpic[inst];
406
0
407
0
    /* Load the state */
408
0
    if ( hvm_load_entry(PIC, h, s) != 0 )
409
0
        return -EINVAL;
410
0
411
0
    return 0;
412
0
}
413
414
HVM_REGISTER_SAVE_RESTORE(PIC, vpic_save, vpic_load, 2, HVMSR_PER_DOM);
415
416
void vpic_reset(struct domain *d)
417
0
{
418
0
    struct hvm_hw_vpic *vpic;
419
0
420
0
    if ( !has_vpic(d) )
421
0
        return;
422
0
423
0
    /* Master PIC. */
424
0
    vpic = &d->arch.hvm_domain.vpic[0];
425
0
    memset(vpic, 0, sizeof(*vpic));
426
0
    vpic->is_master = 1;
427
0
    vpic->elcr      = 1 << 2;
428
0
429
0
    /* Slave PIC. */
430
0
    vpic++;
431
0
    memset(vpic, 0, sizeof(*vpic));
432
0
}
433
434
void vpic_init(struct domain *d)
435
1
{
436
1
    if ( !has_vpic(d) )
437
1
        return;
438
1
439
0
    vpic_reset(d);
440
0
441
0
    register_portio_handler(d, 0x20, 2, vpic_intercept_pic_io);
442
0
    register_portio_handler(d, 0xa0, 2, vpic_intercept_pic_io);
443
0
444
0
    register_portio_handler(d, 0x4d0, 1, vpic_intercept_elcr_io);
445
0
    register_portio_handler(d, 0x4d1, 1, vpic_intercept_elcr_io);
446
0
}
447
448
void vpic_irq_positive_edge(struct domain *d, int irq)
449
0
{
450
0
    struct hvm_hw_vpic *vpic = &d->arch.hvm_domain.vpic[irq >> 3];
451
0
    uint8_t mask = 1 << (irq & 7);
452
0
453
0
    ASSERT(has_vpic(d));
454
0
    ASSERT(irq <= 15);
455
0
    ASSERT(vpic_is_locked(vpic));
456
0
457
0
    TRACE_1D(TRC_HVM_EMUL_PIC_POSEDGE, irq);
458
0
    if ( irq == 2 )
459
0
        return;
460
0
461
0
    vpic->irr |= mask;
462
0
    if ( !(vpic->imr & mask) )
463
0
        vpic_update_int_output(vpic);
464
0
}
465
466
void vpic_irq_negative_edge(struct domain *d, int irq)
467
0
{
468
0
    struct hvm_hw_vpic *vpic = &d->arch.hvm_domain.vpic[irq >> 3];
469
0
    uint8_t mask = 1 << (irq & 7);
470
0
471
0
    ASSERT(has_vpic(d));
472
0
    ASSERT(irq <= 15);
473
0
    ASSERT(vpic_is_locked(vpic));
474
0
475
0
    TRACE_1D(TRC_HVM_EMUL_PIC_NEGEDGE, irq);
476
0
    if ( irq == 2 )
477
0
        return;
478
0
479
0
    vpic->irr &= ~mask;
480
0
    if ( !(vpic->imr & mask) )
481
0
        vpic_update_int_output(vpic);
482
0
}
483
484
int vpic_ack_pending_irq(struct vcpu *v)
485
0
{
486
0
    int irq, vector;
487
0
    struct hvm_hw_vpic *vpic = &v->domain->arch.hvm_domain.vpic[0];
488
0
489
0
    ASSERT(has_vpic(v->domain));
490
0
491
0
    TRACE_2D(TRC_HVM_EMUL_PIC_PEND_IRQ_CALL, vlapic_accept_pic_intr(v),
492
0
             vpic->int_output);
493
0
    if ( !vlapic_accept_pic_intr(v) || !vpic->int_output )
494
0
        return -1;
495
0
496
0
    irq = vpic_intack(vpic);
497
0
    if ( irq == -1 )
498
0
        return -1;
499
0
500
0
    vector = vpic[irq >> 3].irq_base + (irq & 7);
501
0
    return vector;
502
0
}
503
504
/*
505
 * Local variables:
506
 * mode: C
507
 * c-file-style: "BSD"
508
 * c-basic-offset: 4
509
 * indent-tabs-mode: nil
510
 * End:
511
 */