华硕 VM510LI Hotpatch

本笔记参考 RehabMan 指南 [Guide] Using Clover to “hotpatch” ACPI

Hotpatch 简述

什么是 Hotpatch?

RehabMan 的介绍是:

That guide uses what is known as “static patching”. In order to inject patched ACPI files, we extract native ACPI, disassemble them, make changes, then recompile and place the files in ACPI/patched, so that Clover injects the patched ACPI instead of native ACPI. With the techniques detailed in this guide, the changes can be made directly to the ACPI binaries provided by BIOS, skipping the extract, disassembly, and recompilation steps.

该指南使用所谓的“静态修补”。 为了注入修补的ACPI文件,我们提取本地ACPI,反汇编,进行更改,然后重新编译并将文件放在ACPI / patched中,以便Clover注入修补的ACPI而不是本机ACPI。 使用本指南中详述的技术,可以直接对BIOS提供的ACPI二进制文件进行更改,跳过提取,反汇编和重新编译步骤。(翻译来自 Google 翻译)

我的理解:

其实 Hotpatch 就是将所有修补 ACPI 文件的补丁变成一个一个模块,通过重命名主板提供的 ACPI 文件原来的 Method (方法/函数) 来禁用原有的方法,再用 SSDT 来放入我们修改后的(已修补)的 Method (方法/函数).

当然上面所说的是终极解决的办法,其实还有很多很简单的方法能够完成 ACPI 补丁的功能.

如果有兴趣可以去看看 RahabMan 帖子.

之前我在 DSDT 打的补丁有.

1
2
3
4
5
6
7
8
9
10
"Fix _WAK Arg0 v2"
"HPET Fix"
"SMBUS Fix"
"IRQ Fix"
"RTC Fix"
"OS Check Fix(Windows 7)"
"OS Check Fix(Windows 8)"
"Fix Mutex with non-zero SyncLevel" //对比了下 这个补丁是不需要打的
"Rename GFX0 to IGPU"
"Brightness fix(Haswell/Broadwell)"

Clover 常规补丁

  • Fix _WAK Arg0 v2ACPI > DSDT > FIX_WAK_200000
  • IRQ FixHPET FixACPI > DSDT > FixHPET_0010ACPI > DSDT > FixIPIC_0040
  • RTC FixACPI > DSDT > FIX_RTC_20000

OS Check Fix(Windows 7)和 OS Check Fix(Windows 8)

HotPatch 的方式是,将调用 _OSI 的方法重命名为 XOSI. 然后再放入 SSDT-XOSI.aml 到 Clover > ACPI > Patched

1
2
3
Comment: Change _OSI to XOSI
Find: <5f4f 5349>
Replace: <584f 5349>

意思就是,将调用 _OSI 方法的地方都改成调用 XOSI,而 XOSI 方法则是从 SSDT-XOSI.aml 引入的,我们再从 XOSI 方法修补.

Rename GFX0 to IGPU

和上面重命名方法一样

1
2
3
Comment: Rename GFX0 to IGPU
Find: <4746 5830>
Replace: <4947 5055>

Brightness fix(Haswell/Broadwell)

这个补丁方法

  1. GFX 重命名为 IGPU,上一节补丁已经应用了.
  2. 放入 SSDT-PNLF.aml
  3. SSDT-Config.aml 需要定义 BKLT 的值为 0.

USB3_PRW 0X6D(instant wake)

这个补丁方法,的方法遇上面的不太一样.

补丁片段

1
2
3
4
5
6
7
8
9
10
11
# some _PRW have three entries in the Package
into device name_adr 0x001D0000 code_regex Name.*_PRW.*\n.*\n.*\n.*\n.*\n.*\}\) remove_matched;
# 找到设备名称地址 0x001D0000 然后移除该设备的 _PRW 方法.
# seems to work better if _PRW is present, but returns 0 (original was 3) for sleep state
into device name_adr 0x001D0000 insert begin Name(_PRW, Package() { 0x6D, 0 }) end;
# 找到设备名称地址 0x001D0000 输入变量名 _PRW 变量内容 { 0x6D, 0 }
PS: 以上纯属猜测,本人未学习过 IASL 的编程语法,全靠蒙,如果错了请指正.

这样修改后,USB 总线 EHC1和 EHC2 设备中没有了 _PRW 方法了,直接变成了 _PRW 变量.

至于为何需要这样改,我并不清楚,这必须了解 USB 导致睡眠唤醒的代码原理,我看不懂.但是看补丁的修改方法还是会的.

那么将上面的补丁换成 Hotpatch 应该如何表现呢?

搜索了下, DSDT 中还有非常多的 _PRW 方法,他们遍布了整个 DSDT 的各种设备,如果还是使用简单的改名是不可靠的.

因为 Clover 的 Patches 是搜索 DSDT 的二进制版本进行修补,二进制中无法识别整个_PRW 方法是否属于 USB 设备下的,也就是是否属于 EHC1 EHC2 的设备,那么是否就没办法用 Hotpatch 方法来修补这个问题呢?

我们来看看被补丁修补的源代码是什么样的

下面是 EHC1 的 _PRW 方法

1
2
3
4
Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake
{
Return (GPRW (0x6D, 0x03))
}

下面是 EHC2 的 _PRW 方法

1
2
3
4
Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake
{
Return (GPRW (0x6D, 0x03))
}

下面是 XHC 的 _PRW 方法

1
2
3
4
Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake
{
Return (GPRW (0x6D, 0x03))
}

我们可以发现我们补丁修改的每个 _PRW 方法都会调用一个方法 GPRW,并且调用 GPRW 方法的变量内容都是 0X6D.

再看看 RehabMan 写的 SSDT-PRW.dsl 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// For solving instant wake by hooking GPRW or UPRW
DefinitionBlock("", "SSDT", 2, "hack", "PRW", 0)
{
External(XPRW, MethodObj)
// In DSDT, native GPRW is renamed to XPRW with Clover binpatch.
// (or UPRW to XPRW)
// As a result, calls to GPRW (or UPRW) land here.
// The purpose of this implementation is to avoid "instant wake"
// by returning 0 in the second position (sleep state supported)
// of the return package.
Method(GPRW, 2)
{
If (0x6d == Arg0) { Return (Package() { 0x6d, 0, }) } //检查第一个变量是否为 0X6D 如果结果为真,返回 { 0x6d, 0, }
If (0x0d == Arg0) { Return (Package() { 0x0d, 0, }) } //检查第一个变量是否是 0X0D 如果结果为真,返回 { 0x0d, 0, }
Return (XPRW(Arg0, Arg1)) //如果上述都不对,则转跳到 XPRW方法 (函数为转跳过来的值)
}
Method(UPRW, 2)
{
If (0x6d == Arg0) { Return (Package() { 0x6d, 0, }) }
If (0x0d == Arg0) { Return (Package() { 0x0d, 0, }) }
Return (XPRW(Arg0, Arg1))
}
}
//EOF

上面的逻辑很清晰了, 其实就是要将我们需要改的地方,直接用代码发回我们希望他得到的变量,那么我们就不必想如何识别哪些是 USB 的 _PRW 方法了.

Hotpatch 我觉得就是,复杂的逻辑交给 ASL 代码来解决, Clover 的 Patch 只需要修改源代码的调用即可.

那么上面多了个 XPRW 是什么东东?为啥其余调用 GPRW 方法都调用他呢?

这个就是 Clover 需要做的,我要将所有原来调用 GPRW 方法的都调用到这个 SSDT-PRW 来,原来的 GPRW 我们更名为 XPRW.

明白了吧?所有代码都经过上面的逻辑,最终跑回被我们更名为 XPRW 的方法(也就是原来的GPRW 方法).

故此应用这个 SSDT 之前,我们需要将 DSDT 原来的 GPRW 方法重命名为 XPRW 方法

但是由遇到了一个问题,如何定位 GPRW 方法在二进制文件 aml 中的位置呢?

小笔记: aml 是 ASL 编程语言编译后的二进制文件,但是我们一般反编译或者 Patch都是讨论十六进制,因为每8个二进制代表一个字符

RehabMan 在 [Guide] Using Clover to “hotpatch” ACPI Rename and Replace 章节有讲到这个问题

大概的意思就是因为不同主板可能情况不同,我们需要使用 Hex Fiend 这样的软件来验证 GPRW 方法的十六进制代码.

方法就是用 Hex Fiend 打开 DSDT 的 aml 文件,然后搜索 GPRW, 你会看到有很多 GPRW, 我的 GPRW 方法在 DSDT 的最下面,所以我直接拉到最下面反向搜索.

最终找到以下十六进制代码

1
47 50 52 57 02 GPRW

再看看 GPRW 的十六进制代码是怎么样的?

1
2
neros-MBP:test nero$ echo -n GPRW|xxd
00000000: 4750 5257 GPRW

奇怪了,为何我在上面列举的代码多了一个 02 ?

我们看看原来 GPRW 方法源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Method (GPRW, 2, NotSerialized)
{
Store (Arg0, Index (PRWP, Zero))
Store (ShiftLeft (SS1, One), Local0)
Or (Local0, ShiftLeft (SS2, 0x02), Local0)
Or (Local0, ShiftLeft (SS3, 0x03), Local0)
Or (Local0, ShiftLeft (SS4, 0x04), Local0)
If (And (ShiftLeft (One, Arg1), Local0))
{
Store (Arg1, Index (PRWP, One))
}
Else
{
ShiftRight (Local0, One, Local0)
FindSetLeftBit (Local0, Index (PRWP, One))
}
Return (PRWP)
}

看到了吧,方法名称紧挨着就是2,这个2就是 02.

发现下面的方法好像并不是所有都这样,需要验证.

根据上述帖子内容,aml 编译后会留下一个位置符来定义这个前面这个字符到底是方法,还是变量,还是设备.

根据我观察所得,(不同的 ASL 版本或者不同电脑获得的位置符号未必相同)

Method 位置符号是 06 [xx xx xx xx]
Name 位置符号是 08 [xx xx xx xx]

搜索 06 47 50 52 57

一样可以定位到 GPRW 方法上,而且这个代码是唯一的,因为整个 DSDT 里面只有一个 GPRW 函数.

翻看 RM 的帖子,再验证了下,发现一个更好的规律和方法

将我们的 DSDT.dsl 文件使用如下命令编译.

1
iasl -l DSDT.dsl

它会产生两个文件

  • DSDT.aml 编译后文件
  • DSDT.lst 混合列表文件

用文字编辑软件打开 DSDT.lst, 会发现机械码和 iASL 代码混合在一齐,这个就是一个翻译文件一样,列举了各种代码的机械码模式,这样我们就可以更好的找到我们需要打补丁的代码原来是怎么样的.

例如:我们需要将 Method (TACH,1 的方法重命名为 Method(XACH,1 来使得 TACH 方法失效,再注入 SSDT 中写入 TACH 方法,来代替它.

那么我只需要搜索 Method (TACH,1 就能找到他机械码的模样.

1
2
3
4
22490: }
22492: Method (TACH, 1, Serialized)
00015565: 14 40 06 54 41 43 48 09 ".@.TACH."

那么我们补丁就可以这样写

1
2
Find:14 40 06 54 41 43 48 09
Replace:14 40 06 58 41 43 48 09

将我们字符转换成 Hex 形式可以用如下命令

1
2
3
4
neros-MBP:~ nero$ echo -n TACH |xxd
00000000: 5441 4348 TACH
neros-MBP:~ nero$ echo -n XACH |xxd
00000000: 5841 4348 XACH

小结

说了那么多,这个 USB3_PRW 0X6D(instant wake) 唤醒补丁的 Hotpatch 的方法就是

  1. 将 DSDT 的 GPRW 方法重命名为 XPRW
  2. 放入 RehabMan 写的 SSDT-PRW.aml 到 Clover > ACPI > patch

PS: 后来测试发现开机的时候系统日志会提示没有找到 UPRW ,这个是因为 SSDT-PRW.aml 包含了 UPRW 这个方法,而这个方法对我来说是没用的,故此放入 SSDT-PRW.aml 后如果不想出现 ACPI Error 的话,最好确认下自己的唤醒是调用 GPRW 还是 UPRW,然后将另一个方法注释掉.

SMBUS Fix

这个补丁就是在 SBUS 加入 BUS0 设备,只需要放入 SSDT-SMBUS.aml 即可.

Rename B0D3 to HDAU

先将 B0D3 重命名为 HDAU

1
2
3
Comment: change B0D3 to HDAU, optionally pair with SSDT-HDAU.aml
Find: 42304433
Replace: 48444155

再放入 SSDT-HDAU.aml

注意修改 layout-id 的值为你声卡的 layout-id 值,我声卡的 layout-id 是4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Automatic injection of HDAU properties
// Note: Only for Haswell and Broadwell
DefinitionBlock("", "SSDT", 2, "hack", "HDAU", 0)
{
External(_SB.PCI0.HDAU, DeviceObj)
External(RMCF.AUDL, IntObj)
// inject properties for audio
Method(_SB.PCI0.HDAU._DSM, 4)
{
If (CondRefOf(\RMCF.AUDL)) { If (Ones == \RMCF.AUDL) { Return(0) } }
If (!Arg2) { Return (Buffer() { 0x03 } ) }
Local0 = Package()
{
"layout-id", Buffer(4) { 4, 0, 0, 0 }, //需要注意修改此 layout-id 值
"hda-gfx", Buffer() { "onboard-1" },
}
If (CondRefOf(\RMCF.AUDL))
{
CreateDWordField(DerefOf(Local0[1]), 0, AUDL)
AUDL = \RMCF.AUDL
}
Return(Local0)
}
}
//EOF

ApplePS2SmartTouchPad 驱动的 Fn 按键补丁

这个补丁需要打上 Fn 功能键才能调整亮度,我们先看看补丁都干了些什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Patch by EMlyDinEsH (OSXLatitude)
# Enables Fn brightness keys to work with my kext AsusNBFnKeys
# Replacing method _Q0E with code for Brightness down key to work
into Method label _Q0E replace_content begin
If (ATKP)\n
{\n
^^^^ATKD.IANE (0x20)\n
}
end;
# Replacing method _Q0F with code for Brightness up key to work
into Method label _Q0F replace_content begin
If (ATKP)\n
{\n
^^^^ATKD.IANE (0x10)\n
}
end;

上面这个补丁是将 _Q0E 方法和 _Q0F 方法的内容替换如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Method (_Q0E, 0, NotSerialized) // _Qxx: EC Query
{
If (ATKP)
{
^^^^ATKD.IANE (0x20)
}
}
Method (_Q0F, 0, NotSerialized) // _Qxx: EC Query
{
If (ATKP)
{
^^^^ATKD.IANE (0x10)
}
}

参考章节 USB3_PRW 0X6D(instant wake) 的方法.

  1. 重命名 _Q0E 方法和 _Q0F 方法为 XQ0E 和 XQ0F,使原先的方法失效.
  2. 再放入我们自己定义的 _Q0E 方法和 _Q0F 方法.

替换 _Q0E 和 _Q0F 方法名称

命令行查找下以上两个方法的十六进制代码

1
2
3
4
5
6
7
8
neros-MacBook-Pro:hotpatch nero$ echo -n _Q0E | xxd
00000000: 5f51 3045 _Q0E
neros-MacBook-Pro:hotpatch nero$ echo -n XQ0E | xxd
00000000: 5851 3045 XQ0E
neros-MacBook-Pro:hotpatch nero$ echo -n _Q0F | xxd
00000000: 5f51 3046 _Q0F
neros-MacBook-Pro:hotpatch nero$ echo -n XQ0F | xxd
00000000: 5851 3046 XQ0F

那么在 Clover 配置文件 ACPI > DSDT > Patches 写入如下补丁

1
2
3
4
5
6
7
8
9
#补丁-1
Comment: change _Q0E to XQ0E, optionally pair with SSDT-Fn.aml
Find: 5f51 3045
Replace: 5851 3045
#补丁-2
Comment: change _Q0F to XQ0F, optionally pair with SSDT-Fn.aml
Find: 5f51 3046
Replace: 5851 3046

如果觉得不放心,可以用 Hex Fiend 打开 DSDT.aml 文件来搜索替换一下,之后再用 iasl 来反编译,再用 MaciASL dsl 文件看看,是否已经被重命名为 XQ0E 和 XQ0F

重定义新的 _Q0E 和 _Q0F 方法

我不会编写 SSDT, 但是我会抄~

复制一份 SSDT-PRW.dsl 下来重命名为 SSDT-FN.dsl

用 MaciASL 打开.

将其修改为如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//Enables Fn brightness keys to work with my kext AsusNBFnKeys
DefinitionBlock("", "SSDT", 2, "hack", "Fn", 0)
{
// In DSDT, native _Q0E and _Q0F is renamed to XQ0E and XQ0F with Clover binpatch.
// (or UPRW to XPRW)
// As a result, calls to _Q0E and _Q0F land here.
External(ATKP, IntObj)
External(\_SB.ATKD.IANE, MethodObj)
External(\_SB.PCI0.LPCB.EC0, DeviceObj)
Scope(\_SB.PCI0.LPCB.EC0)
{
Method (_Q0E, 0, NotSerialized) // _Qxx: EC Query
{
If (ATKP)
{
\_SB.ATKD.IANE (0x20)
}
}
Method (_Q0F, 0, NotSerialized) // _Qxx: EC Query
{
If (ATKP)
{
\_SB.ATKD.IANE (0x10)
}
}
}
}
//EOF

PS:以上代码纯属乱搞,如果能用,纯属巧合哈~

最终将此 SSDT-FN.dsl 编译成 aml, 再放入 Clover > ACPI > patches

电池补丁

方法分析

根据远景论坛的教学贴,我学会了编写自己笔记本的电池电量补丁.

以下是我 华硕 VM510LI 的电量补丁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#Maintained by: Nero for: Laptop Patches
#battery_ASUS-VM510LI.txt
# created by Nero 1/2017
#Create B1B2 Method
into method label B1B2 remove_entry;
into definitionblock code_regex . insert
begin
Method (B1B2, 2, NotSerialized)\n
{\n
Return(Or(Arg0, ShiftLeft(Arg1, 8)))\n
}\n
end;
# utility methods to read/write buffers from/to EC
into method label RE1B parent_label EC0 remove_entry;
into method label RECB parent_label EC0 remove_entry;
into device label EC0 insert
begin
Method (RE1B, 1, NotSerialized)\n
{\n
OperationRegion(ERAM, EmbeddedControl, Arg0, 1)\n
Field(ERAM, ByteAcc, NoLock, Preserve) { BYTE, 8 }\n
Return(BYTE)\n
}\n
Method (RECB, 2, Serialized)\n
{\n
ShiftRight(Arg1, 3, Arg1)\n
Name(TEMP, Buffer(Arg1) { })\n
Add(Arg0, Arg1, Arg1)\n
Store(0, Local0)\n
While (LLess(Arg0, Arg1))\n
{\n
Store(RE1B(Arg0), Index(TEMP, Local0))\n
Increment(Arg0)\n
Increment(Local0)\n
}\n
Return(TEMP)\n
}\n
end;
# utility method to write to EC buffers
into method label WE1B parent_label EC0 remove_entry;
into method label WECB parent_label EC0 remove_entry;
into device label EC0 insert
begin
Method (WE1B, 2, NotSerialized)\n
{\n
OperationRegion(ERAM, EmbeddedControl, Arg0, 1)\n
Field(ERAM, ByteAcc, NoLock, Preserve) { BYTE, 8 }\n
Store(Arg1, BYTE)\n
}\n
Method (WECB, 3, Serialized)\n
{\n
ShiftRight(Arg1, 3, Arg1)\n
Name(TEMP, Buffer(Arg1) { })\n
Store(Arg2, TEMP)\n
Add(Arg0, Arg1, Arg1)\n
Store(0, Local0)\n
While (LLess(Arg0, Arg1))\n
{\n
WE1B(Arg0, DerefOf(Index(TEMP, Local0)))\n
Increment(Arg0)\n
Increment(Local0)\n
}\n
}\n
end;
#Convert 16 bit to 8 bit registers
into device label EC0 code_regex TAH0,\s+16, replace_matched begin TA00,8,TA01,8, end;
into device label EC0 code_regex TAH1,\s+16, replace_matched begin TA20,8,TA21,8, end;
into device label EC0 code_regex B0C3,\s+16, replace_matched begin B001,8,B002,8, end;
into device label EC0 code_regex B0SN,\s+16, replace_matched begin B0S0,8,B0S1,8, end;
into device label EC0 code_regex B1SN,\s+16 replace_matched begin B1S0,8,B1S1,8 end;
into device label EC0 code_regex DT2B,\s+16 replace_matched begin DT01,8,DT02,8 end;
#Fix 16 bit registers
into method label TACH code_regex \(TAH0, replaceall_matched begin (B1B2(TA00,TA01), end;
into method label TACH code_regex \(TAH1, replaceall_matched begin (B1B2(TA20,TA21), end;
into method label _BIX code_regex \^\^LPCB.EC0.B0C3, replaceall_matched begin B1B2(^^LPCB.EC0.B001,^^LPCB.EC0.B002), end;
into method label BIFA code_regex \(B0SN, replaceall_matched begin (B1B2(B0S0,B0S1), end;
into method label BIFA code_regex \(B1SN, replaceall_matched begin (B1B2(B1S0,B1S1), end;
into method label SMBR code_regex \(DT2B, replaceall_matched begin (B1B2(DT01,DT02), end;
into method label SMBW code_regex Store\s\(Arg4,\sDT2B\) replaceall_matched begin Store (ShiftRight(Arg4,8),DT02)\nStore (Arg4,DT01) end;
#fix 256 bit registers
into method label SMBR code_regex Store\s+\((.*),\s+BDAT\) replaceall_matched begin WECB(0x1c,256,%1) end;
into method label SMBW code_regex Store\s+\((.*),\s+BDAT\) replaceall_matched begin WECB(0x1c,256,%1) end;
into method label ECSB code_regex Store\s+\((.*),\s+BDAT\) replaceall_matched begin WECB(0x1c,256,%1) end;
into method label ECSB code_regex Store\s+\((.*),\s+BDA2\) replaceall_matched begin WECB(0x44,256,%1) end;
into method label ECSB code_regex \(BDAT, replaceall_matched begin (RECB(0x1c,256), end;
into method label ECSB code_regex \(BDA2, replaceall_matched begin (RECB(0x44,256), end;
into method label SMBR code_regex \(BDAT, replaceall_matched begin (RECB(0x1c,256), end;

大家都知道电量修补是一项非常复杂的修改,需要将调用到的变量高于8位的数值拆分成8位来处理.

故此修改代码量非常多,并且变量名称均需要重命名及重新赋值.

Hotpatch 因为是注入 SSDT 来实现补丁效果,故此我们需要找到补丁修改的各个变量赋值与函数调用的变量.

RM 教给大家一个办法,先将 DSDT.dsl 打上电池补丁,然后另存为 DSDT-Patch.dsl 然后使用 DiffMerge 来对比两个文件的修改代码的情况,就可以最简单的知道补丁到底修改了哪些变量.

当然读懂补丁的语法其实并不需要这样做,我们可以读一下补丁到底修改了哪些变量,做了哪些操作就可以了,这个补丁是我自己写的,所以我并不需要用 DiffMerge 来查询.

经过研究,我发现单纯用 Clover Patches 方式来修改变量是行不通的,因为例如

1
2
3
4
5
//拆分前
TAH0,16 Hex: 54 41 48 30 10
//拆分后
TA01,8,TA02,8 Hex: 54 41 30 31 08 54 41 30 32 08

拆分前是占用5个字节,拆分后我们需要10个字节的空间,这样打补丁的话,会导致 TAH0 下面的变量被覆盖,导致出错.

那么如此大量的变量更改,我们应当如何修改呢?

我参考了 RM 给 HP ProBook 的电量补丁以及 Hotpatch 电量修补 SSDT

SSDT:https://raw.githubusercontent.com/RehabMan/HP-ProBook-4x30s-DSDT-Patch/master/hotpatch/SSDT-BATT.dsl

电量补丁:https://raw.githubusercontent.com/RehabMan/HP-ProBook-4x30s-DSDT-Patch/master/patches/06_Battery.txt

发现他的解决办法如下

  1. 新建一个 SSDT 写入 EC 方法,方法中赋予我们拆分后的电池变量.
  2. 在新建的 SSDT 中复制修改好的方法.
  3. 利用 MaciASL 编译功能来协助检查缺少的 External().
  4. 将原来调用电池变量的方法一一用 Clover Patches 更名(将原来的方法全部禁用).

将 EC 赋值的需要拆分的变量写入到 SSDT-BATT.dsl

我不会写 SSDT, 但是我会抄.抄 RM 写的 SSDT 再修改

下面是 EC0 设备拆分后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// battery status patched for Ausu VM510LI
DefinitionBlock ("", "SSDT", 2, "hack", "batt", 0)
{
External(\_SB.PCI0, DeviceObj)
External(\_SB.PCI0.LPCB, DeviceObj)
External(\_SB.PCI0.LPCB.EC0, DeviceObj)
Scope(\_SB.PCI0.LPCB.EC0)
{
// This is an override for battery methods that access EC fields
// larger than 8-bit.
OperationRegion (ECOR, EmbeddedControl, Zero, 0xFF)
Field (ECOR, ByteAcc, Lock, Preserve)
{
//Offset (0x04),
//CMD1, 8,
//...
Offset (0x93),
TA00,8,TA01,8,
TA20,8,TA21,8,
//...
Offset (0xBE),
, 16, //B0TM, 16
, 16, //B0C1, 16,
, 16, //B0C2, 16,
B001,8,B002,8,
//...
Offset (0xF4),
B0S0,8,B0S1,8,
//Offset (0xF8),
//Offset (0xFA),
Offset (0xFC),
B1S0,8,B1S1,8
}
OperationRegion (SMBX, EmbeddedControl, 0x18, 0x28)
Field (SMBX, ByteAcc, NoLock, Preserve)
{
Offset (0x04),
DT01,8,DT02,8
}
}
}

其中 OperationRegion (ECOR, EmbeddedControl, Zero, 0xFF)OperationRegion (SMBX, EmbeddedControl, 0x18, 0x28) 原来的 DSDT 都存在,按照 RM 修改的方法,他的方式应该是从每个 Offset (0X00) 偏移量定义去写,如果偏移量下面第四个才是需要拆的,那么上面的可以使用 , 16, 这种方法来代替偏移量,好像这样就不会覆盖到原先的变量.(猜测罢了)

2017年04月30日:根据远景坛友 gujiangjiang 提醒,EC Field需要改个名字才能用,不然无效,会出现很多ACPI Error。

将上述的 ECOR 和 SMBX 重命名。

在 Clover DSDT Patches 添加如下补丁

1
2
3
4
5
6
7
Comment:change ECOR to XCOR
Find:45434F52
Replace:58434F52
Comment:change SMBX to XMBX
Find:534D4258
Replace:584D4258

将调用了以上变量的方法复制到 SSDT-BATT.dsl

从已经打了电量补丁的 DSDT 中找出上方调用这些变量的方法所在地,不要忘记了补丁中 #fix 256 bit registers 的方法,因为电量补丁中,大于32的变量无需拆分,只需要将调动变量的代码用 WECB 方法与 RECB 方法来更改代替即可.

  • Method (TACH, 1, Serialized)
    • TA00
    • TA01
    • TA20
    • TA21
  • Method (_BIX, 0, NotSerialized)
    • B001
    • B002
  • Method (BIFA, 0, NotSerialized)
    • B0S0
    • B0S1
    • B1S0
    • B1S1
  • Method (SMBR, 3, Serialized)
    • WECB(0x1c,256,Zero)
    • WECB(0x1c,256,Zero)
    • WECB(0x1c,256,Arg4)
    • Store (RECB(0x1c,256), Index (Local0, 0x02))
  • Method (ECSB, 7, NotSerialized)
    • WECB(0x1c,256,DerefOf (Index (Arg6, One)))
    • WECB(0x44,256,DerefOf (Index (Arg6, One)))
    • Store (RECB(0x1c,256), Index (Local1, 0x04))
    • Store (RECB(0x44,256), Index (Local1, 0x04))
  • Method (SMBW, 5, Serialized)
    • Store (ShiftRight(Arg4,8),DT02)
    • Store (Arg4,DT01)

总共发现6个方法,我们将这6个方法一个一个从修不好电量补丁的 DSDT 中复制到 SSDT-BATT.dsl

TACH 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Method (TACH, 1, Serialized)
{
Name (_T_0, Zero) // _T_x: Emitted by ASL Compiler
If (ECAV ())
{
While (One)
{
Store (Arg0, _T_0)
If (LEqual (_T_0, Zero))
{
Store (B1B2(TA00,TA01), Local0)
Break
}
ElseIf (LEqual (_T_0, One))
{
Store (B1B2(TA20,TA21), Local0)
Break
}
Else
{
Return (Ones)
}
Break
}
Multiply (Local0, 0x02, Local0)
If (LNotEqual (Local0, Zero))
{
Divide (0x0041CDB4, Local0, Local1, Local0)
Return (Local0)
}
Else
{
Return (Ones)
}
}
Else
{
Return (Ones)
}
}

当你复制这个方法到 SSDT-BATT.dsl 时候,编译时会遇到一个错误.(这里我已经将拆分需要的 B1B2,WE1B,WECB,RE1B,RECB 等方法放入 Scope(\_SB.PCI0.LPCB.EC0) 域里面)

49, 6084, Object does not exist (ECAV)

看看 ECAV 是哪行调用的.

1
2
3
Name (_T_0, Zero) // _T_x: Emitted by ASL Compiler
If (ECAV ())
{

这个是一个判断语句内嵌了一个方法的调用,初步判断 ECAV 就是一个方法,那么这个方法在哪里呢?回到我们的 DSDT.dsl

搜索 ECAV ,你会在 _SB.PCI0.LPCB.EC0 中发现 ECAV 这个方法,那么我们需要在 SSDT-BATT.dsl 中外部引入这个方法,来达到编译通过的目的.

只需要在 TACH 上方写入 External(\_SB.PCI0.LPCB.EC0.ECAV, MethodObj)

再次编译,已通过编译.

我们需要注意的是外部引入的路径与外部引入的类型.

路径在 MaciASL 中,只要你光标移动到这个方法上,程序的左下角会显示当前方法(或变量)所在的路径.

一般 Name ( ABCD , 0X10) 这类的变量引入我们使用 IntObj 类型引入.

而上述的 Method (ECAV, 0, NotSerialized) 这一类,当然是使用 MethodObj 类型引入了.

注意,复制方法过来的时候,需要注意该方法本来是在哪个域里面的,我们在 SSDT-BATT.dsl 也要将其放到哪个域里

例如:Method (_BIX, 0, NotSerialized)

这个方法是在 _SB.PCI0.BAT0 这个域里面,那么我们需要将_BIX 方法放在这个域里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
External(\_SB.PCI0, DeviceObj)
External(\_SB.PCI0.BAT0, DeviceObj)
Scope(\_SB.PCI0.BAT0)
{
Method (_BIX, 0, NotSerialized) // _BIX: Battery Information Extended
{
If (LNot (^^LPCB.EC0.BATP (Zero)))
{
Return (NBIX)
}
If (LEqual (^^LPCB.EC0.GBTT (Zero), 0xFF))
{
Return (NBIX)
}
_BIF ()
Store (DerefOf (Index (PBIF, Zero)), Index (BIXT, One))
Store (DerefOf (Index (PBIF, One)), Index (BIXT, 0x02))
Store (DerefOf (Index (PBIF, 0x02)), Index (BIXT, 0x03))
Store (DerefOf (Index (PBIF, 0x03)), Index (BIXT, 0x04))
Store (DerefOf (Index (PBIF, 0x04)), Index (BIXT, 0x05))
Store (DerefOf (Index (PBIF, 0x05)), Index (BIXT, 0x06))
Store (DerefOf (Index (PBIF, 0x06)), Index (BIXT, 0x07))
Store (DerefOf (Index (PBIF, 0x07)), Index (BIXT, 0x0E))
Store (DerefOf (Index (PBIF, 0x08)), Index (BIXT, 0x0F))
Store (DerefOf (Index (PBIF, 0x09)), Index (BIXT, 0x10))
Store (DerefOf (Index (PBIF, 0x0A)), Index (BIXT, 0x11))
Store (DerefOf (Index (PBIF, 0x0B)), Index (BIXT, 0x12))
Store (DerefOf (Index (PBIF, 0x0C)), Index (BIXT, 0x13))
If (LEqual (DerefOf (Index (BIXT, One)), One))
{
Store (Zero, Index (BIXT, One))
Store (DerefOf (Index (BIXT, 0x05)), Local0)
Multiply (DerefOf (Index (BIXT, 0x02)), Local0, Index (BIXT, 0x02))
Multiply (DerefOf (Index (BIXT, 0x03)), Local0, Index (BIXT, 0x03))
Multiply (DerefOf (Index (BIXT, 0x06)), Local0, Index (BIXT, 0x06))
Multiply (DerefOf (Index (BIXT, 0x07)), Local0, Index (BIXT, 0x07))
Multiply (DerefOf (Index (BIXT, 0x0E)), Local0, Index (BIXT, 0x0E))
Multiply (DerefOf (Index (BIXT, 0x0F)), Local0, Index (BIXT, 0x0F))
Divide (DerefOf (Index (BIXT, 0x02)), 0x03E8, Local0, Index (BIXT, 0x02))
Divide (DerefOf (Index (BIXT, 0x03)), 0x03E8, Local0, Index (BIXT, 0x03))
Divide (DerefOf (Index (BIXT, 0x06)), 0x03E8, Local0, Index (BIXT, 0x06))
Divide (DerefOf (Index (BIXT, 0x07)), 0x03E8, Local0, Index (BIXT, 0x07))
Divide (DerefOf (Index (BIXT, 0x0E)), 0x03E8, Local0, Index (BIXT, 0x0E))
Divide (DerefOf (Index (BIXT, 0x0F)), 0x03E8, Local0, Index (BIXT, 0x0F))
}
Store (B1B2(^^LPCB.EC0.B001,^^LPCB.EC0.B002), Index (BIXT, 0x08))
Store (0x0001869F, Index (BIXT, 0x09))
Return (BIXT)
}
}

复制了之后,我们还有非常多的错误需要处理

  • 135, 6085, Object not found or not accessible from scope (^^LPCB.EC0.BATP)

代码段: If (LNot (^^LPCB.EC0.BATP (Zero)))

^^LPCB.EC0.BATP 这个方法写法中存在 ^^这两个字符,意思上级的上级域里面的,类似于相对路径, LPCB.EC.BATP 方法,完整路径就是 _SB.PCI0.LPCB.EC0.BATP,因为这个方法不在我们的 SSDT 里面,故此我们需要外部引入他.在 External 的部分加入 External(\_SB.PCI0.LPCB.EC0.BATP, MethodObj)

但是上面的 ^^LPCB.EC0.BATP 这样的调用方法猜测应该是同一 SSDT 或是在 DSDT 中存在才能这样写,如果是外部引入后,我们需要将其改成绝对路径.

^^LPCB.EC0.BATP 替换成 \_SB.PCI0.LPCB.EC0.BATP.

  • 140, 6084, Object does not exist (NBIX)

代码段:Return (NBIX)

这个返回值应该是一个变量,我们看看 DSDT 下他是如何定义的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Name (NBIX, Package (0x14)
{
Zero,
Zero,
0xFFFFFFFF,
0xFFFFFFFF,
One,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
Zero,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
"",
"",
"",
""
})

Name (NBIX, Package (0x14) 它是一个对象集,对象集引入的类型应当是 PkgObj

在头部外部引入部分加入 External(\_SB.PCI0.BAT0.NBIX, PkgObj)

剩下错误用同类的方法修补

最终复制完整个 SSDT-BATT.dsl 是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
// battery status patched for Ausu VM510LI
DefinitionBlock ("", "SSDT", 2, "hack", "batt", 0)
{
External(\BSLF, IntObj)
External(\_SB.PCI0, DeviceObj)
External(\_SB.PCI0.BAT0, DeviceObj)
External(\_SB.PCI0.BAT0.NBIX, PkgObj)
External(\_SB.PCI0.BAT0.PBIF, PkgObj)
External(\_SB.PCI0.BAT0.BIXT, PkgObj)
External(\_SB.PCI0.BAT0._BIF, MethodObj)
External(\_SB.PCI0.LPCB, DeviceObj)
External(\_SB.PCI0.LPCB.EC0, DeviceObj)
External(\_SB.PCI0.LPCB.EC0.ECAV, MethodObj)
External(\_SB.PCI0.LPCB.EC0.BATP, MethodObj)
External(\_SB.PCI0.LPCB.EC0.GBTT, MethodObj)
External(\_SB.PCI0.LPCB.EC0.RDBL, IntObj)
External(\_SB.PCI0.LPCB.EC0.RDWD, IntObj)
External(\_SB.PCI0.LPCB.EC0.RDBT, IntObj)
External(\_SB.PCI0.LPCB.EC0.RCBT, IntObj)
External(\_SB.PCI0.LPCB.EC0.RDQK, IntObj)
External(\_SB.PCI0.LPCB.EC0.MUEC, MutexObj)
External(\_SB.PCI0.LPCB.EC0.PRTC, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.SBBY, IntObj)
External(\_SB.PCI0.LPCB.EC0.ADDR, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.CMDB, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.SWTC, MethodObj)
External(\_SB.PCI0.LPCB.EC0.BCNT, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.DAT0, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.PRT2, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.DAT1, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.ADD2, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.CMD2, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.BCN2, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.DA20, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.DA21, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.SSTS, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.SST2, FieldUnitObj)
External(\_SB.PCI0.LPCB.EC0.WRBL, IntObj)
External(\_SB.PCI0.LPCB.EC0.WRWD, IntObj)
External(\_SB.PCI0.LPCB.EC0.WRBT, IntObj)
External(\_SB.PCI0.LPCB.EC0.SDBT, IntObj)
External(\_SB.PCI0.LPCB.EC0.WRQK, IntObj)
Scope(\_SB.PCI0.LPCB.EC0)
{
// This is an override for battery methods that access EC fields
// larger than 8-bit.
OperationRegion (ECOR, EmbeddedControl, Zero, 0xFF)
Field (ECOR, ByteAcc, Lock, Preserve)
{
//Offset (0x04),
//CMD1, 8,
//...
Offset (0x93),
TA00,8,TA01,8,
TA20,8,TA21,8,
//...
Offset (0xBE),
, 16, //B0TM, 16
, 16, //B0C1, 16,
, 16, //B0C2, 16,
B001,8,B002,8,
//...
Offset (0xF4),
B0S0,8,B0S1,8,
//Offset (0xF8),
//Offset (0xFA),
Offset (0xFC),
B1S0,8,B1S1,8
}
OperationRegion (SMBX, EmbeddedControl, 0x18, 0x28)
Field (SMBX, ByteAcc, NoLock, Preserve)
{
Offset (0x04),
DT01,8,DT02,8
}
// TACH methods are renamed in native DSDT, so calls land here...
Method (TACH, 1, Serialized)
{
Name (_T_0, Zero) // _T_x: Emitted by ASL Compiler
If (ECAV ())
{
While (One)
{
Store (Arg0, _T_0)
If (LEqual (_T_0, Zero))
{
Store (B1B2(TA00,TA01), Local0)
Break
}
ElseIf (LEqual (_T_0, One))
{
Store (B1B2(TA20,TA21), Local0)
Break
}
Else
{
Return (Ones)
}
Break
}
Multiply (Local0, 0x02, Local0)
If (LNotEqual (Local0, Zero))
{
Divide (0x0041CDB4, Local0, Local1, Local0)
Store(Local1, Local1) // Fix Warnings
Return (Local0)
}
Else
{
Return (Ones)
}
}
Else
{
Return (Ones)
}
}
// RE1B,RECB,WE1B,WECB Fix 64 128 256 Method
Method (RE1B, 1, NotSerialized)
{
OperationRegion(ERAM, EmbeddedControl, Arg0, 1)
Field(ERAM, ByteAcc, NoLock, Preserve) { BYTE, 8 }
Return(BYTE)
}
Method (RECB, 2, Serialized)
{
ShiftRight(Arg1, 3, Arg1)
Name(TEMP, Buffer(Arg1) { })
Add(Arg0, Arg1, Arg1)
Store(0, Local0)
While (LLess(Arg0, Arg1))
{
Store(RE1B(Arg0), Index(TEMP, Local0))
Increment(Arg0)
Increment(Local0)
}
Return(TEMP)
}
Method (WE1B, 2, NotSerialized)
{
OperationRegion(ERAM, EmbeddedControl, Arg0, 1)
Field(ERAM, ByteAcc, NoLock, Preserve) { BYTE, 8 }
Store(Arg1, BYTE)
}
Method (WECB, 3, Serialized)
{
ShiftRight(Arg1, 3, Arg1)
Name(TEMP, Buffer(Arg1) { })
Store(Arg2, TEMP)
Add(Arg0, Arg1, Arg1)
Store(0, Local0)
While (LLess(Arg0, Arg1))
{
WE1B(Arg0, DerefOf(Index(TEMP, Local0)))
Increment(Arg0)
Increment(Local0)
}
}
Method (BIFA, 0, NotSerialized)
{
If (ECAV ())
{
If (BSLF)
{
Store (B1B2(B1S0,B1S1), Local0)
}
Else
{
Store (B1B2(B0S0,B0S1), Local0)
}
}
Else
{
Store (Ones, Local0)
}
Return (Local0)
}
Method (SMBR, 3, Serialized)
{
Store (Package (0x03)
{
0x07,
Zero,
Zero
}, Local0)
If (LNot (ECAV ()))
{
Return (Local0)
}
If (LNotEqual (Arg0, RDBL))
{
If (LNotEqual (Arg0, RDWD))
{
If (LNotEqual (Arg0, RDBT))
{
If (LNotEqual (Arg0, RCBT))
{
If (LNotEqual (Arg0, RDQK))
{
Return (Local0)
}
}
}
}
}
Acquire (MUEC, 0xFFFF)
Store (PRTC, Local1)
Store (Zero, Local2)
While (LNotEqual (Local1, Zero))
{
Stall (0x0A)
Increment (Local2)
If (LGreater (Local2, 0x03E8))
{
Store (SBBY, Index (Local0, Zero))
Store (Zero, Local1)
}
Else
{
Store (PRTC, Local1)
}
}
If (LLessEqual (Local2, 0x03E8))
{
ShiftLeft (Arg1, One, Local3)
Or (Local3, One, Local3)
Store (Local3, ADDR)
If (LNotEqual (Arg0, RDQK))
{
If (LNotEqual (Arg0, RCBT))
{
Store (Arg2, CMDB)
}
}
WECB(0x1c,256,Zero)
Store (Arg0, PRTC)
Store (SWTC (Arg0), Index (Local0, Zero))
If (LEqual (DerefOf (Index (Local0, Zero)), Zero))
{
If (LEqual (Arg0, RDBL))
{
Store (BCNT, Index (Local0, One))
Store (RECB(0x1c,256), Index (Local0, 0x02))
}
If (LEqual (Arg0, RDWD))
{
Store (0x02, Index (Local0, One))
Store (B1B2(DT01,DT02), Index (Local0, 0x02))
}
If (LEqual (Arg0, RDBT))
{
Store (One, Index (Local0, One))
Store (DAT0, Index (Local0, 0x02))
}
If (LEqual (Arg0, RCBT))
{
Store (One, Index (Local0, One))
Store (DAT0, Index (Local0, 0x02))
}
}
}
Release (MUEC)
Return (Local0)
}
Method (ECSB, 7, NotSerialized)
{
Store (Package (0x05)
{
0x11,
Zero,
Zero,
Zero,
Buffer (0x20) {}
}, Local1)
If (LGreater (Arg0, One))
{
Return (Local1)
}
If (ECAV ())
{
Acquire (MUEC, 0xFFFF)
If (LEqual (Arg0, Zero))
{
Store (PRTC, Local0)
}
Else
{
Store (PRT2, Local0)
}
Store (Zero, Local2)
While (LNotEqual (Local0, Zero))
{
Stall (0x0A)
Increment (Local2)
If (LGreater (Local2, 0x03E8))
{
Store (SBBY, Index (Local1, Zero))
Store (Zero, Local0)
}
ElseIf (LEqual (Arg0, Zero))
{
Store (PRTC, Local0)
}
Else
{
Store (PRT2, Local0)
}
}
If (LLessEqual (Local2, 0x03E8))
{
If (LEqual (Arg0, Zero))
{
Store (Arg2, ADDR)
Store (Arg3, CMDB)
If (LOr (LEqual (Arg1, 0x0A), LEqual (Arg1, 0x0B)))
{
Store (DerefOf (Index (Arg6, Zero)), BCNT)
WECB(0x1c,256,DerefOf (Index (Arg6, One)))
}
Else
{
Store (Arg4, DAT0)
Store (Arg5, DAT1)
}
Store (Arg1, PRTC)
}
Else
{
Store (Arg2, ADD2)
Store (Arg3, CMD2)
If (LOr (LEqual (Arg1, 0x0A), LEqual (Arg1, 0x0B)))
{
Store (DerefOf (Index (Arg6, Zero)), BCN2)
WECB(0x44,256,DerefOf (Index (Arg6, One)))
}
Else
{
Store (Arg4, DA20)
Store (Arg5, DA21)
}
Store (Arg1, PRT2)
}
Store (0x7F, Local0)
If (LEqual (Arg0, Zero))
{
While (PRTC)
{
Sleep (One)
Decrement (Local0)
}
}
Else
{
While (PRT2)
{
Sleep (One)
Decrement (Local0)
}
}
If (Local0)
{
If (LEqual (Arg0, Zero))
{
Store (SSTS, Local0)
Store (DAT0, Index (Local1, One))
Store (DAT1, Index (Local1, 0x02))
Store (BCNT, Index (Local1, 0x03))
Store (RECB(0x1c,256), Index (Local1, 0x04))
}
Else
{
Store (SST2, Local0)
Store (DA20, Index (Local1, One))
Store (DA21, Index (Local1, 0x02))
Store (BCN2, Index (Local1, 0x03))
Store (RECB(0x44,256), Index (Local1, 0x04))
}
And (Local0, 0x1F, Local0)
If (Local0)
{
Add (Local0, 0x10, Local0)
}
Store (Local0, Index (Local1, Zero))
}
Else
{
Store (0x10, Index (Local1, Zero))
}
}
Release (MUEC)
}
Return (Local1)
}
Method (SMBW, 5, Serialized)
{
Store (Package (0x01)
{
0x07
}, Local0)
If (LNot (ECAV ()))
{
Return (Local0)
}
If (LNotEqual (Arg0, WRBL))
{
If (LNotEqual (Arg0, WRWD))
{
If (LNotEqual (Arg0, WRBT))
{
If (LNotEqual (Arg0, SDBT))
{
If (LNotEqual (Arg0, WRQK))
{
Return (Local0)
}
}
}
}
}
Acquire (MUEC, 0xFFFF)
Store (PRTC, Local1)
Store (Zero, Local2)
While (LNotEqual (Local1, Zero))
{
Stall (0x0A)
Increment (Local2)
If (LGreater (Local2, 0x03E8))
{
Store (SBBY, Index (Local0, Zero))
Store (Zero, Local1)
}
Else
{
Store (PRTC, Local1)
}
}
If (LLessEqual (Local2, 0x03E8))
{
WECB(0x1c,256,Zero)
ShiftLeft (Arg1, One, Local3)
Store (Local3, ADDR)
If (LNotEqual (Arg0, WRQK))
{
If (LNotEqual (Arg0, SDBT))
{
Store (Arg2, CMDB)
}
}
If (LEqual (Arg0, WRBL))
{
Store (Arg3, BCNT)
WECB(0x1c,256,Arg4)
}
If (LEqual (Arg0, WRWD))
{
Store (ShiftRight(Arg4,8),DT02)
Store (Arg4,DT01)
}
If (LEqual (Arg0, WRBT))
{
Store (Arg4, DAT0)
}
If (LEqual (Arg0, SDBT))
{
Store (Arg4, DAT0)
}
Store (Arg0, PRTC)
Store (SWTC (Arg0), Index (Local0, Zero))
}
Release (MUEC)
Return (Local0)
}
}
Scope(\_SB.PCI0.BAT0)
{
Method (_BIX, 0, NotSerialized) // _BIX: Battery Information Extended
{
If (LNot (\_SB.PCI0.LPCB.EC0.BATP (Zero)))
{
Return (NBIX)
}
If (LEqual (\_SB.PCI0.LPCB.EC0.GBTT (Zero), 0xFF))
{
Return (NBIX)
}
_BIF ()
Store (DerefOf (Index (PBIF, Zero)), Index (BIXT, One))
Store (DerefOf (Index (PBIF, One)), Index (BIXT, 0x02))
Store (DerefOf (Index (PBIF, 0x02)), Index (BIXT, 0x03))
Store (DerefOf (Index (PBIF, 0x03)), Index (BIXT, 0x04))
Store (DerefOf (Index (PBIF, 0x04)), Index (BIXT, 0x05))
Store (DerefOf (Index (PBIF, 0x05)), Index (BIXT, 0x06))
Store (DerefOf (Index (PBIF, 0x06)), Index (BIXT, 0x07))
Store (DerefOf (Index (PBIF, 0x07)), Index (BIXT, 0x0E))
Store (DerefOf (Index (PBIF, 0x08)), Index (BIXT, 0x0F))
Store (DerefOf (Index (PBIF, 0x09)), Index (BIXT, 0x10))
Store (DerefOf (Index (PBIF, 0x0A)), Index (BIXT, 0x11))
Store (DerefOf (Index (PBIF, 0x0B)), Index (BIXT, 0x12))
Store (DerefOf (Index (PBIF, 0x0C)), Index (BIXT, 0x13))
If (LEqual (DerefOf (Index (BIXT, One)), One))
{
Store (Zero, Index (BIXT, One))
Store (DerefOf (Index (BIXT, 0x05)), Local0)
Multiply (DerefOf (Index (BIXT, 0x02)), Local0, Index (BIXT, 0x02))
Multiply (DerefOf (Index (BIXT, 0x03)), Local0, Index (BIXT, 0x03))
Multiply (DerefOf (Index (BIXT, 0x06)), Local0, Index (BIXT, 0x06))
Multiply (DerefOf (Index (BIXT, 0x07)), Local0, Index (BIXT, 0x07))
Multiply (DerefOf (Index (BIXT, 0x0E)), Local0, Index (BIXT, 0x0E))
Multiply (DerefOf (Index (BIXT, 0x0F)), Local0, Index (BIXT, 0x0F))
Divide (DerefOf (Index (BIXT, 0x02)), 0x03E8, Local0, Index (BIXT, 0x02))
Divide (DerefOf (Index (BIXT, 0x03)), 0x03E8, Local0, Index (BIXT, 0x03))
Divide (DerefOf (Index (BIXT, 0x06)), 0x03E8, Local0, Index (BIXT, 0x06))
Divide (DerefOf (Index (BIXT, 0x07)), 0x03E8, Local0, Index (BIXT, 0x07))
Divide (DerefOf (Index (BIXT, 0x0E)), 0x03E8, Local0, Index (BIXT, 0x0E))
Divide (DerefOf (Index (BIXT, 0x0F)), 0x03E8, Local0, Index (BIXT, 0x0F))
}
Store (B1B2(^^LPCB.EC0.B001,^^LPCB.EC0.B002), Index (BIXT, 0x08))
Store (0x0001869F, Index (BIXT, 0x09))
Return (BIXT)
}
}
Method (B1B2, 2, NotSerialized)
{
ShiftLeft (Arg1, 8, Local0)
Or (Arg0, Local0, Local0)
Return (Local0)
}
}

好了,以上 SSDT 编译已经通过,那么我们进行最后一步,将上述复制过来的 Method 一一的重命名,使得原有的 Method 失效,这样我们注入的 SSDT 的新 Method 才能生效.

我们需要重命名的 Method 有

1
2
3
4
5
6
- Method (TACH, 1, Serialized)
- Method (_BIX, 0, NotSerialized)
- Method (BIFA, 0, NotSerialized)
- Method (SMBR, 3, Serialized)
- Method (ECSB, 7, NotSerialized)
- Method (SMBW, 5, Serialized)

在 config.plist 中编写补丁 ACPI > DSDT > Patches

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# TACH
Comment: change Method(TACH,1,N) to XACH, optionally pair with SSDT-BATT-VM510LI.aml
Find:14 40 06 54 41 43 48 09
Replace:14 40 06 58 41 43 48 09
# _BIX
Comment: change Method(_BIX,0,N) to XBIX, optionally pair with SSDT-BATT-VM510LI.aml
Find:14 49 26 5F 42 49 58 00
Replace:14 49 26 58 42 49 58 00
# BIFA
Comment: change Method(BIFA,0,N) to XIFA, optionally pair with SSDT-BATT-VM510LI.aml
Find:14 27 42 49 46 41 00
Replace:14 27 58 49 46 41 00
# SMBR
Comment: change Method(SMBR,3,N) to XMBR, optionally pair with SSDT-BATT-VM510LI.aml
Find:14 4B 13 53 4D 42 52 0B
Replace:14 4B 13 58 4D 42 52 0B
# ECSB
Comment: change Method(ECSB,7,N) to XCSB, optionally pair with SSDT-BATT-VM510LI.aml
Find:14 4F 1A 45 43 53 42 07
Replace:14 4F 1A 58 43 53 42 07
# SMBW
Comment: change Method(SMBW,5,N) to XMBW, optionally pair with SSDT-BATT-VM510LI.aml
Find:14 45 10 53 4D 42 57 0D
Replace:14 45 10 58 4D 42 57 0D

提示,不放心自己找的代码,可以拿个从 Ubuntu 提取未修改过的 DSDT.aml, 用 Hex Fiend 来替换下,再反编译看看,反编译后无需修改,直接搜索我们修改的变量名,看是否已经改成 X 开头.

注入显卡 ID

  1. 将 GFX 更名为 IGPU
  2. 放入 SSDT-IGPU.aml 至 Clover > ACPI > patched

屏蔽独立显卡

首先复习一下屏蔽独立显卡的方法:

引用自:屏蔽双显卡笔记本的独显

我们的目标非常简单。通常,在SSDT里,笔记本给我们提供了一个 _OFF 方法,我们可以通过调用这个方法,来切段独显的供电。最最简单的方法,就是在 相应的_INI方法里,调用_OFF方法。需要注意,这个_OFF方法,还可能会在DSDT里,或者可能会有不同的名字(如:GPOF、OPOF、_PS3,等等)。

某些_OFF方法的实现,会由于它包含了对EC(Embedded Controller)的依赖,而使得它不能在_INI方法里被调用。对于这样的情况,整个_OFF方法或者它的一部分代码,需要被移动到_REG方法里,以延迟执行(当_REG方法接收的参数Arg0 == 3 且 Arg1==1时,它会在_INI方法之后被执行)(详见ACPI规范)。对于一些情况,在_REG方法里调用_OFF的时机太迟了,从而导致要么屏蔽独显失败,要么系统五国。对于这样的情况,修改_OFF方法,移除它对于EC的依赖,将变得必要。之后,我们就可以在_INI里调用它(移除了对EC的依赖的_OFF)。同时,在_OFF里移除的代码,需要加到_REG里去。这样,虽然EC关联的代码在后(_INI后)执行(因为代码加到了_REG里,所以后执行),但却能达到更好的效果。贴子提供的例子,就是这种情况。

我是根据上面的方法来屏蔽我华硕 VM510LI 的 AMD 显卡的,很不幸的是,我的 _OFF 也包含了对 EC 的依赖,故此我在 hotpatch 当中,我并不能单纯的使用 SSDT-Disable_DGPU.aml 来禁用独立显卡.

那么该如何使用 hotpatch 方法来屏蔽我们的独立显卡呢?

于是我扒到了这个帖子:https://www.tonymacx86.com/threads/guide-disabling-discrete-graphics-in-dual-gpu-laptops.163772/page-55#post-1232166

帖子提供了一个例子.https://raw.githubusercontent.com/RehabMan/Lenovo-Z50/master/hotpatch/SSDT-NVDA.dsl

仔细查看例子中的代码会发现和上述屏蔽独立显卡不同的地方.

1
2
3
4
5
6
7
8
9
10
11
External(_SB.PCI0, DeviceObj)
Scope(_SB.PCI0)
{
Device(RMD2)
{
Name(_HID, "RMD20000")
Method(_INI)
{
If (CondRefOf(\_SB.PCI0.RP05.PEGP._OFF)) { \_SB.PCI0.RP05.PEGP._OFF() }
}
}

看上述的代码段,他会重新定义一个设备,这个设备叫 RMD20000,然后这个设备有一个 INI 方法,里面就是调用 _OFF, 关闭显卡的系统方法.

那么,其实关闭显卡并不一定要在显卡启动的 INI 方法中运行 OFF 切断显卡电源,我估计是 PCI0 的设备是统一时间初始化的,其他设备的 INI 也一样可以将显卡电源关闭.

而我 _OFF 方法中存在一个 SGOF 的方法,这个方法里面调用了 EC0 的代码,那么需要将他去掉,故此我需要用重命名代替的方法,将原来的 SGOF 方法用重命名方法禁用掉,然后再用 SSDT 注入我们修改的 SGOF 方法, 由于 SGOF 方法中的 EC 相关的代码需要在 _REG 中执行,故此我们也需要将原有的 _REG 方法重命名禁用掉,也在 SSDT 中写入我们的 _REG 方法.

依然老方法,我不会写 SSDT, 但是我会抄.用上述的 SSDT-NVDA.dsl 来改成我们自己的 禁用显卡 SSDT.

最终写出来的 SSDT 应该就是这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// SSDT-DAMD: Disable AMD
DefinitionBlock ("", "SSDT", 2, "hack", "DAMD", 0)
{
External(_SB.PCI0, DeviceObj)
External(\_SB.PCI0.RP05.PEGP._OFF,MethodObj)
External (HLRS, FieldUnitObj)
External (PWEN, FieldUnitObj)
Scope(_SB.PCI0)
{
Device(RMD2)
{
Name(_HID, "RMD20000")
Method(_INI)
{
If (CondRefOf(\_SB.PCI0.RP05.PEGP._OFF)) { \_SB.PCI0.RP05.PEGP._OFF() }
}
}
External(RP05, DeviceObj)
Scope(RP05)
{
External(PEGP, DeviceObj)
Scope(PEGP)
{
External(CCHK, MethodObj)
External(ONOF, IntObj)
External(LCTL,IntObj)
External(ELCT, IntObj)
External(SVID, IntObj)
External(HVID, IntObj)
External(SDID, IntObj)
External(HDID, IntObj)
External(LNKD, IntObj)
External(LNKD, IntObj)
External(LNKS, IntObj)
External(SGPO, MethodObj)
Method (SGOF, 0, Serialized)
{
If (LEqual (CCHK (Zero), Zero))
{
Return (Zero)
}
Store (Zero, ONOF)
//Store (\_SB.PCI0.LPCB.EC0.RRAM (0x0521), Local0)
//And (Local0, 0xCF, Local0)
//\_SB.PCI0.LPCB.EC0.WRAM (0x0521, Local0)
//\_SB.PCI0.LPCB.EC0.WRAM (0x0520, 0x91)
//\_SB.PCI0.LPCB.EC0.WRAM (0x03A4, Zero)
//\_SB.PCI0.LPCB.EC0.WRAM (0x03A5, Zero)
Store (LCTL, ELCT)
Store (SVID, HVID)
Store (SDID, HDID)
Store (One, LNKD)
While (LNotEqual (LNKS, Zero))
{
Sleep (One)
}
SGPO (HLRS, One)
SGPO (PWEN, Zero)
Return (Zero)
}
}
}
External(LPCB, DeviceObj)
Scope(LPCB)
{
External(EC0, DeviceObj)
Scope(EC0)
{
External(ECFL, IntObj)
External(RRAM, MethodObj)
External(WRAM, MethodObj)
Method (_REG, 2, NotSerialized) // _REG: Region Availability
{
If (LEqual (Arg0, 0x03))
{
Store (Arg1, ECFL)
}
If (LAnd(LEqual(Arg0,3),LEqual(Arg1,1)))
{
Store (\_SB.PCI0.LPCB.EC0.RRAM (0x0521), Local0)
And (Local0, 0xCF, Local0)
\_SB.PCI0.LPCB.EC0.WRAM (0x0521, Local0)
\_SB.PCI0.LPCB.EC0.WRAM (0x0520, 0x91)
\_SB.PCI0.LPCB.EC0.WRAM (0x03A4, Zero)
\_SB.PCI0.LPCB.EC0.WRAM (0x03A5, Zero)
}
}
}
}
}
}

然后我们需要重命名的就是两个方法,一个 SGOF, 和 _REG.

在 Clover > ACPI > DSDT > Patches 添加如下

1
2
3
4
5
6
7
8
9
# SGOF
Comment:change Method(SGOF,0,Serialized) to XGOF, optionally pair with SSDT-DAMD-VM510LI.aml
Find:53474F46 08
Replace:58474F46 08
# _REG
Comment:change Method(_REG,0,Serialized) to XREG, optionally pair with SSDT-DAMD-VM510LI.aml
Find:5F 52 45 47 02
Replace:58 52 45 47 02

注入声卡 ID

这个非常简单,只需要放入 SSDT-HDEF.aml 到 Clover > ACPI > patched

但是需要注意自己的 DSDT 有没有定义 HDEF 设备,如果没有,打开 SSDT-HDEF.dsl 将以下注释去掉.

1
2
3
4
5
6
7
8
// Note: If your ACPI set (DSDT+SSDTs) does not define HDEF (or AZAL)
// add this Device definition (by uncommenting it)
//
//Device(_SB.PCI0.HDEF)
//{
// Name(_ADR, 0x001b0000)
// Name(_PRW, Package() { 0x0d, 0x05 }) // may need tweaking (or not needed)
//}

另外注意修改自己需要注入的 ID, 我的是4

无需修改 ID,我们只需在 SSDT-Config.aml 的 Name(AUDL, Ones) 定义我们注入的 ID 即可。(关于SSDT-Config.aml 更多的参数细节,后续会提到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Method(_SB.PCI0.HDEF._DSM, 4)
{
If (CondRefOf(\RMCF.AUDL)) { If (Ones == \RMCF.AUDL) { Return(0) } }
If (!Arg2) { Return (Buffer() { 0x03 } ) }
Local0 = Package()
{
"layout-id", Buffer(4) { 4, 0, 0, 0 },
"hda-gfx", Buffer() { "onboard-1" },
"PinConfigurations", Buffer() { },
}
If (CondRefOf(\RMCF.AUDL))
{
CreateDWordField(DerefOf(Local0[1]), 0, AUDL)
AUDL = \RMCF.AUDL
}
Return(Local0)
}
}

但是不清楚为什么上面的注入不正常,我用回 Clover 的 Devices 里面的 Inject 设置为4 RestHDA 设为 yes

终于知道为何放入上面 SSDT 后声卡依然无声,是因为 SSDT-config.dsl 问题。在下面的章节补上对这个非常非常重要的 SSDT-Config.aml 的讲解

上面的 HDEF 只是给声卡出声而已,还需要放入 SSDT-HDAU.aml 以给 HDMI 出声。

最终 HDMI 声音和声卡声音都能正常出声。

SSDT-Config 说明

这个漏了说,而且这个是 Hotpatch 必用的 SSDT,因为里面包含了一些配置。

首先我们来看看 SSDT-Config 有些啥?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// configuration data for other SSDTs in this pack
DefinitionBlock("", "SSDT", 2, "hack", "RMCF", 0)
{
Device(RMCF)
{
Name(_ADR, 0) // do not remove
Method(HELP)
{
Store("TYPE indicates type of the computer. 0: desktop, 1: laptop", Debug)
Store("HIGH selects display type. 1: high resolution, 2: low resolution", Debug)
Store("DPTS for laptops only. 1: enables/disables DGPU in _WAK/_PTS", Debug)
Store("SHUT enables shutdown fix. 1: disables _PTS code when Arg0==5", Debug)
Store("AUDL indicates audio layout-id for patched AppleHDA. Ones: no injection", Debug)
Store("BKLT indicates the type of backlight control. 0: IntelBacklight, 1: AppleBacklight", Debug)
Store("LMAX indicates max for IGPU PWM backlight. Ones: Use default, other values must match framebuffer", Debug)
}
// TYPE: Indicates the type of computer... desktop or laptop
//
// 0: desktop
// 0: 台式机
// 1: laptop
// 1: 笔记本
Name(TYPE, 1)
// HIGH: High resolution/low resolution selection. Affects IGPU injection.
// HIGH: 高分辨率/低分辨率选择。 影响IGPU注入。
// For 1600x900+ on Sandy/Ivy, use 1
// 对于1600x900 +在Sandy / Ivy,请使用1
// For UHD/QHD+ on Haswell/Broadwell, use 1
// 对于Haswell / Broadwell的UHD / QHD +,请使用1
// Others (low resolution), use 0
// 其他(低分辨率),使用0
Name(HIGH, 0)
// DPTS: For laptops only: set to 1 if you want to enable and
// DPTS:仅适用于笔记本电脑:如果要启用和,请设置为1
// disable the DGPU _PTS and _WAK.
// 禁用DGPU _PTS和_WAK。
// 0: does not manipulate the DGPU in _WAK and _PTS
// 0:不操作_WAK和_PTS中的DGPU
// 1: disables the DGPU in _WAK and enables it in _PTS
// 1:禁用_WAK中的DGPU,并在_PTS中启用它
Name(DPTS, 0)
// SHUT: Shutdown fix, disable _PTS code when Arg0==5 (shutdown)
// SHUT:关闭修复,当Arg0 == 5(关闭)时禁用_PTS代码,
// 0: does not affect _PTS behavior during shutdown
// 0:在关闭期间不影响_PTS行为
// 1: disables _PTS code during shutdown
// 1:在关闭期间禁用_PTS代码
Name(SHUT, 0)
// AUDL: Audio Layout
// AUDL:音频布局
// The value here will be used to inject layout-id for HDEF and HDAU
// 这里的值将用于注入HDEF和HDAU的layout-id
// If set to Ones, no audio injection will be done.
// 如果设置为Ones,则不会进行音频插入。
Name(AUDL, Ones)
// BKLT: Backlight control type
// BKLT:背光控制类型
// 0: Using IntelBacklight.kext
// 0:使用IntelBacklight.kext
// 1: Using AppleBacklight.kext + AppleBacklightInjector.kext
// 1:使用AppleBacklight.kext + AppleBacklightInjector.kext
Name(BKLT, 0)
// LMAX: Backlight PWM MAX. Must match framebuffer in use.
// LMAX:背光PWM最大值。 必须匹配使用中的帧缓冲区。
// Ones: Default will be used (0x710 for Ivy/Sandy, 0xad9 for Haswell/Broadwell)
// Ones:将使用默认(Ivy / Sandy 的默认值是 0x710,Haswell / Broadwell 的默认值是 0xad9)
// Other values: must match framebuffer
// 其他值:必须与framebuffer相匹配
Name(LMAX, Ones)
}
}
//EOF

上面 RM 都注释了,我以 Google 翻译过来,方便理解。

  • 第一个值,TYPE 从 SSDT-IGPU 调用,影响显卡 ID 的注入。查看了下代码,好像是区分是否注入高分屏 ID 有关。
  • 第二个值,HIGH 从 SSDT-IGPU 调用,影响显卡 ID 的注入。当 TYPE 设置成笔记本才生效。
  • 第三个值,DPTS 从 SSDT-PTSWAK 调用,这个 SSDT 是重写了睡眠和唤醒的方法,加入了一些我们补丁需要用到的代码,这里设置 1 则会在睡眠的时候开启显卡,唤醒后关闭显卡。
  • 第四个值,SHUT 从 SSDT-PTSWAK 调用,这个 SSDT 功能同上,这里设置 1 则会在关机的时候禁用 _PTS,和 Shutdown fix 同理。
  • 第五个值,AUDL 从 SSDT-HDAU 和 SSDT-HDEF 调用,这两个 SSDT 前者负责注入声卡 ID,后者则负责注入 HDMI 声卡 ID,而这个值就是控制两个 SSDT 注入声卡的行为,如果写 Ones 则什么都不注入,如果写 3,则声卡的 layout-id 则为 3。
  • 第六个值,BKLT 从 SSDT-PNLF 调用,这个 SSDT 看名字大家应该都很熟悉,就是加入 PNLF 设备,用来启用我们的笔记本亮度调节驱动的,而这个值是控制亮度调节的驱动是用哪一种,0 是使用 IntelBacklight.kext,1 是使用 AppleBacklight.kext + AppleBacklightInjector.kext。
  • 第七个值,LMAX 从 SSDT-PNLF 调用,这个 SSDT 同上,这个值是设置背光的 PWM 最大值,Ones 是使用默认值,Ivy / Sandy 的默认值是 0x710,Haswell / Broadwell 的默认值是 0xad9,你也可以设置其他值,但是这个值必须与 framebuffer 相匹配。

变频\声卡 CC 配置\USB 遮蔽器

这些在黑苹果各大论坛都有介绍,我就不再论述,只需放入 SSDT 即可.

测试

全部修改后,到最重要一步,测试修改的 hotpatch 是否可用.

还挺幸运的,一直写下来的笔记,就 FN 因为完全无脑抄袭没有注意自己的 Q0E 和 Q0F 是在 _SB.PCI0.LPCB.EC0 设备里面,我没把他们放到这个 Scope (域) 里面,故此不生效.

最后附上 VM510LI-Hotpatch 的 EFI

VM510LI-Hotpatch.zip

参考文章