From e63a3e8b740e537750e25bc09a82b8b7b557d4d1 Mon Sep 17 00:00:00 2001 From: Brent Stapleton Date: Mon, 30 Oct 2017 17:01:44 -0700 Subject: fpga load: add update_component function to MPM -update_component takes a byte array containing the data to be written, and a dictionary containing the metadata of the component to be updated -The metadata must contain 'id' and 'filename' -The metadata may contain an md5 hash ('md5') --- mpm/python/usrp_mpm/periph_manager/base.py | 68 ++++++++++++++++++++++++++++++ mpm/python/usrp_mpm/periph_manager/n310.py | 27 ++++++++++++ 2 files changed, 95 insertions(+) diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py index 8c50b060d..6c22e1c5c 100644 --- a/mpm/python/usrp_mpm/periph_manager/base.py +++ b/mpm/python/usrp_mpm/periph_manager/base.py @@ -20,6 +20,7 @@ Mboard implementation base class from __future__ import print_function import os +from hashlib import md5 from builtins import str from builtins import range from builtins import object @@ -122,6 +123,11 @@ class PeriphManagerBase(object): # availability. If the list is empty, no CHDR traffic will be possible over # the network. Example: ['eth1', 'eth2'] chdr_interfaces = [] + # Dictionary containing valid IDs for the update_component function for a + # specific implementation. Each PeriphManagerBase-derived class should list + # information required to update the component, like a callback function + updateable_components = {} + @staticmethod # Yes, this is overridable too: List the required device tree overlays def list_required_dt_overlays(eeprom_md, device_args): @@ -395,6 +401,68 @@ class PeriphManagerBase(object): """ return [dboard.device_info for dboard in self.dboards] + def update_component(self, file_metadata, data): + """ + Updates the device component specified by comp_dict + :param file_metadata: Dictionary of strings containing metadata + :param data: Binary string with the file contents to be written + """ + id_str = file_metadata['id'] + filename = os.path.basename(file_metadata['filename']) + if id_str not in self.updateable_components: + self.log.error("{0} not an updateable component ({1})".format( + id_str, self.updateable_components.keys() + )) + raise NotImplementedError("Update component not implemented for {}".format(id_str)) + self.log.trace("Updating component: {}".format(id_str)) + if 'md5' in file_metadata: + given_hash = file_metadata['md5'] + comp_hash = md5() + comp_hash.update(data) + comp_hash = comp_hash.hexdigest() + if comp_hash == given_hash: + self.log.trace("FPGA bitfile hash matched: {}".format( + comp_hash + )) + raise KeyError("Update component not implemented for {}".format(id_str)) + self.log.trace("Updating component: {}".format(id_str)) + if 'md5' in metadata: + given_hash = metadata['md5'] + comp_hash = md5() + comp_hash.update(data) + comp_hash = comp_hash.hexdigest() + if comp_hash == given_hash: + self.log.trace("Component file hash matched: {}".format( + comp_hash + )) + else: + self.log.error("Component file hash mismatched:\n" + "Calculated {}\n" + "Given {}\n".format( + comp_hash, given_hash)) + raise RuntimeError("Component file hash mismatch") + else: + self.log.error("FPGA bitfile hash mismatched:") + self.log.error("Calculated {}".format(comp_hash)) + self.log.error("Given {}".format(given_hash)) + raise RuntimeError("FPGA Bitfile hash mismatch") + else: + self.log.trace("Loading unverified {} image.".format( + id_str + )) + basepath = os.path.join(os.sep, "tmp", "uploads") + filepath = os.path.join(basepath, filename) + if not os.path.isdir(basepath): + self.log.trace("Creating directory {}".format(basepath)) + os.makedirs(basepath) + self.log.trace("Writing data to {}".format(filepath)) + with open(filepath, 'wb') as f: + f.write(data) + update_func = getattr(self, self.updateable_components[id_str]['callback']) + update_func(filepath, file_metadata) + return True + + def load_fpga_image(self, target=None): """ load a new fpga image diff --git a/mpm/python/usrp_mpm/periph_manager/n310.py b/mpm/python/usrp_mpm/periph_manager/n310.py index 06d9a444c..50123699a 100644 --- a/mpm/python/usrp_mpm/periph_manager/n310.py +++ b/mpm/python/usrp_mpm/periph_manager/n310.py @@ -624,3 +624,30 @@ class n310(PeriphManagerBase): safe_db_eeprom_user_data[blob_id] = blob.encode('ascii') dboard.set_user_eeprom_data(safe_db_eeprom_user_data) + @no_rpc + def update_fpga(self, filepath, metadata): + """ + Update the FPGA image in the filesystem and reload the overlay + :param filepath: path to new FPGA image + :param metadata: Dictionary of strings containing metadata + """ + self.log.trace("Updating FPGA with image at {}" + .format(filepath)) + _, file_extension = os.path.splitext(filepath) + # Cut off the period from the file extension + file_extension = file_extension[1:].lower() + if file_extension == "bit": + self.log.trace("Converting bit to bin file and writing to {}" + .format(self.binfile_path)) + from usrp_mpm.fpga_bit_to_bin import fpga_bit_to_bin + fpga_bit_to_bin(filepath, self.binfile_path, flip=True) + elif file_extension == "bin": + self.log.trace("Copying bin file to {}" + .format(self.binfile_path)) + shutil.copy(filepath, self.binfile_path) + else: + self.log.error("Invalid FPGA bitfile: {}" + .format(filepath)) + raise RuntimeError("Invalid N310 FPGA bitfile") + # TODO: Implement reload procedure + return True -- cgit v1.2.3