因为小可并非硬件编程出身,汇编基础又比较差。。。所以刚开始理解利用IOAPIC重定位技术的时候相当困难。
何为IOAPIC?
首先,必须认识到它是一个硬件,可编程的硬件。我理解的它在整个流程中的作用如图:
首先,必须创建一个新的中断项,也就是在IDT表中搜索到一个空闲的项,代码如下
P2C_U8 p2cGetIdleIdtVec()
{
P2C_U8 i;
PP2C_IDTENTRY idt_addr = (PP2C_IDTENTRY)p2cGetIdt(); // 从vec20搜索到2a即可。
for(i=0x20;i<0x2a;i++)
{
// 如果类型为0说明是空闲位置,返回即可。
if(idt_addr[i].type == )
{
return i;
}
}
return ;
}
复制中断表中索引为0x93的项到新项
P2C_U8 p2cCopyANewIdt93(P2C_U8 id,void *interrupt_proc)
{
// 我们写入一个新的中断门。这个门完全拷贝原来的0x93
// 上的idtentry,只是中断处理函数的地址不同。
PP2C_IDTENTRY idt_addr = (PP2C_IDTENTRY)p2cGetIdt();
idt_addr[id] = idt_addr[0x93];
idt_addr[id].offset_low = P2C_LOW16_OF_32(interrupt_proc);
idt_addr[id].offset_high = P2C_HIGH16_OF_32(interrupt_proc);
return id;
}
然后将ioapic重定位表中关于键盘中断IRQ1的处理地址替换掉,修改ioapic重定位表需要使用两个寄存器。
一个是IOREGSEL寄存器(只低8位有用) 一个是IOWIN寄存器(32位)
首先在IOREGSEL寄存器指定要访问的ioapic寄存器的编号,然后修改IOWIN寄存器中的内容即可达到目的
Windows把这两个寄存器分别映射到内存的物理地址0xFEC00000和0xFEC00010处
获取IOREGSEL寄存器的代码为
P2C_U8 *io_reg_sel;
PHYSICAL_ADDRESS phys ;
PVOID paddr;
RtlZeroMemory(&phys,sizeof(PHYSICAL_ADDRESS));
phys.u.LowPart = 0xfec00000;
paddr = MmMapIoSpace(phys, 0x14, MmNonCached);
IOWIN寄存器往后偏移0x10
P2C_U32 *io_win;
io_win = (P2C_U32 *)((P2C_U8 *)(paddr) + 0x10);
然后使用IOREGSEL寄存器选择第0x12项,为IRQ1的项
*io_reg_sel = 0x12;
设置新的中断号
ch1 = *io_win;
ch1 &= 0xffffff00;
ch1 |= (P2C_U32)new_ch;
*io_win = ch1;
完成!