QEMU的中断配置(GIC_V2)

date
Apr 1, 2022
slug
gic_v2_config
tags
FlexiOS
summary
type
Post
status
Published
💡
在QEMU上写OS的时候发现用不了TIMER中断,发现是GIC_V2没有配置·,于是记录一下配置GIC_V2的方法,主要是配置GIC中的几个步骤,需要会利用设备树信息。

这篇文章记录了啥?

  • 如何利用设备树文件查看设备信息
  • 如何配置qemu中的中断
  • 如何查看设备的中断号

参考资料

正文

如何获取GIC的地址

  • 在qemu dump出设备树文件dtb
  • 利用dtc将dtb文件转化为可读的dts
  • 在dts中寻找中断设备的信息,如:
intc@8000000 {
                phandle = <0x8001>;
                reg = <0x0 0x8000000 0x0 0x10000 0x0 0x8010000 0x0 0x10000>;
                compatible = "arm,cortex-a15-gic";
                ranges;
                #size-cells = <0x2>;
                #address-cells = <0x2>;
                interrupt-controller;
                #interrupt-cells = <0x3>;

                v2m@8020000 {
                        phandle = <0x8002>;
                        reg = <0x0 0x8020000 0x0 0x1000>;
                        msi-controller;
                        compatible = "arm,gic-v2m-frame";
                };
 };

从中可以获得gic设备的相关信息:
- addr:0x800_0000
- reg addr:
    - gicc_reg:0x800_0000
如何获取?Google (compatible = "arm,cortex-a15-gic";) 可以找到相应的设备树描述

如何配置

获取了GIC地址之后可以开始正式配置,具体见注释
//=========================配置distributor======================== 

/*
 GICD_ITARGETSR为寄存器组
 记录了各个中断使用的CPU,每个中断利用8位进行指定
 如:
 GICD_ITARGETSR
 0x0 => | intc0 | intc1 | intc2 | intc3 |
 0x4 => | intc4 | intc5 | intc6 | intc7 |
 对于GIC V2 [0..31]是PPI [32..]是SPI(global interrupts )
*/

fn gicv2_get_cpumask()->u32{
    let mut mask=0;
    for i in (0..32).step_by(4){
        mask = get32(GICD_ITARGETSR+i);
        mask |= mask >>16;
        mask |= mask >>8;
        if mask!=0{
            break;
        }
    }
    return mask;
}

fn gicv2_dist_init(){
    /* Disable the distributor */
    put32(GICD_CTLR, GICD_CTL_DISABLE as u32);
    println!("disbale distributor");
    
    /* 从GICD_TYPER获取gic支持的最大中断数 */
    /*
      具体描述:
			Indicates the maximum number of interrupts that the GIC supports. 
      If ITLinesNumber=N, the maximum number of interrupts is 32(N+1).
      The interrupt ID range is from 0 to (number of IDs - 1).
    */
    let _type = get32(GICD_TYPER);
		let mut nr_lines = get32(GICD_TYPER) & GICD_TYPE_LINES as u32;
	  nr_lines = (nr_lines + 1) * 32;

    /* Set all global interrupts to this CPU only */
    let mut cpumask = gicv2_get_cpumask();
    cpumask |= cpumask <<8;
    cpumask |= cpumask <<16;
		// [32..]的中断是全局的 
    for i in (32..nr_lines as usize).step_by(4){
        put32(GICD_ITARGETSR+i*4/4, cpumask);
    }

    /* Set all global interrupts to be level triggered, active low */
    for i in (32..nr_lines as usize).step_by(16){
        put32(GICD_ICFGR+i/4,GICD_INT_ACTLOW_LVLTRIG as u32);
    }
    
    /* Set priority on all global interrupts */
    for i in (32..nr_lines as usize).step_by(4){
        put32(GICD_IPRIORITYR+i,GICD_INT_DEF_PRI_X4 as u32);
    }


	/*
	 * Deactivate and disable all SPIs. Leave the PPI and SGIs
	 * alone as they are in the redistributor registers on GICv3.
	 */
    for i in (32..nr_lines as usize).step_by(32) {
		put32(GICD_ICACTIVER + i / 8, GICD_INT_EN_CLR_X32 as u32);
		put32(GICD_ICENABLER + i / 8, GICD_INT_EN_CLR_X32 as u32);
	}

    /* Turn on the distributor */
    put32(GICD_CTLR, GICD_CTL_ENABLE as u32);
}

//====================配置cpu interface==========================
//GICD_ISENABLER enable cpu forward
//GICD_ICENABLER disable cpu forward
fn gicv2_cpu_init(){
	/*
	 * Deal with the banked PPI and SGI interrupts - disable all
	 * private interrupts. Make sure everything is deactivated.
	 */
    for i in (0..32).step_by(32){
		put32(GICD_ICACTIVER + i / 8, GICD_INT_EN_CLR_X32 as u32);
		put32(GICD_ICENABLER + i / 8, GICD_INT_EN_CLR_X32 as u32);
	}

	/* Set priority on PPI and SGI interrupts */
	for i in(0..32).step_by(4){
		put32(GICD_IPRIORITYR + i * 4 / 4, GICD_INT_DEF_PRI_X4 as u32);
	}

	/* Ensure all SGI interrupts are now enabled */
  /* SGI is 0-15 */
	put32(GICD_ISENABLER, 0xffff as u32);

	/* Don't mask by priority */
	put32(GICC_PMR, GICC_INT_PRI_THRESHOLD as u32);

	/* Finest granularity of priority */
	put32(GICC_BPR, 0);
    for i in 0..4{
		put32(GICC_APR + i * 4, 0);
	}

	/* Turn on delivery */
	let mut bypass = get32(GICC_CTLR);
	bypass &= GICC_DIS_BYPASS_MASK as u32;
	put32(GICC_CTLR, bypass | GICC_CTRL_EOImodeNS as u32| GICC_ENABLE as u32);
}

/* enable the timer's irq */
put32(GICD_ISENABLER, GICD_INT_EN_CLR_PPI as u32);

如何获取中断号

  • 然后从相应设备的设备树属性获取中断号
timer {
	interrupts = <0x1 0xd 0x104 
								0x1 0xe 0x104 
								0x1 0xb 0x104 
								0x1 0xa 0x104>;
	always-on;
	compatible = "arm,armv8-timer", "arm,armv7-timer";
};
  • timer的中断号是0xd 0xe 0xb 0xa,timer的interrupts类型是PPI,从GICV2可以看出PPI的中断号范围是[16-32],因此timer的中断号是(0xd+16) (0xe+16) (0xb+16) (0xa+16)
  • 其他类型中断同理
 

© ZENOTME 2021 - 2022