Skip to content

Legacy BIOS

Ausdauersportler edited this page Oct 27, 2024 · 11 revisions

This sections describes what you find in general on a PC Laptop MXM card and what tools are needed to make the vBIOS iMac compatible. The AMD BIOS contains in general two parts, the first is called legacy BIOS responsible for all functionality containing data and code for a number or modules, like the VRAM_INFO (responsible for VRAM type support). In general one can assume that the BIOS coming with a card already supports all hardware including the VRAM chips. We will focus on this first part here.

Tools to analyze:

  • atomdis (prints out data and command tables)
  • ATOMTableResize (allow to change complete modules and recalculates the CRC32 checksum)

So very first step will be adjusting the output connector table (OBJ_INFO module) within the legacy BIOS part. This is in general necessary because the PC cards have been used in Laptops with different hardware and different video ports. We need at least one connector of type eDP with correct parameters, and another of type DP to drive the external (miniDP) connected monitor. Only in a single case the BIOS came already correctly configured.

More details to follow ...

Structure of the full BIOS

The usable storage of the BIOS has a fixed length of 0x20000 bytes (131072 in decimal) regardless which chip size you may find on physical MXM cards. These 0x20000 bytes are divided in 256 blocks of 512 bytes. So all BIOS parts together (legacy and GOP and possible other parts) cannot exceed this 256 blocks. In general both the legacy and GOP part have nearly equal size near 120 blocks, which leaves literally no space for additional parts (aka code).

The internal structure demands to start on block boundaries, this is why you always start replace actions at the very same spot or you have to cross check if the starting address were you insert the new BIOS part is a multiple of 512 (byte).

All BIOS parts have a header starting with 0xAA55 and a PCI data structure, short body, starting with 0x50434952 which is PCIR in ascii and both can be visually spotted using hex editor. One can have more than two those parts chained together in a single BIOS as shown below on the right hand side bar.

Recognizing the BIOS structure in Hex Fiend using the PCI_OPTIONS_ROM template

You can see the length of legacy BIOS part counted in blocks is mentioned two times within the print out on the right hand side, in the ROM Header 0 it is called Image Size (79) and within the PCI Data Structure it is called Image Length (79, too). In case you manually modify the BIOS PCI Data structure cutting away code/datat you have to manually recalculate the new block size (measured in 512 byte) and change both entries. In the last step use ATOMTableResize to recalculate the checksum.

You may have noticed from the upper picture that there is a Last Image Index marked. If it has the value 0x80 the computer stops reading/interpreting code after that part. If you add additional parts you have to make sure, the last part has the value 0x80, all former ones the value 0x00. It is quite easy. If you want to disable trailing parts from being used just set the Last Image Index in the last module you want to use to 0x80 and recalculate the checksum. The first part has an index value of 0x00, the latter GOP part an index value of 0x80. We will see EFI BIOS versions having four such parts, of course only the last one using the 0x80.

The checksum does only apply to the first legacy part. Whenever you change data there a recalculation is necessary. Changing the following parts does not change the checksum at all!

Structure of the Legacy Part

In order to understand modifications explained later here a short overview. The legacy BIOS starts with it's ROM Signature:

  • PCI Header starting with AA55
  • PCI Data Structure starting with ascii PCIR
  • a few bytes of more data, a final ascii AMD ATOMBIOS, some trailing 0x00000000 blocks to fill the gap until the VGA parts starts
  • vga bios itself (starting in the W5170M BIOS at 0x280)
  • COMMAND TABLE begins with mit A6000101 (starting in the W5170M BIOS at 0x9D76)
  • DATA TABLE begins either with 4A000101 or 48000101 (starting in the W5170M BIOS at 0x9E1C)
  • commands (code of modules, module by module, as described in COMMAND table)
  • data (of modules)

You can use the tool atomdis to print and disassemble contents:

Usage:  ../../../Tools/ATOMDIS/atomdis [<opts>] <file> <cmd> [<cmd>...]
Opts:   -o <vga_offset>      Specify offset of VGA bios in <file>
        -r <registers.xml>   Load registers specification file
        -a <addr_format>     Format for command addresses. Default: '%04x: '
        -A                   Output addresses instead of offsets
                             for MM register, FB, and BIOS data accesses
Cmds:   i                    Dump info on AtomBIOS
        l                    Info + Table list
        x <start> <len>      Hexdump
        d <nr>               Data table dump
        c <nr>               Command table disasm
        C <start>            Table disasm (debug)
        T                    Test (debug)
        F                    Full dump (long output, sanitized)
all values in hex

If you want to list all command and data tables (start addresses and length in hex values):

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)

If you want to print/disassemble the Object_Info/Object_Header (short OBJ_INFO) module, it is module number 0x16 within the above printed Data Table. This module contains the connector information per port used to identify the connector types (LVDS, eDP, DP, HDMI, VGA, DVI, etc.) and parameters. So it is key to modify it properly or find an already proper working module from a BIOS of the same AMD GPU family.

atomdis W5170M-GOP.rom d 16
Read 20000 bytes of data from W5170M-GOP.rom

data_table  0000a71c  #16  (Object_Info/Object_Header):

  Size         011e
  Format Rev.  01
  Param Rev.   00
  Content Rev. 03

  00000000:            8a02 3e00  0000 ce00  0000 1200        ..>.........
  00000010: 0000 0401  0000 0200  0a00 1431  0011 2121    ...........1..!!
  00000020: 0800 0a00  1331 0011  2122 8000  0a00 1332    .....1..!".....2
  00000030: 0011 2021  0002 0a00  1333 0011  2022 0400    .. !.....3.. "..
  00000040: 0000 1431  6200 6800  0000 1331  7d00 8300    ...1b.h....1}...
  00000050: 0000 1332  9800 9e00  0000 1333  b300 b900    ...2.......3....
  00000060: 0000 0121  2100 0000  040c 0100  1001 0000    ...!!...........
  00000070: 0200 0000  0104 9200  0204 0300  ff01 2122    ..............!"
  00000080: 0000 0004  0c01 0010  0200 0008  0000 0001    ................
  00000090: 0493 0002  0404 00ff  0120 2100  0000 040c    ......... !.....
  000000a0: 0100 2002  0000 8000  0000 0104  9000 0204    .. .............
  000000b0: 0100 ff01  2022 0000  0004 0c01  0030 0200    .... ".......0..
  000000c0: 0000 0200  0001 0491  0002 0402  00ff 0400    ................
  000000d0: 0000 2121  f200 f800  0000 2122  fd00 0301    ..!!......!"....
  000000e0: 0000 2021  0801 0e01  0000 2022  1301 1901    .. !...... "....
  000000f0: 0000 0100  1101 1431  1404 0300  ff01 0011    .......1........
  00000100: 0113 3114  0403 00ff  0100 1101  1332 1404    ..1..........2..
  00000110: 0300 ff01  0011 0113  3314 0403  00ff         ........3.....  

  NOTE: General revisionless dumper only.
  NOTE: Data part see radeonhd

  0000:  ATOM_COMMON_TABLE_HEADER sHeader                    :
    0000:  USHORT usStructureSize                              = 0x011e     (286)
    0002:  UCHAR ucTableFormatRevision                         = 0x01       (1)
    0003:  UCHAR ucTableContentRevision                        = 0x03       (3)
  0004:  USHORT usDeviceSupport                              = 0x028a     (650)
  0006:  USHORT usConnectorObjectTableOffset                 = 0x003e     (62)
  0008:  USHORT usRouterObjectTableOffset                    = 0x0000     (0)
  000a:  USHORT usEncoderObjectTableOffset                   = 0x00ce     (206)
  000c:  USHORT usProtectionObjectTableOffset                = 0x0000     (0)
  000e:  USHORT usDisplayPathTableOffset                     = 0x0012     (18)

  Total data structure size:  0010

use ATOMTableResize to replace modules and recalculate the CRC32 checksum of the legacy part

This Java tool offers a GUI to extract and replace modules completely from the BIOS. While replacing it recalculates all absolute new addresses within the data and command tables and fixes the CRC checksum of the legacy part.

You can mix and match modules found within the legacy BIOS of a common AMD GPU family (e.g. Venus) using this software. So if you already have adjusted the OBJ_INFO module responsible for connector settings to drive the iMac internal display you can pull this module out of a working BIOS and transplant it into a currently non working vBIOS of the same family. In fact I used the OBJ_INFO module found in the W5170M BIOS from a blue AMD card later for all other cards to enable the backlight on internal display on iMacs (here a green HP W5170M, a red Dell M5100 and finally a blue MXM-B M6000). This is the most easy way to get this most important part done.

The same applies to adjustment for VRAM support. As you may have noticed there are mostly three suppliers of VRAM, Samsung, Hynix and Micron/Elpida. Not all BIOS versions support all memory types from the start. If you want to add support for a new card of the same family with different VRAM chips just pull the VRAM_INFO module out of the BIOS coming with that new card and replace it in the already working iMac BIOS. It is that easy!

After replacing modules always save the file either by pressing save or save and fix everything. This way the address tables are rebuild and the checksum will be updated.

use Hex Fiend to manually edit the OBJ_INFO module to adjust the connector settings

If you cannot find a proper working OBJ_INFO module you have to adjust it manually within the hex editor. This is the dark art ....