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.

W5170M legacy header using Hex Fiend and special template to parse content

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.

W5170M BIOS header after adjusting addresses and cutting VGA part

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:

W5170M GOP Data and Command tables

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:

W5170M Command and Data tables (corrected after deleting VGA part)

If you compare both source and result you get this view using the Hex Fiend compare function:

Command and Data tables compared

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?