首页 理论教育ACPI表实例应用:PCIExpress体系结构导读

ACPI表实例应用:PCIExpress体系结构导读

【摘要】:在ACPI提供的各类表中,DSDT描述符表最为重要。当操作系统收到ACPI中断请求,即SCI中断请求时,将根据DSDT中提供的代码对相应的ACPI寄存器进行操作,从而完成所需的功能。图14-4 DSDT、ACPI寄存器和操作系统之间的关系下文将举例说明图14-4中各模块之间的关系,以及系统软件如何处理ACPI表。在ACPI中,每一个设备使用的标识不相同。当GPEx_STS寄存器的第0位为1时,将进一步检查PBP和PBW位。

在ACPI提供的各类表中,DSDT描述符表最为重要。DSDT描述符表包含当前处理器系统使用的一些硬件资源,如某些外部设备使用的地址空间,以及对这些硬件资源的操作等其他描述信息。

当操作系统收到ACPI中断请求,即SCI中断请求时,将根据DSDT中提供的代码对相应的ACPI寄存器进行操作,从而完成所需的功能。DSDT、ACPI寄存器和操作系统之间的关系如图14-4所示。

978-7-111-29822-9-Part03-20.jpg

图14-4 DSDT、ACPI寄存器和操作系统之间的关系

下文将举例说明图14-4中各模块之间的关系,以及系统软件如何处理ACPI表。假设在一个x86处理器系统中,电源按钮(Power Button)使用GPIO(General Purpose I/O)方式与处理器系统连接,而不是使用Fixed hardwa re方式。

在ICH9中定义了一个PWRBTN#信号,该信号用来处理电源按钮。如果在一个处理器系统中,电源按钮信号连接到ICH9的PWRBTN#信号时,处理器系统将使用PM寄存器组处理这个电源按钮,即电源按钮使用了Fixed hardware方式与处理器系统进行连接,而不是GPE方式与处理器系统进行连接。

使用Fixed hardware方式处理电源按钮的主要缺点是不够灵活,有时OEM厂商(Original Equipment Manufacturer)可以利用电源按键实现一些自定义的功能。此时主板设计者可以将电源按钮与EC[7](Embedded Controller)直接相连,并将电源按钮事件与GPE联系在一起。使用这种方式时OEM厂商可以灵活地控制电源按钮。

当用户按下电源按钮时,EC可以根据其按键时间的长短产生不同的电源按钮事件,如“按键小于4s”和“按键大于4s”所对应的电源按钮事件。而且在x86处理器系统处于不同的运行状态,如G0、G1和G2时,对电源按钮事件的解释也并不相同。

x86处理器规定了一系列休眠状态,其中G0为工作状态,与S0状态对应;G1为休眠状态,G1状态分为4个等级,分别为S1~S4,其中编号越大,休眠的程度越深;G2状态为Soft Off状态,该状态与S5状态对应,表示当前处理器处于软下电状态,此时处理器除了一些最基本模块,如EC、ICH中的部分逻辑和Wake-on-LAN机制仍然保持电源供应之外,其他所有模块均不供电;G3状态为Mechanical Off状态[8],此时处理器处于完全断电状态,全部模块均不上电。

下文仅讨论x86处理器处于G0和G1状态时,如何处理电源按钮事件。在x86处理器中,处理电源按钮事件的解释程序在DSDT表中,如源代码14-14[9]所示。

源代码14-14 与电源按钮事件相关的ASL程序

978-7-111-29822-9-Part03-21.jpg

这段程序的说明如下:

●创建一个“PWRB”设备,其标识(_HID)为“PNP0C0C”,即PWRB设备与电源按钮对应。在ACPI中,每一个设备使用的标识不相同。

●将_PRW[10]定义为Package(){0,0x4}。其含义为在处理器处于S1~S4状态时,电源按钮可以将处理器系统唤醒,而且此时使用GPEx_STS寄存器的第0位作为唤醒状态位。处理器在即将进入休眠模式时,需要检查对应设备的_PRW参数,并保证处理器系统进入的休眠等级不大于设备在_PRW的定义,同时需要保证GPEx_EN寄存器的对应位是有效的,否则处理器进入休眠模式时,不能被这个按键事件唤醒。对于本节所提供的实例,如果处理器进入的休眠等级大于S4,或者在进入休眠状态之前GPEx_EN寄存器的第0位没有使能时,用户将不能使用“ACPI机制提供的电源按钮事件”激活处理器系统。

●声明一个PHO变量,使用的I/O端口地址为0x200,这个I/O端口的第0位和第1位分别与PBP和PBW位对应。其中PBP位为1表示处理器系统处于S0状态时电源按钮被按下,此时处理器需要进入休眠状态;PBW位为1表示处理器系统处于S1~S4状态时,电源按钮被按下,此时处理器需要被唤醒。

●从Scope(\_GPE)开始的这段程序描述对电源按钮事件的处理过程。当GPEx_STS寄存器的第0位为1时,将进一步检查PBP和PBW位。

●如果PBP位为1,则首先向PBP位写1,清除这个状态位,之后通知OSPM当前电源按钮对应的回调号为0x80[11]。回调号为0x80表示在处理器处于S0状态时,电源按钮被按下。

●如果PBW位为1,则首先向PBW位写1,清除这个状态位,之后通知OSPM当前电源按钮对应的回调号为0x02。回调号为0x02表示设备发出了一个唤醒信号。

由以上描述,可以发现在源代码14-14中,除了定义了一个PWRB设备之外,还使用ASL语言简单描述了当有PBW或者PBP事件发生时,处理器的执行操作。对于一个具体的操作系统,如Linux,需要将PBW/PBP事件和处理这些事件的执行操作联系在一起。

当电源按钮被按下时,如果GPEx_EN寄存器的相应位被使能,则处理器的GPIO接口将置GPEx_STS寄存器的对应位为1,同时向处理器提交SCI中断请求。Linux系统首先需要提供处理这个SCI中断请求的中断服务例程,然后进一步处理这些SCI中断请求。

在Linux系统中,这个中断服务例程为acpi_ev_sci_xrupt_handler函数,该函数即为SCI中断服务例程。SCI中断服务例程具有3个输入参数,如下所示。

●gsi参数为acpi_gbl_FADT.sci_interrupt,缺省值为0x09。(www.chuimin.cn)

●handler参数为acpi_ev_sci_xrupt_handler。

●context参数为acpi_gbl_gpe_xrupt_list_head。

Linux系统进行初始化,该中断服务例程由acpi_early_init函数调用acpi_enable_subsys-tem函数挂接到Linux系统的中断处理服务主程序(do_IRQ函数)中,acpi_ev_sci_xrupt_han-dler函数的详细说明在./drivers/acpi/acpica/evsci.c文件中。

acpi_enable_subsystem函数的详细实现在./drivers/acpi/acpica/utxface.c文件中,该函数将调用acpi_ev_install_xrupt_handlers→acpi_ev_install_sci_handler函数注册SCI中断服务例程。acpi_ev_install_sci_handler函数如源代码14-15所示。

源代码14-15 acpi_ev_install_sci_handler函数

978-7-111-29822-9-Part03-22.jpg

acpi_ev_install_sci_handler函数将调用acpi_os_install_interrupt_handler函数,并将acpi_gbl_FADT.sci_interrupt、acpi_ev_sci_xrupt_handler和acpi_gbl_gpe_xrupt_list_head参数传递给该函数。acpi_gbl_FADT.sci_interrupt为SCI中断使用的irq号,acpi_ev_sci_xrupt_handler即为SCI中断处理函数,acpi_gbl_gpe_xrupt_list_head为SCI中断处理函数使用的入口参数。

acpi_os_install_interrupt_handler函数的实现如源代码14-16所示。

源代码14-16 acpi_os_install_interrupt_handler函数

978-7-111-29822-9-Part03-23.jpg

978-7-111-29822-9-Part03-24.jpg

这段程序首先调用acpi_irq_stats_init函数建立sysfs中的kobject,之后从FADT[12]中获得ACPI使用的中断向量,在绝大多数x86处理器系统中,SCI中断使用的irq号为0x9。之后这段程序调用request_irq,将acpi_irq函数与irq号0x9联系在一起。之后Linux系统将使用ac-pi_irq函数处理SCI中断请求,该函数的实现如源代码14-17所示。

源代码14-17 acpi_irq函数

978-7-111-29822-9-Part03-25.jpg

acpi_irq函数的主要作用是执行(*acpi_irq_handler)(acpi_irq_context)函数,并检查执行结果是否正确。acpi_irq_handler函数指针在acpi_os_install_interrupt_handler函数中被赋值为acpi_ev_sci_xrupt_handler函数。因此在Linux ACPI的实现中,acpi_ev_sci_xrupt_handler函数为真正的SCI中断服务例程,该函数在./drivers/acpi/acpica/evsci.c文件中,如源代码14-18所示。

源代码14-18 acpi_ev_sci_xrupt_handler函数

978-7-111-29822-9-Part03-26.jpg

978-7-111-29822-9-Part03-27.jpg

该函数首先调用acpi_ev_fixed_event_detect函数检查PM寄存器组,判断是否存在PM事件,之后调用acpi_ev_gpe_detect函数检查是否存在GPE事件。上文中描述的PBW和PBP事件由acpi_ev_gpe_detect函数处理。

acpi_ev_gpe_detect函数的执行逻辑较为简单,本节不再列出该函数的源代码,该函数在./drivers/acpi/acpica/evgpe.c文件中,属于ACPICA提供的Event Management接口函数。该函数首先获得一个自旋锁acpi_gbl_gpe_lock,之后检查GPEx_STS寄存器和GPEx_EN寄存器以确定处理器系统中存在的GPE事件,然后调用acpi_ev_gpe_dispatch函数执行源代码14-14中Method(_L00)之后的程序。

acpi_ev_gpe_dispatch函数在执行源代码14-14中的ASL程序时,采用解释执行的方法。而解释执行相比编译执行而言,执行效率较低,为此该函数调用acpi_os_execute函数[13],使用Linux系统提供的Work Queue机制,脱离中断处理程序的上下文环境,“异步”地分析并解释执行这些ASL程序。

在Linux系统中,与ACPI机制相关的程序虽然数量众多,处理的事务也较多,但是其逻辑结构较为简单,本章对此不做进一步分析和说明。