summaryrefslogtreecommitdiff
path: root/xen/arch/arm/gic-v3-lpi.c
blob: e1594dd20e4c22d0a7ba9986539b8149faaf87a2 (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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
/*
 * xen/arch/arm/gic-v3-lpi.c
 *
 * ARM GICv3 Locality-specific Peripheral Interrupts (LPI) support
 *
 * Copyright (C) 2016,2017 - ARM Ltd
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; under version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; If not, see <http://www.gnu.org/licenses/>.
 */

#include <xen/lib.h>
#include <xen/mm.h>
#include <xen/param.h>
#include <xen/sched.h>
#include <xen/sizes.h>
#include <xen/warning.h>
#include <asm/atomic.h>
#include <asm/domain.h>
#include <asm/gic.h>
#include <asm/gic_v3_defs.h>
#include <asm/gic_v3_its.h>
#include <asm/io.h>
#include <asm/page.h>
#include <asm/sysregs.h>

/*
 * There could be a lot of LPIs on the host side, and they always go to
 * a guest. So having a struct irq_desc for each of them would be wasteful
 * and useless.
 * Instead just store enough information to find the right VCPU to inject
 * those LPIs into, which just requires the virtual LPI number.
 * To avoid a global lock on this data structure, this is using a lockless
 * approach relying on the architectural atomicity of native data types:
 * We read or write the "data" view of this union atomically, then can
 * access the broken-down fields in our local copy.
 */
union host_lpi {
    uint64_t data;
    struct {
        uint32_t virt_lpi;
        uint16_t dom_id;
        uint16_t pad;
    };
};

#define LPI_PROPTABLE_NEEDS_FLUSHING    (1U << 0)

/* Global state */
static struct {
    /* The global LPI property table, shared by all redistributors. */
    uint8_t *lpi_property;
    /*
     * A two-level table to lookup LPIs firing on the host and look up the
     * VCPU and virtual LPI number to inject into.
     */
    union host_lpi **host_lpis;
    /*
     * Number of physical LPIs the host supports. This is a property of
     * the GIC hardware. We depart from the habit of naming these things
     * "physical" in Xen, as the GICv3/4 spec uses the term "physical LPI"
     * in a different context to differentiate them from "virtual LPIs".
     */
    unsigned long int max_host_lpi_ids;
    /*
     * Protects allocation and deallocation of host LPIs and next_free_lpi,
     * but not the actual data stored in the host_lpi entry.
     */
    spinlock_t host_lpis_lock;
    uint32_t next_free_lpi;
    unsigned int flags;
} lpi_data;

struct lpi_redist_data {
    paddr_t             redist_addr;
    unsigned int        redist_id;
    void                *pending_table;
};

static DEFINE_PER_CPU(struct lpi_redist_data, lpi_redist);

#define MAX_NR_HOST_LPIS   (lpi_data.max_host_lpi_ids - LPI_OFFSET)
#define HOST_LPIS_PER_PAGE      (PAGE_SIZE / sizeof(union host_lpi))

static union host_lpi *gic_get_host_lpi(uint32_t plpi)
{
    union host_lpi *block;

    if ( !is_lpi(plpi) || plpi >= MAX_NR_HOST_LPIS + LPI_OFFSET )
        return NULL;

    ASSERT(plpi >= LPI_OFFSET);

    plpi -= LPI_OFFSET;

    block = lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE];
    if ( !block )
        return NULL;

    /* Matches the write barrier in allocation code. */
    smp_rmb();

    return &block[plpi % HOST_LPIS_PER_PAGE];
}

/*
 * An ITS can refer to redistributors in two ways: either by an ID (possibly
 * the CPU number) or by its MMIO address. This is a hardware implementation
 * choice, so we have to cope with both approaches. The GICv3 code calculates
 * both values and calls this function to let the ITS store them when it's
 * later required to provide them. This is done in a per-CPU variable.
 */
void gicv3_set_redist_address(paddr_t address, unsigned int redist_id)
{
    this_cpu(lpi_redist).redist_addr = address;
    this_cpu(lpi_redist).redist_id = redist_id;
}

/*
 * Returns a redistributor's ID (either as an address or as an ID).
 * This must be (and is) called only after it has been setup by the above
 * function.
 */
uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
{
    if ( use_pta )
        return per_cpu(lpi_redist, cpu).redist_addr & GENMASK(51, 16);
    else
        return per_cpu(lpi_redist, cpu).redist_id << 16;
}

void vgic_vcpu_inject_lpi(struct domain *d, unsigned int virq)
{
    /*
     * TODO: this assumes that the struct pending_irq stays valid all of
     * the time. We cannot properly protect this with the current locking
     * scheme, but the future per-IRQ lock will solve this problem.
     */
    struct pending_irq *p = irq_to_pending(d->vcpu[0], virq);
    unsigned int vcpu_id;

    if ( !p )
        return;

    vcpu_id = ACCESS_ONCE(p->lpi_vcpu_id);
    if ( vcpu_id >= d->max_vcpus )
          return;

    vgic_inject_irq(d, d->vcpu[vcpu_id], virq, true);
}

/*
 * Handle incoming LPIs, which are a bit special, because they are potentially
 * numerous and also only get injected into guests. Treat them specially here,
 * by just looking up their target vCPU and virtual LPI number and hand it
 * over to the injection function.
 * Please note that LPIs are edge-triggered only, also have no active state,
 * so spurious interrupts on the host side are no issue (we can just ignore
 * them).
 * Also a guest cannot expect that firing interrupts that haven't been
 * fully configured yet will reach the CPU, so we don't need to care about
 * this special case.
 */
void gicv3_do_LPI(unsigned int lpi)
{
    struct domain *d;
    union host_lpi *hlpip, hlpi;

    irq_enter();

    /* EOI the LPI already. */
    WRITE_SYSREG(lpi, ICC_EOIR1_EL1);

    /* Find out if a guest mapped something to this physical LPI. */
    hlpip = gic_get_host_lpi(lpi);
    if ( !hlpip )
        goto out;

    hlpi.data = read_u64_atomic(&hlpip->data);

    /*
     * Unmapped events are marked with an invalid LPI ID. We can safely
     * ignore them, as they have no further state and no-one can expect
     * to see them if they have not been mapped.
     */
    if ( hlpi.virt_lpi == INVALID_LPI )
        goto out;

    d = rcu_lock_domain_by_id(hlpi.dom_id);
    if ( !d )
        goto out;

    /*
     * TODO: Investigate what to do here for potential interrupt storms.
     * As we keep all host LPIs enabled, for disabling LPIs we would need
     * to queue a ITS host command, which we avoid so far during a guest's
     * runtime. Also re-enabling would trigger a host command upon the
     * guest sending a command, which could be an attack vector for
     * hogging the host command queue.
     * See the thread around here for some background:
     * https://lists.xen.org/archives/html/xen-devel/2016-12/msg00003.html
     */
    vgic_vcpu_inject_lpi(d, hlpi.virt_lpi);

    rcu_unlock_domain(d);

out:
    irq_exit();
}

void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
                                 uint32_t virt_lpi)
{
    union host_lpi *hlpip, hlpi;

    ASSERT(host_lpi >= LPI_OFFSET);

    host_lpi -= LPI_OFFSET;

    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];

    hlpi.virt_lpi = virt_lpi;
    hlpi.dom_id = domain_id;

    write_u64_atomic(&hlpip->data, hlpi.data);
}

static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
{
    uint64_t val;
    void *pendtable;

    if ( this_cpu(lpi_redist).pending_table )
        return -EBUSY;

    val  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
    val |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
    val |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;

    /*
     * The pending table holds one bit per LPI and even covers bits for
     * interrupt IDs below 8192, so we allocate the full range.
     * The GICv3 imposes a 64KB alignment requirement, also requires
     * physically contiguous memory.
     */
    pendtable = _xzalloc(lpi_data.max_host_lpi_ids / 8, SZ_64K);
    if ( !pendtable )
        return -ENOMEM;

    /* Make sure the physical address can be encoded in the register. */
    if ( virt_to_maddr(pendtable) & ~GENMASK(51, 16) )
    {
        xfree(pendtable);
        return -ERANGE;
    }
    clean_and_invalidate_dcache_va_range(pendtable,
                                         lpi_data.max_host_lpi_ids / 8);

    this_cpu(lpi_redist).pending_table = pendtable;

    val |= GICR_PENDBASER_PTZ;

    val |= virt_to_maddr(pendtable);

    *reg = val;

    return 0;
}

/*
 * Tell a redistributor about the (shared) property table, allocating one
 * if not already done.
 */
static int gicv3_lpi_set_proptable(void __iomem * rdist_base)
{
    uint64_t reg;

    reg  = GIC_BASER_CACHE_RaWaWb << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT;
    reg |= GIC_BASER_CACHE_SameAsInner << GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT;
    reg |= GIC_BASER_InnerShareable << GICR_PROPBASER_SHAREABILITY_SHIFT;

    /*
     * The property table is shared across all redistributors, so allocate
     * this only once, but return the same value on subsequent calls.
     */
    if ( !lpi_data.lpi_property )
    {
        /* The property table holds one byte per LPI. */
        void *table = _xmalloc(lpi_data.max_host_lpi_ids, SZ_4K);

        if ( !table )
            return -ENOMEM;

        /* Make sure the physical address can be encoded in the register. */
        if ( (virt_to_maddr(table) & ~GENMASK(51, 12)) )
        {
            xfree(table);
            return -ERANGE;
        }
        memset(table, GIC_PRI_IRQ | LPI_PROP_RES1, MAX_NR_HOST_LPIS);
        clean_and_invalidate_dcache_va_range(table, MAX_NR_HOST_LPIS);
        lpi_data.lpi_property = table;
    }

    /* Encode the number of bits needed, minus one */
    reg |= fls(lpi_data.max_host_lpi_ids - 1) - 1;

    reg |= virt_to_maddr(lpi_data.lpi_property);

    writeq_relaxed(reg, rdist_base + GICR_PROPBASER);
    reg = readq_relaxed(rdist_base + GICR_PROPBASER);

    /* If we can't do shareable, we have to drop cacheability as well. */
    if ( !(reg & GICR_PROPBASER_SHAREABILITY_MASK) )
    {
        reg &= ~GICR_PROPBASER_INNER_CACHEABILITY_MASK;
        reg |= GIC_BASER_CACHE_nC << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT;
    }

    /* Remember that we have to flush the property table if non-cacheable. */
    if ( (reg & GICR_PROPBASER_INNER_CACHEABILITY_MASK) <= GIC_BASER_CACHE_nC )
    {
        lpi_data.flags |= LPI_PROPTABLE_NEEDS_FLUSHING;
        /* Update the redistributors knowledge about the attributes. */
        writeq_relaxed(reg, rdist_base + GICR_PROPBASER);
    }

    return 0;
}

int gicv3_lpi_init_rdist(void __iomem * rdist_base)
{
    uint32_t reg;
    uint64_t table_reg;
    int ret;

    /* We don't support LPIs without an ITS. */
    if ( !gicv3_its_host_has_its() )
        return -ENODEV;

    /* Make sure LPIs are disabled before setting up the tables. */
    reg = readl_relaxed(rdist_base + GICR_CTLR);
    if ( reg & GICR_CTLR_ENABLE_LPIS )
        return -EBUSY;

    ret = gicv3_lpi_allocate_pendtable(&table_reg);
    if ( ret )
        return ret;
    writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
    table_reg = readq_relaxed(rdist_base + GICR_PENDBASER);

    /* If the hardware reports non-shareable, drop cacheability as well. */
    if ( !(table_reg & GICR_PENDBASER_SHAREABILITY_MASK) )
    {
        table_reg &= ~GICR_PENDBASER_INNER_CACHEABILITY_MASK;
        table_reg |= GIC_BASER_CACHE_nC << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;

        writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
    }

    return gicv3_lpi_set_proptable(rdist_base);
}

static unsigned int max_lpi_bits = 20;
integer_param("max_lpi_bits", max_lpi_bits);

/*
 * Allocate the 2nd level array for host LPIs. This one holds pointers
 * to the page with the actual "union host_lpi" entries. Our LPI limit
 * avoids excessive memory usage.
 */
int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
{
    unsigned int nr_lpi_ptrs;

    /* We rely on the data structure being atomically accessible. */
    BUILD_BUG_ON(sizeof(union host_lpi) > sizeof(unsigned long));

    /*
     * An implementation needs to support at least 14 bits of LPI IDs.
     * Tell the user about it, the actual number is reported below.
     */
    if ( max_lpi_bits < 14 || max_lpi_bits > 32 )
        printk(XENLOG_WARNING "WARNING: max_lpi_bits must be between 14 and 32, adjusting.\n");

    max_lpi_bits = max(max_lpi_bits, 14U);
    lpi_data.max_host_lpi_ids = BIT(min(host_lpi_bits, max_lpi_bits), UL);

    /*
     * Warn if the number of LPIs are quite high, as the user might not want
     * to waste megabytes of memory for a mostly empty table.
     * It's very unlikely that we need more than 24 bits worth of LPIs.
     */
    if ( lpi_data.max_host_lpi_ids > BIT(24, UL) )
        warning_add("Using high number of LPIs, limit memory usage with max_lpi_bits\n");

    spin_lock_init(&lpi_data.host_lpis_lock);
    lpi_data.next_free_lpi = 0;

    nr_lpi_ptrs = MAX_NR_HOST_LPIS / (PAGE_SIZE / sizeof(union host_lpi));
    lpi_data.host_lpis = xzalloc_array(union host_lpi *, nr_lpi_ptrs);
    if ( !lpi_data.host_lpis )
        return -ENOMEM;

    printk("GICv3: using at most %lu LPIs on the host.\n", MAX_NR_HOST_LPIS);

    return 0;
}

static int find_unused_host_lpi(uint32_t start, uint32_t *index)
{
    unsigned int chunk;
    uint32_t i = *index;

    ASSERT(spin_is_locked(&lpi_data.host_lpis_lock));

    for ( chunk = start;
          chunk < MAX_NR_HOST_LPIS / HOST_LPIS_PER_PAGE;
          chunk++ )
    {
        /* If we hit an unallocated chunk, use entry 0 in that one. */
        if ( !lpi_data.host_lpis[chunk] )
        {
            *index = 0;
            return chunk;
        }

        /* Find an unallocated entry in this chunk. */
        for ( ; i < HOST_LPIS_PER_PAGE; i += LPI_BLOCK )
        {
            if ( lpi_data.host_lpis[chunk][i].dom_id == DOMID_INVALID )
            {
                *index = i;
                return chunk;
            }
        }
        i = 0;
    }

    return -1;
}

/*
 * Allocate a block of 32 LPIs on the given host ITS for device "devid",
 * starting with "eventid". Put them into the respective ITT by issuing a
 * MAPTI command for each of them.
 */
int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi)
{
    uint32_t lpi, lpi_idx;
    int chunk;
    int i;

    spin_lock(&lpi_data.host_lpis_lock);
    lpi_idx = lpi_data.next_free_lpi % HOST_LPIS_PER_PAGE;
    chunk = find_unused_host_lpi(lpi_data.next_free_lpi / HOST_LPIS_PER_PAGE,
                                 &lpi_idx);

    if ( chunk == - 1 )          /* rescan for a hole from the beginning */
    {
        lpi_idx = 0;
        chunk = find_unused_host_lpi(0, &lpi_idx);
        if ( chunk == -1 )
        {
            spin_unlock(&lpi_data.host_lpis_lock);
            return -ENOSPC;
        }
    }

    /* If we hit an unallocated chunk, we initialize it and use entry 0. */
    if ( !lpi_data.host_lpis[chunk] )
    {
        union host_lpi *new_chunk;

        /* TODO: NUMA locality for quicker IRQ path? */
        new_chunk = alloc_xenheap_page();
        if ( !new_chunk )
        {
            spin_unlock(&lpi_data.host_lpis_lock);
            return -ENOMEM;
        }

        for ( i = 0; i < HOST_LPIS_PER_PAGE; i += LPI_BLOCK )
            new_chunk[i].dom_id = DOMID_INVALID;

        /*
         * Make sure all slots are really marked empty before publishing the
         * new chunk.
         */
        smp_wmb();

        lpi_data.host_lpis[chunk] = new_chunk;
        lpi_idx = 0;
    }

    lpi = chunk * HOST_LPIS_PER_PAGE + lpi_idx;

    for ( i = 0; i < LPI_BLOCK; i++ )
    {
        union host_lpi hlpi;

        /*
         * Mark this host LPI as belonging to the domain, but don't assign
         * any virtual LPI or a VCPU yet.
         */
        hlpi.virt_lpi = INVALID_LPI;
        hlpi.dom_id = d->domain_id;
        write_u64_atomic(&lpi_data.host_lpis[chunk][lpi_idx + i].data,
                         hlpi.data);

        /*
         * Enable this host LPI, so we don't have to do this during the
         * guest's runtime.
         */
        lpi_data.lpi_property[lpi + i] |= LPI_PROP_ENABLED;
    }

    lpi_data.next_free_lpi = lpi + LPI_BLOCK;

    /*
     * We have allocated and initialized the host LPI entries, so it's safe
     * to drop the lock now. Access to the structures can be done concurrently
     * as it involves only an atomic uint64_t access.
     */
    spin_unlock(&lpi_data.host_lpis_lock);

    if ( lpi_data.flags & LPI_PROPTABLE_NEEDS_FLUSHING )
        clean_and_invalidate_dcache_va_range(&lpi_data.lpi_property[lpi],
                                             LPI_BLOCK);

    *first_lpi = lpi + LPI_OFFSET;

    return 0;
}

void gicv3_free_host_lpi_block(uint32_t first_lpi)
{
    union host_lpi *hlpi, empty_lpi = { .dom_id = DOMID_INVALID };
    int i;

    /* This should only be called with the beginning of a block. */
    ASSERT((first_lpi % LPI_BLOCK) == 0);

    hlpi = gic_get_host_lpi(first_lpi);
    if ( !hlpi )
        return;         /* Nothing to free here. */

    spin_lock(&lpi_data.host_lpis_lock);

    for ( i = 0; i < LPI_BLOCK; i++ )
        write_u64_atomic(&hlpi[i].data, empty_lpi.data);

    /*
     * Make sure the next allocation can reuse this block, as we do only
     * forward scanning when finding an unused block.
     */
    if ( lpi_data.next_free_lpi > first_lpi )
        lpi_data.next_free_lpi = first_lpi;

    spin_unlock(&lpi_data.host_lpis_lock);

    return;
}

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * End:
 */