; Read-only dump of the DCE4 display engine state (Evergreen, Northern Islands ; and the PALM/SUMO IGPs). Finds the AMD display controller, maps its register ; aperture, prints what the firmware left in the CRTC, surface and viewport ; blocks, then exits without registering a service. ; ; NOTHING HERE WRITES TO THE HARDWARE. ; ; It exists to answer one question: is the panel already running at its own ; native timing with the firmware scaling a smaller surface up to it? If so, a ; driver in the style of gen9mode can widen the surface to the panel without ; touching the timings, the PLL or the encoder - which is exactly the part that ; leaves the panel dark when a full modeset goes wrong. ; ; VIEWPORT_SIZE answers it on its own: the driver writes the mode there as ; hdisplay shl 16 or vdisplay, while GRPH_X_END and GRPH_Y_END carry the size of ; the framebuffer being scanned out. See evergreen_crtc_do_set_base, ; drivers/video/drm/radeon/atombios_crtc.c:1401-1428. ; ; Named offsets are the ones declared in ; drivers/video/drm/radeon/evergreen_reg.h. The two raw windows are printed by ; offset instead: DCE4 programs its timings through ATOM command tables, so the ; tree declares only a part of that block and its layout is not the AVIVO one it ; grew from. format PE DLL native 0.05 entry START DEBUG = 1 __DEBUG__ = 1 __DEBUG_LEVEL__ = 1 include '../struct.inc' GRPH_ENABLE = 0x6800 GRPH_CONTROL = 0x6804 GRPH_SURF_ADDR = 0x6810 GRPH_PITCH = 0x6818 ; in pixels, not bytes GRPH_OFFSET_X = 0x6824 GRPH_OFFSET_Y = 0x6828 GRPH_X_START = 0x682C GRPH_Y_START = 0x6830 GRPH_X_END = 0x6834 GRPH_Y_END = 0x6838 DATA_FORMAT = 0x6B00 DESKTOP_HEIGHT = 0x6B04 VIEWPORT_START = 0x6D70 VIEWPORT_SIZE = 0x6D74 CRTC_CONTROL = 0x6E70 CRTC_STATUS = 0x6E8C ; EVERGREEN_CRTC1_REGISTER_OFFSET CRTC_STEP = 0xC00 ; the timing block, and the viewport block with whatever follows it: the scaler ; lives in there but the tree does not name it TIMING_BASE = 0x6E00 TIMING_COUNT = 16 VIEWPORT_RAW = 0x6D70 VIEWPORT_COUNT = 20 ; the register aperture is 64K or 256K depending on D0F0x64_x1C[RegApSize] ; (BKDG 43170 rev 3.13, D1F0x18), and everything read here lives below 0x8000, ; so map only the half that is always there RMMIO_SIZE = 0x10000 section '.flat' code readable writable executable include '../proc32.inc' include '../peimport.inc' include '../fdo.inc' include '../macros.inc' START: push ebx esi edi ebp DEBUGF 1, "radinfo: read only, nothing is written\n" ; an integrated display controller sits on bus 0 as function 0 of its device, ; so only those are probed xor ebx, ebx .scan: push 0 push ebx push 0 invoke PciRead32 cmp ax, 0x1002 jnz .next_dev mov ebp, eax push 8 push ebx push 0 invoke PciRead32 shr eax, 16 cmp ax, 0x0300 jz .found .next_dev: add ebx, 8 cmp ebx, 0x100 jb .scan DEBUGF 1, "radinfo: no AMD display controller on bus 0\n" jmp .done .found: shr ebp, 16 DEBUGF 1, "radinfo: AMD display %x at devfn %x\n", ebp, ebx push 0x10 push ebx push 0 invoke PciRead32 and eax, not 0xF DEBUGF 1, "radinfo: VRAM aperture %x\n", eax ; D1F0x18 is the register aperture and keeps that offset whether the BARs are ; 32 or 64 bit; only its high half moves to D1F0x1C push 0x18 push ebx push 0 invoke PciRead32 mov edi, eax and edi, not 0xF test eax, 4 ; type 10b in bits 2:1 means 64 bit jz .have_bar push 0x1C push ebx push 0 invoke PciRead32 test eax, eax jnz .above_4g .have_bar: DEBUGF 1, "radinfo: registers %x\n", edi push 0x1B push RMMIO_SIZE push edi invoke MapIoMem test eax, eax jz .no_map mov esi, eax ; two CRTCs is what the IGPs of this family have xor ebx, ebx .crtc: mov edx, ebx imul edx, CRTC_STEP mov eax, [esi + edx + CRTC_CONTROL] cmp eax, -1 jz .next_crtc DEBUGF 1, "radinfo: CRTC %u CONTROL %x", ebx, eax mov eax, [esi + edx + CRTC_STATUS] DEBUGF 1, " STATUS %x\n", eax mov eax, [esi + edx + VIEWPORT_SIZE] mov ecx, eax shr ecx, 16 movzx eax, ax DEBUGF 1, "radinfo: mode %ux%u", ecx, eax DEBUGF 1, " VIEWPORT_START %x\n", [esi + edx + VIEWPORT_START] cmp ecx, [esi + edx + GRPH_X_END] jnz .scaled cmp eax, [esi + edx + GRPH_Y_END] jnz .scaled DEBUGF 1, "radinfo: surface is the mode, nothing is scaling\n" jmp .surface .scaled: DEBUGF 1, "radinfo: surface is not the mode, something is scaling\n" .surface: mov eax, [esi + edx + GRPH_X_END] mov ecx, [esi + edx + GRPH_Y_END] DEBUGF 1, "radinfo: surface %ux%u", eax, ecx mov eax, [esi + edx + GRPH_PITCH] DEBUGF 1, " pitch %u px", eax mov eax, [esi + edx + DESKTOP_HEIGHT] DEBUGF 1, " desktop_h %u\n", eax mov eax, [esi + edx + GRPH_ENABLE] DEBUGF 1, "radinfo: GRPH_ENABLE %x", eax mov eax, [esi + edx + GRPH_CONTROL] DEBUGF 1, " CONTROL %x", eax mov eax, [esi + edx + DATA_FORMAT] DEBUGF 1, " DATA_FORMAT %x", eax mov eax, [esi + edx + GRPH_SURF_ADDR] DEBUGF 1, " SURF %x\n", eax mov eax, [esi + edx + GRPH_X_START] DEBUGF 1, "radinfo: X_START %x", eax mov eax, [esi + edx + GRPH_Y_START] DEBUGF 1, " Y_START %x", eax mov eax, [esi + edx + GRPH_OFFSET_X] DEBUGF 1, " OFFSET_X %x", eax mov eax, [esi + edx + GRPH_OFFSET_Y] DEBUGF 1, " OFFSET_Y %x\n", eax ; the offset is rebuilt from ebx each time: only ebx, esi and edi survive the ; call, edx does not mov eax, ebx imul eax, CRTC_STEP add eax, TIMING_BASE stdcall dump_range, esi, eax, TIMING_COUNT mov eax, ebx imul eax, CRTC_STEP add eax, VIEWPORT_RAW stdcall dump_range, esi, eax, VIEWPORT_COUNT .next_crtc: inc ebx cmp ebx, 2 jb .crtc invoke FreeKernelSpace, esi jmp .done .above_4g: DEBUGF 1, "radinfo: registers are above 4G, out of reach\n" jmp .done .no_map: DEBUGF 1, "radinfo: cannot map the register aperture\n" ; never register a service: this is a one shot dump, so report failure and let ; the kernel unload us again .done: pop ebp edi esi ebx xor eax, eax ret ; count must be a multiple of four proc dump_range stdcall uses ebx esi edi, base:dword, off:dword, count:dword mov esi, [base] mov edi, [off] mov ebx, [count] .line: mov eax, [esi + edi] mov ecx, [esi + edi + 4] DEBUGF 1, "radinfo: %x: %x %x", edi, eax, ecx mov eax, [esi + edi + 8] mov ecx, [esi + edi + 12] DEBUGF 1, " %x %x\n", eax, ecx add edi, 16 sub ebx, 4 ja .line ret endp ; without a relocation table every absolute address stays at the link time base ; and the kernel loads us somewhere else entirely data fixups end data include_debug_strings