Deleting the VGA - Ausdauersportler/IMAC-EFI-BOOT-SCREEN GitHub Wiki
Before starting this we should do some basic math. All what matters here is the sum of all image length values of all parts. The size available is exactly 128K or 256 blocks of 512 byte each.
Assume the new to be added driver has an image length of 24, both legacy and GOP part together cannot occupy more than 256-24=232 blocks a 512 bytes. The W5170M GOP BIOS used here as an example has an image length of 115 (legacy) and 121 (GOP) which gives us a free space of 20 - not enough to add the new driver. Some work to do!
Historically each legacy video BIOS contains a VGA driver not longer used by any modern OS for more than a decade now. In 2010 AMD started a development to replace the legacy video BIOS completely, but during the next decade the newly developed GOP vBIOS still read contents from ATOM tables with the legacy video BIOS. Same is valid here on MacPro5,1 and iMac11,x and iMac12,x systems.
So we cannot delete the legacy part completely (we need to keep these ATOM tables), but we can cut out the VGA part. Luckily this VGA part is stored in a single big binary blob right after the PCIR header of the legacy part and in front of the ATOM data and command tables. These tables in turn use two little directories with absolute addresses to find the contents of each (data and command) table.
Again the ATOMDIS tool is a valuable resource to read those directories. Confusingly the directories are named code and data table and refer to a number of code and data organized in tables, too. The directory is a table of tables...
If we cut a fixed number N of bytes from the legacy part we would need to do some hex calculation and subtract this number N from each absolute address found in the directories and rewrite the newly calculated addresses.
The PCIR header and the VGA part may have different sizes in each AMD vBIOS so we cannot do the work only once for all AMD cards, we have to adjust each AMD vBIOS family/version separately. Fortunately we can use the same modified legacy part at least for similar cards like the WX4130/WX4150/WX4170 and another modification for both the W6150M/W6170M. The cutting process has only to be done once per AMD graphics card (family).
Since I do no expect any new AMD vBIOS updates for cards released from 2012-2017 (GCN 1-4) there is no need to redo it, again. We planned originally to develop a tool doing this automatically, ATOMDIS already provides all functionality to read and parse the legacy vBIOS. Since it is a change once and forget process a re-usable program is not really necessary once we modded all vBIOS versions (and we did it at least for the AMD MXM cards).
Some thoughts before starting the cutting process. All vBIOS parts are 512 byte aligned (0x200). The image size of each vBIOS part is given in block numbers - each block has 512 bytes. So cutting a multiple of 512 bytes would keep the alignment in place, would make it simple to change this image size by just subtracting the number of blocks deleted, and finally would also make the address math much more easy.
Each address contains two bytes stored in reverse order (endian). If we cut exactly a multiple of 512 we would just have to correct this second address byte. Additionally the remaining fragments of the VGA part can be happily ignored but you can manually overwrite these bytes with 0x00 - I did this to have a more clean view on the result.
To maintain the structure of data potentially stored in larger vBIOS chips beyond the 128k limit we would have to fill the first 128K bytes with padding information (0x00
bytes) of he same length as we just cut from the VGA part. Basically all changes to an AMD vBIOS have to made within the first 128k without moving the latter part (if there is one).
To make this more easy one can copy this first 128K from each vBIOS into a separate file and work on this contents, only. After finishing and checking success using the tools described below you can copy back the complete modified block. This way no move of this so called training data beyond the 128k limit can happen (a lot of vBIOS versions are of size 128K and do not need such special treatment).
To understand the PCIR header structure better I added some lines of code to a Hex fiend template. Please download this file and store in the Hex Fiend templates folder of your Mac.
Basically the image length (counted in blocks of 512 byte, here 0x79) is stored twice in the header (pink boxes), using the template it is called Image Size
and stored at byte position 0x03 (by default) and later in this particular W5170M vBIOS at 0x23c (named Image length
here). This address is not constant and depends on the size of the header, in particular on the number and length of strings added to describe the vBIOS (vendor information). Additionally both directory addresses of data (Data Table Offset) and command (Command Table Offset) tables (red boxes) as well as the beginning of the VGA part (blue box) has been marked visually.
In addition to the contents of the directories (aka Data Table
and Command Table
) we have to change both Image Size/Length entries and the directory addresses, too. The math remains always similar.
Before I describe the process in more detail here the resulting modified header after stripping the VGA part. In the green box you will find the start of the Command Table at 0x376. Image Length/Size and directory/table addresses have been adjusted.
Before editing the vBIOS check the contents using atomdis
, one finds 0x50 command tables and 0x22 data tables listed.
atomdis W5170M-GOP.rom l
Read 20000 bytes of data from W5170M-GOP.rom
Command Tables:
0000: b5e2 Len 006e (ASIC_Init)
0001: b650 Len 0057 (GetDisplaySurfaceSize)
0002: b6a8 Len 010f (ASIC_RegistersInit)
0003: d414 Len 0118 (VRAM_BlockVenderDetection)
0004: e39e Len 01c8 (SetClocksRatio/DIGxEncoderControl)
0005: b7b8 Len 03c5 (MemoryControllerInit)
0006: - (EnableCRTCMemReq)
0007: d52c Len 000d (MemoryParamAdjust)
0008: - (DVOEncoderControl)
0009: bb7e Len 00ff (GPIOPinControl)
000a: bc7e Len 0141 (SetEngineClock)
000b: bdc0 Len 00fe (SetMemoryClock)
000c: bebe Len 052c (SetPixelClock)
000d: c3ea Len 0140 (DynamicClockGating)
000e: c52a Len 0050 (ResetMemoryDLL)
000f: c57a Len 0062 (ResetMemoryDevice)
0010: df7a Len 016d (MemoryPLLInit)
0011: e0e8 Len 0089 (AdjustDisplayPll)
0012: c7a6 Len 0111 (AdjustMemoryController)
0013: c8b8 Len 005f (EnableASIC_StaticPwrMgt)
0014: c918 Len 0074 (ASIC_StaticPwrMgtStatusChange/SetUniphyInstance)
0015: c98c Len 0076 (DAC_LoadDetection)
0016: - (LVTMAEncoderControl)
0017: - (LCD1OutputControl)
0018: ca02 Len 00b2 (DAC1EncoderControl)
0019: - (DAC2EncoderControl)
001a: - (DVOOutputControl)
001b: - (CV1OutputControl)
001c: ef5a Len 0038 (GetConditionalGoldenSetting/SetCRTC_DPM_State)
001d: - (TVEncoderControl)
001e: ef92 Len 00b8 (TMDSAEncoderControl)
001f: f04a Len 0126 (LVDSEncoderControl)
0020: - (TV1OutputControl)
0021: cab4 Len 007a (EnableScaler)
0022: cb2e Len 004b (BlankCRTC)
0023: cb7a Len 003e (EnableCRTC)
0024: cbb8 Len 01ad (GetPixelClock)
0025: cd66 Len 002c (EnableVGA_Render)
0026: cd92 Len 0022 (EnableVGA_Access/GetSCLKOverMCLKRatio)
0027: - (SetCRTC_Timing)
0028: cdb4 Len 0019 (SetCRTC_OverScan)
0029: - (SetCRTC_Replication)
002a: cdce Len 00af (SelectCRTC_Source)
002b: ce7e Len 01e6 (EnableGraphSurfaces)
002c: d064 Len 0058 (UpdateCRTC_DoubleBufferRegisters)
002d: d0bc Len 00d9 (LUT_AutoFill)
002e: - (EnableHW_IconCursor)
002f: d196 Len 004a (GetMemoryClock)
0030: d1e0 Len 004e (GetEngineClock)
0031: d22e Len 0128 (SetCRTC_UsingDTDTiming)
0032: - (ExternalEncoderControl)
0033: - (LVTMAOutputControl)
0034: d356 Len 00be (VRAM_BlockDetectionByStrap)
0035: d53a Len 00e4 (MemoryCleanUp)
0036: d61e Len 0239 (ReadEDIDFromHWAssistedI2C/ProcessI2cChannelTransaction)
0037: - (WriteOneByteToHWAssistedI2C)
0038: d858 Len 005f (ReadHWAssistedI2CStatus/HPDInterruptService)
0039: d8b8 Len 002d (SpeedFanControl)
003a: d8e6 Len 000a (PowerConnectorDetection)
003b: d8f0 Len 0018 (MC_Synchronization)
003c: d908 Len 0088 (ComputeMemoryEnginePLL)
003d: d990 Len 005f (MemoryRefreshConversion)
003e: e566 Len 0029 (VRAM_GetCurrentInfoBlock)
003f: d9f0 Len 01c0 (DynamicMemorySettings)
0040: dbb0 Len 0355 (MemoryTraining)
0041: df06 Len 0074 (EnableSpreadSpectrumOnPPLL)
0042: - (TMDSAOutputControl)
0043: e172 Len 00c6 (SetVoltage)
0044: - (DAC1OutputControl)
0045: - (DAC2OutputControl)
0046: e2aa Len 00f4 (SetupHWAssistedI2CStatus)
0047: c5dc Len 0110 (ClockSource)
0048: c6ec Len 00ba (MemoryDeviceInit)
0049: - (EnableYUV)
004a: - (DIG1EncoderControl)
004b: - (DIG2EncoderControl)
004c: e590 Len 0746 (DIG1TransmitterControl/UNIPHYTransmitterControl)
004d: - (DIG2TransmitterControl/LVTMATransmitterControl)
004e: ecd6 Len 0227 (ProcessAuxChannelTransaction)
004f: eefe Len 005c (DPEncoderService)
Data Tables:
0000: - (UtilityPipeLine)
0001: - (MultimediaCapabilityInfo)
0002: - (MultimediaConfigInfo)
0003: 9e66 Len 00e4 Rev 01:02 (StandardVESA_Timing) (struct size 01c4)
0004: 9f4a Len 006c Rev 02:02 (FirmwareInfo) (struct size 006c)
0005: 9fb6 Len 0304 Rev 02:01 (DAC_Info) (struct size 0008)
0006: a2ba Len 004e Rev 01:03 (LVDS_Info) (struct size 0034)
0007: b186 Len 045b Rev 03:01 (TMDS_Info) (struct size 001e)
0008: - (AnalogTV_Info)
0009: - (SupportedDevicesInfo)
000a: a308 Len 00dc Rev 01:01 (GPIO_I2C_Info)
000b: a3e4 Len 000c Rev 01:05 (VRAM_UsageByFirmware) (struct size 000c)
000c: a3f0 Len 001c Rev 01:01 (GPIO_Pin_LUT)
000d: a40c Len 00a8 Rev 01:01 (VESA_ToInternalModeLUT)
000e: - (ComponentVideoInfo)
000f: a4b4 Len 0268 Rev 06:01 (PowerPlayInfo)
0010: - (CompassionateData)
0011: b16e Len 0018 Rev 02:01 (SaveRestoreInfo/DispDevicePriorityInfo)
0012: - (PPLL_SS_Info/SS_Info)
0013: - (OemInfo)
0014: - (XTMDS_Info)
0015: - (MclkSS_Info)
0016: a71c Len 011e Rev 01:03 (Object_Info/Object_Header) (struct size 0010)
0017: a83a Len 007d Rev 01:01 (IndirectIOAccess)
0018: - (MC_InitParameter/AdjustARB_SEQ)
0019: - (ASIC_VDDC_Info)
001a: b0c0 Len 0040 Rev 03:01 (ASIC_InternalSS_Info/ASIC_MVDDC_Info) (struct size 0034)
001b: b100 Len 006d Rev 02:03 (TV_VideoMode/DispOutInfo)
001c: a8b8 Len 06c0 Rev 02:01 (VRAM_Info) (struct size 0354)
001d: - (MemoryTrainingInfo/ASIC_MVDDQ_Info)
001e: - (IntegratedSystemInfo)
001f: - (ASIC_ProfilingInfo/ASIC_VDDCI_Info)
0020: af78 Len 0148 Rev 03:01 (VoltageObjectInfo/VRAM_GPIO_DetectionInfo)
0021: - (PowerSourceInfo)
The contents of both tables before cutting and changing addresses:
The length of the VGA part to be cut from this BIOS has the length of 0x4D
in 512 byte blocks, after changing all data we got:
If you compare both source and result you get this view using the Hex Fiend compare function:
You can easily detect the pattern, only the 2nd byte of each address has been changed (0x4D x 2 = 9A subtracted - 4D blocks 512 bytes each means 9A when using 256 hex base). Take the first high lighted pair: B5 becomes 1B and magically B5 - 9A == 1B - so my current calculation matches somehow the work I did a year ago.
Finally one can check his result using the same atomdis tool. The output should match the original one, i.e. same number of tables, same length of each table, only the address changed in the first byte here (this tool corrects the swapped bytes found in the BIOS itself):
atomdis W5170M-EFI_adj.rom l
Read 20000 bytes of data from W5170M-EFI_adj.rom
Command Tables:
0000: 1be2 Len 006e (ASIC_Init)
0001: 1c50 Len 0057 (GetDisplaySurfaceSize)
0002: 1ca8 Len 010f (ASIC_RegistersInit)
0003: 3a14 Len 0118 (VRAM_BlockVenderDetection)
0004: 499e Len 01c8 (SetClocksRatio/DIGxEncoderControl)
0005: 1db8 Len 03c5 (MemoryControllerInit)
0006: - (EnableCRTCMemReq)
0007: 3b2c Len 000d (MemoryParamAdjust)
0008: - (DVOEncoderControl)
0009: 217e Len 00ff (GPIOPinControl)
000a: 227e Len 0141 (SetEngineClock)
000b: 23c0 Len 00fe (SetMemoryClock)
000c: 24be Len 052c (SetPixelClock)
000d: 29ea Len 0140 (DynamicClockGating)
000e: 2b2a Len 0050 (ResetMemoryDLL)
000f: 2b7a Len 0062 (ResetMemoryDevice)
0010: 457a Len 016d (MemoryPLLInit)
0011: 46e8 Len 0089 (AdjustDisplayPll)
0012: 2da6 Len 0111 (AdjustMemoryController)
0013: 2eb8 Len 005f (EnableASIC_StaticPwrMgt)
0014: 2f18 Len 0074 (ASIC_StaticPwrMgtStatusChange/SetUniphyInstance)
0015: 2f8c Len 0076 (DAC_LoadDetection)
0016: - (LVTMAEncoderControl)
0017: - (LCD1OutputControl)
0018: 3002 Len 00b2 (DAC1EncoderControl)
0019: - (DAC2EncoderControl)
001a: - (DVOOutputControl)
001b: - (CV1OutputControl)
001c: 555a Len 0038 (GetConditionalGoldenSetting/SetCRTC_DPM_State)
001d: - (TVEncoderControl)
001e: 5592 Len 00b8 (TMDSAEncoderControl)
001f: 564a Len 0126 (LVDSEncoderControl)
0020: - (TV1OutputControl)
0021: 30b4 Len 007a (EnableScaler)
0022: 312e Len 004b (BlankCRTC)
0023: 317a Len 003e (EnableCRTC)
0024: 31b8 Len 01ad (GetPixelClock)
0025: 3366 Len 002c (EnableVGA_Render)
0026: 3392 Len 0022 (EnableVGA_Access/GetSCLKOverMCLKRatio)
0027: - (SetCRTC_Timing)
0028: 33b4 Len 0019 (SetCRTC_OverScan)
0029: - (SetCRTC_Replication)
002a: 33ce Len 00af (SelectCRTC_Source)
002b: 347e Len 01e6 (EnableGraphSurfaces)
002c: 3664 Len 0058 (UpdateCRTC_DoubleBufferRegisters)
002d: 36bc Len 00d9 (LUT_AutoFill)
002e: - (EnableHW_IconCursor)
002f: 3796 Len 004a (GetMemoryClock)
0030: 37e0 Len 004e (GetEngineClock)
0031: 382e Len 0128 (SetCRTC_UsingDTDTiming)
0032: - (ExternalEncoderControl)
0033: - (LVTMAOutputControl)
0034: 3956 Len 00be (VRAM_BlockDetectionByStrap)
0035: 3b3a Len 00e4 (MemoryCleanUp)
0036: 3c1e Len 0239 (ReadEDIDFromHWAssistedI2C/ProcessI2cChannelTransaction)
0037: - (WriteOneByteToHWAssistedI2C)
0038: 3e58 Len 005f (ReadHWAssistedI2CStatus/HPDInterruptService)
0039: 3eb8 Len 002d (SpeedFanControl)
003a: 3ee6 Len 000a (PowerConnectorDetection)
003b: 3ef0 Len 0018 (MC_Synchronization)
003c: 3f08 Len 0088 (ComputeMemoryEnginePLL)
003d: 3f90 Len 005f (MemoryRefreshConversion)
003e: 4b66 Len 0029 (VRAM_GetCurrentInfoBlock)
003f: 3ff0 Len 01c0 (DynamicMemorySettings)
0040: 41b0 Len 0355 (MemoryTraining)
0041: 4506 Len 0074 (EnableSpreadSpectrumOnPPLL)
0042: - (TMDSAOutputControl)
0043: 4772 Len 00c6 (SetVoltage)
0044: - (DAC1OutputControl)
0045: - (DAC2OutputControl)
0046: 48aa Len 00f4 (SetupHWAssistedI2CStatus)
0047: 2bdc Len 0110 (ClockSource)
0048: 2cec Len 00ba (MemoryDeviceInit)
0049: - (EnableYUV)
004a: - (DIG1EncoderControl)
004b: - (DIG2EncoderControl)
004c: 4b90 Len 0746 (DIG1TransmitterControl/UNIPHYTransmitterControl)
004d: - (DIG2TransmitterControl/LVTMATransmitterControl)
004e: 52d6 Len 0227 (ProcessAuxChannelTransaction)
004f: 54fe Len 005c (DPEncoderService)
Data Tables:
0000: - (UtilityPipeLine)
0001: - (MultimediaCapabilityInfo)
0002: - (MultimediaConfigInfo)
0003: 0466 Len 00e4 Rev 01:02 (StandardVESA_Timing) (struct size 01c4)
0004: 054a Len 006c Rev 02:02 (FirmwareInfo) (struct size 006c)
0005: 05b6 Len 0304 Rev 02:01 (DAC_Info) (struct size 0008)
0006: 08ba Len 004e Rev 01:03 (LVDS_Info) (struct size 0034)
0007: 1786 Len 045b Rev 03:01 (TMDS_Info) (struct size 001e)
0008: - (AnalogTV_Info)
0009: - (SupportedDevicesInfo)
000a: 0908 Len 00dc Rev 01:01 (GPIO_I2C_Info)
000b: 09e4 Len 000c Rev 01:05 (VRAM_UsageByFirmware) (struct size 000c)
000c: 09f0 Len 001c Rev 01:01 (GPIO_Pin_LUT)
000d: 0a0c Len 00a8 Rev 01:01 (VESA_ToInternalModeLUT)
000e: - (ComponentVideoInfo)
000f: 0ab4 Len 0268 Rev 06:01 (PowerPlayInfo)
0010: - (CompassionateData)
0011: 176e Len 0018 Rev 02:01 (SaveRestoreInfo/DispDevicePriorityInfo)
0012: - (PPLL_SS_Info/SS_Info)
0013: - (OemInfo)
0014: - (XTMDS_Info)
0015: - (MclkSS_Info)
0016: 0d1c Len 011e Rev 01:03 (Object_Info/Object_Header) (struct size 0010)
0017: 0e3a Len 007d Rev 01:01 (IndirectIOAccess)
0018: - (MC_InitParameter/AdjustARB_SEQ)
0019: - (ASIC_VDDC_Info)
001a: 16c0 Len 0040 Rev 03:01 (ASIC_InternalSS_Info/ASIC_MVDDC_Info) (struct size 0034)
001b: 1700 Len 006d Rev 02:03 (TV_VideoMode/DispOutInfo)
001c: 0eb8 Len 06c0 Rev 02:01 (VRAM_Info) (struct size 0354)
001d: - (MemoryTrainingInfo/ASIC_MVDDQ_Info)
001e: - (IntegratedSystemInfo)
001f: - (ASIC_ProfilingInfo/ASIC_VDDCI_Info)
0020: 1578 Len 0148 Rev 03:01 (VoltageObjectInfo/VRAM_GPIO_DetectionInfo)
0021: - (PowerSourceInfo)
Note: atomdis does not show all data tables. At least the W7170 and S7100X vBIOS version had a data table 0x22 - it's address has to be adjusted, too. A simple check of complete success will be finishing the next and final step. If ATOMTableResize refuses to load the modified vBIOS file you likely missed this hidden table.
Final step, most important one:
Since we changed the legacy part the checksum will be wrong. Luckily the Java tool ATOMTableResize will load the result of our work and by pressing fix and save everything
button we will get a newly calculated checksum. If ATOMTableResize refuses to load the modified vBIOS there is still a address to fix.
Of course one can use every other known tool to calculate AMD video BIOS checksums and fix it manually using Hex Fiend. The checksum is saved at absolute address 0x21.
Any questions?