闲淡酱杂货店

修改DSDT实现电量显示

前言

玩黑苹果除了要解决显卡、声卡的驱动问题,对于笔记本来说电量显示也是很重要的东西。部分笔记本直接放入ACPIBatteryManager.kext就可以正常显示电量了,大部分笔记本还需要修改DSDT才可以正常驱动。

屏幕快照 2019-01-19 下午4.49.56.png
▲ 没有正确驱动电池

一开始觉得修改DSDT是一件很难的事情,甚至想放弃……经过四处爬帖摸索,在这里分享一下我的经验,我觉得小白也许都可以看得懂(因为我也是小白

准备所需的东西

MaciASL(用于编辑提取的DSDT文件)
ACPIBatteryManager.kext (放入Clover用来驱动电池)
iASL(非必须,看情况来使用)

前期工作

在Clover的启动界面,按F4提取你机子的DSDT,确保之前没有放过DSDT文件在里面,要提取纯净的DSDT。

提取好后在clover下找到ACPI目录的origin目录,正常情况下会有这么多的文件。

屏幕快照 2019-01-19 下午5.00.29.png

很多人说提取到DSDT后不能直接修改,要进行反编译,不然后面修改会报错。经过我的实测,如果直接打开然后编译没有报错的情况,直接修改是没有影响的。不排除有的机子提取的会有错误,这时候可以尝试下用iASL反编译一下。如果还是有错,请自行排错。

屏幕快照 2019-01-19 下午5.11.35.png

用MaciASL打开DSDT.aml,如果0 Errors则说明无报错,可以开始修改,其他的不用管。

修改阶段

https://github.com/RehabMan/Laptop-DSDT-Patch/tree/master/battery

可以先去这个地方,看看有没有自己机型的DSDT。如果很幸运的话,恰巧你只是为了解决电量显示问题,可以直接拿来用了。如果没有,也可以尝试下型号相近的机子的DSDT。

把里面的代码全部复制,粘贴到如图第2步的位置,然后单击Apply即可。

QQ20190119-173116.png

如果没有的话,就可以开始自己做DSDT了。以下的步骤以我的机子为例子来操作。

电量显示其实只要把大于8位的字段进行拆分即可,只要记住这点就好了。基本上没有烧脑的操作,都是繁琐的重复步骤。下面所用到的所有参数,并不是全部机子都一样的,请根据自己的实际情况进行替换。

打开搜索,搜索EmbeddedControl

QQ20190119-173630.png

然后搜索 Field (ECOR ,不同机型的参数都不一样。

QQ20190119-173737.png

可以看到搜索框后面反馈的有7个,我们就在这7个里找到大于8位的字段出来,新建一个记事本来记录。确保不要有遗漏,可以按Done前面的左右按钮来跳到每一个 Field (ECOR 里。

QQ20190119-174148.png
这就是一个大于8位的字段,是一个16位的字段。

QQ20190119-174312.png
这里就是两个32位的字段。每找到一个,就把它记录到记事本里。

记录好后如下
屏幕快照 2019-01-19 下午5.46.23.png

只有16和32位的字段才需要拆分,大于32位的不用拆,只需替换访问属性即可。当然,也并不是所有在 Field (ECOR 找出来的的16位和32位的字段都要拆分,只要拆分有访问到的字段就可以了,没有访问的字段不用管。

如何查看字段是否有访问,可以利用搜索,然后把字段名复制进去。
QQ20190119-175051.png

这里我们用SBDC这个字段为例子,可以看到SBDC有4处,意味着这个属性有被访问到。相反,如果字段只找到1处的,就说明这个字段只是被定义了,并没有被访问到。通过这个方法把记录的字段逐个搜索一下,只保留搜索到大于1处的字段。

拆分之前需要先用两段代码,因为拆分后的字段要存储在B1B2和B1B4的变量里。

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;

into method label B1B4 remove_entry;
into definitionblock code_regex . insert
begin
Method (B1B4, 4, NotSerialized)\n
{\n
    Store(Arg3, Local0)\n
    Or(Arg2, ShiftLeft(Local0, 8), Local0)\n
    Or(Arg1, ShiftLeft(Local0, 8), Local0)\n
    Or(Arg0, ShiftLeft(Local0, 8), Local0)\n
    Return(Local0)\n
}\n
end;

打开Patch,复制上面的代码,然后Apply即可。
屏幕快照 2019-01-19 下午5.59.16.png

现在可以开始拆分了。我们用HWAK这个字段举个例子,这是个16位的字段,我们可以拆分成两个8位的字段,可以命名为 AK00AK01 。当然,命名可以随意,但是要确保这个命名没有出现在DSDT里。

//拆分前
HWAK, 16
// 拆分后
AK00, 8
AK01, 8

然后,我们需要写一个类似正则表达式的代码,来替换DSDT里的字段。

into device label EC code_regex HWAK,\s+16, replace_matched begin AK00,8,AK01,8, end;

device label 后面的EC就是第一步搜索EmbeddedControl,在EmbeddedControl上方的Device,不要和Filed里的参数搞混了。

然后把刚才写好的代码复制到Patch里,注意一定要以下两个地方都显示出东西才算写好了。

QQ20190119-181117.png

再来拆分个32位的字段,跟16位一样,32位就拆分成4个8位的字段。以SBCH为例,可以拆分成CH00,CH01,CH02,CH03。

//拆分前
SBCH, 32
// 拆分后
CH00, 8
CH01, 8
CH02, 8
CH03, 8

同样来写一段代码来替换

into device label EC code_regex SBCH,\s+32 replace_matched begin CH00,8,CH01,8,CH02,8,CH03,8 end;

屏幕快照 2019-01-19 下午6.16.56.png

接着我们可以尝试编译下,点击Compile,这时会发现有报错

屏幕快照 2019-01-19 下午6.34.17.png

点击报错信息,看看错误的位置。其实是因为我们只替换了定义的字段名,并没有把其它访问这个字段的地方也一起修改,访问不到字段,固然会报错。

所以我们需要替换属性访问,把访问的HWAK也换成我们刚刚替换的AK00和AK01。打开搜索,选中刚刚报错的那一行代码,然后搜索框里输入method,我们要找到这段代码包含在哪个method里,用搜索框旁边的左右键,单击“<”,不然就会找到下面的method里去。

一般情况下直接用下面的代码就可以了。

into method label \_WAK code_regex \(HWAK, replaceall_matched begin (B1B2(AK00,AK01), end;

当然也有特殊情况,在我的这台机子上,就出现了\_SB.PCI0.LPC.EC.HWAK这样的调用。

into method label \_WAK code_regex \(\\\_SB.PCI0.LPC.EC.HWAK replaceall_matched begin \(\\_SB.B1B2(\\_SB.PCI0.LPC.EC.AK00, \\_SB.PCI0.LPC.EC.AK01) end;

屏幕快照 2019-01-19 下午6.49.52.png

同样的,在Patch里试试看是否正确替换。后面的报错以此类推的,找出来进行替换即可。32位的字段替换属性访问也类似的操作

into method label GBIF code_regex \(SBCH, replaceall_matched begin (B1B4(CH00,CH01,CH02,CH03), end;

16位和32位的字段处理完成后,就开始解决大于32位的字段了。以我机子的DSDT为例子,在这里大于32位的字段只找到两个128位的。

QQ20190119-190257.png

然后我们需要计算偏移量,偏移量的计算大概是这样的,第一个字段的偏移量从起始值开始。所以,SBMN和SBDN的偏移量就是0xA0。很幸运,我的这台机子不需要自己计算,直接改就可以了。

需要先Patch一段代码,如果要改的参数挨着左边的括号。例如SBMN这样,就是挨着左边的。

QQ20190119-191023.png

用下面的这一段,其中H_EC请改成自己的Device。

into method label RE1B parent_label H_EC remove_entry;
into method label RECB parent_label H_EC remove_entry;
into device label H_EC 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
// Arg0 - offset in bytes from zero-based EC\n
// Arg1 - size of buffer in bits\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;

挨着右边的,用下面的代码

into method label WE1B parent_label H_EC remove_entry;
into method label WECB parent_label H_EC remove_entry;
into device label H_EC 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
// Arg0 - offset in bytes from zero-based EC\n
// Arg1 - size of buffer in bits\n
// Arg2 - value to write\n
{\n
    ShiftRight(Add(Arg1,7), 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;

然后直接用下面的代码替换即可。

into method label GBIF code_regex \(SBMN, replaceall_matched begin (RECB(0xA0,128), end;

如果是56或者64位什么的,就把128改了就可以了。如果需要计算偏移量,逢8进1,从起始值往下算即可。

Offset (0x04),
                        CMCM,   8, //0x04
                        CMD1,   8, //0x05(8位是1字节,所以加1)
                        CMD2,   8, //0x06
                        CMD3,   8, //0x07
                        Offset (0x18), 这里空了一些,不用纠结,按原始DSDT给出的偏移量计算就好(会给开头的偏移量)
                        Offset (0x19),
                        SMST,   8, //0x19
                        MBMN,   80, //0x1A
                        MBPN,   96, //0x25 = 0x1A+A+1(0x1A是上一个的起始地址,A的得来:80除以8得10,也就是上一个占了10个字节,16进制表示就是A。 0x2A+A是它占到了哪个地址,它的下一个地址才是下一个开始,所以再加1。)
                        GPB1,   8, // 0x32 = 0x25 + C(96位占了12个字节)+1
                        GPB2,   8, //0x33
                        GPB3,   8, //0x34
                        GPB4,   8, //0x35

最后

把修改好的DSDT.aml保存好,放到clover的ACPI目录下的patched目录里,如果没有就自己手动创建一个,不出意外的话,重启电脑就可以看到电池图标了。

才疏学浅,如有错漏,欢迎指出。

参考文献

已经有 27 条评论啦

  1. 曾经我也想上黑苹果,结果嫌麻烦就没搞了

    季悠然 回复
    1. 一开始接触我也觉得很麻烦,最初连显卡都不会驱动,到处爬贴都看不懂……当时看到电池还要自己改DSDT顿时弃坑😂

      闲淡酱 回复
      1. 太厉害了,想试试,有没有远景的邀请码QAQ

        小新 回复
        1. 我有远景的账号,你需要的话我试试看可不可以邀请你。

          闲淡酱 回复
          1. 谢谢,具体怎么邀请啊,需要邀请码

            小新
          2. GG。。没达到邀请条件。你可以试试关注远景的微信公众号,不知道现在还有没有邀请码。

            闲淡酱
  2. 这个好

    evan 回复
    1. 就自己总结的一些经验,有什么问题都可以交流下。

      闲淡酱 回复
      1. 请问\_wak
        怎么找啊

        niko 回复
  3. 你好!不知道我这里有什么问题,编辑DSDT的时候和你有同样的字段以及错误,在修改HWAK为AK00和AK01后,同样编译出了一个问题,错误行:Local0 = \_SB.PCI0.LPC.EC.HWAK,那么我该如何修改此字段?根据文章的正则表达式,我无法修改此字段。。(╯°A°)╯︵○○○

    Sakitami 回复
    1. 你只是修改了定义的字段,需要把访问的字段也进行修改。
      into method label \_WAK code_regex \(\\\_SB.PCI0.LPC.EC.HWAK replaceall_matched begin \(\\_SB.B1B2(\\_SB.PCI0.LPC.EC.AK00, \\_SB.PCI0.LPC.EC.AK01) end;
      像这样修改,请注意下method label后面的参数,最后确定下是否正确替换即可。

      闲淡酱 回复
      1. 先谢谢楼主了,真的是一篇好文章,让我这种纯小白也看懂了(远景好多贴我看了都一脸懵)

        今天按照楼主的步骤来做还比较顺利,就是这两个SBDV打不了补丁请问是啥原因呀?

        ElseIf (SBDV)
        {
        Divide (0x00030D40, SBDV, Local2, Index (Arg1, 0x06))
        }
        我对比了下成功了的,代码确实有点小区别,下面是已经成功打上补丁的代码:
        Else
        {
        Store (B1B2(AQ00,AQ01), Local0)
        }

        再次感谢了

        byz 回复
  4. 请问\_wak这个是不是都是一样的呢 如果不是要怎么修改呀

    niko 回复
    1. 不是,具体看你报错的位置,找到包含它的method,文章中有讲到方法啊。

      闲淡酱 回复
      1. 哦我知道了

        niko 回复
  5. 高端操作!!!

    Rinvay 回复
    1. 远景大佬一抓一大把,谈不上高端😂

      闲淡酱 回复
      1. 那是那是,依稀让我想起了曾经我也搞

        Rinvay 回复
  6. 我的第一个搜索就出了两个,后面的什么Field (ECOR 根本找不到,我一脸懵逼

    limingmsn 回复
    1. 仔细看文章,Filed后面的参数,不同的机型都有可能会不一样的,根据实际的参数进行替换然后查找。

      闲淡酱 回复
  7. OperationRegion (ECMP, EmbeddedControl, Zero, 0xFF)
    Field (ECMP, ByteAcc, Lock, Preserve)
    {
    Offset (0x7B),
    DEVS, 8
    }

    OperationRegion (ECXP, EmbeddedControl, Zero, 0xFF)
    Field (ECXP, ByteAcc, Lock, Preserve)
    {
    XIF0, 16,
    XIF1, 16,
    XIF2, 16,
    XIF3, 16,
    XIF4, 16,
    XIF5, 16,
    XIF6, 16,
    XIF7, 16,
    XIF8, 16,
    XIF9, 64,
    XIFA, 64,
    XIFB, 64,
    XIFC, 64,
    XST0, 16,
    XST1, 16,
    XST2, 16,
    XST3, 16,
    XTP0, 16,
    XCIN, 1,
    , 1,
    XTIN, 1,
    Offset (0x3D),
    Offset (0x3E),
    XHPP, 7,
    Offset (0x3F),
    Offset (0x40),
    XSEC, 8,
    XLPT, 8,
    Offset (0x43),
    XBRL, 3,
    , 4,
    XBKL, 1,
    Offset (0x56),
    , 4,
    MDTP, 2,
    Offset (0x58),
    , 7,
    EIST, 1,
    Offset (0x5A),
    DEXS, 8,
    XSLT, 1,
    , 2,
    GC6I, 1,
    GPST, 1,
    Offset (0x5C),
    Offset (0x62),
    SLUX, 16,
    Offset (0x72),
    , 4,
    XS4F, 1,
    Offset (0x76),
    , 1,
    SLID, 1,
    , 1,
    S3WA, 1,
    , 1,
    HPEN, 1,
    Offset (0x77),
    Offset (0x78),
    HBTN, 8,
    BRTS, 8,
    W8BR, 8,
    , 1,
    TOPD, 1,
    WUSB, 1,
    FGPT, 1,
    WEBC, 1,
    BLTH, 1,
    DV3G, 1,
    WLAN, 1,
    OSDF, 8,
    Offset (0x8A),
    LDAT, 8,
    HDAT, 8,
    RFLG, 1,
    WFLG, 1,
    BFLG, 1,
    CFLG, 1,
    , 3,
    DRDY, 1,
    CMDL, 8,
    CMDH, 8,
    Offset (0x90),
    , 1,
    BPST, 1,
    BSOK, 1,
    Offset (0x9F),
    ACWT, 1,
    HTMF, 1,
    ACFL, 1,
    Offset (0xA0),
    Offset (0xC0),
    BIF0, 16,
    BIF1, 16,
    BIF2, 16,
    BIF3, 16,
    BIF4, 16,
    BIF5, 16,
    BIF6, 16,
    BIF7, 16,
    BIF8, 16,
    BIF9, 64,
    BIFA, 64,
    BIFB, 64,
    BIFC, 64,
    BST0, 16,
    BST1, 16,
    BST2, 16,
    BST3, 16,
    BTP0, 16,
    ACIN, 1,
    , 1,
    BTIN, 1,
    Offset (0xFD)
    }

    AE 回复
    1. 这个怎么拆???

      AE 回复
      1. 先把大于8的字段找出来,按文章一步步来。

        闲淡酱 回复
  8. dsdt所有字段都是8位的怎么处理?有点蒙……

    Resun 回复
    1. 都是8位的话就不用管

      闲淡酱 回复
  9. 这篇文章真的很棒,给作者点个赞,我按照作者大大的思路竟然真的改完了dsdt,真的很厉害,可就是不知道为什么,改完还是不能显示电量😂,如果作者大大知道的话,感激不尽!!

    小羊 回复
    1. 谢谢支持。不能显示的话,看看kext和dsdt哪里出问题了。

      闲淡酱 回复