• 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" )
    
    • 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

    在这里插入图片描述

    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); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    smp初始化gic的流程和core0的是一致的,具体参考上一章节即可。

    3. 问题

    questionanswer
    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);
                        }
    
    • 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

    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);
    	}
    
    • 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

    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;
            }
            ......
        }
    
    • 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

    4. redistributor的mmio处理

    三、涉及到的寄存器

    RegisterDisciption
    GICD_TYPERRedistributor Type Register,提供关于此Redistributor的配置信息
    GICD_CTLR
    GICD_IIDR
    GICD_ICFGR
    GICD_IPRIORITYRInterrupt Priority Registers,保存相应中断的优先级
    GICD_ICENABLERInterrupt Clear-Enable Registers,禁止将相应的中断转发到 CPU 接口
    GICD_ICACTIVERInterrupt Clear-Active Registers,取消相应的中断。这些寄存器用于保存和恢复GIC状态
    GICD_IGROUPRInterrupt Group Registers,控制对应的中断是在Group-0还是Group-1中
    GICD_IROUTERInterrupt Routing Registers,
    GICR_PIDR2Peripheral ID2 Register,获取GIC版本
    GICR_PENDBASER
    GICR_WAKERRedistributor Wake Register,允许软件控制与Redistributor对应的WakeRequest电源管理信号的行为
    ICH_VTR_EL2Interrupt Controller VGIC Type Register,记录了支持的GIC虚拟化特性
    ICH_VMCR_EL2Interrupt Controller Virtual Machine Control Register,允许hypervisor保存和恢复虚拟机的GIC state
    ICH_HCR_EL2Interrupt 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
  • 相关阅读:
    不知道怎么把英文文档翻译成中文?手把手教你怎么操作
    【2022年11月15日提高A组】路径计数【DP】
    27岁Python程序员做独立开发年收入超900万,家中有屋又有田,生活乐无边
    NLP模型(一)——word2vec实现
    02 Truffle TutorialToken 示例
    AngularJS对于SQL的操作心得以及DOM的研究
    光流法动目标检测
    2020java面试总结
    06-Go语言中数组和切片
    Mysql系列(一) - Mysql的架构体系
  • 原文地址:https://blog.csdn.net/u012010054/article/details/132957032