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);
如何获取中断号
- 首先可以从GIC的设备树定义获取interrupts
- 然后从相应设备的设备树属性获取中断号
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)
- 其他类型中断同理