Xen-hypervisor中SPI中断虚拟化的实现-gic的初始化

4 篇文章 0 订阅

介绍

xen支持arm64的type 1的Hypervisor。
wiki: https://wiki.xenproject.org/wiki/Main_Page
github:https://github.com/topics/xen
研究了下SPI中断虚拟化的代码实现
基础知识
《Arm Generic Interrupt Controller v3 and v4 - Virtualization》
https://blog.csdn.net/weixin_47191387/article/details/130188662
《Arm Generic Interrupt Controller v3 and v4》
https://blog.csdn.net/weixin_47191387/article/details/130330239

涉及文件

/xen/arch/arm/arm64/entry.S
/xen/arch/arm/setup.c
/xen/arch/arm/arm64/traps.c
/xen/arch/arm/gic.c
/xen/arch/arm/vgic.c
/xen/arch/arm/gic-v3.c
/xen/arch/arm/gic-vgic.c
/xen/arch/arm/irq.c
/xen/arch/arm/vgic-v3.c
/xen/arch/arm/domain_build.c

gic的初始化

start_xen相当于linux中的start_kernel函数。
start_xen中主要调用了下面三个函数来初始化xen的中断子系统
–>init_traps :el2的trap相关的系统寄存器的设置
–>gic_preinit :初始化gic_hw_ops成gicv3 ops
–>gic_init :gicv3的初始化

init_traps

实现如下

void init_traps(void)
{
    /*
     * Setup Hyp vector base. Note they might get updated with the
     * branch predictor hardening.
     */
    WRITE_SYSREG((vaddr_t)hyp_traps_vector, VBAR_EL2);//VBAR_EL2中的地址设置为hyp_traps_vectord的中断向量表

    /* Trap Debug and Performance Monitor accesses */
    WRITE_SYSREG(HDCR_TDRA|HDCR_TDOSA|HDCR_TDA|HDCR_TPM|HDCR_TPMCR,
                 MDCR_EL2);//Monitor Debug Configuration Register调试相关的先忽略

    /* Trap CP15 c15 used for implementation defined registers */
    WRITE_SYSREG(HSTR_T(15), HSTR_EL2);//Hypervisor System Trap Register

    WRITE_SYSREG(get_default_cptr_flags(), CPTR_EL2);//Architectural Feature Trap Register (EL2)

    /*
     * Configure HCR_EL2 with the bare minimum to run Xen until a guest
     * is scheduled. {A,I,F}MO bits are set to allow EL2 receiving
     * interrupts.
     */
    WRITE_SYSREG(HCR_AMO | HCR_FMO | HCR_IMO, HCR_EL2);//设置HCR_EL2的serror,irq,fiq路由到el2的中断向量表。
    注:关于irq,fiq路由到el2还是el3,在SCR_EL3, Secure Configuration Register寄存器中的IRQ, bit [1]和FIQ, bit [2]也有配置,这两个寄存器同时配置应该以SCR_EL3的配置为准。
    isb();
}

gic_preinit

gicv3_ops的实现如下

static const struct gic_hw_operations gicv3_ops = {
    .info                = &gicv3_info,//gicv3的info
    .init                = gicv3_init,//gicv3的初始化
    .save_state          = gicv3_save_state,//虚拟化vcpu的上下文切换,保存ICH_VMCR_EL2和ICC_SRE_EL1寄存器。
    .restore_state       = gicv3_restore_state,//虚拟化vcpu的上下文切换,加载ICH_VMCR_EL2和ICC_SRE_EL1寄存器。
    .dump_state          = gicv3_dump_state,//dump某个vcpu的list register
    .gic_host_irq_type   = &gicv3_host_irq_type,//用作host irq的desc.handler的ops
    .gic_guest_irq_type  = &gicv3_guest_irq_type,//用作guest irq的desc.handler的ops
    .eoi_irq             = gicv3_eoi_irq,//中断EOI(通过设置ICC_EOIR1_EL1寄存器)同时降低priority(从Running priority到idle priority)
    .deactivate_irq      = gicv3_dir_irq,//deactivate中断,中断变成inactive状态
    .read_irq            = gicv3_read_irq,//中断ack读取ICC_IAR1_EL1寄存器
    .set_active_state    = gicv3_set_active_state,//设置中断状态为active
    .set_pending_state   = gicv3_set_pending_state,//设置中断状态为pending
    .set_irq_type        = gicv3_set_irq_type,//中断类型设置边沿触发或者电平触发
    .set_irq_priority    = gicv3_set_irq_priority,//设置中断的优先级
    .send_SGI            = gicv3_send_sgi,//发送SGI
    .disable_interface   = gicv3_disable_interface,//关闭per-CPU GIC interface
    .update_lr           = gicv3_update_lr,//更新当前cpu的list register中的一个
    .update_hcr_status   = gicv3_hcr_status,//设置ICH_HCR_EL2寄存器
    .clear_lr            = gicv3_clear_lr,//清除当前cpu的list register中的一个
    .read_lr             = gicv3_read_lr,//读取当前cpu的list register中的一个
    .write_lr            = gicv3_write_lr,//写当前cpu的list register中的一个
    .read_vmcr_priority  = gicv3_read_vmcr_priority,//读取ICH_VMCR_EL2中priority的值
    .read_apr            = gicv3_read_apr,//读取ICH_AP1R<n>_EL2寄存器,EL2的group 1 虚拟活动优先级
    .read_pending_state  = gicv3_read_pending_state,//读取中断的pending状态-寄存器GICD_ISPENDR
    .secondary_init      = gicv3_secondary_cpu_init,//secondary cpu启动的时候,percpu相关的的gicv3初始化
    .make_hwdom_dt_node  = gicv3_make_hwdom_dt_node,//处理dt
#ifdef CONFIG_ACPI //假设我们用dt没有用acpi
    .make_hwdom_madt     = gicv3_make_hwdom_madt,
    .get_hwdom_extra_madt_size = gicv3_get_hwdom_extra_madt_size,
#endif
    .iomem_deny_access   = gicv3_iomem_deny_access,//acpi用
    .do_LPI              = gicv3_do_LPI,//处理LPI中断
};

gicv3_ops的注册

void register_gic_ops(const struct gic_hw_operations *ops)
{
    gic_hw_ops = ops;
}

static int __init gicv3_dt_preinit(struct dt_device_node *node, const void *data)
{
    gicv3_info.hw_version = GIC_V3;
    gicv3_info.node = node;
    register_gic_ops(&gicv3_ops);//注册到全局变量gic_hw_ops
    dt_irq_xlate = gic_irq_xlate;

    return 0;
}

DT_DEVICE_START(gicv3, "GICv3", DEVICE_GIC)//gicv3的device
        .dt_match = gicv3_dt_match,
        .init = gicv3_dt_preinit,
DT_DEVICE_END

start_xen会调用gic_preinit


void __init gic_preinit(void)
{
    if ( acpi_disabled )//假设我们用dt没有用acpi
        gic_dt_preinit();
    else
        gic_acpi_preinit();
}

static void __init gic_dt_preinit(void)
{
    int rc;
    struct dt_device_node *node;
    uint8_t num_gics = 0;

    dt_for_each_device_node( dt_host, node )
    {
        if ( !dt_get_property(node, "interrupt-controller", NULL) )
            continue;

        if ( !dt_get_parent(node) )
            continue;

        rc = device_init(node, DEVICE_GIC, NULL);//device init的时候初始化了gicv3的device
        if ( !rc )
        {
            /* NOTE: Only one GIC is supported */
            num_gics = 1;
            break;
        }
    }
    if ( !num_gics )
        panic("Unable to find compatible GIC in the device tree\n");

    /* Set the GIC as the primary interrupt controller */
    dt_interrupt_controller = node;
    dt_device_set_used_by(node, DOMID_XEN);
}

gic_init

start_xen调用gic_init函数

/* Set up the GIC */
void __init gic_init(void)
{
    if ( gic_hw_ops->init() )//调用preinit注册的gicv3_init函数
        panic("Failed to initialize the GIC drivers\n");
    /* Clear LR mask for cpu0 */
    clear_cpu_lr_mask();//清除当前cpu的lr_mask
}

gicv3_init实现主要调用以下函数
–>gicv3_dt_init ://dt的解析
–>gicv3_dist_init:gicv3对Distributor的设置。
–>gicv3_its_init:gicv3 its的初始化,本文不涉及。
–>gicv3_cpu_init:gicv3关于per-cpu的设置,主要有Redistributor和 CPU interface。
–>gicv3_hyp_init:gicv3关于per-cpu的hypervisor的设置。

gicv3_dt_init主要是对dt中的distributor和redistributor做解析。

gicv3对Distributor的设置实现如下:

static void __init gicv3_dist_init(void)
{
    uint32_t type;
    uint64_t affinity;
    unsigned int nr_lines;
    int i;

    /* Disable the distributor */
    writel_relaxed(0, GICD + GICD_CTLR);//关闭distributor

    type = readl_relaxed(GICD + GICD_TYPER);//读取distributor中的GICD_TYPER寄存器
    nr_lines = 32 * ((type & GICD_TYPE_LINES) + 1);//读取ITLinesNumber位,查看支持的SPI数量

    if ( type & GICD_TYPE_LPIS )//查看是不是支持LPI
        gicv3_lpi_init_host_lpis(GICD_TYPE_ID_BITS(type));

    /* Only 1020 interrupts are supported */
    nr_lines = min(1020U, nr_lines);//spi最大的支持数量为1020
    gicv3_info.nr_lines = nr_lines;

    printk("GICv3: %d lines, (IID %8.8x).\n",
           nr_lines, readl_relaxed(GICD + GICD_IIDR));

    /* Default all global IRQs to level, active low */
    for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i += 16 )
        writel_relaxed(0, GICD + GICD_ICFGR + (i / 16) * 4);//设置默认的spi触发方式为电平触发level-sensitive.

    /* Default priority for global interrupts */
    for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i += 4 )
        writel_relaxed(GIC_PRI_IRQ_ALL, GICD + GICD_IPRIORITYR + (i / 4) * 4);//设置spi的默认priority

    /* Disable/deactivate all global interrupts */
    for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i += 32 )
    {
        writel_relaxed(0xffffffffU, GICD + GICD_ICENABLER + (i / 32) * 4);//设置spi默认为disable
        writel_relaxed(0xffffffffU, GICD + GICD_ICACTIVER + (i / 32) * 4);//设置spi默认为inactive
    }

    /*
     * Configure SPIs as non-secure Group-1. This will only matter
     * if the GIC only has a single security state.
     */
    for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i += 32 )
        writel_relaxed(GENMASK(31, 0), GICD + GICD_IGROUPR + (i / 32) * 4);//设置spi默认为non-secure Group-1

    gicv3_dist_wait_for_rwp();

    /* Turn on the distributor */
    writel_relaxed(GICD_CTL_ENABLE | GICD_CTLR_ARE_NS |
                GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1, GICD + GICD_CTLR);//设置GICD_CTLR寄存器。打开Group 0中断,打开Non-secure Group 1中断,设置Affinity routing enabled for Secure state。Non-secure的Affinity Routing没有打开。
          
    /* Route all global IRQs to this CPU */
    affinity = gicv3_mpidr_to_affinity(smp_processor_id());
    /* Make sure we don't broadcast the interrupt */
    affinity &= ~GICD_IROUTER_SPI_MODE_ANY;

    for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i++ )
        writeq_relaxed_non_atomic(affinity, GICD + GICD_IROUTER + i * 8);//默认所有spi中断都路由到当前cpu
}

gicv3_cpu_init的实现如下,这里面关于Redistributor的配置主要用于SGI,PPI和LPI,本文不涉及,主要看CPU interface的配置:

static int gicv3_cpu_init(void)
{
    int i, ret;

    /* Register ourselves with the rest of the world */
    if ( gicv3_populate_rdist() )//Redistributor的设置本文不涉及
        return -ENODEV;

    if ( gicv3_enable_redist() )//Redistributor的设置本文不涉及
        return -ENODEV;

    /* If the host has any ITSes, enable LPIs now. */
    if ( gicv3_its_host_has_its() )//its的支持本文不涉及
    {
        if ( !gicv3_enable_lpis() )
            return -EBUSY;
        ret = gicv3_its_setup_collection(smp_processor_id());
        if ( ret )
            return ret;
    }

    /* Set priority on PPI and SGI interrupts */
    for (i = 0; i < NR_GIC_SGI; i += 4)
        writel_relaxed(GIC_PRI_IPI_ALL,
                GICD_RDIST_SGI_BASE + GICR_IPRIORITYR0 + (i / 4) * 4);//设置SGI的priority

    for (i = NR_GIC_SGI; i < NR_GIC_LOCAL_IRQS; i += 4)
        writel_relaxed(GIC_PRI_IRQ_ALL,
                GICD_RDIST_SGI_BASE + GICR_IPRIORITYR0 + (i / 4) * 4);//设置PPI的priority

    /*
     * The activate state is unknown at boot, so make sure all
     * SGIs and PPIs are de-activated.
     */
    writel_relaxed(0xffffffffU, GICD_RDIST_SGI_BASE + GICR_ICACTIVER0);
    /*
     * Disable all PPI interrupts, ensure all SGI interrupts are
     * enabled.
     */
    writel_relaxed(0xffff0000U, GICD_RDIST_SGI_BASE + GICR_ICENABLER0);
    writel_relaxed(0x0000ffffU, GICD_RDIST_SGI_BASE + GICR_ISENABLER0);
    /* Configure SGIs/PPIs as non-secure Group-1 */
    writel_relaxed(GENMASK(31, 0), GICD_RDIST_SGI_BASE + GICR_IGROUPR0);

    gicv3_redist_wait_for_rwp();

    /* Enable system registers */
    gicv3_enable_sre();

    /* No priority grouping */
    WRITE_SYSREG(0, ICC_BPR1_EL1);//设置Binary Point Register

    /* Set priority mask register */
    WRITE_SYSREG(DEFAULT_PMR_VALUE, ICC_PMR_EL1);//设置priority mask register

    /* EOI drops priority, DIR deactivates the interrupt (mode 1) */
    WRITE_SYSREG(GICC_CTLR_EL1_EOImode_drop, ICC_CTLR_EL1);//设置EOI drops priority, DIR deactivates the interrupt

    /* Enable Group1 interrupts */
    WRITE_SYSREG(1, ICC_IGRPEN1_EL1);//当前安全状态下启用Group 1中断

    /* Sync at once at the end of cpu interface configuration */
    isb();

    return 0;
}

gicv3_hyp_init的实现如下

static void gicv3_hyp_init(void)
{
    register_t vtr;

    vtr = READ_SYSREG(ICH_VTR_EL2);//读取ICH_VTR_EL2寄存器
    gicv3_info.nr_lrs  = (vtr & ICH_VTR_NRLRGS) + 1;//支持的List registers 数量
    gicv3.nr_priorities = ((vtr >> ICH_VTR_PRIBITS_SHIFT) &//获取priorities的位数
                          ICH_VTR_PRIBITS_MASK) + 1;

    if ( !((gicv3.nr_priorities > 4) && (gicv3.nr_priorities < 8)) )//priorities的位数大于4位小于8位
        panic("GICv3: Invalid number of priority bits\n");

    WRITE_SYSREG(ICH_VMCR_EOI | ICH_VMCR_VENG1, ICH_VMCR_EL2);//ICH_VMCR_EL2(Interrupt Controller Virtual Machine Control Register)。ICH_VMCR_VENG1是设置Virtual Group 1 interrupt enable。ICH_VMCR_EOI设置Virtual EOI的模式。
    WRITE_SYSREG(GICH_HCR_EN, ICH_HCR_EL2);//设置ICH_HCR_EL2(Interrupt Controller Hyp Control Register)的第0位,使能virtual CPU interface。
}

secondary cpu init的时候(启动cpu的时候)也要做per-cpu的gicv3_cpu_init和gicv3_hyp_init

/* Set up the per-CPU parts of the GIC for a secondary CPU */
static int gicv3_secondary_cpu_init(void)
{
    int res;

    spin_lock(&gicv3.lock);

    res = gicv3_cpu_init();
    if ( res )
        goto out;

    gicv3_hyp_init();

out:
    spin_unlock(&gicv3.lock);

    return res;
}

关于寄存器的配置请查看另一个文章
Xen-hypervisor中SPI中断虚拟化的实现-重要的寄存器

domain中virq的初始化

结构体

struct domain
{
    domid_t          domain_id;
    ......
    struct vcpu    **vcpu;
    ......
    struct arch_domain arch;
    ......
}
struct arch_domain
{
#ifdef CONFIG_ARM_64
    enum domain_type type;
#endif
   ......
   struct vgic_dist vgic;
   ........
}
struct vgic_dist {
    /* Version of the vGIC */
    enum gic_version version;
    /* GIC HW version specific vGIC driver handler */
    const struct vgic_ops *handler;//注册成v3_ops
    /*
     * Covers access to other members of this struct _except_ for
     * shared_irqs where each member contains its own locking.
     *
     * If both class of lock is required then this lock must be
     * taken first. If multiple rank locks are required (including
     * the per-vcpu private_irqs rank) then they must be taken in
     * rank order.
     */
    spinlock_t lock;
    uint32_t ctlr;//GICD_CTLR寄存器的备份
    int nr_spis; /* Number of SPIs */
    unsigned long *allocated_irqs; /* bitmap of IRQs allocated */
    struct vgic_irq_rank *shared_irqs;//domain上的spi的rank数组指针
    /*
     * SPIs are domain global, SGIs and PPIs are per-VCPU and stored in
     * struct arch_vcpu.
     */
    struct pending_irq *pending_irqs;//domian上每个spi中断申请一个pending_irq
    /* Base address for guest GIC */
    paddr_t dbase; /* Distributor base address */
    paddr_t cbase; /* CPU interface base address */
    paddr_t csize; /* CPU interface size */
    paddr_t vbase; /* Virtual CPU interface base address */
#ifdef CONFIG_GICV3
    /* GIC V3 addressing */
    /* List of contiguous occupied by the redistributors */
    struct vgic_rdist_region {
        paddr_t base;                   /* Base address */
        paddr_t size;                   /* Size */
        unsigned int first_cpu;         /* First CPU handled */
    } *rdist_regions;
    int nr_regions;                     /* Number of rdist regions */
    unsigned long int nr_lpis;
    uint64_t rdist_propbase;
    struct rb_root its_devices;         /* Devices mapped to an ITS */
    spinlock_t its_devices_lock;        /* Protects the its_devices tree */
    struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
    rwlock_t pend_lpi_tree_lock;        /* Protects the pend_lpi_tree */
    struct list_head vits_list;         /* List of virtual ITSes */
    unsigned int intid_bits;
    /*
     * TODO: if there are more bool's being added below, consider
     * a flags variable instead.
     */
    bool rdists_enabled;                /* Is any redistributor enabled? */
    bool has_its;
#endif
};
static const struct vgic_ops v3_ops = {
    .vcpu_init   = vgic_v3_vcpu_init,//获取vcpu的Redistributors的base地址,添加到vcpu的结构体
    .domain_init = vgic_v3_domain_init,//1.获取当前domain所有vcpu的Redistributors的base地址,以及distributors的base地址 2.domain的its初始化 3.为Distributor注册mmio句柄 4.为redistributors占用的每个连续区域注册mmio处理程序。
    .domain_free = vgic_v3_domain_free,
    .emulate_reg  = vgic_v3_emulate_reg,
    .lpi_to_pending = vgic_v3_lpi_to_pending,//根据虚拟LPI编号,在pending的LPI树中查找LPI
    .lpi_get_priority = vgic_v3_lpi_get_priority,//根据虚拟LPI编号,在pending的LPI树中查找LPI,并返回priority
};

//vgic_v3_domain_init中为Distributor注册mmio句柄
static const struct mmio_handler_ops vgic_distr_mmio_handler = {
    .read  = vgic_v3_distr_mmio_read,//通过iomap对gic的Distributor的读
    .write = vgic_v3_distr_mmio_write,//通过iomap对gic的Distributor的写
};

每个rank中支持32个中断,所以priority和vcpu都是32个元素

struct vgic_irq_rank {
    spinlock_t lock; /* Covers access to all other members of this struct */

    uint8_t index;//rank的index

    uint32_t ienable;//rank上enable virq的mask
    uint32_t icfg[2];

    /*
     * Provide efficient access to the priority of an vIRQ while keeping
     * the emulation simple.
     * Note, this is working fine as long as Xen is using little endian.
     */
    union {
        uint8_t priority[32];//提供对vIRQ优先级的有效访问
        uint32_t ipriorityr[8];
    };

    /*
     * It's more convenient to store a target VCPU per vIRQ
     * than the register ITARGETSR/IROUTER itself.
     * Use atomic operations to read/write the vcpu fields to avoid
     * taking the rank lock.
     */
    //每个vIRQ的目标VCPU设置,比使用寄存器ITARGETSR/IROUTER本身更方便。
    uint8_t vcpu[32];
};

pending_irq定义的宏代表
GIC_IRQ_GUEST_QUEUED://inject一个virq,且没有添加到list register寄存器的状态。
GIC_IRQ_GUEST_ACTIVE://virq的active状态
GIC_IRQ_GUEST_VISIBLE://添加到list register寄存器的状态
GIC_IRQ_GUEST_ENABLED://virq使能标志
GIC_IRQ_GUEST_MIGRATING:暂时先不考虑MIGRATING的情况
GIC_IRQ_GUEST_PRISTINE_LPI:关于LPI的本文不涉及

struct pending_irq
{
#define GIC_IRQ_GUEST_QUEUED   0
#define GIC_IRQ_GUEST_ACTIVE   1
#define GIC_IRQ_GUEST_VISIBLE  2
#define GIC_IRQ_GUEST_ENABLED  3
#define GIC_IRQ_GUEST_MIGRATING   4
#define GIC_IRQ_GUEST_PRISTINE_LPI  5
    unsigned long status;   //status状态参看上面的宏
    struct irq_desc *desc; /* only set if the irq corresponds to a physical irq */
    unsigned int irq;  //irq或者virq中断号
#define GIC_INVALID_LR         (uint8_t)~0
    uint8_t lr;      //当前pending_irq占用的list register的number
    uint8_t priority;    //当前pending_irq的优先级
    uint8_t lpi_priority;       /* Caches the priority if this is an LPI. */
    uint8_t lpi_vcpu_id;        /* The VCPU for an LPI. */
    /* inflight is used to append instances of pending_irq to
     * vgic.inflight_irqs */
    struct list_head inflight; 
    /* lr_queue is used to append instances of pending_irq to
     * lr_pending. lr_pending is a per vcpu queue, therefore lr_queue
     * accesses are protected with the vgic lock.
     * TODO: when implementing irq migration, taking only the current
     * vgic lock is not going to be enough. */
    struct list_head lr_queue;//将当前的pending_irq挂在per vcpu的lr_pending上
};

调用流程

domain_create
–>arch_domain_create
---->domain_vgic_register
---->domain_vgic_init

domain_vgic_register主要注册了vgic_ops到domain.arch.vgic的handler

int domain_vgic_register(struct domain *d, int *mmio_count)
{
    switch ( d->arch.vgic.version )
    {
#ifdef CONFIG_GICV3
    case GIC_V3:
        if ( vgic_v3_init(d, mmio_count) )//默认适用gicv3
           return -ENODEV;
        break;
#endif
    case GIC_V2:
        if ( vgic_v2_init(d, mmio_count) )
            return -ENODEV;
        break;
    default:
        printk(XENLOG_G_ERR "d%d: Unknown vGIC version %u\n",
               d->domain_id, d->arch.vgic.version);
        return -ENODEV;
    }

    return 0;
}

int vgic_v3_init(struct domain *d, int *mmio_count)
{
    if ( !vgic_v3_hw.enabled )
    {
        printk(XENLOG_G_ERR
               "d%d: vGICv3 is not supported on this platform.\n",
               d->domain_id);
        return -ENODEV;
    }

    /* GICD region + number of Redistributors */
    *mmio_count = vgic_v3_max_rdist_count(d) + 1;

    /* one region per ITS */
    *mmio_count += vgic_v3_its_count(d);

    register_vgic_ops(d, &v3_ops);//d->arch.vgic.handler初始化为v3_ops

    return 0;
}

domain.arch.vgic的初始化

int domain_vgic_init(struct domain *d, unsigned int nr_spis)
{
    int i;
    int ret;

    d->arch.vgic.ctlr = 0;//ctlr的值默认为0

    /*
     * The vGIC relies on having a pending_irq available for every IRQ
     * described in the ranks. As each rank describes 32 interrupts, we
     * need to make sure the number of SPIs is a multiple of 32.
     */
    nr_spis = ROUNDUP(nr_spis, 32);

    /* Limit the number of virtual SPIs supported to (1020 - 32) = 988  */
    if ( nr_spis > (1020 - NR_LOCAL_IRQS) )
        return -EINVAL;

    d->arch.vgic.nr_spis = nr_spis;//spi的数量

    spin_lock_init(&d->arch.vgic.lock);

    d->arch.vgic.shared_irqs =//domain上的spi中断的rank数组指针
        xzalloc_array(struct vgic_irq_rank, DOMAIN_NR_RANKS(d));
    if ( d->arch.vgic.shared_irqs == NULL )
        return -ENOMEM;

    d->arch.vgic.pending_irqs =//为每个spi中断申请一个pending_irq
        xzalloc_array(struct pending_irq, d->arch.vgic.nr_spis);
    if ( d->arch.vgic.pending_irqs == NULL )
        return -ENOMEM;

    for (i=0; i<d->arch.vgic.nr_spis; i++)
        vgic_init_pending_irq(&d->arch.vgic.pending_irqs[i], i + 32);//初始化pending_irq数组的变量设置p->irq = virq(i + 32)
    /* SPIs are routed to VCPU0 by default */
    for ( i = 0; i < DOMAIN_NR_RANKS(d); i++ )//设置rank的index,以及默认路由到VCPU0
        vgic_rank_init(&d->arch.vgic.shared_irqs[i], i + 1, 0);

    ret = d->arch.vgic.handler->domain_init(d);//1.获取当前domain所有vcpu的Redistributors的base地址,以及distributors的base地址 2.domain的its初始化 3.为Distributor注册mmio句柄 4.为redistributors占用的每个连续区域注册mmio处理程序。
    if ( ret )
        return ret;

    d->arch.vgic.allocated_irqs =//分配中断bitmap的内存申请
        xzalloc_array(unsigned long, BITS_TO_LONGS(vgic_num_irqs(d)));
    if ( !d->arch.vgic.allocated_irqs )
        return -ENOMEM;

    /* vIRQ0-15 (SGIs) are reserved */
    for ( i = 0; i < NR_GIC_SGI; i++ )//SGI默认配置成allocated
        set_bit(i, d->arch.vgic.allocated_irqs);

    return 0;
}

vcpu上的vgic初始化

结构体

struct vcpu
{
    int              vcpu_id;
    ......
    struct arch_vcpu arch;
}

struct arch_vcpu
{
    ......
    struct vgic_cpu vgic;
    ......
}

struct vgic_cpu {
    /*
     * SGIs and PPIs are per-VCPU, SPIs are domain global and in
     * struct arch_domain.
     */
    struct pending_irq pending_irqs[32];//sgi和ppi的pending_irq结构体
    struct vgic_irq_rank *private_irqs;//irq_rank管理当前vcpu的私有(SGIs/PPIs)中断

    /* This list is ordered by IRQ priority and it is used to keep
     * track of the IRQs that the VGIC injected into the guest.
     * Depending on the availability of LR registers, the IRQs might
     * actually be in an LR, and therefore injected into the guest,
     * or queued in gic.lr_pending.
     * As soon as an IRQ is EOI'd by the guest and removed from the
     * corresponding LR it is also removed from this list. */
    //此列表按IRQ优先级排序,用于跟踪VGIC注入到guest中的IRQ。根据LR寄存器的可用性,IRQ实际上可能在LR中,因此可能被注入到guest中,或者在gic.LR_pending中排队。一旦IRQ被guest EOI’d并从相应的LR中删除,它也会从该列表中删除。
    struct list_head inflight_irqs;
    /* lr_pending is used to queue IRQs (struct pending_irq) that the
     * vgic tried to inject in the guest (calling gic_raise_guest_irq) but
     * no LRs were available at the time.
     * As soon as an LR is freed we remove the first IRQ from this
     * list and write it to the LR register.
     * lr_pending is a subset of vgic.inflight_irqs. */
    //lr_pending用于对vgic试图注入到guest中的irq(struct pending_irq)进行排队(调用gic_raise_guest_irq),但当时没有可用的lr。一旦LR被释放,我们就从这个列表中删除第一个IRQ,并将其写入LR寄存器。lr_pending是vgic.inflight_irqs的子集。
    struct list_head lr_pending;
    spinlock_t lock;

    /* GICv3: redistributor base and flags for this vCPU */
    paddr_t rdist_base;//per-vcpu的redistributor base
    uint64_t rdist_pendbase;
#define VGIC_V3_RDIST_LAST      (1 << 0)        /* last vCPU of the rdist */
#define VGIC_V3_LPIS_ENABLED    (1 << 1)
    uint8_t flags;
};

调用流程

vcpu_create
–>arch_vcpu_create
---->vcpu_vgic_init

int vcpu_vgic_init(struct vcpu *v)
{
    int i;

    v->arch.vgic.private_irqs = xzalloc(struct vgic_irq_rank);//一个rank支持32个中断,够一个vcpu的sgi和ppi用了
    if ( v->arch.vgic.private_irqs == NULL )
      return -ENOMEM;

    /* SGIs/PPIs are always routed to this VCPU */
    vgic_rank_init(v->arch.vgic.private_irqs, 0, v->vcpu_id);//rank的index设置为0,路由到当前vcpu

    v->domain->arch.vgic.handler->vcpu_init(v);//设置当前vcpu的Redistributors的base地址

    memset(&v->arch.vgic.pending_irqs, 0, sizeof(v->arch.vgic.pending_irqs));
    for (i = 0; i < 32; i++)//设置pending_irq结构体的irq变量为0-31(sgi和ppi)
        vgic_init_pending_irq(&v->arch.vgic.pending_irqs[i], i);

    INIT_LIST_HEAD(&v->arch.vgic.inflight_irqs);
    INIT_LIST_HEAD(&v->arch.vgic.lr_pending);
    spin_lock_init(&v->arch.vgic.lock);

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值