How To Patch DSDT For Working Battery Status
How To Patch DSDT For Working Battery Status
Background
Because the battery hardware in PCs is not compatible with Apple SMbus hardware, we use
ACPI to access battery state when running OS X on laptops. Generally I recommend you use
ACPIBatteryManager.kext, available here: https://github.com/RehabMan/OS-X-ACPI-
Battery-Driver[1]
Later releases of AppleACPIPlatform are unable to correctly access fields within the EC
(embedded controller). This causes problems for ACPIBatteryManager as the various ACPI
methods for battery fail (_BIF, _STA, _BST, etc). Although it is possible to use an older version
of AppleACPIPlatform (from Snow Leopard), it is desirable to use the latest version of
AppleACPIPlatform because with computers that have Ivy Bridge CPUs it enables native
power management for those computers. To use the latest version, DSDT must be changed to
comply with the limitations of Apple's AppleACPIPlatform.
In particular, any fields in the EC larger than 8-bit, must be changed to be accessed 8-bits at
one time. This includes 16, 32, 64, and larger fields.
Existing Patches
First of all, it could be that there is patch already available for your laptop. See my patches at:
https://github.com/RehabMan/Laptop-DSDT-Patch[3]
In order to match your DSDT with a patch, it is often necessary to understand how the
patches are made in the first place, so you know what to look for in your DSDT and can match
what you see with the patches already available.
*** Note: Do not use DSDT Editor or any program other than MaciASL. I do not test my
patches with DSDT Editor. I test only with MaciASL.
In addition to the multi-byte EC fields, there are a few other DSDT issues that can affect
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 1/14
8/11/2015 [Guide] How to patch DSDT for working battery status
battery status. These particular problems are not specific to battery status, but they are
usually noticed for the first time when trying to implement battery status.
The battery code may depend on having a recognized version of Windows as the host OS. To
fix, apply "OS Check Fix" from the laptop DSDT patch repository. This will cause the DSDT to
take the same actions as it would when running "Windows 2006" You can change the patch to
effect different selections (eg. "Windows 2012".
Another common problem is the fact that OS X's ACPI implementation has difficulty with
Mutex objects declared with a non-zero SyncLevel (for more info read the ACPI spec). To fix,
apply "Fix Mutex with non-zero SyncLevel" from the laptop DSDT patch repository.
Skills Required
Also, it is a good idea to be familiar with ACPI. You can download the specification here:
https://www.acpica.org/[5]
It is not the purpose of this guide to teach you basic programming skills, regular expressions,
or the ACPI language.
I use a rather 'mechanical' process to patching DSDT for battery status. I simply look for the
parts that OS X finds offensive and mechanically convert it. I don't try too hard to determine
what sections of the code are actually going to execute, I just convert everything that I see.
To follow along, download the example DSDT from this post and follow along. This particular
example DSDT is for an HP Envy 14. The final, complete patch, is available from my patch
repo as "HP Envy 14."
First start by identifing the areas of the DSDT that are likely to need changes. Load the DSDT
into MaciASL and search for 'EmbeddedControl'. There can be several 'EmbeddedControl'
sections in a single DSDT, each with field declarations attached to it.
So, I always start out looking for 'embeddedcontrol' in order to find this declaration.
Code:
We know it is called ECF2, so now we want to search for 'Field (ECF2'. As you can see in the
example DSDT, there is only one Field definition referring to this region. Sometimes there are
many.
The Field definition describes a breakdown of that 255 byte EC region above. You can tell it is
related because the name ECF2 is referred to by the Field. Think of this as a structure (struct
for C programmers) into the EC.
The next step is to examine the items in the Field definition, looking for items which are larger
than 8-bit. For example, the first field declared is BDN0, 56:
Code:
It is a 56-bit field. Larger than 8-bit and if it is accessed in the DSDT, the code there will need
edits as will the definition of this field. In the example DSDT, if you search the rest of the
DSDT for "BDN0", you will find:
Code:
This is intended to store the value at BDN0 (in the EC) into BDN. When fields larger than 32-
bit are accessed, they are accessed as type Buffer. Fields 32-bit or under are accessed as
Integers. This is important to realize as you change the code. Buffers are a bit more work to
change. Also, realize this code is "reading" from the EC. Reads and writes must be handled
differently.
So, for this particular line of code, the goal is to read this 56-bit field (7 bytes) 8-bits at time
into a buffer, so the resulting buffer can be stored into BDN. We will get back to how this is
accomplished later, for now let's explore the rest of the fields in this EC.
Looking through the rest of the the EC, we look for all fields larger than 8-bit, and for each
one, search the rest of the DSDT to see if they are accessed. It is common that some fields are
not accessed and for those we don't have to do anything. So, the next field we see is BMN0:
Code:
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 3/14
8/11/2015 [Guide] How to patch DSDT for working battery status
BMN0, 32,
If we search the DSDT for 'BMN0' we find only this declaration, so it is not accessed. We can
ignore this one. Same with BMN4. BCT0, on the other hand, is 128 bit and is accessed, much
like BDN0:
Code:
BDN0, 56,
BCT0, 128,
BDN1, 56,
BCT1, 128,
...
BDC0, 16,
BDC1, 16,
BFC0, 16,
BFC1, 16,
...
BDV0, 16,
BDV1, 16,
...
BPR0, 16,
BPR1, 16,
BRC0, 16,
BRC1, 16,
BCC0, 16,
BCC1, 16,
CV01, 16,
CV02, 16,
CV03, 16,
CV04, 16,
CV11, 16,
CV12, 16,
CV13, 16,
CV14, 16,
...
BMD0, 16,
BMD1, 16,
BPV0, 16,
BPV1, 16,
BSN0, 16,
BSN1, 16,
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 4/14
8/11/2015 [Guide] How to patch DSDT for working battery status
BCV0, 16,
BCV1, 16,
CRG0, 16,
CRG1, 16,
BTY0, 32,
BTY1, 32,
...
CBT0, 16,
CBT1, 16,
As you can see, there are quite a few fields in this DSDT that need work, and all of various
sizes. 16-bit, 32-bit, 56-bit and 128-bit.
Fields that are 16-bit and 32-bit are the easiest to deal with, so let's start there. Let's take for
example, the first 16-bit field in the list above, BDC0. What we want to do is change this field
so it is broken into two peices (low-byte, high-byte). To do that we need to come up with a 4-
character name that does not conflict with any other names in the DSDT. It is often easy to
remove the first letter and use the following three.
Code:
// was: BDC0, 16
DC00, 8,
DC01, 8,
into device label H_EC code_regex BDC0,\s+16, replace_matched begin DC00,8,DC01,8, end;
That patch says, look at the code in a device with label H_EC, search for "BDC0,<spaces>16,"
and replace it with "DC00,8,DC01,8," This effectively breaks the field into two parts. If you
apply this patch, and attempt to compile the modified DSDT, you will get errors because the
code is still accessing BDC0. These errors actually help us identify what code needs to change:
Code:
As you can see this code is reading from BDC0, which is now in two parts. To make a patch
easier to construct, we use a utility method called B1B2, which we insert into DSDT with this
patch:
Code:
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 5/14
8/11/2015 [Guide] How to patch DSDT for working battery status
This method takes two arguments (low-byte, high-byte), and returns a 16-bit value from the
two 8-bit values.
A patch can be constructed to automate this, and as you will see patches for other 16-bit
fields follow this same pattern:
Code:
into method label GBTI code_regex \(BDC0, replaceall_matched begin (B1B2(DC00,DC01), end;
Such optimizations can only be made by using your brain, and generally it is not worth it. The
goal here is to come up with an automated method of fixing this code and not to attempt to
use our brain too much as we could introduce bugs into the code if we change it too
drastically. Also, this kind of code is rare (I have only seen it only in two DSDTs out of more
than 20 that I've written patches for).
Now that you understand how to deal with 16-bit registers, it is probably easiest to just
convert all of them. Here is the comprehensive patch for the 16-bit EC fields:
Code:
# 16-bit registers
into device label H_EC code_regex BDC0,\s+16 replace_matched begin DC00,8,DC01,8 end;
into device label H_EC code_regex BDC1,\s+16 replace_matched begin DC10,8,DC11,8 end;
into device label H_EC code_regex BFC0,\s+16 replace_matched begin FC00,8,FC01,8 end;
into device label H_EC code_regex BFC1,\s+16 replace_matched begin FC10,8,FC11,8 end;
into device label H_EC code_regex BDV0,\s+16 replace_matched begin DV00,8,DV01,8 end;
into device label H_EC code_regex BDV1,\s+16 replace_matched begin DV10,8,DV11,8 end;
into device label H_EC code_regex BPR0,\s+16 replace_matched begin PR00,8,PR01,8 end;
into device label H_EC code_regex BPR1,\s+16 replace_matched begin PR10,8,PR11,8 end;
into device label H_EC code_regex BRC0,\s+16 replace_matched begin RC00,8,RC01,8 end;
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 6/14
8/11/2015 [Guide] How to patch DSDT for working battery status
into device label H_EC code_regex BRC1,\s+16 replace_matched begin RC10,8,RC11,8 end;
into device label H_EC code_regex BCC0,\s+16 replace_matched begin CC00,8,CC01,8 end;
into device label H_EC code_regex BCC1,\s+16 replace_matched begin CC10,8,CC11,8 end;
into device label H_EC code_regex CV01,\s+16 replace_matched begin CV10,8,CV11,8 end;
into device label H_EC code_regex CV02,\s+16 replace_matched begin CV20,8,CV21,8 end;
into device label H_EC code_regex CV03,\s+16 replace_matched begin CV30,8,CV31,8 end;
into device label H_EC code_regex CV04,\s+16 replace_matched begin CV40,8,CV41,8 end;
into device label H_EC code_regex CV11,\s+16 replace_matched begin CV50,8,CV51,8 end;
into device label H_EC code_regex CV12,\s+16 replace_matched begin CV60,8,CV61,8 end;
into device label H_EC code_regex CV13,\s+16 replace_matched begin CV70,8,CV71,8 end;
into device label H_EC code_regex CV14,\s+16 replace_matched begin CV80,8,CV81,8 end;
into device label H_EC code_regex HPBA,\s+16 replace_matched begin PBA0,8,PBA1,8 end;
into device label H_EC code_regex HPBB,\s+16 replace_matched begin PBB0,8,PBB1,8 end;
into device label H_EC code_regex BMD0,\s+16 replace_matched begin MD00,8,MD01,8 end;
into device label H_EC code_regex BMD1,\s+16 replace_matched begin MD10,8,MD11,8 end;
into device label H_EC code_regex BPV0,\s+16 replace_matched begin PV00,8,PV01,8 end;
into device label H_EC code_regex BPV1,\s+16 replace_matched begin PV10,8,PV11,8 end;
into device label H_EC code_regex BSN0,\s+16 replace_matched begin SN00,8,SN01,8 end;
into device label H_EC code_regex BSN1,\s+16 replace_matched begin SN10,8,SN11,8 end;
into device label H_EC code_regex BCV0,\s+16 replace_matched begin BV00,8,BV01,8 end;
into device label H_EC code_regex BCV1,\s+16 replace_matched begin BV10,8,BV11,8 end;
into device label H_EC code_regex CRG0,\s+16 replace_matched begin RG00,8,RG01,8 end;
into device label H_EC code_regex CRG1,\s+16 replace_matched begin RG10,8,RG11,8 end;
into device label H_EC code_regex CBT0,\s+16 replace_matched begin BT00,8,BT01,8 end;
into device label H_EC code_regex CBT1,\s+16 replace_matched begin BT10,8,BT11,8 end;
And all the code which accesses those registers needs to be fixed:
Code:
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 7/14
8/11/2015 [Guide] How to patch DSDT for working battery status
into method label BTST code_regex \sBPR1, replaceall_matched begin B1B2(PR10,PR11), end;
into method label BTST code_regex \(BPR0, replaceall_matched begin (B1B2(PR00,PR01), end;
into method label BTST code_regex \(BPR1, replaceall_matched begin (B1B2(PR10,PR11), end;
into method label BTST code_regex \(BRC0, replaceall_matched begin (B1B2(RC00,RC01), end;
into method label BTST code_regex \(BRC1, replaceall_matched begin (B1B2(RC10,RC11), end;
into method label GBTI code_regex \(BRC0, replaceall_matched begin (B1B2(RC00,RC01), end;
into method label GBTI code_regex \(BRC1, replaceall_matched begin (B1B2(RC10,RC11), end;
into method label _Q09 code_regex \(BRC0, replaceall_matched begin (B1B2(RC00,RC01), end;
into method label GBTI code_regex \(BCC0, replaceall_matched begin (B1B2(CC00,CC01), end;
into method label GBTI code_regex \(BCC1, replaceall_matched begin (B1B2(CC10,CC11), end;
into method label GBTI code_regex \(CV01, replaceall_matched begin (B1B2(CV10,CV11), end;
into method label GBTI code_regex \(CV02, replaceall_matched begin (B1B2(CV20,CV21), end;
into method label GBTI code_regex \(CV03, replaceall_matched begin (B1B2(CV30,CV31), end;
into method label GBTI code_regex \(CV04, replaceall_matched begin (B1B2(CV40,CV41), end;
into method label GBTI code_regex \(CV11, replaceall_matched begin (B1B2(CV50,CV51), end;
into method label GBTI code_regex \(CV12, replaceall_matched begin (B1B2(CV60,CV61), end;
into method label GBTI code_regex \(CV13, replaceall_matched begin (B1B2(CV70,CV71), end;
into method label GBTI code_regex \(CV14, replaceall_matched begin (B1B2(CV80,CV81), end;
into method label BTIF code_regex \(BMD0, replaceall_matched begin (B1B2(MD00,MD01), end;
into method label BTIF code_regex \(BMD1, replaceall_matched begin (B1B2(MD10,MD11), end;
into method label GBTI code_regex \sBMD0\) replaceall_matched begin B1B2(MD00,MD01)) end;
into method label GBTI code_regex \(BMD0, replaceall_matched begin (B1B2(MD00,MD01), end;
into method label GBTI code_regex \sBMD1\) replaceall_matched begin B1B2(MD10,MD11)) end;
into method label GBTI code_regex \(BMD1, replaceall_matched begin (B1B2(MD10,MD11), end;
into method label BTST code_regex \(BPV0, replaceall_matched begin (B1B2(PV00,PV01), end;
into method label BTST code_regex \(BPV1, replaceall_matched begin (B1B2(PV10,PV11), end;
into method label GBTI code_regex \(BPV0, replaceall_matched begin (B1B2(PV00,PV01), end;
into method label GBTI code_regex \(BPV1, replaceall_matched begin (B1B2(PV10,PV11), end;
into method label BTIF code_regex \(BSN0, replaceall_matched begin (B1B2(SN00,SN01), end;
into method label BTIF code_regex \(BSN1, replaceall_matched begin (B1B2(SN10,SN11), end;
into method label GBTI code_regex \(BSN0, replaceall_matched begin (B1B2(SN00,SN01), end;
into method label GBTI code_regex \(BSN1, replaceall_matched begin (B1B2(SN10,SN11), end;
into method label GBTI code_regex \(BCV0, replaceall_matched begin (B1B2(BV00,BV01), end;
into method label GBTI code_regex \(BCV1, replaceall_matched begin (B1B2(BV10,BV11), end;
into method label GBTI code_regex \(CRG0, replaceall_matched begin (B1B2(RG00,RG01), end;
into method label GBTI code_regex \(CRG1, replaceall_matched begin (B1B2(RG10,RG11), end;
into method label GBTI code_regex \(CBT0, replaceall_matched begin (B1B2(BT00,BT01), end;
into method label GBTI code_regex \(CBT1, replaceall_matched begin (B1B2(BT10,BT11), end;
Now, what about the 32-bit registers, BTY0, and BTY1? These are handled similarly to 16-
bit, except we need a B1B4 method that can construct a 32-bit value out of four 8-bit values:
Code:
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 8/14
8/11/2015 [Guide] How to patch DSDT for working battery status
{\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;
And we need to convert BTY0 and BTY1 into four 8-bit registers:
Code:
# 32-bit registers
into device label H_EC code_regex BTY0,\s+32 replace_matched begin TY00,8,TY01,8,TY02,8,TY03,8 end;
into device label H_EC code_regex BTY1,\s+32 replace_matched begin TY10,8,TY11,8,TY12,8,TY13,8 end;
Back to our original search for fields larger than 8-bit, we had these fields larger than 32-bit:
Code:
BDN0, 56,
BCT0, 128,
BDN1, 56,
BCT1, 128,
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 9/14
8/11/2015 [Guide] How to patch DSDT for working battery status
To access these fields 8-bit at a time is rather tedious, so I like to access them by offset, and to
make sure no existing code accesses them directly, we rename them:
Code:
into device label H_EC code_regex (BDN0,)\s+(56) replace_matched begin BDNX,%2,//%1%2 end;
into device label H_EC code_regex (BDN1,)\s+(56) replace_matched begin BDNY,%2,//%1%2 end;
into device label H_EC code_regex (BCT0,)\s+(128) replace_matched begin BCTX,%2,//%1%2 end;
into device label H_EC code_regex (BCT1,)\s+(128) replace_matched begin BCTY,%2,//%1%2 end;
Next we need to determine the offsets within the EC that these fields are placed. Keep in mind
the sizes are in bits, but the offsets are in bytes. The offsets I have in the comments below are
in hex. See if you can come up with the same numbers.
Code:
Once you run the patch for renaming, and then compile, the compiler will tell you what code
needs attention. In our case, we see this code with errors:
Code:
...
Store (BCT0, CTN)
...
Store (BDN0, BDN)
...
Store (BCT1, CTNB)
...
Store (BDN1, BDNB)
...
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 10/14
8/11/2015 [Guide] How to patch DSDT for working battery status
There are errors because we renamed them from BCT0, BDN0, BCT1, BDN1 to BCTX, BDNX,
BCTY, BDNY, respectively.
As you can see this code is reading from these EC buffer fields and storing them somewhere
else. In order to read these items 8-bits at at time, we need a utility method:
Code:
"RECB" stands for "Read EC Buffer". It takes two parameters indicating the offset within the
EC and the size in bits of the field you wish to read. The size in bits must be a multiple of eight
(8). The code does not check.
These helper methods must be defined in the EC device, in the case of this DSDT, named
H_EC:
Code:
Device (H_EC)
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 11/14
8/11/2015 [Guide] How to patch DSDT for working battery status
{
Name (_HID, EisaId ("PNP0C09"))
You will need to change the patches that create RECB/RE1B if the name of your EC device is
different. Common names are EC, EC0, and as in this example H_EC.
Store(RECB(0x30,128), CTN)
The 0x30 is the offset of the BTC0 field (now called BCTX) and the 128 is the number of bits.
into method label GBTI code_regex \(BCT0, replaceall_matched begin (RECB(0x30,128), end;
into method label GBTI code_regex \(BCT1, replaceall_matched begin (RECB(0x60,128), end;
into method label GBTI code_regex \(BDN0, replaceall_matched begin (RECB(0x10,56), end;
into method label GBTI code_regex \(BDN1, replaceall_matched begin (RECB(0x40,56), end;
This DSDT does not have any writes to EC buffers, but if it did, this utility method is useful:
Code:
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 12/14
8/11/2015 [Guide] How to patch DSDT for working battery status
Store(Local0, BCT0)
In this case, the BCT0 access cannot be replaced with a call to RECB. It is a write, not a read.
WECB must be used instead.
Code:
WECB(0x30,128, Local0)
The first two parameters to WECB are the same as those to RECB (offset and size of the EC
field). The third parameter (Arg2) is the value that should be written to the EC field. In this
case it is the original source of the Store (the first parameter to Store)... Local0.
Store is not the only AML opcode that can write. Just as Store is not the only AML opcode that
can read. Just as an example, consider Add:
Code:
Add(X, Y, Z)
The example above reads from X, reads from Y, performs an addition... and writes the result
to Z.
When you're not sure what an AML opcode does, read the ACPI spec. It is fully documented
there, but outside the scope of this post.
The existing laptop repo is a good source of example and information. There are numerous
WECB/RECB examples in the existing patches in the laptop repo.
Contributing
If you do come up with a patch for your battery methods, you are encouraged to contribute to
my patch repository, so please share your patch along with information about your computer,
so I can add the patch to my laptop DSDT patch repository. Please provide both the text file
containing the patches and the native DSDT (so I'm able to review the patches against the
native DSDT). I will not add patches to the repository unless I can review the patches as they
apply to the native DSDT.
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 13/14
8/11/2015 [Guide] How to patch DSDT for working battery status
Links
1. https://github.com/RehabMan/OS-X-ACPI-Battery-Driver
2. http://www.tonymacx86.com/yosemite-laptop-support/152573-guide-patching-laptop-dsdt-
ssdts.html
3. https://github.com/RehabMan/Laptop-DSDT-Patch
4. http://sourceforge.net/projects/maciasl/
5. https://www.acpica.org/
Buat akun
chromeextension://iooicodkiihhpojmmeghjclgihfjdjhj/front/in_isolation/reformat.html 14/14