UEFI
UEFI-ASL动态修改ACPI表
文章目录
- UEFI-ASL动态修改ACPI表
- 简述ACPI
- ACPI是什么?
- ACPI的组成及使用?
- ACPI的优点?
- ACPI的详细功能
- ASL语言
- ASL基本准则
- 如何使用ASL?
- 动态修改CPU频率
目前,龙芯平台早已将固件 -> 内核的2.3接口规范升级到了 3.0接口规范,这里所说的3.0接口就是UEFI到内核的桥梁-ACPI接口规范,所以不得不说现在的龙芯越来越标准化了。
简述ACPI
UEFI早已谈过,那么我们现在来谈谈ACPI。
ACPI是什么?
ACPI全称 Advanced Configuration and Power Interface ,高级配置和电源接口
首先,从固件到内核之间,无非就是提供一些设备资源(包含内存资源),一些运行时服务及一些平台差异化的讯息需要内核Attention的事项。
而如何管理这些信息,就需要定义一套套标准,ACPI即是时代的产物,一套被Intel,微软等巨头规范化的标准接口。
ACPI的组成及使用?
ACPI可以说是将众多不同种类及不同设备间的资源进行了高级抽象,然后连接在了一起,由一系列不同对象的表项所组成,简说就是ACPI表。
对于它自身的面向对象的抽象思想,有其对应的编写语言ASL(ACPI Source Language)进行译化为AML(ACPI Machine Language),然后由OS解码执行。
ACPI表 | 含义 | 作用 |
---|---|---|
MADT | Multiple APIC Description Table | 可以管理多处理器平台上支持的处理器数量,当然也包括内核所对应的逻辑核信息 |
DSDT | Differentiated System Description Table | 差异系统描述表:下面会将其中的一部分 |
SRAT | System Resource Affinity Table | 系统资源关联表 |
… | … | … |
其余的表项先就不说了,点击此处ACPI_Spec上有更详细的介绍。
ACPI的优点?
目前,多个主流操作系统都支持ACPI(Linux,NetBSD,OpenBSD,Windows Vista,Windows等),而ACPI则是一套与架构无关的资源管理接口,所以我们可以跨平台使用,理论上只要OS支持ACPI接口即可,无论Windows还是Linux。
其实ACPI的最大的进步在于Power Management(电源管理方面),这里我们就不详细说了,有关一些术语可以点击维基百科-ACPI参见。
ACPI的ASL语言里面向对象高级抽象的思想很需要我们C语言开发者学习,人家造le轮子,我们不仅要学会如何使用,关键的是学习人家的思想,如何将资源整合在一起,清晰明了,也不至于我们写的代码那么的Low。
总之,ACPI还是蛮强大的,你直接使用它控制OS都没问题。(当然,也很危险,你如果给OS传错了话,OS可就按你的话来执行了哈~).
ACPI的详细功能
关于ACPI在规范中究竟都定义了哪些功能,我就不一一介绍了,详见上述维基百科或SPEC规范。
简单列举几个功能例子:
- 控制EC,控制风扇,电池管理,温度管理,设备电源管理,系统电源管理,中断管理,处理器核信息管理等,如何实现还请大家一起探讨学习~
ASL语言
ASL是本章节的重点,我们讲述一下使用时的基本准则和怎么描述设备信息。
ASL基本准则
- 1)变量命名不超过4个字符,且不能以数字开头,稍后看DSDT代码即可理解。
- 2)Spec规范中定义了大量的对象,它们的名字一般以_开头,所以自己定义时还请区别开来。
- 3)ASL的操作符基本满足C的标准,只是关键字需要记忆,比如:逻辑与,算术运算等。
- 4)表达式:ASL定义了好多操作符,例如:定义变量 NAME(object,Value)等,详细查看19.6章节:
- 5)ASL中的路径有相对路径和绝对路径之分,Scope和Device都会形成自己的作用域,类似与C++中的namespace和class。
- 6)对于使用Method定义函数:可使用Arg0-Arg7,最多8个传递参数,而对于局部变量,也最多可默认使用Local0-Local7,无需区分大小写.
- 7)数据类型:integer(整数),String(字符串),Event(事件),Buffer(数组),Package(对象集合),这些类型都可以直接引用,是用NAME定义变量。
太详细的就不说了,下面具体以代码为例介绍。
如何使用ASL?
我们以DSDT表为例讲解:
UEFI下的ACPI驱动如何将信息信息传递的就不说了,DSDT表 示例代码如下:
/** Original Table Header:* Signature "DSDT"* Length OP* Revision 0x02* Checksum 0x0* OEM ID "LGSON "* OEM Table ID "TP_R00 "* OEM Revision 0x00000470* Compiler ID "LNGSN"* Compiler Version 0x20141107 (538185991)*/DefinitionBlock ("Dsdt.aml", "DSDT", 2, "LGSON ", "TP-R00 ", 0x00000476){include ("PcieTree.asl")include ("PciDevice.asl")//include ("Ec.asl")include ("Cpu.asl")include ("Platform.asl")//include ("Tz.asl")Scope (\_SB.PCI0){Name (PCIG, ToUUID ("e5c937d0-3553-4d7a-9117-ea4d19c3434d") /* Device Labeling Interface */)Method (PCID, 4, Serialized){If (LEqual (Arg0, PCIG)){If (LGreaterEqual (Arg1, 0x03)){If (LEqual (Arg2, 0x00)){Return (Buffer (0x02){0x01, 0x03})}If (LEqual (Arg2, 0x09)){Return (Package (0x06){0xC350,Ones,Ones,0xC350,Ones})}}}
//etc...
这里的DefinitionBlock是一个Operator(可以看作ASL的一个入口):它以数据和控制方法(编译成AML)的形式包含关于硬件实现和配置详细信息。
ACPI-Tables中必须至少提供一个Definition Block:Differentiated Definition Block,它描述了基本的系统。
在装载Differentiated Definition Block之后,紧接着OS会把Differentiated Deffinition Block的内容插入到ACPI Namespace。Os可以动态的从the active ACPI Namespace插入和删除其他definition blocks,可以包含指向Differentiated Definition Block的引用。
ACPI名字空间(ACPI Namespace)
一个树状层次机构,在受操作系统控制的内存里面,这段内存里面包含命名对象(named objects)等。这些对象(objects)可以是数据对象,控制方法对象,总线/设备包对象等。操作系统通过从驻留在 ACPI BIOS 中的 ACPI Tables 加载出(loading and/or unloading)定义块(definition blocks),来动态改变名字空间(namespace)的内容。在ACPI Namespace 中的所有信息都来自 Differentiated System Description Table (DSDT),DSDT 里面包含了 Differentiated Definition Block 还有一个或者多个其他的定义块(definition blocks)。
简言之:就是建立一个Block,里面包含了很多数据对象,最终放在了内存,供OS动态加载。
对于我们开发者需要理解的是:
- 首先我们看Method (PCID, 4, Serialized)这个关键字,定义了含有四个入参的函数(PCID),它干的事情就是下面一些列比较LEqual判断等,我们只需调用PCID(Arg0,Arg1,Arg2,Arg3)和C一致,但是这些关键字需要记忆。
- 我们再看Scope (\_SB.PCI0),引用\_SB.PCI0这个路径,添加了Name (PCIG, ToUUID (“e5c937d0-3553-4d7a-9117-ea4d19c3434d”),即创建了一个PCIG的名字的UUID,关于ToUUID这个方法自己可以查看Spec了解。
- 关于上述PCI0为什么放在作用域\_SB,那我们还需要了解规范下根作用域下定义的5个作用域:
1: \_GPE: ACPI的事件处理
2: \_PR: 处理器
3: \_SB: 设备和总线
4: \_SI: 系统指示灯
5: \_TZ: 热区,用于读取某些温度。
所以PCI的设备资源放在\_SB你应该就理解了。
动态修改CPU频率
我们需要修改的是Cpu.asl内的数据.
代码如下:
Scope (\_SB){Name (IDDR,0x1fe00020)Name (FREQ,0x000009c4) //Normal Max Freq:2000OperationRegion(BASE, SystemMemory, IDDR, 0x8)Field(BASE,AnyAcc,NoLock,Preserve){PRID,64}//NAME(MEM2,0x0000303030354133)//Name(qqq,Buffer("3B5000"){})//ToString(MEM2,6,qqq)NAME(VETB,Package (){Package (0x02) {0x0000303030354133, //3A50002500},Package (0x02) {0x4C4C303030354133, //3A5000LL2300},Package (0x02) {0x004D303030354133, //3A5000M2000},Package (0x02) {0x0000303030354233, //3B50002300},Package (0x02) {0x004C303030354333, //3C5000L2200},Package (0x02) {0x0049303030354133, //3A5000I2200},Package (0x02) {0x0069303030354133, //3A5000i2200}})For (Local0 = 0,Local0 < SizeOf(VETB),Local0++){if ( LEqual ( DeRefOf ( Index ( DeRefOf ( Index ( VETB, Local0 ) ), 0 ) ), PRID ) ){Store ( DeRefOf ( Index ( DeRefOf ( Index ( VETB,Local0 ) ) , 1 ) ) , FREQ )}}Device (C000){Name (LPSS, Package (0x10){Package (0x06){FREQ,0x00003A98,20000,20000,0x00000103,0x00000003},
通过上述简摘的一部分代码,我们可以看出通过OperationRegion定义一段操作空间,然后通过读取内存或者一些IO来获取我们外部的信息,从而告知内核我们需要随时变化的信息让OS去动态获取。其中FREQ就是我们需要动态修改的值,被集合在一套频率表中,我们可以通过Linux下的lscpu来查询我们动态获取的数据。
以上,就是ASL的简单实例,更多详细操作和复杂逻辑可以查阅ACPI_Spec规范,来学习!
学无止境~
UEFI
UEFI-ASL动态修改ACPI表
文章目录
- UEFI-ASL动态修改ACPI表
- 简述ACPI
- ACPI是什么?
- ACPI的组成及使用?
- ACPI的优点?
- ACPI的详细功能
- ASL语言
- ASL基本准则
- 如何使用ASL?
- 动态修改CPU频率
目前,龙芯平台早已将固件 -> 内核的2.3接口规范升级到了 3.0接口规范,这里所说的3.0接口就是UEFI到内核的桥梁-ACPI接口规范,所以不得不说现在的龙芯越来越标准化了。
简述ACPI
UEFI早已谈过,那么我们现在来谈谈ACPI。
ACPI是什么?
ACPI全称 Advanced Configuration and Power Interface ,高级配置和电源接口
首先,从固件到内核之间,无非就是提供一些设备资源(包含内存资源),一些运行时服务及一些平台差异化的讯息需要内核Attention的事项。
而如何管理这些信息,就需要定义一套套标准,ACPI即是时代的产物,一套被Intel,微软等巨头规范化的标准接口。
ACPI的组成及使用?
ACPI可以说是将众多不同种类及不同设备间的资源进行了高级抽象,然后连接在了一起,由一系列不同对象的表项所组成,简说就是ACPI表。
对于它自身的面向对象的抽象思想,有其对应的编写语言ASL(ACPI Source Language)进行译化为AML(ACPI Machine Language),然后由OS解码执行。
ACPI表 | 含义 | 作用 |
---|---|---|
MADT | Multiple APIC Description Table | 可以管理多处理器平台上支持的处理器数量,当然也包括内核所对应的逻辑核信息 |
DSDT | Differentiated System Description Table | 差异系统描述表:下面会将其中的一部分 |
SRAT | System Resource Affinity Table | 系统资源关联表 |
… | … | … |
其余的表项先就不说了,点击此处ACPI_Spec上有更详细的介绍。
ACPI的优点?
目前,多个主流操作系统都支持ACPI(Linux,NetBSD,OpenBSD,Windows Vista,Windows等),而ACPI则是一套与架构无关的资源管理接口,所以我们可以跨平台使用,理论上只要OS支持ACPI接口即可,无论Windows还是Linux。
其实ACPI的最大的进步在于Power Management(电源管理方面),这里我们就不详细说了,有关一些术语可以点击维基百科-ACPI参见。
ACPI的ASL语言里面向对象高级抽象的思想很需要我们C语言开发者学习,人家造le轮子,我们不仅要学会如何使用,关键的是学习人家的思想,如何将资源整合在一起,清晰明了,也不至于我们写的代码那么的Low。
总之,ACPI还是蛮强大的,你直接使用它控制OS都没问题。(当然,也很危险,你如果给OS传错了话,OS可就按你的话来执行了哈~).
ACPI的详细功能
关于ACPI在规范中究竟都定义了哪些功能,我就不一一介绍了,详见上述维基百科或SPEC规范。
简单列举几个功能例子:
- 控制EC,控制风扇,电池管理,温度管理,设备电源管理,系统电源管理,中断管理,处理器核信息管理等,如何实现还请大家一起探讨学习~
ASL语言
ASL是本章节的重点,我们讲述一下使用时的基本准则和怎么描述设备信息。
ASL基本准则
- 1)变量命名不超过4个字符,且不能以数字开头,稍后看DSDT代码即可理解。
- 2)Spec规范中定义了大量的对象,它们的名字一般以_开头,所以自己定义时还请区别开来。
- 3)ASL的操作符基本满足C的标准,只是关键字需要记忆,比如:逻辑与,算术运算等。
- 4)表达式:ASL定义了好多操作符,例如:定义变量 NAME(object,Value)等,详细查看19.6章节:
- 5)ASL中的路径有相对路径和绝对路径之分,Scope和Device都会形成自己的作用域,类似与C++中的namespace和class。
- 6)对于使用Method定义函数:可使用Arg0-Arg7,最多8个传递参数,而对于局部变量,也最多可默认使用Local0-Local7,无需区分大小写.
- 7)数据类型:integer(整数),String(字符串),Event(事件),Buffer(数组),Package(对象集合),这些类型都可以直接引用,是用NAME定义变量。
太详细的就不说了,下面具体以代码为例介绍。
如何使用ASL?
我们以DSDT表为例讲解:
UEFI下的ACPI驱动如何将信息信息传递的就不说了,DSDT表 示例代码如下:
/** Original Table Header:* Signature "DSDT"* Length OP* Revision 0x02* Checksum 0x0* OEM ID "LGSON "* OEM Table ID "TP_R00 "* OEM Revision 0x00000470* Compiler ID "LNGSN"* Compiler Version 0x20141107 (538185991)*/DefinitionBlock ("Dsdt.aml", "DSDT", 2, "LGSON ", "TP-R00 ", 0x00000476){include ("PcieTree.asl")include ("PciDevice.asl")//include ("Ec.asl")include ("Cpu.asl")include ("Platform.asl")//include ("Tz.asl")Scope (\_SB.PCI0){Name (PCIG, ToUUID ("e5c937d0-3553-4d7a-9117-ea4d19c3434d") /* Device Labeling Interface */)Method (PCID, 4, Serialized){If (LEqual (Arg0, PCIG)){If (LGreaterEqual (Arg1, 0x03)){If (LEqual (Arg2, 0x00)){Return (Buffer (0x02){0x01, 0x03})}If (LEqual (Arg2, 0x09)){Return (Package (0x06){0xC350,Ones,Ones,0xC350,Ones})}}}
//etc...
这里的DefinitionBlock是一个Operator(可以看作ASL的一个入口):它以数据和控制方法(编译成AML)的形式包含关于硬件实现和配置详细信息。
ACPI-Tables中必须至少提供一个Definition Block:Differentiated Definition Block,它描述了基本的系统。
在装载Differentiated Definition Block之后,紧接着OS会把Differentiated Deffinition Block的内容插入到ACPI Namespace。Os可以动态的从the active ACPI Namespace插入和删除其他definition blocks,可以包含指向Differentiated Definition Block的引用。
ACPI名字空间(ACPI Namespace)
一个树状层次机构,在受操作系统控制的内存里面,这段内存里面包含命名对象(named objects)等。这些对象(objects)可以是数据对象,控制方法对象,总线/设备包对象等。操作系统通过从驻留在 ACPI BIOS 中的 ACPI Tables 加载出(loading and/or unloading)定义块(definition blocks),来动态改变名字空间(namespace)的内容。在ACPI Namespace 中的所有信息都来自 Differentiated System Description Table (DSDT),DSDT 里面包含了 Differentiated Definition Block 还有一个或者多个其他的定义块(definition blocks)。
简言之:就是建立一个Block,里面包含了很多数据对象,最终放在了内存,供OS动态加载。
对于我们开发者需要理解的是:
- 首先我们看Method (PCID, 4, Serialized)这个关键字,定义了含有四个入参的函数(PCID),它干的事情就是下面一些列比较LEqual判断等,我们只需调用PCID(Arg0,Arg1,Arg2,Arg3)和C一致,但是这些关键字需要记忆。
- 我们再看Scope (\_SB.PCI0),引用\_SB.PCI0这个路径,添加了Name (PCIG, ToUUID (“e5c937d0-3553-4d7a-9117-ea4d19c3434d”),即创建了一个PCIG的名字的UUID,关于ToUUID这个方法自己可以查看Spec了解。
- 关于上述PCI0为什么放在作用域\_SB,那我们还需要了解规范下根作用域下定义的5个作用域:
1: \_GPE: ACPI的事件处理
2: \_PR: 处理器
3: \_SB: 设备和总线
4: \_SI: 系统指示灯
5: \_TZ: 热区,用于读取某些温度。
所以PCI的设备资源放在\_SB你应该就理解了。
动态修改CPU频率
我们需要修改的是Cpu.asl内的数据.
代码如下:
Scope (\_SB){Name (IDDR,0x1fe00020)Name (FREQ,0x000009c4) //Normal Max Freq:2000OperationRegion(BASE, SystemMemory, IDDR, 0x8)Field(BASE,AnyAcc,NoLock,Preserve){PRID,64}//NAME(MEM2,0x0000303030354133)//Name(qqq,Buffer("3B5000"){})//ToString(MEM2,6,qqq)NAME(VETB,Package (){Package (0x02) {0x0000303030354133, //3A50002500},Package (0x02) {0x4C4C303030354133, //3A5000LL2300},Package (0x02) {0x004D303030354133, //3A5000M2000},Package (0x02) {0x0000303030354233, //3B50002300},Package (0x02) {0x004C303030354333, //3C5000L2200},Package (0x02) {0x0049303030354133, //3A5000I2200},Package (0x02) {0x0069303030354133, //3A5000i2200}})For (Local0 = 0,Local0 < SizeOf(VETB),Local0++){if ( LEqual ( DeRefOf ( Index ( DeRefOf ( Index ( VETB, Local0 ) ), 0 ) ), PRID ) ){Store ( DeRefOf ( Index ( DeRefOf ( Index ( VETB,Local0 ) ) , 1 ) ) , FREQ )}}Device (C000){Name (LPSS, Package (0x10){Package (0x06){FREQ,0x00003A98,20000,20000,0x00000103,0x00000003},
通过上述简摘的一部分代码,我们可以看出通过OperationRegion定义一段操作空间,然后通过读取内存或者一些IO来获取我们外部的信息,从而告知内核我们需要随时变化的信息让OS去动态获取。其中FREQ就是我们需要动态修改的值,被集合在一套频率表中,我们可以通过Linux下的lscpu来查询我们动态获取的数据。
以上,就是ASL的简单实例,更多详细操作和复杂逻辑可以查阅ACPI_Spec规范,来学习!
学无止境~