Uncategorized

Python shim over QEMU to startup BMC

In case anyone wants to run a/some Supermicro firmware image under QEMU… here’s a short script that sets some network features (thanks to binarly for figuring out the hard stuff!)

It still crashes and burns because I didn’t use the gdb trick they mention in the URL, but you can now talk to the BMC as if it were almost a real boy (you must run as root if doing ports < 1024, blah blah.) Here how it looks like to me when starting QEMU with an unzip'd version from of the firmware

python3 qemu-bmc.py REDFISH_X10_323.bin

Executing QEMU... while starting, control-C should stop it.
After it's booted, QEMU's control-a followed by an 'x' should kill it.

qemu-system-arm
    -m 128
    -M supermicrox11-bmc
    -nographic
    -drive file=REDFISH_X10_323.bin,format=raw,if=mtd
    -net nic
    -net user,hostfwd=:127.0.0.1:22-:22,hostfwd=:127.0.0.1:443-:443,hostfwd=udp::623-:623,hostfwd=:127.0.0.1:23-:23,hostname=qemu

qemu-system-arm: warning: nic ftgmac100.1 has no peer


U-Boot 2009.01 (Nov 12 2014 - 11:37:48) ASPEED (v.0.21)

I2C:   ready
DRAM:  128 MB
Flash: 32 MB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
H/W:   AST2400 series chip
COM:   port1 and port2
PWM:   port[ABCDH]
Hit any key to stop autoboot:  0
## Booting kernel from Legacy Image at 21400000 ...

[....]

Here’s the program –

#!/usr/bin/env python3

#
# start qemu with BMC firmware under python's expect
#
#   Usage: $0 firmware.img
#
# Requires root because it forwards 3 ports to localhost -
#
#   TCP - 22 and 443
#   UDP - 623
#
# (This could be changed by using ports > 1024, presumably, but I wanted standard
# tools - e.g. ipmitool and the like - to work w/o muss, simply by pointing to
# localhost)
#

import os
import pexpect
import sys

if len(sys.argv) != 2:
    print("Usage: %s firmware.img" % sys.argv[0])
    sys.exit(1)
else:
    # FW   = "BMC_X11AST2400-3101MS_20230214_1.66_STDsp.bin"
    FW = sys.argv[1]

# what's the uid - well, effective uid
eid = os.geteuid()

if eid:
    sys.stdout.write("Error - must be run as root, not UID %d" % eid)
    sys.exit(2)

#
# emulate a supermicro BMC with QEMU
#
# Ports forwarded:
#   TCP: 443 => localhost:443
#   TCP:  22 => localhost:22
#   UDP: 623 => (udp) localhost:623
#

QEMU="""qemu-system-arm
    -m 128
    -M supermicrox11-bmc
    -nographic
    -drive file=%s,format=raw,if=mtd
    -net nic
    -net user,hostfwd=:127.0.0.1:22-:22,hostfwd=:127.0.0.1:443-:443,hostfwd=udp::623-:623,hostfwd=:127.0.0.1:23-:23,hostname=qemu
"""
% FW

print("""
Executing QEMU... while starting, control-C should stop it.
After it's booted, QEMU's control-a followed by an 'x' should kill it.
"""
)

print(QEMU)

qemu = pexpect.spawn(QEMU)

# don't
qemu.timeout = 999999
# echo output
qemu.logfile = sys.stdout.buffer

# start the fireworks

qemu.expect("Please press Enter to activate this console.")
qemu.sendline("")
qemu.expect("/ #")

qemu.sendline("id")
qemu.expect("/ #")

# make networking a bit more sane
qemu.sendline("ip link set eth0 addr 4A:0A:AB:7C:96:2F")
qemu.expect("/ #")
qemu.sendline("ifconfig eth0 10.0.2.15")
qemu.expect("/ #")
qemu.sendline("ifconfig eth0 netmask 255.255.255.0")
qemu.expect("/ #")
qemu.sendline("ifconfig eth0 broadcast 10.0.2.255")
qemu.expect("/ #")
qemu.sendline("ifconfig eth0 up")
qemu.expect("/ #")
qemu.sendline("ip route add 0.0.0.0/0.0.0.0 via 10.0.2.2")
qemu.expect("/ #")

print("Ok... ready to rumble?")

qemu.logfile = None
qemu.interact()
qemu.kill(1)