Blood On Ice Mac OS
@FireWolf8 months ago
Donate Blood with Versiti Blood Center of Michigan (formerly Michigan Blood). Schedule an appointment, view upcoming events, participate in local promotions, and more. Visit our community blood centers in Illinois, Indiana, Michigan and Wisconsin. From the Blood on Ice albumLyrics:The old Crow's cry the first warningThe rumbling frozen ground the last.Hooves thundering on the three feet snow,The icy da.
Ice Lake Intel Iris Plus Graphics on macOS Catalina: A solution to the kernel panic due to unsupported core display clock frequencies in the framebuffer driver
Hi folks! I haven’t updated my blog for a long time, but today I have something new about the graphics driver for Ice Lake platforms on macOS Catalina 10.15.6 to share with you. It’s time to write a new blog post.
If this page looks strange due to font or layout issues, you could find a pretty-printed version at here.
>> Introduction
It has been quite a while since Apple released the graphics driver for Intel Ice Lake platforms. While we expect that it should not be difficult to make the integrated graphics card on an Ice Lake-based laptop work under macOS Catalina, a large number of people has encountered a kernel panic due to an unsupported Core Display Clock frequency. Core Display Clock (CDCLK
) is one of the primary clocks used by the display engine to do its work. Apple’s graphics driver expects that the EFI firmware has already set the clock frequency to either 652.8 MHz or 648 MHz, but quite a few laptops set it to a much lower value (e.g. 172.8 MHz), and hence a kernel panic is triggered. In the following sections, I will focus on how the graphics driver verifies and configures the Core Display Clock and how we add support for these valid yet unsupported frequencies.
>> Analyze the framebuffer driver
We need to locate the kernel panic first, so we obtain a panic report as follows. We could observe that the kernel panic is triggered inside the function AppleIntelFramebufferController::probeCDClockFrequency()
while the driver tries to start the framebuffer controller.
The probe()
function is invoked by initCDClock()
, so we disassemble the graphics driver to find what they are doing. Fortunately, these functions are relatively small compared to the ones I analyzed before, so let’s take a look at my translated pseudocode directly.
The probe()
function first reads a 32-bit integer from the register at 0x46070
and checks whether the value is signed or not. According to Intel’s graphics driver developer manual, the highest bit of the register CDCLK_PLL_ENABLE
(at 0x46070
) is set when PLL is enabled, and hence the driver expects that PLL has already been enabled before it probes the Core Display Clock frequency. Subsequently, the driver retrieves the current frequency from the CDCLK_CTL
register (at 0x46000
) and triggers a kernel panic if the value is not one of 0x50E
and 0x518
. Finally, the driver uses the field stored at 0xE60
in the framebuffer controller as an index to retrieve some value from a global table PllRatioCdClkPair
and returns it back to the caller.
So far, we know that a Core Display Clock frequency other than 652.8 MHz or 648 MHz would trigger a kernel panic, but the meaning of the instance field at 0xE60
and contents of the global table are still mysterious. As such, we need to analyze the caller function initCDClock()
to see if we could have a further insight. We could observe that there is an instruction that writes to the instance field at 0xE60
in this function, so if we can find its concrete value, we will be able to read from the table PllRatioCdClkPair
. Let’s now take a look at the translated version of initCDClock()
.
The initCDClock()
function first fetches the hardware reference frequency from the DSSM
register (at 0x51004
). There are only three valid values, 24 MHz (0x0
), 19.2 MHz (0x1
), and 38.4 MHz (0x2
), and a kernel panic will occur if the register value is invalid. The driver then stores the hardware reference frequency to the field at 0xE60
and calls probeCDClockFrequency()
. When the probe()
function returns, the return value is stored to the field at 0xE50
. If the return value is nonzero, it is also stored to the field at 0xE58
. Otherwise, the driver stores the value read from the table PllRatioCdClkPair
instead as a “fallback”.
Now that we have three possible concrete values of the field at 0xE60
, we could retrieve and try to demystify the value from the table.
Intel mentions in its developer manual that the Core Display Clock PLL is the main source for Core Display Clock, and it must be programmed by the graphics driver to enable or disable a display, so we hypothesize that both 0x4D3F6400
and 0x4DD1E000
represent the PLL frequency. The PLL frequency is the product of the PLL ratio for a Core Display Clock frequency and the hardware reference frequency. The manual lists all possible pairs, and indeed our hypothesis is correct. It is also worth noting that we can find the same calculation in Intel Graphics Drivers for Linux.
>> Simple but not optimal solutions
So far, we know that the probeCDClockFrequency()
function assumes that the BIOS has set the Core Display Clock frequency to one of supported values and finds the PLL frequency that corresponds to the current hardware reference frequency. Its caller function initCDClock()
just stores the PLL frequency to the controller and finishes the initialization sequence. Some Ice Lake-based laptops, however, do not satisfy these preconditions, and hence a kernel panic occurs. We have several ways to patch the framebuffer driver to support other valid Core Display Clock frequencies, and we always want to keep the number of modifications as minimal as possible.
First Attempt: Use the fallback mechanism
Further investigations have revealed that the field at 0xE50
stores the current Core Display Clock PLL frequency while the field at 0xE58
stores the new value pending to be changed to. The graphics driver has code to check the inequality of these two fields and then switches to the new frequency in other functions. Since initCDClock()
has a fallback mechanism, we could patch the probe()
function to return 0 if it finds an unsupported Core Display Clock frequency. As such, the graphics driver will reconfigure the Core Display Clock to a supported value based on the actual hardware reference frequency. Unfortunately, it does not work as expected, because the current frequency value is used as a divider later and a divide-by-zero error will occur. We deduce that the fallback mechanism is totally useless, and we should ignore it from now on.
Second Attempt: Remove the frequency check
Even though a frequency of 172.8 MHz
is low, it is still capable of driving the built-in full HD display. Consequently, we should be able to light up the eDP panel if we remove the frequency check from the probeCDClockFrequency()
function. Indeed, our experiments have shown that kernel panic is no longer triggered but the display now blinks or presents garbled images frequently. As a result, we conclude that a low Core Display Clock frequency might not be enough to deliver the best user experience even though it is valid in theory.
>> An optimal solution
Since the graphics driver expects that the Core Display Clock frequency is set to the highest one and there are quite a few locations that rely on this assumption as well, we should reprogram the clock and switch its frequency to the one “recommended” by Apple. We inject code into the probeCDClockFrequency()
function to check whether the current frequency is natively supported. If not, we read the reference frequency to find the appropriate PLL frequency for the hardware and then sets the new clock. Since the display engine is not started yet, the sequence of setting a new clock can be summarized as follows.
- Disable the PLL by clearing the highest bit of the CDCLK_PLL_ENABLE register.
- Inform the power controller that the clock frequency will be changed.
- Enable the PLL by setting the highest bit and writing the new PLL ratio to the CDCLK_PLL_ENABLE register.
- Write the new value (Divider, Pipe, SSA Precharge, CD Frequency Decimal) to the CDCLK_CTL register.
- Inform the power controller of the new Core Display Clock voltage level.
- Wait for the hardware to complete the request.
Fortunately, we don’t need to manually implement a function to perform aforementioned operations. Apple has already provided two convenient helper functions, disableCDClock()
to disable the PLL and setCDClockFrequency()
to set a new PLL frequency. As such, our new probeCDClockFrequency()
supports non-native Core Display Clock frequencies as follows. Once the patch is enabled, the built-in display no longer constantly blinks or presents garbled images, and we can read from the register again to ensure that the new Core Display Clock frequency is indeed effective.
Blood On Ice Mac Os 11
>> Conclusion
We have discovered that a kernel panic saying “Unsupported CD clock decimal frequency” occurs because Apple only supports the highest frequencies. We have identified several assumptions and preconditions specified by the graphics driver, and we have analyzed functions related to configuring the Core Display Clock frequency. We add support for those “unsupported” yet valid frequencies to the graphics driver and reprogram the clock to deliver the best user experience. The fix will be integrated into WhateverGreen
, and I hope that this fix would help you make progress toward configuring the integrated graphics card to fully work on your Ice Lake-based laptops.
That’s the end of the story. Take care and stay healthy. See you in the next post.
>> Relevant Notes, Additional Resources and References
– Intel Graphics Developer Manual for Ice Lake Platforms, Volume 2c: Command and Register References [PDF]
You could find detailed descriptions of registers mentioned in this article.
– Intel Graphics Developer Manual for Ice Lake Platforms, Volume 12: Display Engine [PDF]
You could find the Core Display Clock and PLL programming guide.
– Intel Graphics Driver for Linux (Linux Kernel 5.8.3)
The intel_cdclk.c file can be considered as a reference implementation to configure the Core Display Clock. The Linux driver “sanitizes” the current Core Display Clock configurations to avoid any invalid values set by the BIOS or other operating systems. The driver then sets the frequency that is appropriate for the hardware at the end of the initialization sequence. In comparison, Apple assumes that everything is set probably because its firmware has already configured the clock appropriately. However, those assumptions might not be true on some Ice Lake-based laptops, and hence the driver triggers a kernel panic to indicate the error. The following functions might be interesting to see how the graphics driver on Linux initializes the Core Display Clock. The indentation represents the caller and the callee.
>> Update Logs
Revision 0: Initial Release
Blood On Ice Mac Os Catalina
>> License
This article is licensed under Attribution-NonCommercial 4.0 International License.
Copyright (C) 2020 FireWolf @ FireWolf Pl. All Rights Reserved.