参考文章:
http://forums.gentoo.org/viewtopic.php?t=122145
http://bbs.gter.net/bbs/viewthread.php?tid=1093610
说明:这篇文章写于2004年,作者主要是针对Linux内核进行一些DSDT相关修复,但文章内容对于理解DSDT非常有益,这里翻译DSDT相关的内容,供参考。内容上有一些删减和增添。
参照ACPI Spec(ACPI规范,2011年12月6日第五版),第17页:
大致是说AML是一种虚拟程序码,用于ACPI兼容操作系统支持的虚拟机,程序码内部写有ACPI控制方法和对象。AML的编码定义在第19章“ACPI机器语言(AML)规范”提供。
也就是说AML是DSDT编译后的程序码。一台ACPI兼容操作系统必须能够理解AML。我们没有必要理解。
AML是一种BYTECODE,类似JAVA BYTECODE。也就是说,他并不是直接在机器上执行的2进制代码,而是需要OS来解释后执行。这样做的好处是方便错误检查,减少由于代码没写好而带来的负面影响。
还是参照ACPI规范,第18页:
用于AML的编程语言。ASL被编译为AML文件。ASL语句在第18章中“ACPI源语言(ASL)参考”中定义。ASL是写DSDT的语言。如果你需要修复你的DSDT,你必须至少会写一点ASL。
1、ACPI 的NAMESPACE与一般的常量,变量的区别。
一般的编程语言中操作的是常量和变量。这些变量之间一般没啥关系,可以说是一堆平行(有序或者随机排列的)的内存地址而已。而在ACPI中,这个发生了明显变化。ACPI引入了一个NAMESPACE的观念。也就是说所有的OBJECT之间是有等级关系的。类似一个文件或者注册表系统,各个ACPI OBJECT(类似常量)之间都存在于一个路径下面,其中的根目录就是以符号“\"来表示。然后上下级目录之间用“.”来连接起来。
比如\_SB_. FOO.BAR 就表示根目录下的_SB_这个OBJECT下的FOO OBJECT下的BAR OBJECT。
因此,在ACPI中,很多操作都是作用在这个NAME SPACE 中的某个OBJECT上面。并由此引入了一系列相关概念。比如SCOPE。
为什么要这样设计呢?因为ACPI本身是一个针对性很强的规范,就是电源管理和硬件配置。因此把这些常用的OBJECT排列好,分类好。处理起来也方便。灵活性比一般的编程语言差了,但是简单,并且能满足设计要求。
2、ASL中有大量的OPERATOR(操作符)。
基本上看一段ASL代码,其中操作符占掉了大部分。比如ASL中很多都是如下形式:Device(PCI0)。一般在小括号前面的都是操作符,也就是预先定义好的。这也是因为ASL本身的目的就很简单,所以很多东西可以先定好。
看完了第一篇中关于 ASL的简单介绍之外,现在来看个具体的例子把. (PS: 俺喜欢栗子.) 本人最近对S3, S4 sleep 比较感兴趣,于是找出相应的代码看了下。去哪里找代码?如果你是在公司里干活的开发人员,那么恭喜你,你应该已经有源代码了。如果你是像我一样的外行,那么可以看看 COREBOOT (WWW.COREBOOT.ORG)。 COREBOOT 是一个开源的BIOS。支持的板子不多,比较老,但是开源,免费。。。
目前的COREBOOT也不是所有板子都支持了ACPI,只有一部分支持。我现在看的是 ASUS M2V-MX_SE的这块。在LINUX下,GREP 下他的源文件,就可以发现下面在mainboard/asus/m2v-mx_se/dsdt.asl 里有几行和 SLEEP 有关的定义:
Name (\_S0, Package () { 0x00, 0x00, 0x00, 0x00 })
Name (\_S3, Package () { 0x01, 0x01, 0x00, 0x00 })
Name (\_S5, Package () { 0x02, 0x02, 0x00, 0x00 })
这是什么意思呢?我们来具体看下。本系列的第一篇说了,ASL里面有“一堆”的操作符,一般都在小括号前面。比如上面代码里的Name就是一个操作符。
啰嗦一下,这个ASL里面的操作符看着很奇怪。一般的C语言里,操作符也就是+,-,*,/ 等这么几个。到了 ASL, 这个Name看上去很像一个C函数,但是又没有具体实现的地方。我觉得可以理解成ASL内部实现了的一个函数。。。
好了,言归正传,既然Name是个操作符,那么就是ASL 本身已经定义好的。查下ACPI SPEC 就可以找到他的含义了。如下 (ACPI 4.0 Page 620):
18.5.80
Name (Declare Named Object)
Syntax:
Name (ObjectName, Object)
Arguments:
Creates a new object named ObjectName. Attaches Object to ObjectName in the Global ACPI namespace.
Description:
Creates ObjectName in the namespace, which references the Object.
Example:
The following example creates the name PTTX in the root of the namespace that references a package.
Name (\PTTX, // Port to Port Translate Table
Package () {Package () {0x43, 0x59}, Package) {0x90, 0xFF}}
)
The following example creates the name CNT in the root of the namespace that references an integer data object with the value 5.
Name (\CNT, 5)
简单的说Name就是定义了一个Object和他的名字。可以想象成是一个常量和这个常量的名字。在他的具体定义里,又包括了一个Package的定义。于是用同样的方法查下Package的定义( ACPI SPEC 4.0 PAGE 625). 就知道他的含义了。就是把一堆Object放在一起,类似一个C语言里的结构体。
1.3 系统差异描述表(DSDT)
参考ACPI规范,第19页:
初始设备制造厂家(OEM)必须提供一个DSDT给ACPI兼容操作系统。DSDT包含差异定义块,它提供关于基本系统(base system)的执行和配置信息。操作系统总是在系统启动时将DSDT信息插入到ACPI命名空间并且从不移除。
基本上,可以把DSDT归结为来描述你的系统配置的。它包括所有ACPI支持的设备的定义并描述它们的兼容性。例如,它描述电池、交流适配器、嵌入式控制器、风扇、发热区。它以分级形式呈现这些信息,所以ACPI(和兼容操作系统)能够了解系统硬件之间的依存关系。DSDT在启动时加载。基本上,它告诉ACPI驱动要注意些什么。
1.4 系统描述从表(SSDT)
参见ACPI规范,第22页:
SSDT是DSDT的延续。多个SSDT可以用来作为平台描述的一部分。在DSDT被加载到ACPI命名空间后,每个RSDT/XSDT列举的SSDT和它唯一的OEM表识别号被加载。这允许OEM在一个表里提供基本支持,同时添加在其他表里添加小的系统选项。
注意:附加的表只能添加数据,不能覆盖之前表里的数据。一台电脑可能有多个SSDT表。也就是说,DSDT可能只含有设备(device)的基本信息,然后将详细信息放在具有唯一识别号的SSDT中,这也是为什么我们有时候需要让变色龙加载SSDT实现CPU变频或者将SSDT信息添加到DSDT中去实现一些功能。
2. 问题描述
ACPI规范对DSDT的要求(和其它的内容)进行了定义而且非常严格。英特尔的编译器iASL用于将DSDT从ASL编译为AML,如果基本的ASL有错误,编译器会给出错误(error)和警告(warning)。不幸的是微软的ASL编译器允许很多这样的错误和警告通过。结果就是很多OEM厂商写有错误的DSDT,而且结果是Windows对于这些微软的编译器产生的这些DSDT中的错误非常宽容(一点都不奇怪)。
这也就是说一个不符合ACPI规范的DSDT会在Windows下工作,即便它不应该工作。当你试图在Linux、MacOS下使用时,ACPI的开发者期望DSDT是按照标准(并且用英特尔的ASL编译器)编写的,DSDT中的错误部分就不被支持。如果你有一个有错误的DSDT,ACPI可能识别设备的存在。或者能够识别,但不支持所有的功能。如果你遇到这些症状(/proc/acpi缺失或者不完全支持),那么这可能是一个有错误的DSDT引起的。
你可以通过在dmesg中查看DSDT相关行是否有“MSFT”来确定你的DSDT是否通过微软的编译器编译。
ACPI: RSDP (v000 GBT ) @ 0x000f6d20
ACPI: RSDT (v001 GBT AWRDACPI 0x42302e31 AWRD 0x01010101) @ 0x3fff3000
ACPI: FADT (v001 GBT AWRDACPI 0x42302e31 AWRD 0x01010101) @ 0x3fff3040
ACPI: MADT (v001 GBT AWRDACPI 0x42302e31 AWRD 0x01010101) @ 0x3fff7080
ACPI: DSDT (v001 GBT AWRDACPI 0x00001000 MSFT 0x0100000c) @ 0x00000000 /*这一行有MSFT*/
理想情况下,我们可以相信我们最爱的电脑制作商来修复他们的DSDT。那是个好主意,但是我们在如地狱般的漫长等待中,我们也可以自己来修复我们的DSDT。
我们可以手动修复这些bug使之兼容MAC系统。DSDT在bios里是以编译后的aml文件存在,需要把它解出来,反编译成aml代码形式(dsl文件,文本文件),代码修改完毕再重新编译,替换掉bios里原来的dsdt表。不过PC-EFI,Chameleon的出现,使得我们不再需要冒险刷bios,只要把制作好的DSDT.aml文件放在根目录或者/Extra文件夹里,启动的时候就会自动使用这个DSDT.aml,从而达到修复ACPI系统bug的作用。
3. 免责声明
这里列举的一些方法应该可以解决Linux(Mac OS)下ACPI相关的很多问题。然而,它们不一定会解决你所有的ACPI问题。不幸的是,有很多ACPI引起的故障。如果在尝试这些修复后,你的ACPI问题依然存在,那你可能在你的ACPI驱动代码中有错误(MacOS不支持硬件)。你可能会遇到不可预测的系统问题,包括硬件和软件。对此你需要自行承担风险。我们概不负责。
好了,关于不能解决的先说这么多,下面我们来看看我们能做的。第一件事情,获取你电脑的DSDT。有很多方法可以获取到DSDT,比如通过变色龙官方偏好面板、AIDA64(Everest)、Chameleon Wizard等。推荐在Windows下使用AIDA64获取,方法不再赘述。
4. 修复DSDT你需要什么
在开始修复你的DSDT之前,你需要准备:
1、已经提取的DSDT文件(SSDT文件)。
2、英特尔iASL编译器。
3、一些常见的DSDT修复代码(参考HJMac UDT)。
5. 诊断一个有错误的DSDT
很明显,修复DSDT你需要先知道DSDT是不是有错。所以我们要做的第一件事就是找出DSDT是否有错。最简单的办法是将DSDT重新编译(反编译后再编译),然后看我们是不是能从英特尔的编译器得到报错。你需要一个英特尔的iASL编译器来做这个工作。(可以直接用iASLMe,将aml文件拖到窗口上会反编译,将asl文件拖放到窗口上会编译。)
如果你的DSDT有错误,重新编译时你会看到相应的错误和/或警告提示。举例来说,从我的笔记本上,我在重新编译时得到下面的信息:
Intel ACPI Component Architecture
ASL Optimizing Compiler / AML Disassembler version 20030228 [Feb 28 2003]
Copyright (C) 2000 - 2003 Intel Corporation
Supports ACPI Specification Revision 2.0b
dsdt.dsl 163: Method (_WAK, 1, NotSerialized)
Warning 2026 - ^ Reserved method must return a value (_WAK)
dsdt.dsl 2626: Field (ECR, DWordAcc, Lock, Preserve)
Error 1048 - ^ Host Operation Region requires ByteAcc access
dsdt.dsl 2672: Method (_GLK, 1, NotSerialized)
Warning 2024 - ^ Reserved method has too many arguments ( _GLK requires 0)
ASL Input: dsdt.dsl - 3759 lines, 123154 bytes, 1862 keywords
Compilation complete. 1 Errors, 2 Warnings, 0 Remarks, 390 Optimizations
一个错误,两个警告。在这种情况下,我们可以来修复DSDT中的错误。
6. 修复DSDT
The first thing that you should do if you have a buggy DSDT is to head over to theDSDT repository at acpi.sourceforge.net. They have fixed DSDTs for a number of laptops, so your model may already have a fix available. If there is a fix there, then just download it, extract it, recompile the asl using iasl as above, and proceed to Section 9. If there isn't already a fixed DSDT available, then you will have to try to fix your DSDT yourself. Read on for an example from my machine.
Unfortunately, this step will differ for every system. In my case, the fix was simple and self-evident, but I can't guarantee that that will be the case with you. You can find solutions to common DSDT compilation errorshere. At the end of the document, I'll have a list of useful resources in case you get stuck. OK - on to the fixing!
In order to fix the DSDT, you'll have to edit the dsdt.dsl file that we created in the diagnosis. Let's use mine as an example. Unless you also have a Gateway 200X, your process will be different (and if you do have a 200X, then you can get the fixed DSDThere). However, this should at least give you an overview of the process.
As you may recall, when I compiled my DSDT, I got one error:
代码: |
dsdt.dsl 2626: Field (ECR, DWordAcc, Lock, Preserve) Error 1048 - ^ Host Operation Region requires ByteAcc access |
This tells me the following:
- The error is on line 2626
- The region in question requires ByteAcc Access
Since I see that this line has a DWordAcc value specified, I assume that that is what is causing the problem. So, I opened dsdt.dsl in a text editor and fixed that line:
代码: |
vi dsdt.dsl |
代码: |
Field (ECR, DWordAcc, Lock, Preserve) |
代码: |
Field (ECR, ByteAcc, Lock, Preserve) |
代码: |
./iasl -tc dsdt.dsl |
代码: |
Intel ACPI Component Architecture ASL Optimizing Compiler / AML Disassembler version 20030228 [Feb 28 2003] Copyright (C) 2000 - 2003 Intel Corporation Supports ACPI Specification Revision 2.0b dsdt.dsl 163: Method (_WAK, 1, NotSerialized) Warning 2026 - ^ Reserved method must return a value (_WAK) dsdt.dsl 2672: Method (_GLK, 1, NotSerialized) Warning 2024 - ^ Reserved method has too many arguments ( _GLK requires 0) ASL Input: dsdt.dsl - 3759 lines, 123153 bytes, 1862 keywords AML Output: DSDT.aml - 14600 bytes 499 named objects 1363 executable opcodes Compilation complete. 0 Errors, 2 Warnings, 0 Remarks, 390 Optimizations |
代码: |
dsdt.dsl 2672: Method (_GLK, 1, NotSerialized) Warning 2024 - ^ Reserved method has too many arguments ( _GLK requires 0) |
代码: |
Method (_GLK, 1, NotSerialized) |
代码: |
Method (_GLK) |
代码: |
dsdt.dsl 163: Method (_WAK, 1, NotSerialized) Warning 2026 - ^ Reserved method must return a value (_WAK) |
代码: |
Return(Package(0x02){0x00, 0x00}) |
代码: |
Arguments: 0 The value of the sleeping state (1 for S1, 2 for S2, and so on). Result Code (2 DWORD package): Status Bit field of defined conditions that occurred during sleep. 0x00000000 Wake was signaled and was successful 0x00000001 Wake was signaled but failed due to lack of power. 0x00000002 Wake was signaled but failed due to thermal condition. Other Reserved PSS If non-zero, the effective S-state the power supply really entered. This value is used to detect when the targeted S-state was not entered because of too much current being drawn from the power supply. For example, this might occur when some active device’s current consumption pushes the system’s power requirements over the low power supply mark, thus preventing the lower power mode from being entered as desired. |
代码: |
PackageTerm := Package( NumElements //Nothing | //ByteConstExpr | //TermArg=>Integer ) {PackageList} => Package |
代码: |
Intel ACPI Component Architecture ASL Optimizing Compiler / AML Disassembler version 20030228 [Feb 28 2003] Copyright (C) 2000 - 2003 Intel Corporation Supports ACPI Specification Revision 2.0b ASL Input: dsdt.dsl - 3760 lines, 123177 bytes, 1863 keywords AML Output: DSDT.aml - 14606 bytes 499 named objects 1364 executable opcodes Compilation complete. 0 Errors, 0 Warnings, 0 Remarks, 392 Optimizations |
Excellent! No errors, no warnings. We now have a "fixed" DSDT (remember, the _WAK method isn't really fixed, we've just shut up the warning on compile). Many of the suggestions in the DSDT HOWTOs that I found are really just workarounds, not proper fixes. If you would like a more thorough analysis of your DSDT, you may want to ask the folks on the acpi-devel mailing list. If you succesfully used this process to fix your DSDT, please considerposting the fix to the DSDT repository, so that others can benefit from your work.
All that remains now is to convince our kernel to use the new DSDT.
As stated above, this will create two files, dsdt.hex andDSDT.aml. You will need to use one or the other of these files in the next step, depending on which method you use to override your DSDT. If you use the static DSDT override method, then you will needdsdt.hex. If you use the initrd method, then you will needDSDT.aml.
8. Incorporating the fixed DSDT into the kernel
There are two ways to incorporate your new DSDT into the kernel. The first way is to include it statically at compile time. The second is to pass it to the kenel at boot time as an initrd. The initrd approach is probably preferable, especially if you need to make a lot of changes to your DSDT, because it doesn't require that you recompile your kernel for each new DSDT. The static method does. Each method requires a kernel patch. Let's start with the static method first:
9a. Static DSDT override
To statically override your DSDT at kernel compile time, you will have to apply a patch to your kernel to have it read in the new DSDT, and then copy your fixed DSDT .hex file(dsdt.hex) to the kernel source tree for inclusion in the kernel.
So, first things first, let's patch the kernel.
代码: |
cd /usr/src/linux-2.4.23 patch -p1 < /path/to/dsdt_override.diff |
代码: |
cp /path/to/dsdt.hex /usr/src/linux-2.4.23/include/acpi/dsdt_table.h |
That ought to do it. After recompiling, move your new kernel image to /boot (maybe don't overwrite your current kernel - just to be safe), and reboot. If any of your acpi problems were caused by the buggy DSDT, then they should be fixed. /proc/acpi/dsdt should now contain your new fixed DSDT, so you can always cat that back out and recompile it to make sure that you are getting what you expect (i.e. no errors).
Remember, if you make more changes to your DSDT, then you'll have to recopy it to
9b. initrd DSDT override
The initrd method requires about the same amount of setup as the static method for the initial DSDT, but any subsequent changes can be incorporated much more easily. Basically, you will have to patch your kernel, copy your fixed DSDT .aml file(DSDT.aml) to /boot, and direct the kernel to incorporate it as an initrd. If you are already using an initrd for something, then there is a bit more work involved. I'll go over that after describing the basic case.
So, again, the first thing we have to do is patch the kernel.
代码: |
cd /usr/src/linux-2.6.0 patch -p1 < /path/to/acpi-dsdt-initrd-patch-v0.4-2.6.0.diff |
代码: |
Device Drivers ---> Block Devices ---> <*> RAM disk support [*] Initial RAM disk (initrd) support |
代码: |
Power management options (ACPI, APM) ---> ACPI (Advanced Configuration and Power Interface) Support ---> [*] Read DSDT from initrd |
代码: |
mount /boot cp /path/to/DSDT.aml /boot |
代码: |
title=Gentoo Linux (2.6.0 - DSDT initrd) root (hd0,5) kernel (hd0,5)/boot/linux-2.6.0-dsdt-initrd root=/dev/hd8 initrd=/boot/DSDT.aml |
When you reboot, the new kernel should pick up the DSDT from the /boot partition and load it up as directed by the initrd parameter. You should see a message in dmesg that says "Looking for DSDT in initrd ..."
Now, if you make changes to your dsdt, all you have to do is copy the new DSDT.aml to /boot and reboot to incorporate those changes. No kernel recompile required.
9c. initrd override with bootsplash
If you are already using an initrd for something, like bootsplash, you can still use this method. You just have to create the initrd a bit differently. Instead of simply copying DSDT.aml to /boot and using it as the initrd, you have to cat a signature for the DSDT into your existing initrd, and then cat the DSDT into it as well. For example, my bootsplash initrd is currently called initrd-1024x768. So, here's what I did to add the DSDT.
代码: |
cp /boot/initrd-1024x768 /boot/initrd-1024x768-dsdt |
代码: |
echo "INITRDDSDT123DSDT123" >> /boot/initrd-1024x768-dsdt |
代码: |
cat /path/to/DSDT.aml >> /boot/initrd-1024x768-dsdt |
代码: |
title=Gentoo Linux (2.6.0 gentoo - bootsplash + DSDT initrd) root (hd0,5) kernel (hd0,5)/boot/linux-2.6.0-gentoo-dsdt root=/dev/hd8 video=vesa:ywrap,mtrr vga=0x317 initrd=/boot/initrd-1024x768-dsdt |
Voila! When you boot up, you should now get your lovely bootsplash screen and your new dsdt, both incorporated from the initrd.
If you make changes to the DSDT, you just have to rebuild your initrd as above (which is a good reason to make a copy of your existing one, rather than appending to it directly).
9. My DSDT is fixed, but I still have ACPI errors. Now what?
10a. Windows-only DSDT functionality
You may find that you have no errors in your DSDT, but there are still errors in dmesg, or missing ACPI functionality. This may be because your DSDT is testing for the name of your OS. Many DSDTs do this, and enable certain functionality only if you are running a particular OS (usually, of course, Windows XP). To test for this, look for lines in your DSDT that check the value or length of the "_OS" variable. For example, you may find lines like this:
代码: |
If (LEqual (SizeOf (_OS), 0x14)) |
代码: |
acpi_os_name="Microsoft Windows XP" |
代码: |
evregion-0251 [22] ev_address_space_dispa: No handler for Region [ECR_] (df5ec688) [EmbeddedControl] exfldio-0284 [21] ex_access_region : Region EmbeddedControl(3) has no handler dswexec-0435 [14] ds_exec_end_op : [LEqual]: Could not resolve operands, AE_NOT_EXIST dswstate-0273 [16] ds_result_pop_from_bot: No result objects! State=df5fb428 dsutils-0525 [16] ds_create_operand : Missing or null operand, AE_AML_NO_RETURN_VALUE psparse-1120: *** Error: Method execution failed [\_SB_.ADP1._STA] (Node df5f42c8), AE_AML_NO_RETURN_VALUE evregion-0251 [22] ev_address_space_dispa: No handler for Region [ECR_] (df5ec688) [EmbeddedControl] exfldio-0284 [21] ex_access_region : Region EmbeddedControl(3) has no handler dswexec-0435 [14] ds_exec_end_op : [LEqual]: Could not resolve operands, AE_NOT_EXIST dswstate-0273 [16] ds_result_pop_from_bot: No result objects! State=df5fb428 dsutils-0525 [16] ds_create_operand : Missing or null operand, AE_AML_NO_RETURN_VALUE psparse-1120: *** Error: Method execution failed [\_SB_.BAT1._STA] (Node df5f4848), AE_AML_NO_RETURN_VALUE |
代码: |
ACPI: Power Button (FF) [PWRF] ACPI: Lid Switch [LID0] ACPI: Sleep Button (CM) [SLPB] |
代码: |
title=Gentoo Linux (2.6.1 - all ACPI patches) root (hd0,5) kernel (hd0,5)/boot/linux-2.6.1 root=/dev/hda8 ignore_ff_buttons=PWRF initrd=/boot/DSDT.aml |
代码: |
ACPI: Lid Switch [LID0] ACPI: Sleep Button (CM) [SLPB] ACPI: Power Button (CM) [PWRB] |
代码: |
ACPI: Vendor "PTLTD " System " DSDT " Revision 0x6040000 has a known ACPI BIOS problem. ACPI: Reason: Multiple problems. This is a non-recoverable error ACPI: BIOS listed in blacklist, disabling ACPI support |
代码: |
{"PTLTD ", " DSDT ", 0x06040000, ACPI_DSDT, less_than_or_equal, "Multiple problems", 1}, |
11. Acknowledgements
I'd like to thank the ACPI developers, expecially Luming Yu and Bruno Ducrot. All of them have been very helpful and very responsive. In general, the ACPI mailing lists and bugzilla are wonderful resources for ACPI help.