【发布时间】:2019-08-06 09:57:29
【问题描述】:
我正在按照一本书编写类似 linux 的内核,但是遇到了 APIC 章节的问题。
首先,我将列出我的平台。我在 Windows 10 上,使用 Virtual Box 运行 Ubuntu 18.04,并在其中运行测试代码。
目前我对APIC的理解如下:
1、每个内核都内置Local APIC,主板上内置I/O APIC
2、可以使用内存映射或MSR引用访问本地APIC
3、I/O APIC由3个寄存器IOREGSEL、IOWIN、EOI访问。基本思路是设置IOREGSEL的值,用IOWIN访问对应的寄存器。
4、有3种模式,感兴趣的是Symmetric I/O模式
5,I/O APIC有24个pin,pin 1连接到键盘
6、要启用APIC和I/O APIC,有一系列工作要做:
a) 屏蔽 8529A 中断
b) 启用 xAPIC 和 2xAPIC,以便 MSR 访问成为可能
c) 屏蔽所有 LVT(如果不需要本地中断)
d) 为 I/O APIC 设置 RTE 条目
e) 将 IMCR 寄存器设置为 0x01h,强制 8529A 中断传递信号到 I/O APIC
f) 通过Root Complex Base Address Register(RCBA)找到其他中断控制寄存器(OIC),并设置OIC[8]=1b使能I/O APIC
现在我将提出我的问题:
1、在bochs和Virtual Box上,检测到Max LVT Entry数量为6(根据Manual,有6+1=7个LVT entry),无法访问LVT_CMCI entry(gp故障)。
2,据说主板上不同的芯片会将RCBA映射到不同的端口,我必须通过手册查找。但是有没有办法通过软件本身来检测它,否则商业操作系统如何适应不同的平台。
3、我在虚拟机上,如何检测RCBA的可访问性
感谢任何可以为我的问题提供线索或帮助我进一步了解本章的人。
我将介绍一些关于为简单的键盘中断设置 APIC 的代码。
首先是中断处理函数
void IRQ0x21_interrupt(Int_Info_No_Err STK)
{
Ent_Int;
color_printk(RED,BLACK,"do_IRQ: 0x21\t");
unsigned char x;
x = io_in8(0x60);
color_printk(RED,BLACK,"key code:%#08x\n",x);
wrmsr(0x80b, 0UL);
//io_out8(0x20,0x20);
Ret_Int;
}
Ret_Int & Ent_Int 是定义处理中断堆栈的宏,wrmsr() 函数将 0 写入 MSR 地址 0x80b(EOI)
接下来是 LAPIC 和 I/O APIC 的设置函数,假设物理地址 0xFEC00000 已经映射到页表中
void APIC_init(void)
{
int i;
int virtual_index_address;
int virtual_data_address;
int virtual_EOI_address;
unsigned long tmp;
//Set interrupt, note No.33 link to IRQ0x21_interrupt() function
for(i = 32;i < 56;i++)
{
_Set_INT(IDT_PTR.Offset + i, ATTR_INTR_GATE, 2, interrupt[i - 32]);
}
//Mask 8529A
io_out8(0x21,0xff);
io_out8(0xa1,0xff);
//enable IMCR
io_out8(0x22,0x70);
io_out8(0x23,0x01);
#pragma region Init_LAPIC
//Enabling xAPIC(IA32_APIC_BASE[10]) and 2xAPIC(IA32_APIC_BASE[11])
tmp = rdmsr(0x1b);
tmp |= ((1UL << 10) | (1UL << 11));
wrmsr(0x1b,tmp);
//Enabling LAPIC(SVR[8])
tmp = rdmsr(0x80f);
tmp |= (1UL << 8); //No support for EOI broadcast, no need to set bit SVR[12]
wrmsr(0x80f,tmp);
//Mask all LVT
tmp = 0x10000;
//wrmsr(0x82F, tmp); Virtual machine do not support
wrmsr(0x832, tmp);
wrmsr(0x833, tmp);
wrmsr(0x834, tmp);
wrmsr(0x835, tmp);
wrmsr(0x836, tmp);
wrmsr(0x837, tmp);
#pragma endregion
#pragma region Init_IOAPIC
virtual_index_address = (unsigned char*)(0xFEC00000 + PAGE_OFFSET);
virtual_data_address = (unsigned int*)(0xFEC00000 + PAGE_OFFSET + 0x10);
virtual_EOI_address = (unsigned int*)(0xFEC00000 + PAGE_OFFSET + 0x40);
//Setting RTEs, mask all but 0x01 RTE table for keyboard
for(i = 0x10;i < 0x40;i += 2){
*virtual_index_address = i;
io_mfence;
*IOAPIC_MAP.virtual_data_address = 0x10020 + ((i - 0x10) >> 1) & 0xffffffff;
io_mfence;
*IOAPIC_MAP.virtual_index_address = i + 1;
io_mfence;
*IOAPIC_MAP.virtual_data_address = ((0x10020 + ((i - 0x10) >> 1)) >> 32) & 0xffffffff;
io_mfence;
}
*virtual_index_address = 0x12;
io_mfence;
*IOAPIC_MAP.virtual_data_address = 0x10020 + (2 >> 1) & 0xffffffff;
io_mfence;
*IOAPIC_MAP.virtual_index_address = i + 1;
io_mfence;
*IOAPIC_MAP.virtual_data_address = ((0x10020 + (2 >> 1)) >> 32) & 0xffffffff;
io_mfence;
#pragma endregion
}
因此,根据答案,一旦我完成 RTE 的初始化,I/O APIC 就会设置为打开。如果有人可以告诉我上面的代码是否有效(对于简单的键盘中断)。非常感谢。
【问题讨论】:
标签: assembly operating-system x86-64 apic