Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documenting BIOS-EC flash comms and potential alternative flasher with non-Lenovo BIOSes #241

Open
nullableVoidPtr opened this issue Jul 1, 2023 · 1 comment

Comments

@nullableVoidPtr
Copy link

I've taken to reversing the Thinkpad BIOS and EC flashing procedure recently. (Side note: if there was something missing or unknown like the F12 action byte, I could probably take a look)

This has been rather fruitful and the MEC1618 perfectly lines up with what I'm observing in the assembly (so I wonder what is the actual difference?), and I've even identified that this EC definition could mostly be applied to X230.

That aside, here's a somewhat vague overview of the process on the BIOS' side:

  1. Authentication via EC-SMB-HC
    • Write Block (Address 02h, Command 14h)
      5A 84 65 60 47
    • Assert that the command has completed (SMB_STS[7] == 1)
  2. Initialise flasher
    Since the embedded flash cannot be written to while executed, a small program must be sideloaded onto the EC's RAM which can then update the embedded flash
    • Preparation
      • Pad the code to the nearest -2 mod 8th byte
      • Append the CRC16_CCITT_FALSE checksum
      • Ensure the payload is smaller than 1000h bytes, as it will be written to 63000h
      • Encrypt the payload using Blowfish in CBC mode with the following parameters:
        Key: 964C64A9FE22DB7A4AFE9FD50A84FB0A6126E3D7A21986D8B8FB49913A9691BB
        IV: 243F6A8885A308D3
    • Upload
      • Send command CFh to EC
      • Receive one byte from EC data port and assert that it is 88h
      • Send 55h to EC data port
      • Send payload length as big-endian short to EC data port
      • Send encrypted payload to EC data port
        The official flasher program does the following:
    • Disable the watchdog timer by setting the 0th bit of the WDT control register (WDT_CONTROL @ F00404h) to 0
    • Copy Blowfish keys passed to it by the EC firmware at entrypoint call
    • Allow Register Control by setting the 0th bit of the embedded flash configuration register (FLASH_CONFIG @ FF3910h) to 1
    • Enable Register Control by setting the 0th bit of the embedded flash command register (FLASH_COMMAND @ FF3908h) to 1
    • Expose 3 new EC commands
      • Erase: F0h <sector index 1b>
        • Note: a sector in MEC is 2048 bytes
        • Toggle flash access by setting the 8th bit (EEPROM_Access) of FLASH_CONFIG to 1
        • Check the status of the flash address FIFO by reading the 2nd bit (Address_Full) of the embedded flash status register (FLASH_STATUS @ FF390Ch)
        • If full, return error status FEh over EC data
        • Check the mode of the embedded flash controller by reading the bits 0 and 1 (Flash_Mode) of FLASH_STATUS
        • If the flash controller is not on standby (0), return error status FEh over EC data
        • Switch to erase mode by setting Flash_Mode to 3
        • Erase the specified sector by writing it to bits 17 through 11 inclusive of the embedded flash address register (FLASH_ADDRESS @ FF3904h)
        • Wait until the 0th bit (Busy) of FLASH_STATUS is 0
        • Change the mode of the embedded flash controller to standby
        • Return success status F9h over EC data
      • Write: F1h <address 3b> <length 1b>
        • Ensure that length is less than or equal to 0x80
        • Read length-bytes of data
        • If a portion of data is within an encrypted region, decrypt using the initialised Blowfish keys
        • Extend the CRC16_CCITT_FALSE checksum with each byte of data unless the flash address is 2FFFCh, at which point it compares the checksum
        • If the checksum fails, set the previous byte to 0xaf(?)
        • Toggle flash access
        • If there is an error (1 in any of bits 8 through 10 of the embedded flash status register), return error status FCh over EC data
        • Switch to burst and program mode by setting bits 1 and 2 of the embedded flash command register to 1
        • Wait until the flash address FIFO is not full
        • Write the address to FLASH_ADDRESS
        • Wait until the flash controller is not busy
        • For every 4 bytes of data
          • Write to the flash data FIFO register (FLASH_DATA @ FF3900h)
          • Wait until the flash controller is not busy
        • Disable burst mode and switch to standby mode by clearing the first 3 bits of the embedded flash command register
        • Return success status FAh over EC data
      • Reset: F9h
        • Send success status F9h over EC data
        • Disable Register Control
        • Disallow Register Control
        • Reload the watchdog timer, writing 0005h to the WDT load register (F00400h)
        • Enable the watchdog timer while clearing its status (1st bit to 1).
        • Start the watchdog timer by writing 01h to the WDT kick Register (F00408h)
        • With the low interval, the EC is forcefully reset by the watchdog timer
      • Default handler returns invalid status FFh over EC data
  3. Flash firmware
    • For each sector 0 through 95, call Erase command
    • For each sector 0 through 95:
      • For each 80-byte chunk, call Write command
      • If an error status is returned, redo once:
        • Read an EC data byte (status) and assert it is FDh
        • Call Erase command on the current sector
        • Call unknown command (F4h) and assert a return status of FAh
        • Restart writing from the first chunk
    • Clear the 6th bit of CMOS register 45h and update the CMOS checksum
    • Call Reset command
    • Call several unknown UEFI interfaces
      • One appears to be modifying a byte variable
    • Call gRS->ResetSystem

This analysis was done on the last modifiable version, so there are signature checks not documented here; I suspect that they are done on BIOS side before uploading to EC though. The unknown EC commands may only be applicable to other Lenovo lines

I'll look at making a PR for docs/ outlining the above, but one could hypothetically develop an alternative EC flash for use with corebooted systems which does not implement the Lenovo update processes, eliminating the need for DOSFLASH or reverting to official BIOS to patch EC.

@hamishcoleman
Copy link
Owner

This sounds all very interesting. Would love to see a PR with some more docs!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants