def __init__(self, dbdf, debug_level="critical"): """ """ self.dbdf = dbdf self.logger = LoggerFactory().get('mlxpci',debug_level) temp_dir = tempfile.gettempdir() self.dump_file_path = os.path.join(temp_dir, "{0}.pkl".format(str(self.dbdf))) self._pci_conf_space = {} # <capability-id> : <value:integer> self._pci_express_offset = None
class PCIDeviceBase(object): """ Base class that represent a PCI device """ def __init__(self, dbdf, debug_level="critical"): """ """ self.dbdf = dbdf self.logger = LoggerFactory().get('mlxpci',debug_level) temp_dir = tempfile.gettempdir() self.dump_file_path = os.path.join(temp_dir, "{0}.pkl".format(str(self.dbdf))) self._pci_conf_space = {} # <capability-id> : <value:integer> self._pci_express_offset = None def read(self, offset, size, skip_offset_list=None): """ Read from PCI configuration space skip_offset_list is a list of offsets to skip reading for some offsets """ if self._is_valid_range(offset, size) is False: raise Exception("offset [{0}] with size {1} is not a valid offset to read from.".format(offset, size)) self.logger.debug("Reading offset[{0}] with size [{1}] for PCI device[{2}]".format(offset, size, self.dbdf)) def write(self, offset, size, bytes_list): """ Write to PCI configuration space """ if self._is_valid_range(offset, size) is False: raise Exception("offset [{0}] with size {1} is not a valid offset to write.".format(offset, size)) self.logger.debug("Writing [{0}] on offset[{1}] for PCI device[{2}]".format(bytes_list, offset, self.dbdf)) def save_configuration_space(self, to_file=False): """ Save PCI configuration space of the device """ self.logger.debug("Saving configurations for PCI device[{0}]".format(self.dbdf)) visited_capabilities = [] # A variable to save the visited capability to avoid an infinite loop # read and save PCI header 0x0-0x3f self.logger.debug("Reading and saving pci header [0x0-0x3f] ...") self._pci_conf_space["pci_header"] = self.read(0x0, 0x40) self.logger.debug("Reading and saving legacy list ...") pci_legacy_ptr = self.read_byte(CONFIG_SPACE_PTR_OFFSET) while pci_legacy_ptr != 0: assert 0 < pci_legacy_ptr < MAXIMUN_LEGACY_CAP_SIZE, "Legacy pointer (<{0:#x}>) is out of range".format(pci_legacy_ptr) capability_id = self.read_byte(pci_legacy_ptr) if capability_id in CAP_LEGACY_DICT: if capability_id in visited_capabilities: raise RuntimeError("Capability id {0} was seen before (avoid infinite loop).".format(capability_id)) cap_size = CAP_LEGACY_DICT[capability_id] self._pci_conf_space["{0}_leg".format(capability_id)] = self.read(pci_legacy_ptr, cap_size) visited_capabilities.append(capability_id) pci_legacy_ptr = self.read_byte(pci_legacy_ptr + 1) self.logger.debug("Reading and saving extended list ...") pci_extended_ptr = MAXIMUN_LEGACY_CAP_SIZE visited_capabilities = [] while pci_extended_ptr and pci_extended_ptr != 0xfff: # Reading PCI Extended Configurations on VM will return 0XFFFF For FBSD assert pci_extended_ptr >= MAXIMUN_LEGACY_CAP_SIZE, "Extended pointer (<{0:#x}>) is out of range".format(pci_extended_ptr) capability_id = self.read_word(pci_extended_ptr) if capability_id is None: # Reading PCI Extended Configurations on VM will return None break if capability_id in CAP_EXTENDED_DICT: if capability_id in visited_capabilities: raise RuntimeError("Capability id {0} was seen before (avoid infinite loop).".format(capability_id)) cap_size = CAP_EXTENDED_DICT[capability_id] self._pci_conf_space["{0}_ext".format(capability_id)] = self.read(pci_extended_ptr, cap_size) visited_capabilities.append(capability_id) pci_extended_ptr = self.read_word(pci_extended_ptr + 2) >> 4 if to_file: self.logger.debug("Save PCI configuration space to a file ...") with open(self.dump_file_path, 'wb') as f: pickle.dump(self._pci_conf_space, f, pickle.HIGHEST_PROTOCOL) self.logger.debug("PCI Configuration space dict {0} saved to a file {1}".format(self._pci_conf_space, self.dump_file_path)) self._pci_conf_space = {} self.logger.info("PCI Configurations for [{0} was saved successfully]".format(self.dbdf)) def restore_configuration_space(self): """ Restore PCI configuration space of the device """ self.logger.debug("Restoring configurations for PCI device[{0}]".format(self.dbdf)) if self._pci_conf_space == {}: self._pci_conf_space = self._get_pci_conf_from_file() visited_capabilities = [] # Save the visited capability to avoid infinit loop # Read and save PCI configuration space offset from 0x0-0xfff # Reading the pci conf space one time to have better performance cached_data = self.read(offset=0x0, size=MAX_PCI_OFFSET, skip_offset_list=MELLANOX_PCI_SKIP_LIST) # write pci header 0x0-0x3f self.logger.debug("Writing PCI header [0x0-0x3f] ...") self.write(0x0, 0x40, self._pci_conf_space["pci_header"]) # Write Legacy list self.logger.debug("Writing back Legacy list ...") pci_legacy_ptr = self._fetch_byte(cached_data, CONFIG_SPACE_PTR_OFFSET) while pci_legacy_ptr: assert 0 < pci_legacy_ptr < MAXIMUN_LEGACY_CAP_SIZE, "Legacy pointer (<{0:#x}>) is out of range".format(pci_legacy_ptr) capability_id = self._fetch_byte(cached_data, pci_legacy_ptr) if capability_id in CAP_LEGACY_DICT and "{0}_leg".format(capability_id) in self._pci_conf_space: if capability_id in visited_capabilities: raise RuntimeError("Capability id {0} was seen before (avoid infinite loop).".format(capability_id)) cap_size = CAP_LEGACY_DICT[capability_id] self.write(pci_legacy_ptr, cap_size, self._pci_conf_space["{0}_leg".format(capability_id)]) visited_capabilities.append(capability_id) pci_legacy_ptr = self._fetch_byte(cached_data, pci_legacy_ptr + 1) # Write Extended list self.logger.debug("Writing back Extended list ...") pci_extended_ptr = MAXIMUN_LEGACY_CAP_SIZE visited_capabilities = [] while pci_extended_ptr and pci_extended_ptr != 0xfff: assert pci_extended_ptr >= MAXIMUN_LEGACY_CAP_SIZE, "Extended pointer (<{0:#x}>) is out of range".format(pci_extended_ptr) capability_id = self._fetch_word(cached_data, pci_extended_ptr) if capability_id is None: # Reading PCI Extended Configurations on VM will return None break if capability_id in CAP_EXTENDED_DICT and "{0}_ext".format(capability_id) in self._pci_conf_space: if capability_id in visited_capabilities: raise RuntimeError("Capability id {0} was seen before (avoid infinite loop).".format(capability_id)) cap_size = CAP_EXTENDED_DICT[capability_id] self.write(pci_extended_ptr, cap_size, self._pci_conf_space["{0}_ext".format(capability_id)]) visited_capabilities.append(capability_id) pci_extended_ptr = self._fetch_word(cached_data, pci_extended_ptr + 2) >> 4 self.logger.info("PCI Configurations for [{0} was restored successfully]".format(self.dbdf)) def _is_valid_range(self, offset, size): """ A method to validate offset and size it returns True if the offset is valid, else False """ return (offset + size <= 0xfffe) def _get_pci_express_offset(self): """ Get PCI express offset """ visited_capabilities = [] pci_legacy_ptr = self.read_byte(CONFIG_SPACE_PTR_OFFSET) while pci_legacy_ptr != 0: assert 0 < pci_legacy_ptr < MAXIMUN_LEGACY_CAP_SIZE, "Legacy pointer (<{0:#x}>) is out of range".format(pci_legacy_ptr) capability_id = self.read_byte(pci_legacy_ptr) if capability_id == 0x10: return pci_legacy_ptr if capability_id in CAP_LEGACY_DICT: if capability_id in visited_capabilities: raise RuntimeError("Capability id {0} was seen before (avoid infinite loop).".format(capability_id)) visited_capabilities.append(capability_id) pci_legacy_ptr = self.read_byte(pci_legacy_ptr + 1) raise RuntimeError("Failed to find pci express offset") @property def dll_link_active(self): """ Get dll link active value """ link_status_reg_offset = 0x12 link_status_reg_address = self._pci_express_offset + link_status_reg_offset link_status_reg_value = self.read_word(link_status_reg_address) dll_value_bin = bin(link_status_reg_value).replace('0b', '').zfill(16)[-13-1] #bit 13 return int(dll_value_bin) @property def dll_link_active_reporting_capable(self): """ return Link-capabilites[dll-link-active-reporting-capbale] bit (indicates if dll-link-active is valid) """ link_capabilities_reg_offset = 0xc link_capabilities_reg_address = self._pci_express_offset + link_capabilities_reg_offset link_capabilities_reg_value = self.read_long(link_capabilities_reg_address) dll_reporting_cable_bin = bin(link_capabilities_reg_value).replace('0b', '').zfill(32)[-20-1] #bit 20 return int(dll_reporting_cable_bin) # Helper methods ################ def _fetch_byte(self, cached_pci_conf, offset): """ Helper method that extract a byte from a data that was read before """ return cached_pci_conf[offset] def _fetch_word(self, cached_pci_conf, offset): """ Helper method that extract a word from a data that was read before """ if offset + 1 < len(cached_pci_conf): # Device does not have extended PCI configurationfor VMs byte0 = "{0:x}".format(cached_pci_conf[offset]).zfill(2) byte1 = "{0:x}".format(cached_pci_conf[offset + 1]).zfill(2) return int("{0}{1}".format(byte1, byte0), 16) else: return None def _get_pci_conf_from_file(self): """ This is to load pci conf space from a saved pickle """ self.logger.debug("Reading pci space conf from [{0}]".format(self.dump_file_path)) try: with open(self.dump_file_path, 'rb') as f: return pickle.load(f) except Exception: raise RuntimeError( "{0} doesn't exist. Please save before load".format(self.dump_file_path))