acrn-hypervisor/hypervisor/common/irq.c
Yifan Liu 5c9456462b hv && config-tool: Add compilation option to disable all interrupts in HV
This patch adds an option CONFIG_KEEP_IRQ_DISABLED to hv (default n) and
config-tool so that when this option is 'y', all interrupts in hv root
mode will be permanently disabled.

With this option to be 'y', all interrupts received in root mode will be
handled in external interrupt vmexit after next VM entry. The postpone
latency is negligible. This new configuration is a requirement from x86
TEE's secure/non-secure interrupt flow support. Many race conditions can be
avoided when keeping IRQ off.

v5:
Rename CONFIG_ACRN_KEEP_IRQ_DISABLED to CONFIG_KEEP_IRQ_DISABLED

v4:
Change CPU_IRQ_ENABLE/DISABLE to
CPU_IRQ_ENABLE_ON_CONFIG/DISABLE_ON_CONFIG and guard them using
CONFIG_ACRN_KEEP_IRQ_DISABLED

v3:
CONFIG_ACRN_DISABLE_INTERRUPT -> CONFIG_ACRN_KEEP_IRQ_DISABLED
Add more comment in commit message

Tracked-On: #6571
Signed-off-by: Yifan Liu <yifan1.liu@intel.com>
Reviewed-by: Wang, Yu1 <yu1.wang@intel.com>
Acked-by: Anthony Xu <anthony.xu@intel.com>
2021-12-10 09:50:17 +08:00

232 lines
5.4 KiB
C

/*
* Copyright (C) 2021 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <errno.h>
#include <asm/lib/bits.h>
#include <irq.h>
#include <common/softirq.h>
#include <asm/irq.h>
#include <asm/per_cpu.h>
static spinlock_t irq_alloc_spinlock = { .head = 0U, .tail = 0U, };
uint64_t irq_alloc_bitmap[IRQ_ALLOC_BITMAP_SIZE];
struct irq_desc irq_desc_array[NR_IRQS];
static uint64_t irq_rsvd_bitmap[IRQ_ALLOC_BITMAP_SIZE];
/*
* alloc an free irq if req_irq is IRQ_INVALID, or else set assigned
* return: irq num on success, IRQ_INVALID on failure
*/
static uint32_t alloc_irq_num(uint32_t req_irq, bool reserve)
{
uint32_t irq = req_irq;
uint64_t rflags;
uint32_t ret;
if ((irq >= NR_IRQS) && (irq != IRQ_INVALID)) {
pr_err("[%s] invalid req_irq %u", __func__, req_irq);
ret = IRQ_INVALID;
} else {
spinlock_irqsave_obtain(&irq_alloc_spinlock, &rflags);
if (irq == IRQ_INVALID) {
/* if no valid irq num given, find a free one */
irq = (uint32_t)ffz64_ex(irq_alloc_bitmap, NR_IRQS);
}
if (irq >= NR_IRQS) {
irq = IRQ_INVALID;
} else {
bitmap_set_nolock((uint16_t)(irq & 0x3FU),
irq_alloc_bitmap + (irq >> 6U));
if (reserve) {
bitmap_set_nolock((uint16_t)(irq & 0x3FU),
irq_rsvd_bitmap + (irq >> 6U));
}
}
spinlock_irqrestore_release(&irq_alloc_spinlock, rflags);
ret = irq;
}
return ret;
}
uint32_t reserve_irq_num(uint32_t irq)
{
return alloc_irq_num(irq, true);
}
/*
* @pre: irq is not in irq_static_mappings
* free irq num allocated via alloc_irq_num()
*/
static void free_irq_num(uint32_t irq)
{
uint64_t rflags;
if (irq < NR_IRQS) {
spinlock_irqsave_obtain(&irq_alloc_spinlock, &rflags);
if (bitmap_test((uint16_t)(irq & 0x3FU),
irq_rsvd_bitmap + (irq >> 6U)) == false) {
bitmap_clear_nolock((uint16_t)(irq & 0x3FU),
irq_alloc_bitmap + (irq >> 6U));
}
spinlock_irqrestore_release(&irq_alloc_spinlock, rflags);
}
}
void free_irq(uint32_t irq)
{
uint64_t rflags;
struct irq_desc *desc;
if (irq < NR_IRQS) {
desc = &irq_desc_array[irq];
spinlock_irqsave_obtain(&desc->lock, &rflags);
desc->action = NULL;
desc->priv_data = NULL;
desc->flags = IRQF_NONE;
spinlock_irqrestore_release(&desc->lock, rflags);
free_irq_arch(irq);
free_irq_num(irq);
}
}
/*
* There are four cases as to irq/vector allocation:
* case 1: req_irq = IRQ_INVALID
* caller did not know which irq to use, and want system to
* allocate available irq for it. These irq are in range:
* nr_gsi ~ NR_IRQS
* an irq will be allocated and a vector will be assigned to this
* irq automatically.
* case 2: req_irq >= NR_LAGACY_IRQ and irq < nr_gsi
* caller want to add device ISR handler into ioapic pins.
* a vector will automatically assigned.
* case 3: req_irq >=0 and req_irq < NR_LEGACY_IRQ
* caller want to add device ISR handler into ioapic pins, which
* is a legacy irq, vector already reserved.
* Nothing to do in this case.
* case 4: irq with speical type (not from IOAPIC/MSI)
* These irq value are pre-defined for Timer, IPI, Spurious etc,
* which is listed in irq_static_mappings[].
* Nothing to do in this case.
*
* return value: valid irq (>=0) on success, otherwise errno (< 0).
*/
int32_t request_irq(uint32_t req_irq, irq_action_t action_fn, void *priv_data,
uint32_t flags)
{
struct irq_desc *desc;
uint32_t irq;
uint64_t rflags;
int32_t ret;
irq = alloc_irq_num(req_irq, false);
if (irq == IRQ_INVALID) {
pr_err("[%s] invalid irq num", __func__);
ret = -EINVAL;
} else {
if (!request_irq_arch(irq)) {
pr_err("[%s] failed to alloc vector for irq %u",
__func__, irq);
free_irq_num(irq);
ret = -EINVAL;
} else {
desc = &irq_desc_array[irq];
if (desc->action == NULL) {
spinlock_irqsave_obtain(&desc->lock, &rflags);
desc->flags = flags;
desc->priv_data = priv_data;
desc->action = action_fn;
spinlock_irqrestore_release(&desc->lock, rflags);
ret = (int32_t)irq;
} else {
ret = -EBUSY;
pr_err("%s: request irq(%u) failed, already requested",
__func__, irq);
}
}
}
return ret;
}
void set_irq_trigger_mode(uint32_t irq, bool is_level_triggered)
{
uint64_t rflags;
struct irq_desc *desc;
if (irq < NR_IRQS) {
desc = &irq_desc_array[irq];
spinlock_irqsave_obtain(&desc->lock, &rflags);
if (is_level_triggered) {
desc->flags |= IRQF_LEVEL;
} else {
desc->flags &= ~IRQF_LEVEL;
}
spinlock_irqrestore_release(&desc->lock, rflags);
}
}
static inline void handle_irq(const struct irq_desc *desc)
{
irq_action_t action = desc->action;
pre_irq_arch(desc);
if (action != NULL) {
action(desc->irq, desc->priv_data);
}
post_irq_arch(desc);
}
void do_irq(const uint32_t irq)
{
struct irq_desc *desc;
if (irq < NR_IRQS) {
desc = &irq_desc_array[irq];
per_cpu(irq_count, get_pcpu_id())[irq]++;
/* XXX irq_alloc_bitmap is used lockless here */
if (bitmap_test((uint16_t)(irq & 0x3FU), irq_alloc_bitmap + (irq >> 6U))) {
handle_irq(desc);
}
}
do_softirq();
}
static void init_irq_descs(void)
{
uint32_t i;
for (i = 0U; i < NR_IRQS; i++) {
struct irq_desc *desc = &irq_desc_array[i];
desc->irq = i;
spinlock_init(&desc->lock);
}
init_irq_descs_arch(irq_desc_array);
}
void init_interrupt(uint16_t pcpu_id)
{
init_interrupt_arch(pcpu_id);
if (pcpu_id == BSP_CPU_ID) {
init_irq_descs();
setup_irqs_arch();
init_softirq();
}
CPU_IRQ_ENABLE_ON_CONFIG();
}