Thursday, August 8, 2013

Minnowboard SPI EEPROM UEFI Recovery

So lets say your UEFI upgrade goes horribly wrong... power outage... bad checksum... wanted to test if that "DO NOT POWER OFF" warning really wasn't lying... whatever... so how do you recover?

Your choices are either RMAing the board or order a DediProg SPI flasher (~$250) both of which can be costly and time consuming for the average hobbyist. Another option is you can prove to be the professional you are and dig out that BeagleBone and few breadboard cables.

Sidenote: The title should read any board with a DediProg header can be saved this way. But really important to pay attention to logic level of the SPI flash, since it can be 1v8, 3v3 (Beaglebone/Minnowboard IO logic level), or even 5v. But don't fret even in that case you can use a few cheap logic shifters (https://www.sparkfun.com/products/8745) since the BeagleBone header provides all those 3 common logic level voltages on the expansion headers.

Now the DediProg header on the Minnow is labeled J11 on the silkscreen (has PROG as well) which the pinout is as below. Additional note be sure the power supply is completely disconnected from the Minnowboard's barrel plug or you MAY LET out the magic smoke.




DediProg header
3V3 GND
CS (Chip Select) CLK (Clock)
MISO (Master In - Slave Out) MOSI (Master Out - Slave In)
NC DEDIPROG_IO3_N (tie to GND)


Now only listing the important pins on BeagleBone you'll need to connect up to the DediProg header.

Beaglebone Expansion Header
P9.1 GND -> GND
P9.2 GND -> DEDIPROG_IO3_N
P9.3 3V3 -> 3V3
P9.28 SPI1_CS -> CS
P9.29 SPI1_D0 -> MISO
P9.30 SPI1_D1 -> MOSI
P9.31 SPI1_CLK -> CLK



Now with everything hooked up accordingly run the following commands on the Beaglebone. This all assumes you have flashrom installed from source or a binary build.

Enabled SPIDEV SPI1 Device Tree overlay

bbb# echo BB-SPIDEV1 > /sys/devices/bone_capemgr.8/slots

Backing up UEFI before a upgrade attempt...

bbb# flashrom -p linux_spi:dev=/dev/spidev1.0 --read backup-uefi-spi.img
Calibrating delay loop... OK.
Found Winbond flash chip "W25Q32.V" (4096 kB, SPI) on linux_spi.
Reading flash... done. 


Writing a UEFI image to the device...

bbb# flashrom -p linux_spi:dev=/dev/spidev1.0 --write backup-uefi-spi.img
flashrom v0.9.6.1-r1705 on Linux 3.8.13-00702-g8ed5354 (armv7l)
flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OK.
Found Winbond flash chip "W25Q32.V" (4096 kB, SPI) on linux_spi.
Reading old flash chip contents... done.
Erasing and writing flash chip...
Erase/write done.
Verifying flash... VERIFIED.

Slight Update - 08/09/2013


My colleague +Darren Hart brought up a good point that if you are doing this from a non-backup UEFI image it will not have the EFI variable defined MAC address within the firmware(offset 0x3B7000 - 0x3B7005)...

Below I created a simple python script that allows you alter the MAC address within the UEFI firmware image. Note I haven't really tested this step by writing the altered image to a board but there is no reason it shouldn't work.


mranostay@flasher:~# cat mac_uefi.py
#!/usr/bin/env python

import sys
OFFSET = 0x3B7000

def usage():
    print "./mac_uefi.py [read|write] minnow.fd"
    sys.exit(0)

def dump_macaddr(file):
    with open(file) as f:
        f.seek(OFFSET)
        mac_data = f.read(6)

    mac_addr = ""
    for i in mac_data:
        mac_addr += hex(ord(i))[2:] + ":"

    print "MAC Address: " + mac_addr[:-1]

def get_mac_addr():
    data = raw_input("Enter MAC address from sticker on board (format 00:13:20:xx:xx:xx) : ")
    data = data.strip()
    data = data.split(":")

    if not len(data) == 6:
        return

    digits = []
    for i in data:
        number = int("0x" + i,16)
        if not number in range(256):
            return
        digits.append(chr(number))

    return digits

def write_mac_addr(mac_addr, file):
    global OFFSET

    with open(file, "rb") as f:
        data = f.read()

    with open(file, "wb") as f:
        process = data[:OFFSET]
        process += "".join(mac_addr)
        process += data[OFFSET + 6:]
        f.write(process)
         
     
def main():
    if not len(sys.argv) == 3:
        usage()


    if sys.argv[1] == "read":
        dump_macaddr(sys.argv[2])


    mac_addr = ""

    if sys.argv[1] == "write":
        while True:
            mac_addr = get_mac_addr()
            if mac_addr:
                write_mac_addr(mac_addr, sys.argv[2])
                break


if __name__ == '__main__':
    main()


mranostay@flasher:~# python mac_uefi.py read MINNOW.fd
MAC Address: ff:ff:ff:ff:ff:ff

mranostay@flasher:~# python mac_uefi.py write MINNOW.fd
Enter MAC address from sticker on board (format 00:13:20:xx:xx:xx) : 00:13:20:11:22:33

root@flasher:~# python mac_uefi.py read MINNOW.fd
MAC Address: 0:13:20:11:22:33

No comments:

Post a Comment