逆它纯粹就是无聊,后面想想以后中断虚拟化应该能用到,由于工作项目时间很紧,勉强断断续续逆向了一部分加自己做些实验来验证,目前先将这部分分享出来,带和我一样的新手入个门,建议先看下面推荐的三本书里的APIC章节,文章下面关于基础知识的介绍,来自网络各个大佬的笔墨,小弟就搬抄过来,省去自己写的麻烦事,建议先看书,文章纯粹是辅助(王者荣耀里的孙膑,顶多给你加加速),以下的步骤就是APIC的初始化流程,对了,AMD和INTEL内部实现会有部分区别,我的是INTEL的,AMD就将就看看吧,区别不大。
参考书籍:《Intel手册》、《x86/x64体系探索及编程》、《一个64位操作系统的设计与实现》
APIC (Advanced Programmable Interrupt Controller)是90年代Intel为了应对将来的多核趋势提出的一整套中断处理方案,用于取代老旧的8259A PIC。这套方案适用于多核(Multi-Processor)机器,每个CPU拥有一个Local APIC,整个机器拥有一个或多个IOAPIC,设备的中断信号先经由IOAPIC汇总,再分发给一个或多个CPU的Local APIC。为了配合APIC,还推出了MPSpec (Multiprocessor Specification),为BIOS向OS提供中断配置信息的方式提供了规范。
自90年代以来,PCI总线发展出了MSI (Message Signalled Interrupt),目前的机器中是以MSI为主要的中断机制,IOAPIC作为辅助,但CPU处仍使用Local APIC接收和处理中断。当时提出的MPSpec经过演化目前已成为了ACPI规范的一部分,BIOS可以通过ACPI表向OS报告中断配置情况(e.g. IOAPIC的引脚连接到哪个设备)。
初代奔腾(Pentium)上初次引入Local APIC时,它是外置的Intel 82489DX芯片。在一些奔腾型号以及P6 family(即从奔腾Pro到奔腾3)上则将其改为了内置,但功能保持不变。自奔腾4及至强(Xeon)开始取消了APIC Bus以及一部分相关设置,于是改称xAPIC,目前Intel CPU的默认模式就是xAPIC。后来又增加了x2APIC模式作为xAPIC的扩展。
通过上面【BIOS可以通过ACPI表向OS报告中断配置情况】这段话,我们知道windows内核是通过ACPI表知道中断硬件的配置情况。
没办法,简单介绍一下啥是ACPI,简单来说:就是固件(BIOS/UEFI)向系统传递硬件架构信息的机制。
那传递哪些硬件架构信息了?
MADT/"APIC":每个处理器的Local APIC信息(包括ID以及NMI等信息)以及系统的I/O APIC信息(包括与8259A相比的板载基础设备的中断重定向信息)
"HPET":系统的HPET信息
"MCFG":系统的PCIe的MCFG信息
"DSDT"、"SSDT":其它信息,包括板载基础设备的配置信息(包括内存、I/O、中断等信息),PCI/PCIe设备在8259A和APIC状态下的中断路由信息,以及 ACPI的一些特定的电源配置信息等
好了,正主来了,MADT(多APIC描述表),它描述了APIC的工作原理,windows通过以下API来得到,内部实现并不复杂,就是通过关键字去查找你需要的是那个硬件配置信息,有兴趣可以自行逆向研究,当然整个ACPI机制还是较为复杂的,有兴趣可以带小弟一起学习。
pMapic=HalSocGetAcpiTable('CIPA')
不同的版本,有不同的结构体,APICTABLES内部不同,为了方便,我们通过RW来查看
内部各个结构字段的意义,还请各位看官自己动动手BAIDU/GOOGLE吧!
前面的基础介绍里面,介绍了APIC->xAPIC->x2APIC的由来,知道从Apic总线变成了SystemBus,当然变化的不只这一点,CPU的个数变了,更重要的是访问方式也改变了,LAPIC的寄存器是通过MMIO访问的(即xAPIC模式),后来添加的x2APIC模式则通过MSR来访问其寄存器(可以向前兼容),windows需要根据你是哪个模式,采用哪种方式访问,所以用了下面这个API。
status = HalpApicSetupRegisterAccess();
主要实现了
如果是xAPIC:
HalpApicRead=HalpApic1ReadRegister
HalpApicWrite=HalpApic1WriteRegister
HalpApicWriteCommand=HalpApic1WriteIcr
HalpApicWaitForCommand=HalpApic1WaitForIcr
HalpApicEndOfInterrupt=HalpApic1EndOfInterrupt
如果是x2APIC
HalpApicRead=HalpApicX2ReadRegister
HalpApicWrite=HalpApicX2WriteRegister
HalpApicWriteCommand=HalpApicX2WriteCommand
HalpApicWaitForCommand=HalAcquireDisplayOwnership
HalpApicEndOfInterrupt==HalpApicX2EndOfInterrupt
实际上就是生成一个中断控制器对象,然后放进HalpRegisteredInterruptControllers链表里,(不好理解?你就理解类似EPROCESS的创建,后续只要调用APIC的函数,就需要这个对象)
HalpApicRegisterIoUnit(__int64 IoapicPhyAddr, int IoApicId, int GsiBase)
至于这个GsiBase是啥,百度吧,简单介绍一下就是:GSI是ACPI引入的概念,它为系统中每个中断源指定一个唯一的中断号,如果你只是想简单的在windows上了解APIC的工作机制,这个可以不用去深究,如果你想了解windows上APIC的管理,这个你得去深究了,windows把LocalApi和IoApic的引脚弄了一套中断线(INTERRUPT_LINE)的概念,一个Interrupt Line可以管理对应1/多个引脚,对中断控制器进行抽象 ,类似于对象管理器(object/object_header等等),Apic / Gic / Bcm2836ic / pic就是实例,实现很复杂,目前我没有逆完,就不在这里误人子弟了。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-8-21 23:12
被学技术打豆豆编辑
,原因: