xen-gic初始化流程

xen-gic初始化流程

调试平台使用的是gic-600,建议参考下面的文档来阅读代码,搞清楚相关寄存器的功能。

  1. 《corelink_gic600_generic_interrupt_controller_technical_reference_manual_100336_0106_00_en》

  2. 《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" )

xen-gic初始化流程_第1张图片

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的是一致的,具体参考上一章节即可。

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

你可能感兴趣的:(虚拟化,linux,arm开发)