xen-gic初始化流程
调试平台使用的是gic-600,建议参考下面的文档来阅读代码,搞清楚相关寄存器的功能。
-
《corelink_gic600_generic_interrupt_controller_technical_reference_manual_100336_0106_00_en》
-
《IHI0069H_gic_architecture_specification》
一、xen-gic代码分析
1. core0流程
start_xen
......
init_traps();
WRITE_SYSREG(HCR_AMO | HCR_FMO | HCR_IMO, HCR_EL2); /* 让A/F/I异常都在EL2去执行,每个cpu都会这样设置 */
......
--->init_IRQ(); /* 初始化每个中断号的中断描述符信息 */
for ( irq = 0; irq < NR_LOCAL_IRQS; irq++ ) /* NR_LOCAL_IRQS = 32,这里的local irq是包含了SGI以及PPI */
local_irqs_type[irq] = IRQ_TYPE_INVALID;
init_local_irq_data /* 初始化每个local irq的desc结构体(从处理流程上看,对于gic的每个中断源,系统分配一个irq_desc数据结构与之对应) */
struct irq_desc *desc = irq_to_desc(irq);
for ( irq = 0; irq < NR_LOCAL_IRQS; irq++ )
{
init_one_irq_desc(desc);
desc->status = IRQ_DISABLED; /* 初始化中断状态 */
desc->handler = &no_irq_type; /* 初始化中断处理函数 */
cpumask_setall(desc->affinity); /* 初始化中断亲和度 */
desc->arch.type = IRQ_TYPE_INVALID; /* 初始化中断类型 */
desc->irq = irq; /* 初始化中断号 */
desc->action = NULL;
desc->arch.type = local_irqs_type[irq]; /* 初始化中断类型,统一设置为IRQ_TYPE_INVALID */
}
init_irq_data /* 初始化流程与init_local_irq_data类似 */
/* NR_LOCAL_IRQS = 32,NR_IRQ = 1024,初始化中断号为32~1024的desc结构体 */
for ( irq = NR_LOCAL_IRQS; irq < NR_IRQS; irq++ )
{
init_one_irq_desc(desc);
desc->irq = irq;
desc->action = NULL;
}
......
--->gic_preinit(); /* gic初始化的前期准备 */
gic_dt_preinit(); /* Find the interrupt controller and set up the callback to translate device tree */
device_init(node, DEVICE_GIC, NULL);
return desc->init(dev, data); /* 调试环境用的是gicv3,因此这里执行gicv3_dt_preinit */
gicv3_info.hw_version = GIC_V3;
gicv3_info.node = node; /* gicv3 dtb节点 */
register_gic_ops(&gicv3_ops);
dt_irq_xlate = gic_irq_xlate;
......
/* Set the GIC as the primary interrupt controller */
dt_interrupt_controller = node;
dt_device_set_used_by(node, DOMID_XEN); /* 该gic节点被xen使用 */
......
--->gic_init();
--->gic_hw_ops->init(); /* 执行gicv3_init */
--->gicv3_dt_init();
dt_device_get_address(node, 0, &dbase, NULL); /* 获取设备树中关于distributor的mmio地址,存放在dbase */
gicv3_ioremap_distributor(dbase); /* 映射distributor的mmio地址 */
gicv3.map_dbase = ioremap_nocache(dist_paddr, SZ_64K);
if ( !dt_property_read_u32(node, "#redistributor-regions", &gicv3.rdist_count) )
gicv3.rdist_count = 1; /* sunxi的设备树没有“#redistributor-regions”属性,所以这里默认就是1 */
for ( i = 0; i < gicv3.rdist_count; i++ )
{
dt_device_get_address(node, 1 + i, &rdist_base, &rdist_size); /* 获取gicv3的redistributor的mmio地址 */
rdist_regs[i].base = rdist_base;
rdist_regs[i].size = rdist_size;
}
gicv3.rdist_stride = 0; /* sunxi的设备树没有“redistributor-stride”属性,所以这里默认就是0 */
gicv3.rdist_regions= rdist_regs; /* 保存redistributor的地址信息 */
res = platform_get_irq(node, 0); /* 获取gicv3的Maintenence中断号(经过translate) */
gicv3_info.maintenance_irq = res;
/* sunxi的phy cpu interface以及virt cpu interface都是使用系统寄存器的方式来实现,不走mmio,所以下面的代码逻辑无效 */
res = dt_device_get_address(node, 1 + gicv3.rdist_count, &cbase, &csize);
if ( !res )
dt_device_get_address(node, 1 + gicv3.rdist_count + 2, &vbase, &vsize);
--->for ( i = 0; i < gicv3.rdist_count; i++ )
/* 映射redistributor的mmio地址 */
gicv3.rdist_regions[i].map_base = ioremap_nocache(gicv3.rdist_regions[i].base, gicv3.rdist_regions[i].size);
/* 读取Interrupt Controller Type Register的bit23~19(Interrupt identifier bits) */
--->reg = readl_relaxed(GICD + GICD_TYPER);
--->intid_bits = GICD_TYPE_ID_BITS(reg); /* The maximum number of INTIDs that the GIC implementation supports. */
--->vgic_v3_setup_hw(dbase, gicv3.rdist_count, gicv3.rdist_regions, intid_bits); /* 初始化vgic_v3_hw结构体 */
vgic_v3_hw.dbase = dbase;
......
--->gicv3_dist_init();
writel_relaxed(0, GICD + GICD_CTLR); /* Disable the distributor */
type = readl_relaxed(GICD + GICD_TYPER);
/*
获取GICD_TYPER的bit4~0(ITLineNumber, Number of SPIs divided by 32
表示最大支持的spi数量:max num = 32*(N+1)
*/
nr_lines = 32 * ((type & GICD_TYPE_LINES) + 1);
if ( type & GICD_TYPE_LPIS )
gicv3_lpi_init_host_lpis(GICD_TYPE_ID_BITS(type));
/* Only 1020 interrupts are supported */
nr_lines = min(1020U, nr_lines);
gicv3_info.nr_lines = nr_lines;
printk("GICv3: %d lines, (IID %8.8x).\n", nr_lines, readl_relaxed(GICD + GICD_IIDR));
/* (XEN) GICv3: 288 lines, (IID 0201643b). */
/* 设置所有的spi中断为低电平触发 */
/* 每个中断有2个bits设置,1个寄存器包含16个中断的配置 */
for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i += 16 )
writel_relaxed(0, GICD + GICD_ICFGR + (i / 16) * 4);
/*
配置默认的中断优先级为0xa0
每个中断有8个bits设置,1个寄存器包含4个中断的配置
#define GIC_PRI_LOWEST 0xf0
#define GIC_PRI_IRQ 0xa0
#define GIC_PRI_IPI 0x90
#define GIC_PRI_HIGHEST 0x80 /* Higher priorities belong to Secure-World */
*/
for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i += 4 )
{
priority = (GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 | GIC_PRI_IRQ << 8 | GIC_PRI_IRQ);
writel_relaxed(priority, GICD + GICD_IPRIORITYR + (i / 4) * 4);
}
/* 禁用/停用所有spi中断 */
for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i += 32 )
{
/* If written, disables forwarding of the corresponding interrupt */
writel_relaxed(0xffffffff, GICD + GICD_ICENABLER + (i / 32) * 4);
/* If written, deactivates the corresponding interrupt */
writel_relaxed(0xffffffff, GICD + GICD_ICACTIVER + (i / 32) * 4);
}
/* 将 SPI 配置为非安全组 1(non-secure Group-1) */
for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i += 32 )
writel_relaxed(GENMASK(31, 0), GICD + GICD_IGROUPR + (i / 32) * 4);
gicv3_dist_wait_for_rwp(); /* 判断前面的寄存器值是否完成写入 */
/* 使能distributor模块 */
writel_relaxed(GICD_CTL_ENABLE | GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1, GICD + GICD_CTLR);
/* Route all global IRQs to this CPU */
/* MPIDR_EL1(Multi-Processor Affinity Register),通常aff0代表在cluster内部的core ID,aff1代表cluster ID */
affinity = gicv3_mpidr_to_affinity(smp_processor_id()); /* 读取核号(Core ID) */
/*
Interrupt_Routing_Mode, bit [31]
0b0 Interrupts routed to the PE specified by a.b.c.d. In this routing, a, b, c, and d are the
values of fields Aff3, Aff2, Aff1, and Aff0 respectively
0b1 Interrupts routed to any PE defined as a participating node
设置中断只路由到对应的PE
*/
affinity &= ~GICD_IROUTER_SPI_MODE_ANY;
/* 配置GICD_IROUTER寄存器,让中断路由到当前core ID对应的核 */
for ( i = NR_GIC_LOCAL_IRQS; i < nr_lines; i++ )
writeq_relaxed(affinity, GICD + GICD_IROUTER + i * 8);
--->gicv3_cpu_init();
--->gicv3_populate_rdist();
uint64_t mpidr = cpu_logical_map(smp_processor_id()); /* 读取当前core的mpidr寄存器 */
/* 转换mpidr的affinity值为一个32bit数据 */
aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 24 |
MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
MPIDR_AFFINITY_LEVEL(mpidr, 0));
for ( i = 0; i < gicv3.rdist_count; i++ )
{
void __iomem *ptr = gicv3.rdist_regions[i].map_base; /* redistributor映射后的虚拟地址 */
readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK;
do {
typer = readq_relaxed(ptr + GICR_TYPER);
/* GICR_TYPER的bits [63:32]与aff进行比较,一致则代表当前core与该redistributor是一组的 */
if ( (typer >> 32) == aff )
{
this_cpu(rbase) = ptr; /* 设置per_cpu__rbase变量 */
if ( typer & GICR_TYPER_PLPIS )
{
rdist_addr = gicv3.rdist_regions[i].base;
rdist_addr += ptr - gicv3.rdist_regions[i].map_base;
procnum = (typer & GICR_TYPER_PROC_NUM_MASK);
procnum >>= GICR_TYPER_PROC_NUM_SHIFT;
gicv3_set_redist_address(rdist_addr, procnum);
this_cpu(lpi_redist).redist_addr = address;
this_cpu(lpi_redist).redist_id = redist_id;
gicv3_lpi_init_rdist(ptr);
gicv3_lpi_allocate_pendtable(&table_reg); /* 分配lpi中断的pending table */
writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
gicv3_lpi_set_proptable(rdist_base);
}
}
}
}
--->gicv3_enable_redist();
s_time_t deadline = NOW() + MILLISECS(1000);
/* 唤醒当前cpu的redistributor */
val = readl_relaxed(GICD_RDIST_BASE + GICR_WAKER);
/*
0b0 This PE is not in, and is not entering, a low power state
0b1 The PE is either in, or is in the process of entering, a low power state
*/
val &= ~GICR_WAKER_ProcessorSleep;
writel_relaxed(val, GICD_RDIST_BASE + GICR_WAKER);
do {
val = readl_relaxed(GICD_RDIST_BASE + GICR_WAKER);
/*
判断连接的PE是否处于静默状态
0b0 An interface to the connected PE might be active.
0b1 All interfaces to the connected PE are quiescen
*/
if ( !(val & GICR_WAKER_ChildrenAsleep) )
break;
if ( NOW() > deadline )
{
timeout = true;
break;
}
cpu_relax();
udelay(1);
} while ( timeout );
/* 设置PPI(IPI)中断的优先级为0x90 */
--->priority = (GIC_PRI_IPI << 24 | GIC_PRI_IPI << 16 | GIC_PRI_IPI << 8 | GIC_PRI_IPI);
for (i = 0; i < NR_GIC_SGI; i += 4)
writel_relaxed(priority, GICD_RDIST_SGI_BASE + GICR_IPRIORITYR0 + (i / 4) * 4);
/* 设置SGI中断的优先级为0xa0 */
priority = (GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 | GIC_PRI_IRQ << 8 | GIC_PRI_IRQ);
for (i = NR_GIC_SGI; i < NR_GIC_LOCAL_IRQS; i += 4)
writel_relaxed(priority, GICD_RDIST_SGI_BASE + GICR_IPRIORITYR0 + (i / 4) * 4);
/* 设置所有的SGI以及PPI中断为de-activated */
writel_relaxed(0xffffffff, GICD_RDIST_SGI_BASE + GICR_ICACTIVER0);
/* disable所有PPI中断,使能所有SGI中断 */
writel_relaxed(0xffff0000, GICD_RDIST_SGI_BASE + GICR_ICENABLER0); /* Interrupt Clear-Enable Register 0 */
writel_relaxed(0x0000ffff, GICD_RDIST_SGI_BASE + GICR_ISENABLER0); /* Interrupt Set-Enable Register 0 */
/* 设置所有的SGI以及PPI中断为非安全组1 */
writel_relaxed(GENMASK(31, 0), GICD_RDIST_SGI_BASE + GICR_IGROUPR0);
gicv3_enable_sre();
/* System Register Enable (SRE) */
/* 允许在EL2访问CPU & Virtual interface的系统寄存器 */
WRITE_SYSREG(val, ICC_SRE_EL2);
/* 设置为没有优先级分组 */
WRITE_SYSREG(0, ICC_BPR1_EL1);
/*
设置Interrupt Priority Mask寄存器的bit[7:0]为0xff
如果中断的优先级比这里设置的值要大的话,则cpu或者vcpu interface
会拉起irq line,通知PE(作用类似于一个滤波器)
*/
WRITE_SYSREG(DEFAULT_PMR_VALUE, ICC_PMR_EL1);
/*
设置EOI mode,需要操作EOI以及DIR寄存器来完成一次中断响应:
EOI:优先级降权
DIR:中断无效
*/
WRITE_SYSREG(GICC_CTLR_EL1_EOImode_drop, ICC_CTLR_EL1);
/* 使能group1的中断 */
WRITE_SYSREG(1, ICC_IGRPEN1_EL1);
--->gicv3_hyp_init();
vtr = READ_SYSREG(ICH_VTR_EL2); /* gic虚拟化特性 */
gicv3_info.nr_lrs = (vtr & ICH_VTR_NRLRGS) + 1; /* 获取支持的LR registers的数量 */
gicv3.nr_priorities = ((vtr >> ICH_VTR_PRIBITS_SHIFT) & ICH_VTR_PRIBITS_MASK) + 1; /* 获取实现的虚拟优先级位数 */
/*
ICH_VMCR_EOI(bit9) 0b1 Virtual EOI mode,需要操作EOI以及DIR寄存器来完成一次中断响应
ICV_EOIR0_EL1 and ICV_EOIR1_EL1 provide priority drop functionality only.
ICV_DIR_EL1 provides interrupt deactivation functionality
ICH_VMCR_VENG1(bit1): 0b1 Virtual Group 1 interrupts are enabled
*/
WRITE_SYSREG(ICH_VMCR_EOI | ICH_VMCR_VENG1, ICH_VMCR_EL2); /* 具体含义参照下图 */
/*
Enable. Global enable bit for the virtual CPU interface
0b0 Virtual CPU interface operation disabled
0b1 Virtual CPU interface operation enable
*/
WRITE_SYSREG(GICH_HCR_EN, ICH_HCR_EL2); /* 使能vcpu interface */
--->clear_cpu_lr_mask(); /* Clear LR mask for cpu0 */
this_cpu(lr_mask) = 0ULL;
......
--->init_maintenance_interrupt(); /* 注册gic controller的中断服务函数 */
request_irq(gic_hw_ops->info->maintenance_irq, 0, maintenance_interrupt, "irq-maintenance", NULL);
......
--->local_irq_enable();
/* DAIFClr D, A, I, F Directly clears any of the PSTATE.{D, A, I, F} bits to 0 */
/* 清除PSTATE的相关中断mask bit */
asm volatile ( "msr daifclr, #2\n" ::: "memory" )
文章来源:https://uudwc.com/A/20mOe
2. smp流程
start_xen
......
for_each_present_cpu
cpu_up(i)
__cpu_up(cpu);
arch_cpu_up(cpu);
smp_enable_ops[cpu].prepare_cpu(cpu); //多核启动使用psci的方式,这里执行call_psci_cpu_on
--->arm_smccc_smc(psci_cpu_on_nr, cpu_logical_map(cpu), __pa(virt_boot_xen((vaddr_t)init_secondary)), &res);
init_secondary
start_secondary
init_traps();
WRITE_SYSREG(HCR_AMO | HCR_FMO | HCR_IMO, HCR_EL2); /* 让A/F/I异常都在EL2去执行,每个cpu都会这样设置 */
......
gic_init_secondary_cpu();
gic_hw_ops->secondary_init(); /* 执行gicv3_secondary_cpu_init */
--->gicv3_secondary_cpu_init
gicv3_cpu_init();
gicv3_hyp_init();
clear_cpu_lr_mask(); /* Clear LR mask for secondary cpus */
init_secondary_IRQ();
init_local_irq_data();
local_irq_enable();
smp初始化gic的流程和core0的是一致的,具体参考上一章节即可。文章来源地址https://uudwc.com/A/20mOe
3. 问题
question | answer |
---|---|
1.在xen代码的gicv3_dist_init中,把所有的spi中断都路由到了core0,那其他的core怎么响应中断呢? | 在注册对应中断的时候会调用gicv3_irq_set_affinity接口重新设置中断路由(目前irq_set_affinity使用时设置的cpu都是随机的,没有考虑指定cpu) |
二、dom-gic代码分析
1. dom create分支
create_domUs
--->dt_for_each_child_node(chosen, node)
{
struct domain *d;
--->d = domain_create(++max_init_domid, &d_cfg, flags);
arch_domain_create(d, config, flags);
......
domain_vgic_register(d, &count)
vgic_v3_init(d, mmio_count);
register_vgic_ops(d, &v3_ops);
domain_vgic_init(d, config->arch.nr_spis); /* nr_spis = gic_hw_ops->info->nr_lines - 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;
d->arch.vgic.shared_irqs = xzalloc_array(struct vgic_irq_rank, DOMAIN_NR_RANKS(d));
d->arch.vgic.pending_irqs = xzalloc_array(struct pending_irq, d->arch.vgic.nr_spis);
for (i=0; i<d->arch.vgic.nr_spis; i++)
vgic_init_pending_irq(&d->arch.vgic.pending_irqs[i], i + 32); /* 从32开始赋值给vgic.pending_irqs[i]->irq */
/* SPIs are routed to VCPU0 by default */
for ( i = 0; i < DOMAIN_NR_RANKS(d); i++ )
vgic_rank_init(&d->arch.vgic.shared_irqs[i], i + 1, 0);
d->arch.vgic.handler->domain_init(d); /* 执行vgic_v3_domain_init */
/* Allocate memory for Re-distributor regions */
rdist_count = vgic_v3_max_rdist_count(d);
rdist_regions = xzalloc_array(struct vgic_rdist_region, rdist_count);
d->arch.vgic.nr_regions = rdist_count;
d->arch.vgic.rdist_regions = rdist_regions;
radix_tree_init(&d->arch.vgic.pend_lpi_tree);
if ( domain_use_host_layout(d) ) /* 目前使用的是Direct-mapped,走这个分支 */
{
d->arch.vgic.dbase = vgic_v3_hw.dbase; /* 设置vgic的Distributor基地址 */
for ( i = 0; i < vgic_v3_hw.nr_rdist_regions; i++ )
{
d->arch.vgic.rdist_regions[i].base = vgic_v3_hw.regions[i].base; /* 设置vgic的Redistributor基地址 */
/* Set the first CPU handled by this region */
d->arch.vgic.rdist_regions[i].first_cpu = first_cpu;
}
d->arch.vgic.intid_bits = vgic_v3_hw.intid_bits;
}
else
......
vgic_v3_its_init_domain(d);
/* 为Distributor注册mmio处理函数 */
register_mmio_handler(d, &vgic_distr_mmio_handler, d->arch.vgic.dbase, SZ_64K, NULL);
/* 为Redistributor注册mmio处理函数 */
for ( i = 0; i < d->arch.vgic.nr_regions; i++ )
{
struct vgic_rdist_region *region = &d->arch.vgic.rdist_regions[i];
register_mmio_handler(d, &vgic_rdistr_mmio_handler, region->base, region->size, region);
}
2. domain construct分支
create_domUs
......
--->construct_domU(d, node);
......
prepare_dtb_domU(d, &kinfo);
--->domain_handle_dtb_bootmodule(d, kinfo);
--->if ( dt_node_cmp(name, "gic") == 0 ) /* 不存在,后续由xen创建虚拟gic节点 */
{
kinfo->phandle_gic = fdt_get_phandle(pfdt, node_next);
continue;
}
--->if ( dt_node_cmp(name, "passthrough") == 0 )
scan_pfdt_node(kinfo, pfdt, node_next, DT_ROOT_NODE_ADDR_CELLS_DEFAULT, DT_ROOT_NODE_SIZE_CELLS_DEFAULT, true);
--->handle_prop_pfdt(kinfo, pfdt, nodeoff, address_cells, size_cells, scan_passthrough_prop);
--->handle_passthrough_prop(kinfo, xen_reg, xen_path, xen_force, cacheable, address_cells, size_cells);
--->handle_device_interrupts(kinfo->d, node, true); /* 核心 */
struct dt_raw_irq rirq;
nirq = dt_number_of_irq(dev); /* 获取中断数量 */
for ( i = 0; i < nirq; i++ )
{
dt_device_get_raw_irq(dev, i, &rirq);
res = platform_get_irq(dev, i); /* 获取中断号,需要调试确认该值!!!! */
map_irq_to_domain(d, res, need_mapping, dt_node_name(dev));
irq_permit_access(d, irq);
vgic_reserve_virq(d, irq);
route_irq_to_guest(d, irq, irq, devname); /* 将IRQ路由到指定的guest OS */
}
--->make_gic_domU_node(kinfo);
make_gicv3_domU_node(kinfo); /* 给dom的设备树创建gic节点 */
vgic_dist_base(&d->arch.vgic);
return vgic->vgic_dist_base;
dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
vgic_dist_base(&d->arch.vgic), GUEST_GICV3_GICD_SIZE);
for ( i = 0; i < d->arch.vgic.nr_regions; i++)
dt_child_set_range(&cells, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
d->arch.vgic.rdist_regions[i].base, d->arch.vgic.rdist_regions[i].size);
}
3. distributor的mmio处理
/* xen/arch/arm/vgic-v3.c */
static int vgic_v3_distr_mmio_write(struct vcpu *v, mmio_info_t *info, register_t r, void *priv)
int gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase);
switch ( gicd_reg )
{
case VREG32(GICD_CTLR):
{
vreg_reg32_update(&ctlr, r, info);
vreg_reg_update
/* Only EnableGrp1A can be changed */
/*
Enable Non-secure Group 1 interrupts
0b1 Non-secure Group 1 interrupts are enabled
*/
if ( ctlr & GICD_CTLR_ENABLE_G1A )
v->domain->arch.vgic.ctlr |= GICD_CTLR_ENABLE_G1A;
else
v->domain->arch.vgic.ctlr &= ~GICD_CTLR_ENABLE_G1A;
}
......
case VRANGE32(GICD_IGROUPR, GICD_IGROUPRN): /* 无效 */
case VRANGE32(GICD_ISENABLER, GICD_ISENABLERN):
case VRANGE32(GICD_ICENABLER, GICD_ICENABLERN):
case VRANGE32(GICD_ISPENDR, GICD_ISPENDRN):
case VRANGE32(GICD_ICPENDR, GICD_ICPENDRN):
case VRANGE32(GICD_ISACTIVER, GICD_ISACTIVERN): /* 无效 */
case VRANGE32(GICD_ICACTIVER, GICD_ICACTIVERN): /* 无效 */
case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
case VRANGE32(GICD_ICFGR, GICD_ICFGRN):
/* Above registers are common with GICR and GICD Manage in common */
return __vgic_v3_distr_common_mmio_write("vGICD", v, info, gicd_reg, r);
......
case VRANGE64(GICD_IROUTER32, GICD_IROUTER1019):
{
uint64_t irouter;
if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
rank = vgic_rank_offset(v, 64, gicd_reg - GICD_IROUTER, DABT_DOUBLE_WORD);
if ( rank == NULL )
goto write_ignore;
vgic_lock_rank(v, rank, flags);
irouter = vgic_fetch_irouter(rank, gicd_reg - GICD_IROUTER); /* 获取vaff */
vreg_reg64_update(&irouter, r, info);
vgic_store_irouter(v->domain, rank, gicd_reg - GICD_IROUTER, irouter);
new_vcpu = vgic_v3_irouter_to_vcpu(d, irouter);
/*
* When the Interrupt Route Mode is set, the IRQ targets any vCPUs.
* For simplicity, the IRQ is always routed to vCPU0.
*/
if ( irouter & GICD_IROUTER_SPI_MODE_ANY )
return d->vcpu[0];
vcpu_id = vaffinity_to_vcpuid(irouter);
if ( vcpu_id >= d->max_vcpus )
return NULL;
return d->vcpu[vcpu_id];
old_vcpu = d->vcpu[read_atomic(&rank->vcpu[offset])];
/* Only migrate the IRQ if the target vCPU has changed */
if ( new_vcpu != old_vcpu )
{
if ( vgic_migrate_irq(old_vcpu, new_vcpu, virq) ) /* 当前vcpu和旧的vcpu不一致时,进行中断迁移 */
write_atomic(&rank->vcpu[offset], new_vcpu->vcpu_id);
}
vgic_unlock_rank(v, rank, flags);
return 1;
}
......
}
4. redistributor的mmio处理
三、涉及到的寄存器
Register | Disciption |
---|---|
GICD_TYPER | Redistributor Type Register,提供关于此Redistributor的配置信息 |
GICD_CTLR | |
GICD_IIDR | |
GICD_ICFGR | |
GICD_IPRIORITYR | Interrupt Priority Registers,保存相应中断的优先级 |
GICD_ICENABLER | Interrupt Clear-Enable Registers,禁止将相应的中断转发到 CPU 接口 |
GICD_ICACTIVER | Interrupt Clear-Active Registers,取消相应的中断。这些寄存器用于保存和恢复GIC状态 |
GICD_IGROUPR | Interrupt Group Registers,控制对应的中断是在Group-0还是Group-1中 |
GICD_IROUTER | Interrupt Routing Registers, |
GICR_PIDR2 | Peripheral ID2 Register,获取GIC版本 |
GICR_PENDBASER | |
GICR_WAKER | Redistributor Wake Register,允许软件控制与Redistributor对应的WakeRequest电源管理信号的行为 |
ICH_VTR_EL2 | Interrupt Controller VGIC Type Register,记录了支持的GIC虚拟化特性 |
ICH_VMCR_EL2 | Interrupt Controller Virtual Machine Control Register,允许hypervisor保存和恢复虚拟机的GIC state |
ICH_HCR_EL2 | Interrupt Controller Hyp Control Register,虚拟机环境控制 |
GICR_PIDR2 | |
GICR_TYPER | |
GICR_TYPER_PLPIS | |
GICR_TYPER_VLPIS | |
GICR_TYPER_LAST | |
GICR_WAKER | |
GICR_IPRIORITYR0 | |
GICR_ICACTIVER0 | |
GICR_ICENABLER0 | |
GICR_ISENABLER0 | |
GICR_IGROUPR0 | |
ICC_SRE_EL2 | |
ICC_BPR1_EL1 | |
ICC_PMR_EL1 | |
ICC_CTLR_EL1 | |
ICC_IGRPEN1_EL1 | |
ICH_VTR_EL2 | |
ICH_VMCR_EL2 | |
ICH_HCR_EL2 | |
ICV_EOIR0_EL1 | |
ICV_DIR_EL1 | |