>> A simple but not optimal solution

We have successfully identified the real issue that causes the kernel panic, so it’s time to find a new solution and we have to go back to the assembly.

// Assembly code of AppleIntelCFLGraphicsFramebuffer on macOS 10.12.2 beta 1
// `dpcd_caps` is a local variable and therefore resides on the stack.
// It is a byte array of length 16 and starts at address [%rbp - 0x40].
// `dpcd_caps->maxLinkRate` is the 2nd element in the array, so its address is [%rbp - 0x3F].
0000000000008dd9         movb       -0x3F(%rbp), %al
0000000000008ddc         cmpb       $0x13, %al
0000000000009791         movb       -0x3F(%rbp), %al
0000000000009794         movb       %al, 0x2472(%r12)

A quick and simple fix is to provide a valid link rate value before the verification starts. However, we cannot simply set the value to the register %rax, because later the local variable is referenced again and %rax is used for other purposes in between.

// Assembly code of AppleIntelCFLGraphicsFramebuffer on macOS 10.12.2 beta 1
// The graphics driver uses 17 lines (0x8d62 - 0x8daf) to just print out the OUI information.
// We can replace some of these lines with our code to set a custom link rate to the `dpcd_caps` buffer.
008d62         movl       0x1dc(%r12), %esi           // arg1: %rsi = framebuffer->field_0x1dc (fb port)
008d6a         leaq       aIgfbinfoFbdOui, %rdi       // arg0: %rdi = 
                                                      // "[IGFB][INFO   ] FB%d: OUI for display\\n"
008d71         xorl       %eax, %eax                     
008d73         call       _kprintf                    // Print to the kernel logs

008d78         leaq       aIgfbinfoXXXXN, %r14        // %r14 = "[IGFB][INFO   ]  %X %X %X %X \\n"
008d7f         xorl       %ebx, %ebx
008d81         incq       qword_aa928+6048            // Used for debugging only, ignore this
008d88         movzxb     -0x48(%rbp,%rbx), %esi      // arg1: *(%rbp + %rbx - 0x48) (oui[index * 4 + 0])
008d8d         movzxb     -0x47(%rbp,%rbx), %edx      // arg2: *(%rbp + %rbx - 0x47) (oui[index * 4 + 1])
008d92         movzxb     -0x46(%rbp,%rbx), %ecx      // arg3: *(%rbp + %rbx - 0x46) (oui[index * 4 + 2])
008d97         movzxb     -0x45(%rbp,%rbx), %r8d      // arg4: *(%rbp + %rbx - 0x45) (oui[index * 4 + 3])
008d9d         xorl       %eax, %eax
008d9f         movq       %r14, %rdi                  // arg0: "[IGFB][INFO   ]  %X %X %X %X \\n"                          
008da2         call       _kprintf                    // Print to the kernel logs                
008da7         addq       $0x4, %rbx                  // index += 4
008dab         cmpq       $0x8, %rbx                  // if rbx < 8
008daf         jb         loc_8d81                    // then print the rest elements in the OUI bytes
                                                      // else print the version and verify the link rate

008db1         movl       0x01dc(%r12), %esi          // arg1: %rsi = framebuffer->field_0x01dc (fb port)
008db9         movzxb     0x2386(%r12), %edx          // arg2: %rdx = framebuffer->field_0x2386 ([1].4)  
008dc2         movzxb     0x2385(%r12), %ecx          // arg3: %rcx = framebuffer->field_0x2385 (1.[4])
008dcb         leaq       aIgfbinfoFbdDis, %rdi       // arg0: %rdi = 
                          "[IGFB][INFO   ] FB%d: Display port config ver is %d.%d\\n"

008dd2         xorl       %eax, %eax
008dd4         call       _kprintf                    // Print to the kernel logs                       
008dd9         movb       var_3F(%rbp), %al           // %rax = dpcd_caps->maxLinkRate (dpcd_caps[1])
008ddc         cmpb       $0x13, %al                  // -> Start to verify the link rate
008dde         jg         loc_8df5                    // -> Handle each case of link rate

Since GetDPCDInfo() just prints the OUI for the connected display and the DisplayPort version before the link rate verification, we can replace those lines with our assembly code that writes a valid value to dpcd_caps->maxLinkRate.

// [Simple Solution]
// Assembly code of setting a custom maximum link rate value to the `dpcd_caps` buffer
// @Author: FireWolf
movb           $0x14, %al           // %rax = 0x14 (Link Rate = 5.4 Gbps, HBR2)
movb           $al, -0x3F(%rbp)     // dpcd_caps[1] = %rax

However, this fix also introduces a new problem that the custom maximum link rate value is used for EVERY connected display. To be more specific, the value 0x14 (HBR2, 5.4 Gbps) in the above example is used to establish a link between the integrated GPU and our built-in 4K display but might not be appropriate for an external 1080p display. In such cases, the maximum link rate reported by a low-resolution display could be a smaller value than 0x14. Therefore, using an inappropriate value might lead to a link training failure or even a hardware damage.

results matching ""

    No results matching ""