Contribute
Register

[Guide] HackrNVMeFamily co-existence with IONVMeFamily using class-code spoof

Status
Not open for further replies.
Overview

The purpose of this guide is to explain how to use the class-code spoof technique to keep un-patched IONVMeFamily.kext (incompatible with most PC NVMe SSDs) from loading such that HackrNVMeFamily can be used along side it in EFI/Clover/kexts or installed to the system volume (/Library/Extensions, /System/Library/Extensions).

I first published the technique on insanelymac: http://www.insanelymac.com/forum/to...er-macos-sierra-is-ready/page-29#entry2322636

You should be familiar with the patch_nvme.sh script as discussed in the patch-nvme README on github: https://github.com/RehabMan/patch-nvme

Although there are many ways to patch IONVMeFamily, only the class-code spoof and HackrNVMeFamily is to be discussed here. It is my opinion that it is the best way. Keeping this post focused only on this single technique, used from installation through post-installation, is the least confusing way to approach the topic.

There are two different ways to implement the class-code spoof:
- SSDT with _DSM method to inject the modified class-code
OR
- using config.plist/Devices/Arbitrary to inject the modified class-code

Because using config.plist/Devices/Arbitrary precludes use of some commonly used Clover features (config.plist/Graphics/Inject), this post will discuss only the SSDT method.

Assumptions:
- installation of Windows on the target
- running install of macOS/OS X (same version as you plan for the target) on existing hack or Mac
- basic config.plist editing skills
- basic MaciASL usage
- basic Clover knowledge
- basic knowledge of installing/injecting kexts

The MaciASL used by this guide is available here: https://bitbucket.org/RehabMan/os-x-maciasl-patchmatic/downloads/
Make sure you have ACPI 6.1 selected in MaciASL->Preferences->iASL.


Determining the ACPI path of your NVMe device(s)

In order to create an SSDT to inject class-code, you must know the ACPI path that describes where your SSD is located in ACPI namespace. You can use Windows Device Manager, or advanced users can determine it from ioreg. Both methods are described at the insanelymac link provided in the patch-nvme README.

Because this guide is intended to be used prior to installation, it will use Windows Device Manager. You will find your NVMe device in Windows Device Manager under "Storage Controllers" -> "Standard NVM Express Controller". Right click and choose Properties. Then click on the "Details" tab. Then find "BIOS device name" within the "Property" dropdown. Select it. Your NVMe SSD ACPI path will be displayed in the "Value" box.

View attachment 226134

If you have more than one NVMe SSD, you must find the ACPI path for each one.

Note: It is also possible that your SSD has no ACPI identity. I don't have such a computer at the moment (and the situation is probably rare), but in that case you may be able to determine only the ACPI identity of the parent (in the case of my NUC, the parent is _SB.PCI0.RP13), and slightly different SSDT would have to be created. If you don't see "BIOS device name", the SSD path is probably without one or more ACPI identities. In that case look for "Location paths" and record the path that starts with "ACPI(_SB_)". Be sure to read "Examples with incomplete ACPI identities" for additional information and instructions.


Creating SSDT-NVMe-Pcc.aml

Below you'll find a template you can use for your own SSDT that injects class-code. This is the actual SSDT that I use on my NUC6i7KYK with a Samsung 950 Pro NVMe SSD:
Code:
// Inject bogus class-code for NVMe SSD to prevent IONVMeFamily.kext from loading
DefinitionBlock("", "SSDT", 2, "hack", "NVMe-Pcc", 0)
{
    External(_SB.PCI0.RP13.PXSX, DeviceObj)
    Method(_SB.PCI0.RP13.PXSX._DSM, 4)
    {
        If (!Arg2) { Return (Buffer() { 0x03 } ) }
        Return(Package()
        {
            "class-code", Buffer() { 0xff, 0x08, 0x01, 0x00 },
            "built-in", Buffer() { 0 },
        })
    }
}
//EOF

The path shown above _SB.PCI0.RP13.PXSX is the path that my NUC6i7KYK uses. You will notice the same path was shown in the image from Windows Device Manager. You will need to replace both instances of it in the above SSDT to match the path for your own NVMe SSD.

Note regarding "built-in": In all my experience it has been enough only to have an ACPI identity to make the device appear builtin, not external. But some report that their device showing as external... so I add "built-in" as we never want an M.2 SSD to appear external.

If your SSD had no ACPI identity (eg. no ACPI identity for the M.2 port/device), you would need to create one. For example, if the OEM had left out the PXSX identity, it would have been required that I add one:
Code:
// Inject bogus class-code for NVMe SSD to prevent IONVMeFamily.kext from loading
DefinitionBlock("", "SSDT", 2, "hack", "NVMe-Pcc", 0)
{
    External(_SB.PCI0.RP13, DeviceObj)
    // Add PXSX device under RP13
    Device(_SB.PCI0.RP13.PXSX) { Name(_ADR, 0) }
    /// Now we can inject the method at RP13.PXSX
    Method(_SB.PCI0.RP13.PXSX._DSM, 4)
    {
        If (!Arg2) { Return (Buffer() { 0x03 } ) }
        Return(Package()
        {
            "class-code", Buffer() { 0xff, 0x08, 0x01, 0x00 },
            "built-in", Buffer() { 0 },
        })
    }
}
//EOF

I don't know what is displayed in Windows Device Manager in that case, although it is predictable what ioreg would look like. Of course, the idea here is to create the SSD before installing macOS, so let me know if you have a situation with no ACPI identity for the SSD.

And if you have multiple NVMe SSDs, you must create a _DSM method for each. For example, my NUC also has a M.2 port at RP09 for an SSD. Currently, I have an SM951/AHCI installed there, so it is not NVMe. But if it was an NVMe drive, my SSDT would have to modified to include a _DSM method for it as well:

Code:
// Inject bogus class-code for NVMe SSD to prevent IONVMeFamily.kext from loading
DefinitionBlock("", "SSDT", 2, "hack", "NVMe-Pcc", 0)
{
    External(_SB.PCI0.RP09.PXSX, DeviceObj)
    Method(_SB.PCI0.RP09.PXSX._DSM, 4)
    {
        If (!Arg2) { Return (Buffer() { 0x03 } ) }
        Return(Package()
        {
            "class-code", Buffer() { 0xff, 0x08, 0x01, 0x00 },
            "built-in", Buffer() { 0 },
        })
    }
    External(_SB.PCI0.RP13.PXSX, DeviceObj)
    Method(_SB.PCI0.RP13.PXSX._DSM, 4)
    {
        If (!Arg2) { Return (Buffer() { 0x03 } ) }
        Return(Package()
        {
            "class-code", Buffer() { 0xff, 0x08, 0x01, 0x00 },
            "built-in", Buffer() { 0 },
        })
    }
}
//EOF

In order to use this SSDT, you must compile it as AML. You should use my version of MaciASL, which is linked above. Make sure you have MaciASL->Preferences->iASL set to ACPI 6.1.

After running MaciASL, close the default window (which has your DSDT), then choose File->New to create an empty document. Paste the code and edit as appropriate for your ACPI path, then use File.Save As, "SSDT-NVMe-Pcc.aml", Format: ACPI Machine Language Binary. Save to a location you can find, such as your desktop.

Now you can copy the SSDT-NVMe-Pcc.aml file such that Clover can load it. If you're preparing your USB, you'll place it on the EFI for USB, otherwise it will be on the EFI for your boot drive. In any case, it goes to EFI/Clover/ACPI/patched/SSDT-NVMe-Pcc.aml.

Note: If you're using config.plist/ACPI/SortedOrder (to set SSDT load order for patched OEM SSDTs), you must be certain SSDT-NVMe-Pcc.aml is in the SortedOrder list. When SortedOrder is specified, SSDTs in ACPI/patched that are not in SortedOrder are ignored by Clover. For most desktop scenarios and laptop scenarios where Clover ACPI hotpatching is being used, SortedOrder will not be specified and copying the file to ACPI/patched is sufficient to cause it be loaded.


Examples with incomplete ACPI identities

As mentioned in "Determining the ACPI path of your NVMe device(s)" above, your SSD path may be incomplete therefore without ACPI identities that lead all the way to the leaf node of the SSD. It is not possible to inject properties without a full ACPI path available (not even with config.plist/Devices/Arbitrary). In this case, we must complete the missing ACPI identity(s) for each node in the path including the leaf node that represents the SSD.

This is a simple example that shows an ACPI identity all the way to the parent of the SSD, but the SSD leaf node is without an ACPI identity:
View attachment 228169

We can derive the path of the parent from ACPI(_SB_)#ACPI(PCI0)#ACPI(POP4) as _SB.PCI0.POP4, but the leaf node representing the SSD, #PCI(000), we will need to define ourselves as it doesn't exist in the OEM ACPI code.

The result is this:
Code:
// Inject bogus class-code for NVMe SSD to prevent IONVMeFamily.kext from loading
DefinitionBlock("", "SSDT", 2, "hack", "NVMe-Pcc", 0)
{
    External(_SB.PCI0.P0P4, DeviceObj)
    Device(_SB.PCI0.P0P4.SSD0) { Name(_ADR, 0) } // adding SSD0 identity under _SB.PCI0.P0P4
    Method(_SB.PCI0.P0P4.SSD0._DSM, 4)
    {
        If (!Arg2) { Return (Buffer() { 0x03 } ) }
        Return(Package()
        {
            "class-code", Buffer() { 0xff, 0x08, 0x01, 0x00 },
            "built-in", Buffer() { 0 },
        })
    }
}
//EOF

And here is a bit more complex example of the SSD behind a PCI bridge. The PCI bridge, #PCI(0800), is without an ACPI identity as is the SSD, #PCI(000), itself:
View attachment 228170

The path to the parent of the PCI bridge can be derived from ACPI(_SB_)#ACPI(PCI0)#ACPI(PEG0)#ACPI(PEGP) as _SB.PCI0.PEG0.PEGP, but for the #PCI(0800)#PCI(0000), we must create the ACPI identity.

The result is as follows:
Code:
// Inject bogus class-code for NVMe SSD to prevent IONVMeFamily.kext from loading
DefinitionBlock("", "SSDT", 2, "hack", "NVMe-Pcc", 0)
{
    External(_SB.PCI0.PEG0.PEGP, DeviceObj)
    // create identities for the bridge @8 and SSD0 @0
    Device(_SB.PCI0.PEG0.PEGP.PBR8)
    {
        Name(_ADR, 0x00080000)  // corresponds to #PCI(0800), MSW byte reversed
        Device(SSD0) { Name(_ADR, 0) } // corresponds to #PCI(0000)
    }
    // now we can inject the _DSM at the newly created ACPI path
    Method(_SB.PCI0.PEG0.PEGP.PBR8.SSD0._DSM, 4)
    {
        If (!Arg2) { Return (Buffer() { 0x03 } ) }
        Return(Package()
        {
            "class-code", Buffer() { 0xff, 0x08, 0x01, 0x00 },
            "built-in", Buffer() { 0 },
        })
    }
}
//EOF


Special injections required for 10.11.x and Samsung 960 EVO (or other 144d:a804 NVMe device)

Because the 10.11.x IONVMeFamily.kext has a direct IOName match for pci144d,a804, which happens to be the device-id for the Samsung 960 EVO, if you have that combination (10.11.x + 960 EVO), you must inject some additional properties to prevent the native IONVMeFamily.kext from loading.

For example:
Code:
// Inject bogus class-code for NVMe SSD to prevent IONVMeFamily.kext from loading
// also inject "compatible", "IOName", and "name" such that 10.11.x IONVMeFamily
// does not match on pci144d,a804 (Samsung 960 EVO)
DefinitionBlock("", "SSDT", 2, "hack", "NVMe-Pcc", 0)
{
    External(_SB.PCI0.RP13.PXSX, DeviceObj)
    Method(_SB.PCI0.RP13.PXSX._DSM, 4)
    {
        If (!Arg2) { Return (Buffer() { 0x03 } ) }
        Return(Package()
        {
            "class-code", Buffer() { 0xff, 0x08, 0x01, 0x00 },
            "built-in", Buffer() { 0 },
            "compatible", Buffer() { "pci144d,a801" },
            "IOName", "pci144d,a801",
            "name", Buffer() { "pci144d,a801" },
        })
    }
}
//EOF

My initial/complete explanation is here: https://www.tonymacx86.com/threads/...stall-el-capitan-on-nvme.210837/#post-1409008


Existing _DSM methods

It is possible your OEM already defined a _DSM method at the ACPI path that defines your NVMe SSD in ACPI namespace. ACPI does not allow duplicate symbols at the same path, so if you have an existing _DSM, the _DSM we are trying to add via SSDT-NVMe-Pcc.aml will be ignored.

Although you can patch DSDT (or any OEM SSDT that may have the offending _DSM), I like to use a Clover patch to rename all _DSM methods to XDSM. Clover ACPI patches are specified at config.plist/ACPI/DSDT/Patches.

An entry for _DSM->XDSM would be:
Comment: Change all _DSM to XDSM
Find: <5f44534d>
Replace: <5844534d>

As it appears in Xcode:
View attachment 226136

Renaming all _DSM to XDSM insures no existing _DSM at the NVMe SSD path. The _DSM injection via SSDT-NVMe-Pcc.aml will not work if there is an existing _DSM at that path.

You can find the _DSM to XDSM patch in config_patches.plist in the patch-nvme github project. It makes for easy copy/paste into your own config.plist.

Note: If you already have a statically patched DSDT.aml in ACPI/patched, you should not use the _DSM->XDSM patch. Instead, make sure you removed the _DSM at the path (or renamed to XDSM) in your patched DSDT (or patched SSDTs) at ACPI/patched/DSDT.aml. Typically, this would be done by applying "Remove _DSM methods", or "Rename _DSM methods to XDSM" as per my laptop ACPI patching guide.


Creating HackrNVMeFamily*.kext with the spoofed class-code

This is described in the patch-nvme README quite clearly, but here is a quick overview with special attention to the --spoof option.

As per patch-nvme README, we must make a copy of the github project:

Code:
mkdir ~/Projects
cd Projects
git clone https://github.com/RehabMan/patch-nvme.git patch-nvme.git
cd patch-nvme.git

Since your NVMe SSD will not be using the standard class-code, but instead a non-standard one, the resulting HackrNVMeFamily*.kext must use the non-standard IOPCIClassMatch instead of the standard one.

To do this we use the --spoof option. For example, to create HackrNVMeFamily for 10.12.2:
Code:
cd ~/Projects/patch-nvme.git
./patch_nvme.sh --spoof 10_12_2

The result will be HackrNVMeFamily-10_12_2.kext which can be placed in EFI/Clover/kexts/Other for injection via Clover (installer or recovery scenario), or installed to the system volume (post-install scenario).

You can install kexts easily with Terminal. For example, to install the generated HackrNVMeFamily-10_12_2.kext:
Code:
cd ~/Projects/patch-nvme.git
sudo cp -R HackrNVMeFamily-10_12_2.kext /Library/Extensions

You can rebuild cache to verify cache can be built without error:
Code:
sudo kextcache -i /


Summary

To use the resulting HackrNVMeFamily*.kext:
- make sure the SSDT-NVMe-Pcc.aml is in EFI/Clover/ACPI/patched
- make sure existing _DSM methods at the NVMe SSD ACPI path are renamed or deleted
- make sure you use the --spoof option to generate HackrNVMeFamily*.kext
- install the HackrNVMeFamily*.kext or inject it via EFI/Clover/kexts

By using the SSDT, we inject a bogus class-code such that IONVMeFamily.kext does not match and therefore does not load. But because of the modified IOPCIClassMatch in the HackrNVMeFamily Info.plist created by the patch_nvme.sh script with the --spoof option, it does match on the modified class-code, and is patched for the 512 byte block size used by typical PC NVMe SSDs.

By using the class-code spoof from the very beginning, you're using the same mechanism during installation and post-installation. And once the class-code spoof is implemented, there is never any chance of IONVMeFamily.kext causing a problem. Updating is simplified as well, since you can most likely use the previous HackrNVMeFamily for the updated system until you have a chance to create a new HackrNVMeFamily after the update. And because IONVMeFamily was never deleted, it is always easy to create the new HackrNVMeFamily*.kext after the update is complete and the patch set for that version is available.

System Updates

Once you have the class-code spoof in place, system updates are simplified because it is not necessary to delete IONVMeFamily.kext. But you should still plan to update HackrNVMeFamily*.kext after each update such that you have current NVMe driver code.

The procedures are somewhat obvious, but listed here (example given for update from 10.12.2 to 10.12.3):

- update via App Store
- wait for RehabMan to update the patch-nvme github project
- update your copy of the patch-nvme github repo
Code:
cd ~/Projects/patch-nvme.git
git pull
- create new HackrNVMeFamily*.kext
Code:
./patch_nvme.sh --spoof 10_12_3
- remove previous HackrNVMeFamily*.kext (eg. HackrNVMeFamily-10_12_2.kext)
Code:
sudo rm -Rf /Library/Extensions/HackrNVMeFamily-10_12_2.kext
- install new HackrNVMeFamily-10_12_3.kext
Code:
sudo cp -R HackrNVMeFamily-10_12_3.kext /Library/Extensions


Problem Reporting

Provide a detailed and concise description of the problem.

If your drive is not recognized by the macOS/OS X installer or you are getting a panic...

Provide image from Windows Device Manager showing the ACPI namespace path of your SSD.

Attach EFI/Clover folder as ZIP (press F4 at main Clover screen before collecting). Please eliminate 'themes' directory. Provide only EFI/Clover, not the entire EFI folder.


alright, i am going to reboot and get the clover log files and attach it in the next post, shortly give me about 10 mins.
 
The other items under Patches in the guide are other patches I have for the specific machine I took the screen shot. They are patches unrelated to this guide.

If you have an issue, you must provide "Problem Reporting" files.

Hi @RehabMan attached is my EFI files excluding theme as mentioned in guide to be removed :)
let me know what is wrong with my EFI files
 

Attachments

  • EFI.zip
    2 MB · Views: 85
Hi @RehabMan attached is my EFI files excluding theme as mentioned in guide to be removed :)
let me know what is wrong with my EFI files

You have no _DSM->XDSM patch in config.plist/ACPI/DSDT/Patches.
Looks like you put it in config.plist/ACPI/DSDT instead of config.plist/aCPI/DSDT/Patches.
Use cut/paste to move it.
 
You have no _DSM->XDSM patch in config.plist/ACPI/DSDT/Patches.
Looks like you put it in config.plist/ACPI/DSDT instead of config.plist/aCPI/DSDT/Patches.
Use cut/paste to move it.

is it correct now?

so does it mean that i am able to see the nvme drive after this?

Screen Shot 2017-04-17 at 23.59.18.png

@RehabMan
 
is it correct now?

Looks correct.

so does it mean that i am able to see the nvme drive after this?

No idea. I cannot predict the future... and I was unable to verify the content in your SSDT-NVMe-Pcc.aml because you didn't provide any images from Windows Device Manager (as requested in post #1, "Problem Reporting").
 
No idea. I cannot predict the future... and I was unable to verify the content in your SSDT-NVMe-Pcc.aml because you didn't provide any images from Windows Device Manager (as requested in post #1, "Problem Reporting").

@RehabMan
i think i did, maybe you overlooked it. :)
View attachment 249072
 
Last edited by a moderator:
OK. It looks fine then.
I don't look at previous posts when "Problem Reporting" files are requested and then provided.
I expect all data requested to provided in a single reply.

okay, noted. sorry, i forgot to include the EFI clover files for your review earlier with those screenshot.

thanks for sharing..

i am trying to see if i can see the nvme drive now, brb. @RehabMan
 
OK. It looks fine then.
I don't look at previous posts when "Problem Reporting" files are requested and then provided.
I expect all data requested to provided in a single reply.

@RehabMan i am still not able to see the nvme after doing all those above, am i missing out anything?
are there any configurations other than setting the fakecpuid in clover options?
 
Status
Not open for further replies.
Back
Top