diff options
Diffstat (limited to 'meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/psu-update-delta.py')
-rwxr-xr-x | meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/psu-update-delta.py | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/psu-update-delta.py b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/psu-update-delta.py new file mode 100755 index 0000000..a92cf8e --- /dev/null +++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/psu-update-delta.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python +from __future__ import print_function + +import os.path +import socket +import struct +import sys +import argparse +import traceback + +import hexfile + + +def auto_int(x): + return int(x, 0) + + +parser = argparse.ArgumentParser() +parser.add_argument('--addr', type=auto_int, required=True, + help="PSU Modbus Address") +parser.add_argument('file', help="firmware file") + + +class ModbusTimeout(Exception): + pass + + +class ModbusCRCFail(Exception): + pass + + +class ModbusUnknownError(Exception): + pass + + +class BadMEIResponse(Exception): + pass + + +def rackmon_command(cmd): + srvpath = "/var/run/rackmond.sock" + replydata = [] + if os.path.exists(srvpath): + client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + client.connect(srvpath) + cmdlen = struct.pack("@H", len(cmd)) + client.send(cmdlen) + client.send(cmd) + while True: + data = client.recv(1024) + if not data: + break + replydata.append(data) + client.close() + return ''.join(replydata) + + +def pause_monitoring(): + COMMAND_TYPE_PAUSE_MONITORING = 0x04 + command = struct.pack("@Hxx", COMMAND_TYPE_PAUSE_MONITORING) + result = rackmon_command(command) + (res_n, ) = struct.unpack("@B", result) + if res_n == 1: + print("Monitoring was already paused when tried to pause") + elif res_n == 0: + print("Monitoring paused") + else: + print("Unknown response pausing monitoring: %d" % res_n) + + +def resume_monitoring(): + COMMAND_TYPE_START_MONITORING = 0x05 + command = struct.pack("@Hxx", COMMAND_TYPE_START_MONITORING) + result = rackmon_command(command) + (res_n, ) = struct.unpack("@B", result) + if res_n == 1: + print("Monitoring was already running when tried to resume") + elif res_n == 0: + print("Monitoring resumed") + else: + print("Unknown response resuming monitoring: %d" % res_n) + + +def modbuscmd(raw_cmd, expected=0, timeout=0): + COMMAND_TYPE_RAW_MODBUS = 1 + send_command = struct.pack("@HxxHHL", + COMMAND_TYPE_RAW_MODBUS, + len(raw_cmd), + expected, + timeout) + raw_cmd + result = rackmon_command(send_command) + if len(result) == 0: + raise ModbusUnknownError() + (resp_len,) = struct.unpack("@H", result[:2]) + if resp_len == 0: + (error, ) = struct.unpack("@H", result[2:4]) + if error == 4: + raise ModbusTimeout() + if error == 5: + raise ModbusCRCFail() + print("Unknown modbus error: " + str(error)) + raise ModbusUnknownError() + return result[2:resp_len] + + +def mei_command(addr, func_code, mei_type=0x64, data=None, timeout=0): + i_data = data + if i_data is None: + i_data = ("\xFF" * 7) + if len(i_data) < 7: + i_data = i_data + ("\xFF" * (7 - len(i_data))) + assert len(i_data) == 7 + command = struct.pack("BBBB", addr, 0x2b, mei_type, func_code) + i_data + return modbuscmd(command, expected=13, timeout=timeout) + + +def enter_bootloader(addr): + try: + print("Entering bootloader...") + mei_command(addr, 0xFB, timeout=4000) + except ModbusTimeout: + print("Enter bootloader timed out (expected.)") + pass + + +def mei_expect(response, addr, data_pfx, error, success_mei_type=0x71): + expected = struct.pack("BBB", addr, 0x2B, success_mei_type) + \ + data_pfx + ("\xFF" * (8 - len(data_pfx))) + if response != expected: + print(error + ", response: " + response.encode('hex')) + raise BadMEIResponse() + + +def start_programming(addr): + print("Send start programming...") + response = mei_command(addr, 0x70, timeout=10000) + mei_expect(response, addr, "\xB0", "Start programming failed") + print("Start programming succeeded.") + + +def get_challenge(addr): + print("Send get seed") + response = mei_command(addr, 0x27, timeout=3000) + expected = struct.pack("BBBB", addr, 0x2B, 0x71, 0x67) + if response[:len(expected)] != expected: + print("Bad response to get seed: " + response.encode('hex')) + raise BadMEIResponse() + challenge = response[len(expected):len(expected) + 4] + print("Got seed: " + challenge.encode('hex')) + return challenge + + +def send_key(addr, key): + print("Send key") + response = mei_command(addr, 0x28, data=key, timeout=3000) + mei_expect(response, addr, "\x68", "Start programming failed") + print("Send key successful.") + + +def delta_seccalckey(challenge): + (seed, ) = struct.unpack(">L", challenge) + for i in range(32): + if seed & 1 != 0: + seed = seed ^ 0xc758a5b6 + seed = (seed >> 1) & 0x7fffffff + seed = seed ^ 0x06854137 + return struct.pack(">L", seed) + + +def verify_flash(addr): + print("Verifying program...") + response = mei_command(addr, 0x76, timeout=60000) + mei_expect(response, addr, "\xB6", "Program verification failed") + + +def set_write_address(psu_addr, flash_addr): + # print("Set write address to " + hex(flash_addr)) + data = struct.pack(">LB", flash_addr, 0xEA) + response = mei_command(psu_addr, 0x61, data=data, timeout=3000) + mei_expect(response, psu_addr, "\xA1\xEA", "Set address failed") + + +def write_data(addr, data): + assert(len(data) == 8) + command = struct.pack(">BBB", addr, 0x2b, 0x65) + data + response = modbuscmd(command, expected=13, timeout=3000) + expected = struct.pack(">B", addr) +\ + "\x2b\x73\xf0\xaa\xff\xff\xff\xff\xff\xff" + if response != expected: + print("Bad response to writing data: " + + response.encode('hex')) + raise BadMEIResponse() + + +def send_image(addr, fwimg): + total_chunks = sum([len(s) for s in fwimg.segments]) / 8 + sent_chunks = 0 + for s in fwimg.segments: + if len(s) == 0: + continue + print("Sending " + str(s)) + set_write_address(addr, s.start_address) + for i in xrange(0, len(s), 8): + chunk = s.data[i:i+8] + if len(chunk) < 8: + chunk = chunk + ("\xFF" * (8 - len(chunk))) + sent_chunks += 1 + print("\r[%.2f%%] Sending chunk %d of %d..." % + (sent_chunks * 100.0 / total_chunks, + sent_chunks, total_chunks), end="") + sys.stdout.flush() + write_data(addr, str(bytearray(chunk))) + print("") + + +def reset_psu(addr): + print("Resetting PSU...") + try: + response = mei_command(addr, 0x72, timeout=10000) + except ModbusTimeout: + print("No reply from PSU reset (expected.)") + return + expected = struct.pack(">BBBB", addr, 0x2b, 0x71, 0xb2) +\ + ("\xFF" * 7) + if response != expected: + print("Bad response to unit reset request: " + + response.encode('hex')) + raise BadMEIResponse() + + +def erase_flash(addr): + print("Erasing flash... ") + sys.stdout.flush() + response = mei_command(addr, 0x65, timeout=30000) + expected = struct.pack(">BBBB", addr, 0x2b, 0x71, 0xa5) +\ + ("\xFF" * 7) + if response != expected: + print("Bad response to erasing flash: " + + response.encode('hex')) + raise BadMEIResponse() + + +def update_psu(addr, filename): + pause_monitoring() + fwimg = hexfile.load(filename) + enter_bootloader(addr) + start_programming(addr) + challenge = get_challenge(addr) + send_key(addr, delta_seccalckey(challenge)) + erase_flash(addr) + send_image(addr, fwimg) + verify_flash(addr) + reset_psu(addr) + + +def main(): + args = parser.parse_args() + try: + update_psu(args.addr, args.file) + except: + traceback.print_exc() + print("Firmware update failed") + resume_monitoring() + sys.exit(1) + resume_monitoring() + sys.exit(0) + +if __name__ == "__main__": + main() |