ACPI相关(5)- PCI热插拔(二)

ACPI热插拔在系统中的实现

1、系统初始化阶段

在上电阶段PCI设备的扫描节点,ACPI模式根节点(主桥)被PCI总线驱动枚举,从驱动程序的角度上来看可以把root看作一个PCI桥,包含4个地址区间用于描述PCI桥下次一级的总线的地址分配情况,通过扫描PCI root得到root,把从ACPI BIOS获得的资源(中断,总线号)绑定在PCI的root的描述结构acpi_pci_root上。

1.初始化root :acpi_pci_root_init是初始化root的第一步,在这里首先调用注册函数acpi_bus_register_driver,它的带入参数是acpi设备的驱动程序描述结构struct acpi_driver acpi_pci_root_driver,该函数注册支持ACPI PCI总线的驱动程序,同样一个ACPI PCI桥也和在名字空间创建之后,搜索名字空间中所有设备的类型和名字,并且把它们绑定到相对应的驱动程序上,启动过程中搜索名字空间中已经定义的ACPI PCI,ACPI桥设备都使用一个acpi_device数据结构表示,这个数据结构是从ACPI的层次上描述设备,支持ACPI的PCI桥和标准的PCI-PCI桥使用相同的模式遍历,但是在ACPI中可以使用(_ADR,_BBN,_PRT)从名字空间中获得相关的ACPI PCI桥资源信息(例如设备地址资源,总线号,设备的ID号,中断路由),当对acpi_device的所有的字段都完成初始化之后,就会调用acpi_pci_root_add把得到的root的数据结构挂在acpi_pci_root队列上,顾名思义这个队列就是系统内所有的root的集合。

2.初始化支持Hot Plug的root插槽:在完成root的初始化之后,接着就是要对ACPI PCI Hot Plug的插槽进行初始化,在系统初始化ACPI HotPlug驱动模块的时候,会首先遍历在acpi_pci_root队列上的所有ACPI root,并且调用Hot Plug驱动模块acpi_pci_register_driver中的add_bridge方法把当前的具备热拔插功能ACPI的root都找到。

如何确定是否支持热拔插呢?在Linux HotPlug中有一个函数detect_ejectable_slots检测是否有可以支持热拔插的SLOT(插槽以acpihp_slot结构表示),插槽是在名字空间中定义的,可以理解为物理设备上的插槽,也可以理解为在PCI桥或者root下的一个负责支持或者管理热拔插的设备,插槽上包含了功能模块(function),表示功能是针对具体设备,如果找到支持热拔插的插槽则表示当前的root是支持热拔插的,反之就会对当前的root下的PCI总线做深度遍历,寻找到下一级的支持Hot Plug的ACPI PCI-PCI桥;

热拔插的SLOT是连接在宿主桥也可以连接在PCI-PCI桥之上的,在宿主桥之下有PCI-PCI桥,它采用传统的PCI桥递归方式查找下一级总线的PCI桥;宿主桥获取资源(IO和MEM)是从地址空间中获得。从名字空间中调用_CRS对象找到地址资源后挂上当前的桥描述结构的资源树上。而PCI-PCI桥的资源则是从当前桥设备上的配置空间中获得, 例如在名字空间中,下面是某个设备内的资源表示:

 

Device(EC0)

{

... ...

Name(_CRS,Buffer(){//表示该_CRS所归属的设备的系统空间,IO或者MEM,中断等资源

IO(Decode16,FC00,FC03,4,4)//表示IO区间宽度为16个Bit,开始地址为FC00结束为FC03,4个字节对//齐,长度为4个字节

IRQ(Level,ActiveHigh,Shared{5}//表示当前的中断为电平,高电平有效,共享的,系统中断5

}

)

... ...

}

        例1-5 嵌入式控制器EC0的CRS对象在名字空间中的描述

通过acpi_evaluate_integer调用指定设备的方法_BBN和_SEG,获得段号和总线号然后调用add_host_bridge初始化PCI宿主桥上的资源,(IO,MEM)获得总线资源:

 

status = acpi_walk_resources(handle, METHOD_NAME__CRS,

decode_acpi_resource, bridge);

资源在ACPI中是作为一个对象节点保存在BIOS中的名字空间,acpi_walk_resources函数使用指定的_CRS方法可以读指定设备的资源对象,放在acpi_resource结构中,然后调用decode_acpi_resource对资源对象解析出合适的资源存储空间,IO空间,下级地址,总线地址,预存取存储空间(采用pci_resource数据结构),把解析完毕的资源绑定在桥芯片描述结构的四个资源队列树上(mem_head,io_head,bus_head,p_mem_head),然后调用专门的函数acpiphp_resource_sort_and_combine对资源树进行整理,并且回收/合并一些相连的资源区间;在获取地址空间资源上,传统PCI-PCI桥和PCI宿主桥是一致的。

完成了PCI-PCI桥和PCI宿主桥的资源配置之后,就是调用init_bridge_misc来初始化这两种根集中器,以及一些已经在PCI总线上的设备,并且完成对这些设备的枚举,另外在这里对ACPI PCI系统或者是设备事件的处理句柄的注册,当PCI桥出现了GPE事件发生时刻处理句柄的注册,会用于对这些事件的响应:

首先调用decode_hpp(调用acpi_evaluate_object)获得ACPI hot-plug的一些参数,例如Cache Line的长度,PCI总线延迟周期(以PCI总线的时钟来记数),PERR和SERR使能寄存器。

然后调用acpiphp_detect_pci_resource把在PCI总线上完成枚举的设备上已经占用的资源从桥的资源列表减掉,这样在PCI桥之下留下来的就是空闲的资源树。

遍历ACPI的名字区间,得到指定设备类型的ACPI设备的对象,并且调用相关的用户函数对其进行初始化,这里遍历函数中调用句柄register_slot,参数acpiphp_bridge *bridge为当前的PCI宿主桥或者是PCI-PCI桥结构,如下:

status = acpi_walk_namespace(ACPI_TYPE_DEVICE,

bridge->handle, (u32)1,register_slot, bridge, NULL);

register_slot是一个回调函数,遍历所有的已经存在于名字空间中的PCI总线上的PCI设备(也就是PCI的功能function)。遍历的过程中,有的设备是不支持拔插的,那么直接跳过,我们知道在Linux驱动中PCI的每个功能都是结构pci_dev,每个PCI插槽(SLOT)中对应有8个PCI功能设备(function),而在ACPI的名字空间中则列出每个PCI功能设备(下面的例子是一个关于Hot-plug方式的名字空间,例子中就非常明白地显示出每个PCI槽相对应的功能function)。并且为已经在名字空间中体现出来的设备建立数据描述结构acpiphp_func *newfunc,如果设备存在(也许是Legacy设备或者是已经在启动时候插入的热插拔板卡)那么就对其进行对应的资源初始化工作(acpiphp_init_func_resource),直接把在PCI设备配置空间中的地址资源读出,最后把设备挂在对应的插槽结构中(acpiphp_slot)。如果对应的设备没有插槽结构acpiphp_slot,那么就重新建立新的插槽结构。

最后调用acpi_install_notify_handler把控制句柄handle_hotplug_event_func安装在桥上等待ACPI系统事件和设备事件的触发,这两类事件将引发热拔插的后ACPI热拔插系统的动作。

2、发生拔插事件的处理

在设备插入后,有两个比较重要的GPE会从设备端或者是系统端发生:ACPI_NOTIFY_BUS_CHECK,ACPI_NOTIFY_DEVICE_CHECK,他们分别表示Bus Check事件和Device Check事件,都通告当前的PCI总线发生了变化,这两个事件的含义见表1-1,其实两者的处理模式基本是一致的,我们介绍一下Device Check的执行流程:

(1) 首先是遍历当前宿主桥下所有的SLOT,通过调用其_STA方法获得当前每个插槽上的状态,如果没有_STA方法,就直接检查当前的SLOT上的厂商号是否为全F,如果不是,表示有设备插入。

(2) 当获得当前状态是ACPI_STA_ALL(表示所有的功能模块均有效)的时候,表示当前的设备上需要使能所有的功能模块,这个时候就要调用使能一个SLOT的函数enable_slot。

(3) 使能一个SLOT的第一步是给当前设备上电--power_on_slot,调用当前设备的_PS0方法。

(4) 第二步是使能并且配置一个SLOT--enable_device,在enable_device中的函数acpiphp_configure_slot为每个插槽上的功能模块(function)进行初始化和配置工作,首先检查每个function是否存在,如果不存在,表明该模块是没有对应设备的,这样才进一步调用函数init_config_space对找到的每一个功能块进行初始化,对于一个标准的PCI设备(function),一般有6个存储空间,init_config_space程序将依次扫描6个存储空间,根据每个存储空间的长度(mem/pmem/io)从桥的地址空间"批发"地址资源到pci_resource *res中,并且写入当前找到的PCI功能模块(function)的配置空间中。

(5) 接下来调用pci_scan_slot扫描每个Slot上设备的功能模块,这里已经进入PCI层的初始化阶段,在上段中已经对ACPI通告的PCI设备的IO/MEM/Pre-MEM的PCI配置空间中的地址寄存器配置完毕,现在就在从PCI配置空间进一步读入设备信息,并根据设备的具体类型进一步初始化设备,获取PCI的IO和存储空间的基地址(pci_dev结构中的资源字段resource),中断输入脚(pci_read_irq函数会读出PCI_INTERRUPT_PIN/PCI_INTERRUPT_LINE寄存器中的内容,它表示了当前PCI设备的中断输出到8259A的中断输入或者是IO APIC的中断输入上),在PCI核心层上完成PCI设备结构(pci_dev结构)的构建工作。从而最终脱离ACPI层到达PCI设备层。

说到中断,ACPI层的PCI中断和标准的PCI设备的处理方式有一些不一样;对于ACPI层来说,这里为ACPI引入了一个新的中断表--PCI Routing Table,它在BIOS中保存了对PCI设备的中断输入脚映射到中断控制器的输入,需要在初始化ACPI子系统的以后,由设备驱动层调用pci_enable_device在PCI层次上初始化设备以及其驱动,特别是使能中断输入和使能配置空间中的各个IO/MEM空间,这里需通过_PRT获取某个设备中断输入;从_PRT方法获得的PCI设备中断引脚映射将覆盖从PCI枚举阶段所建立的PCI设备到中断控制器之间的中断映射;传统的PCI设备是使用中断修正的方式来进行PCI设备的中断映射。

 

name(_PRT,Package{/*名字空间中PRT采用Package的方式包装中断定义*/

Package{0x0004fff,0,LINKA,8},/*设备的基地址为0x00004fff,中断请求线为中断A,中断输入为8*/

... ...

Package{0x0004fff,3,LINKD,8},/*设备的基地址为0x00004fff,中断请求线为中断D,中断输入为8*/

})

例1-5 显卡中的_PRT表在名字空间中的部分描述

_PRT实质上是和PCI设备从BIOS中获得的中断路径表类似,对ACPI中断驱动初始化而言是实现从名字空间设备的_PRT节点中获取中断资源并且修正中断过程,从上面的表可以看出,设备中断脚到中断控制器的中断路径事实上也是被定义在_PRT中的,ACPI中规定如果是硬件上直接定义了中断输入脚到中断控制器的路径,那么就确定表示使用那个全局的中断(例如系统中断8),而中断INTA-INTD就不会被定义。

所以对于使用ACPI Hot-Plug PCI设备的驱动程序而言,一定需要使能PCI设备的有效资源,对中断重新安排,所以在热拔插的设备在完成插入之后,需要手工使用/sbin/modprobe命令载入设备内核驱动模块,调用.probe设备驱动接口,绝大部分的热拔插都是使用PCI共享中断,所以一般来说会在PRT方法中直接定义成系统的共享中断,直接获取设备_PRT的中断。

3、内核流程分析

1、acpiphp_enumerate_slots分析:

首先判断是否为主桥设备,当时主桥设备时,初始化struct acpiphp_root_context *root_context结构,然后加入&bridge_list全局链表中,遍历名称空间,调用acpiphp_add_context函数,在这个函数中,评估DSDT中pci主桥下设备的ADR对象,不存在或出错返回。获取成功则初始化设备节点的热插拔上下文,具体为初始化struct acpiphp_context *context结构:

(1) context->hp.notify = acpiphp_hotplug_notify热插拔事件处理函数:

获取指定设备的热插拔上下文。调用hotplug_event,处理当发生拔插事件:ACPI_NOTIFY_BUS_CHECK,ACPI_NOTIFY_DEVICE_CHECK,他们分别表示Bus Check事件和Device Check事件。

ACPI_NOTIFY_BUS_CHECK --- Bus Check事件。此通知在设备对象上执行,以指示OSPM需要在设备树上执行即插即用的重新枚举操作,该操作从通知它的位置开始。OSPM通常会在启动时自动执行完整的枚举,但是在系统初始化之后,ACPI AML代码有责任在需要重新枚举操作时通知OSPM。通知在设备树中越准确、越接近实际的变化,操作系统的响应就越有效; 但是,当无法确认设备更改时,也可能出现问题。例如,如果硬件在系统休眠状态下无法识别某个特定位置的设备更改,它会在唤醒时发出一个总线检查通知,通知OSPM需要检查设备更改的配置。

ACPI_NOTIFY_DEVICE_CHECK --- 设备检查。用于通知OSPM设备出现或消失。如果设备已经出现,OSPM将从父节点重新枚举。如果设备已经消失,OSPM将使设备状态无效。OSPM可以优化我们的重新枚举。如果_DCK存在,则假定Notify(object,1)表示取消停靠请求。如果设备是桥,OSPM可以重新枚举桥和子总线。

  1. 当为ACPI_NOTIFY_BUS_CHECK时,当为桥时:检测桥下设备是否支持热插拔,评估_STA对象,正常则检测移除没有响应的pci设备。如设备存在且有响应,则使能设备的所有功能(enable_slot)。否则将禁用设备。当为设备时,调用enable_slot使能设备(没有置位slot->flags & SLOT_IS_GOING_AWAY)。
  2. 当为ACPI_NOTIFY_DEVICE_CHECK时,当为桥时,和上述流程相同。当为设备时,重新扫描pci slot。
  3. 当为ACPI_NOTIFY_EJECT_REQUEST时,是pci设备的配置信息无效,遍历slot中所有func,检测DSDT中是否有_EJ0方法,有则执行_EJ0方法将设备移除。

2、热插拔调用栈

[   50.496093] [<ffffffff8021be2c>] show_stack+0x9c/0x130

[   50.500000] [<ffffffff80f0bc20>] dump_stack+0xb0/0xf0

[   50.503906] [<ffffffff8087c790>] acpiphp_hotplug_notify+0x30/0x258

[   50.507812] [<ffffffff808e2380>] acpi_device_hotplug+0xb0/0x500

[   50.515625] [<ffffffff808d8298>] yun : Got handle forn+0x20/0x38

[   50.519531] [<ffffffff80286588>] process_one_work+0x160/0x370

[   50.527343] [<ffffffff802868f4>] worker_thread+0x15c/0x558

[   50.531250] [<ffffffff8028d47c>] kthread+0x11c/0x150

[   50.535156] [<ffffffff80213524>] ret_from_kernel_thread+0x14/0x1c

中断调用流程

[   74.433593] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G           O      4.19.90+ #138

[   74.433593] Bus check in hotplug_event()

[74.441406]Hardware name: Loongson Loongson-LS3A3000-7A1000-1w-V1.2-Dev/Loongson-LS3A3000-7A1000-1w-V1.2-Dev, BIOS Loongson-UDK2018-V1.5.7 04/15/2020

[   74.441406] Stack : ffffffffd500cce0 ffffffff802c7848 ffffffffd500cce0 78375c2ea329aaf8

[   74.441406]        78375c2ea329aaf8 0000000000000000 980000045e02fcb8 000000000000db78

[   74.457031]         0000000000000000 000000000000000a ffffffffffffffff 0000000000000001

[   74.457031]         000000000000008a 0000000000000002 ffffffff81441738 ffffffff81440000

[   74.472656]         ffffffff81440000 ffffffff81240000 0000000000000000 ffffffff812d0000

[   74.472656]         ffffffff811189c8 ffffffff812d0000 000000000000006f 0000000000000000

[   74.511718]         980000045fbf6b00 0000000000000002 ffffffff80959258 0000000000000000

[   74.519531]         ffffffff81430000 ffffffff81230000 980000045e02fcb0 980000045e02fee4

[   74.527343]         ffffffff80f0bc20 0000000000000000 0000000000000000 0000000000000000

[   74.539062]         0000000000000000 0000000000000000 ffffffff8021be2c 78375c2ea329aaf8

[   74.546875]         ...

[   74.546875] Call Trace:

[   74.550781] [<ffffffff8021be2c>] show_stack+0x9c/0x130

[   74.554687] [<ffffffff80f0bc20>] dump_stack+0xb0/0xf0

[   74.558593] [<ffffffff808f8d70>] acpi_ev_sci_xrupt_handler+0x28/0x70

[   74.566406] [<ffffffff808d83a0>] acpi_irq+0x20/0x68

[   74.570312] [<ffffffff802cc3c4>] __handle_irq_event_percpu+0xbc/0x198

[   74.578125] [<ffffffff802cc4d0>] handle_irq_event_percpu+0x30/0x90

[   74.585937] [<ffffffff802cc580>] handle_irq_event+0x50/0x90

[   74.589843] [<ffffffff802d181c>] handle_level_irq+0xf4/0x1a0

[   74.593750] [<ffffffff802cb1f4>] generic_handle_irq+0x2c/0x48

[   74.601562] [<ffffffff80f31228>] do_IRQ+0x18/0x28

[   74.605468] [<ffffffff80207ae0>] ls7a_msi_irq_dispatch+0x238/0x298

[   74.613281] [<ffffffff80213ab8>] except_vec_vi_end+0xb8/0xc4

[   74.617187] [<ffffffff802137a0>] __r4k_wait+0x20/0x40

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
笔记本的风扇控制 ---------------------------------------- 09 November 2006. Summary of changes for version 20061109: 1) ACPI CA Core Subsystem: Optimized the Load ASL operator in the case where the source operand is an operation region. Simply map the operation region memory, instead of performing a bytewise read. (Region must be of type SystemMemory, see below.) Fixed the Load ASL operator for the case where the source operand is a region field. A buffer object is also allowed as the source operand. BZ 480 Fixed a problem where the Load ASL operator allowed the source operand to be an operation region of any type. It is now restricted to regions of type SystemMemory, as per the ACPI specification. BZ 481 Additional cleanup and optimizations for the new Table Manager code. AcpiEnable will now fail if all of the required ACPI tables are not loaded (FADT, FACS, DSDT). BZ 477 Added #pragma pack(8/4) to acobject.h to ensure that the structures in this header are always compiled as aligned. The ACPI_OPERAND_OBJECT has been manually optimized to be aligned and will not work if it is byte-packed. Example Code and Data Size: These are the sizes for the OS- independent acpica.lib produced by the Microsoft Visual C++ 6.0 32- bit compiler. The debug version of the code includes the debug output trace mechanism and has a much larger code and data size. Previous Release: Non-Debug Version: 78.1K Code, 17.1K Data, 95.2K Total Debug Version: 155.4K Code, 63.1K Data, 218.5K Total Current Release: Non-Debug Version: 77.9K Code, 17.0K Data, 94.9K Total Debug Version: 155.2K Code, 63.1K Data, 218.3K Total 2) iASL Compiler/Disassembler and Tools: Fixed a problem where the presence of the _OSI predefined control method within complex expressions could cause an internal compiler error. AcpiExec: Implemented full region support for multiple address spaces. SpaceId is now part of the REGION object. BZ 429 ---------------------------------------- 11 Oc

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值