Jump to content

Mieze

Mieze

Member Since 27 Mar 2012
Offline Last Active Yesterday, 11:45 PM
*****

Topics I've Started

Tracing back the AMD GPU wakeup issue to its origin

16 October 2017 - 10:05 PM

Introduction

 

With Whatevergreen.kext we already have a workaround for the AMD GPU wakeup issue which first arose with the release of El Capitan, but as a workaround is just a second class solution for a problem I decided to trace back the reason for the issue to it's origin and this post is the result of my research. As I used a R9 270X to do my research, which is the only AMD GPU I have, my patch has only been verified to work properly with this chip but according to the information sources I used, I have no reason to believe it won't work on other AMD GPU's too. In case there is still some uncertainty left in a particular point, I will mention this explicitly.

 

 

Materials Used

  • The Linux kernel sources of the Radeon driver in order to get a better understanding of the GPU's internals: https://git.kernel.o...h=next-20171013
  • A copy of the ACPI 6.0 specs in order to find out how to dump the chip's control register space.
  • A disassembler, e.g. objdump in Terminal or the trial version of Hopper Disassembler.

 

 

What I Did

 

As it's been a well known fact that wakeup with AMD GPUs still works with El Capitan and Sierra provided you

  1. select the IGPU as primary GPU
  2. enabled CSM and use Legacy VBIOS of the AMD GPU

in BIOS setup. I was wondering what is different with UEFI VBIOS and decided to I create dumps of the GPU's control register space while using Legacy VBIOS with CSM enabled and while using UEFI VBIOS with CSM disabled in order to find out. Comparing the register space contents it became obvious where the root of the problem lies and how it can be fixed with a DSDT patch.

 

 

Technical Background

 

Using Legacy VBIOS only the primary GPU is initialized by the VBIOS, i.e. only the IGPU is initialized while the AMD GPU is left untouched. When OS X boots up the framebuffer controller kext will find the AMD GPU in vanilla state, initialize it properly and wakeup will work as expected. That's also the reason why you have to blind boot in this configuration.

 

Using UEFI VBIOS the AMD GPU will be initialized too, provided it has a display connected to one of it's ports. You'll see the BIOS splash screen and will be able to access the BIOS settings but unfortunately macOS's framebuffer controller kext will notice that the GPU has already been initialized and skips the basic setup so that the configuration made by the VBIOS will be used and this is the point where things start to go wrong because this configuration seems to be broken causing wakeup to fail.

 

First of all you have to locate the AMD GPU in your DSDT. In my case it can be found at _SB.PCI0.PEG0.PEGP but it needs to be renamed to GFX0 for AppleGraphicsDevicePolicy.kext (AGDP) to work properly. This can be done manually or using a Clover patch (this is what I did) and I assume that this problem has been already solved before. The reason why I mention it explicitly here, is that you should be aware of it and don't get confused when your AMD GPU has a different name in the DSDT than in IORegistry.

 

Second, we need to get access to the GPU's control register space. According to the Linux sources, PCI Base Address Register 2 (BAR2) is used to address the control register space on Radeon HD5000, HD6000 and HD7000 GPUs. It's a 64bit base address register but newer GPUs (BONAIRE and above, i.e. Radeon HD8000 and HD9xxx) are different as the use BAR5 instead of BAR2. Unlike BAR2, BAR5 is a 32bit base address register. On my R9 270X (PITCAIRN) BAR5 is zero so that I decided to use this as an indication to use BAR2 but I must confess that I haven't checked if it works for all supported GPUs too. In case my patch doesn't work for you, be aware that this might be a pitfall!

 

The Radeon driver's source code tells us that the first display controller engine's registers can be found starting at offset 0x6800. It also tells us a lot about the meaning of the register contents. Using Legacy VBIOS my R9 270X's display controller engine's registers are still at their default values when macOS boots:

00006800  01 00 00 00 08 80 00 0a  00 00 00 00 00 00 00 00  |................|
00006810  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006820  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006830  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006840  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006850  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006860  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006870  00 00 00 00 08 80 00 14  00 00 00 00 00 00 00 00  |................|
00006880  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006890  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000068a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000068b0  00 00 00 00 10 00 00 00  00 00 00 20 00 00 00 20  |........... ... |
000068c0  00 00 00 20 10 00 00 00  00 00 00 20 00 00 00 20  |... ....... ... |
000068d0  00 00 00 20 00 00 00 00  00 20 00 00 00 00 00 00  |... ..... ......|
000068e0  00 00 00 20 00 00 00 00  00 00 00 00 00 20 00 00  |... ......... ..|
000068f0  00 00 00 00 00 20 00 00  00 00 00 00 00 00 00 20  |..... ......... |

With UEFI VBIOS the display controller engine's registers look quite different. Using the Linux driver sources you can easily make sense out of these values and will discover that I've got a 4K display connected to my R9 270X which is configured to it's native resolution using 32 bits per pixel.

00006800  01 00 00 00 0a 80 00 0a  00 00 00 00 00 00 00 00  |................|
00006810  00 00 00 00 00 00 00 00  00 0f 00 00 f4 00 00 00  |................|
00006820  f4 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006830  00 00 00 00 00 0f 00 00  70 08 00 00 00 00 00 00  |........p.......|
00006840  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006850  00 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  |................|
00006860  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006870  00 00 00 00 08 80 00 14  00 00 00 00 00 00 00 00  |................|
00006880  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00006890  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000068a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000068b0  00 00 00 00 10 00 00 00  00 00 00 20 00 00 00 20  |........... ... |
000068c0  00 00 00 20 10 00 00 00  00 00 00 20 00 00 00 20  |... ....... ... |
000068d0  00 00 00 20 00 00 00 00  00 20 00 00 00 00 00 00  |... ..... ......|
000068e0  00 00 00 20 00 00 00 00  00 00 00 00 00 20 00 00  |... ......... ..|
000068f0  00 00 00 00 00 20 00 00  00 00 00 00 00 00 00 20  |..... ......... |

The rest was just a little bit of laborious work and trial and error, comparing register contents, understanding their meanings and see what happens when you reset these registers to their default values. If you try to play around with your GPU's control registers a little bit more, be prepared to get a garbled screen for a few seconds. After all I've managed to create a DSDT patch which fixes the wrong registers while preserving screen output during boot and, most important, solves the wakeup issue.

 

 

The Patch

 

Putting things together I created a DSDT patch to fix the incorrectly initialized registers. With this patch applied, I now have working wakeup with my R9 270X under Sierra (10.12.6) using UEFI VBIOS with CSM disabled in UEFI setup. No kext patches or additional kexts are needed anymore for sleep/wake to work properly with my R9 270X anymore. I can see the BIOS splash screen on my display and can access UEFI setup but the best of all is that there hasn't been a single wakeup issue since I applied this patch.

Device (PEGP)
{
    Name (_ADR, Zero)  // _ADR: Address
    OperationRegion (PCIB, PCI_Config, Zero, 0x0100)
    Field (PCIB, AnyAcc, NoLock, Preserve)
    {
        Offset (0x10),
        BAR0,   32,
        BAR1,   32,
        BAR2,   64,
        BAR4,   32,
        BAR5,   32
    }
    
    Method (_INI, 0, NotSerialized)  // _INI: Initialize
    {
        If (LEqual (BAR5, Zero))
        {
            Store (BAR2, Local0)
        }
        Else
        {
            Store (BAR5, Local0)
        }
        
        OperationRegion (GREG, SystemMemory, And (Local0, 0xFFFFFFFFFFFFFFF0), 0x8000)
        Field (GREG, AnyAcc, NoLock, Preserve)
        {
            Offset (0x6800),
            GENA,   32,
            GCTL,   32,
            LTBC,   32,
            Offset (0x6810),
            PSBL,   32,
            SSBL,   32,
            PTCH,   32,
            PSBH,   32,
            SSBH,   32,
            Offset (0x6848),
            FCTL,   32,
            Offset (0x6EF8),
            MUMD,   32
        }
        
        Store (Zero, FCTL)
        Store (Zero, PSBH)
        Store (Zero, SSBH)
        Store (Zero, LTBC)
        Store (One, GENA)
        Store (Zero, MUMD)
    }
}

In case you have in-detail questions or need AML code for debugging (code to dump BARs or to dump the GPUs control register space) please let me know. I'm willing to share all my information in order support further research. Below you can find the register dumps I created attached to this post.

 

FAQ

  • Do I still have to select the IGPU as the primary display?

No. Although I haven't tried this on my own, user chh1 confirmed that this is no longer required when using the patch (please see http://www.insanelym...n/#entry2519884). Nevertheless I still recommend to select the IGPU as primary as there is absolutely no reason not to do so, in particular as the IGPU will be unusable for multimedia acceleration on Haswell based systems when it's not the primary one (IGPU's dev id is different when it's not the primary one).

 

 

Credits

  • vit9696 for developing Whatevergreen.kext and pointing me to the right direction.
  • RehabMan for developing ACPIDebug.kext
  • The Linux Radeon driver kernel developers for providing me with the background information I needed.

Pulsing Power LED while sleeping

01 February 2017 - 11:12 PM

I was wondering for a long time if it is possible to have the power LED pulsing on a hackintosh in the same as a real Mac but never really found the time for an in-depth research. Reading through the datasheet of my board's SuperIO, a Nuvoton NCT6776D, I found the following table which confirms that the hardware supports this feature too. 

Attached File  Bildschirmfoto 2017-02-01 um 23.24.49.png   585.29KB   8 downloads

As sleep/wake is controlled by the DSDT, I had the idea that it might be possible to get the pulsing power LED just with a small DSDT patch and, to my great surprise, its much simpler than expected. But see for yourself. In the DSDT of my board, an Asrock B85 Killer, I found method SLED controlling the switch between on in S0 and blinking mode in S3 (sleep):

Method (SLED, 1, NotSerialized)
{
    ENFG (0x0B)
    If (Arg0)
    {
        If (LEqual (OPT7, 0x87))
        {
            Store (0x45, OPT7)
        }
    }
    Else
    {
        If (LNotEqual (OPT7, Zero))
        {
            Store (0x87, OPT7)
        }
    }

    EXFG ()
}

Replacing the line "Store (0x45, OPT7)" with "Store (0x4A, OPT7)" gave me a slowly pulsing power LED while the machine sleeps. That's really trivial. Ok, it's not as pretty as on a real Mac, but very close to the original. This patch should work for most of Asrock's series 8 and 9 ATX/µATX boards as far as I can see. The mini-ITX boards may be different, at least my Asrock B85M-ITX is, but it should be possible to get this feature working with them too.

 

Many of Asrock's 100 series board also come with the same SuperIO chip but the DSDT looks different. Here is an example from  an Asrock Z170M-Pro4S which woks in the same way. The following code snippet was found in method SIOS, which is called in order to prepare the SuperIO for sleep mode:

If (LEqual (Arg0, 0x03))
{
    Store (0x0A, LDN)
    Or (OPT0, 0x20, OPT0)
    Or (RGE4, 0x10, RGE4)
    Store (0x0B, LDN)
    If (LEqual (OPT7, 0x87))
    {
        Store (0x45, OPT7)
    }
}

Changing 0x45 to 0x4A should give you the pulsing power LED on this board too.

 

As far as I know, Nuvoton SuperIOs can be also found on many ASUS and MSI boards. The NCT5577D supports this feature too. Other chips might support it as well but you need to have a datasheet to verify it and learn how to control the LED.

 

Mieze

© 2017 InsanelyMac  |   News  |   Forum  |   Downloads  |   OSx86 Wiki  |   Designed by Ed Gain  |   Logo by irfan  |   Privacy Policy