UEFI-ASL动态修改ACPI表

UEFI-ASL动态修改ACPI表


目前,龙芯平台早已将固件 -> 内核的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:2000
       OperationRegion(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, //3A5000
           2500
         },
         Package (0x02) {
           0x4C4C303030354133, //3A5000LL
           2300
         },
         Package (0x02) {
           0x004D303030354133, //3A5000M
           2000
         },
         Package (0x02) {
           0x0000303030354233, //3B5000
           2300
         },
         Package (0x02) {
           0x004C303030354333, //3C5000L
           2200
         },
         Package (0x02) {
           0x0049303030354133, //3A5000I
           2200
         },
         Package (0x02) {
           0x0069303030354133, //3A5000i
           2200
         }
       })
     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规范,来学习!
学无止境~

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
BIOS / Legacy Boot of UEFI-only media问题可以通过以下方法解决。 首先,我们需要了解BIOS和UEFI之间的差异。BIOS(Basic Input/Output System)是计算机启动并加载操作系统之前的一段软件。它使用传统的启动方式来识别和引导存储设备。相反,UEFI(Unified Extensible Firmware Interface)是最新的固件接口标准,支持更多功能和更大的扩展性。 当我们使用遵循UEFI标准的媒体时,由于它们没有传统的MBR(Master Boot Record)分区,就会出现BIOS / Legacy Boot无法引导的问题。为了解决这个问题,我们可以采取以下措施: 1. 更新BIOS:首先,确保计算机的BIOS是最新的版本。有时,旧版本的BIOS可能无法支持UEFI引导。您可以通过访问计算机制造商的官方网站来检查最新的BIOS版本并进行更新。 2. 启用UEFI模式:进入计算机的BIOS设置界面,并启用UEFI模式。这样,您的计算机将能够使用UEFI标准的引导方式。 3. 禁用Secure Boot:如果UEFI模式下的引导仍然失败,您可以尝试禁用Secure Boot安全引导功能。Secure Boot是一种安全特性,要求引导的操作系统必须由受信任的数字证书进行签名。禁用Secure Boot可能会允许计算机引导UEFI-only媒体。 4. 使用第三方工具:如果以上方法无效,您还可以尝试使用第三方工具。例如,可以使用一些特定的引导管理器软件来模拟UEFI环境,实现在BIOS / Legacy Boot下引导UEFI-only媒体。 总之,解决BIOS / Legacy Boot of UEFI-only media的问题可以通过更新BIOS、启用UEFI模式、禁用Secure Boot以及使用第三方工具来实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来杯清咖_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值