admin管理员组

文章数量:1122851

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表含义作用
MADTMultiple APIC Description Table可以管理多处理器平台上支持的处理器数量,当然也包括内核所对应的逻辑核信息
DSDTDifferentiated System Description Table差异系统描述表:下面会将其中的一部分
SRATSystem 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