tools/ocp_recovery/ocp_recovery.py (60 lines of code) (raw):

#!/usr/bin/env python3 """ Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. """ import os import sys from periphery import I2C # NOTE: This script is incomplete. Development halted on it because the periphery module was not # returning the I2C data for read operations. The read was succeeding, but the data was not # populated. # # It remains as a starting point should it ever become useful in the future. def print_usage (): print ("Usage: {0} <device> <addr>".format (sys.argv[0])) sys.exit (1) if (len (sys.argv) < 3): print_usage () def calculate_smbus_pec (data): """ Calculate the SMBus pec for a block of data. :param data: The raw data to use for the calculation. This must include all address and overhead bytes. :return The calculated PEC value. """ crc = 0 for byte in data: crc = (crc ^ byte) & 0xff for i in range (0, 8): if (crc & 0x80): crc = ((crc << 1) ^ 0x07) & 0xff else: crc = (crc << 1) & 0xff return crc def verify_smbus_pec(data, pec): """ Verify the PEC byte for an SMBus block read or write. An exception is raised if the PEC does not match. :param data: The raw data that is protected by the PEC. This must include any and all address bytes as well as all data bytes. """ crc = calculate_smbus_pec (data) if (crc != pec): raise Exception ("Rx PEC error. PEC={0}, Rx={1}".format (hex (pec), hex (crc))) class OcpRecovery: """ Interface to a device that implements the recovery protocol specified by the OCP security working group. https://www.opencompute.org/wiki/Security """ def __init__ (self, i2c, address): """ Initialize the OCP recovery interface using and I2C device. :param i2c: A device number or path to use for device communications. :param address: The 7-bit I2C address of the target device. """ if (str (i2c).__contains__ ("i2c")): self.i2c = I2C (i2c) else: self.i2c = I2C ("/dev/i2c-{0}".format (i2c)) self.addr = int (address, 16) def block_read (self, command, bytes, pec=True): """ Execute an SMBus block read command. :param command: The command to send to the device. :param bytes: The number of bytes to read back. :param pec: Flag to indicate if the PEC byte should be read. :return A bytearray containing the data payload. No SMBus overhead will be returned. """ smbus_overhead = 1 if (pec): smbus_overhead += 1 rx_data = bytearray (bytes + smbus_overhead) tx = I2C.Message (bytearray ([command])) rx = I2C.Message (rx_data, read=True) xfer = [tx, rx] self.i2c.transfer (self.addr, xfer) print (rx_data) if (not pec): return rx_data[1:] else: raw_data = [self.addr << 1, command, (self.addr << 1) | 1] raw_data += rx_data[:-1] verify_smbus_pec (raw_data, rx_data[-1]) return rx_data[1:-1] def block_write (self, command, data, pec=True): """ Execute an SMBus block write command. :param command: The command to send to the device. :param data: The payload data. :param pec: Flag to indicate if the PEC byte should be sent. """ tx_data = [self.addr << 1, command, len (data) & 0xff] tx_data += data if (pec): tx_data.append (calculate_smbus_pec (tx_data)) tx = I2C.Message (tx_data[1:]) xfer = [tx] self.i2c.transfer (self.addr, xfer) class ProtCap: """ Handler for the PROT_CAP message in the recovery protocol. This command is used to retrieve device capabilities. """ def __init__ (self, recovery): """ Read and parse the capabilities of a device. :param recovery: An OcpRecovery instance that will be used to communicate with the device. """ data = recovery.block_read (34, 15) print (data) ocp = OcpRecovery (sys.argv[1], sys.argv[2]) ProtCap (ocp)