1# Copyright (C) 2021-2022 Intel Corporation. 2# 3# SPDX-License-Identifier: BSD-3-Clause 4# 5 6import ctypes 7import copy 8import inspectorlib.cdata as cdata 9 10class Capability: 11 # Capability names from PCI Local Bus Specification and PCI Express Base Specification 12 _cap_names_ = { 13 0x01: "Power Management", 14 0x02: "AGP", 15 0x03: "VPD", 16 0x04: "Slot Identification", 17 0x05: "MSI", 18 0x06: "CompactPCI Hot Swap", 19 0x07: "PCI-X", 20 0x08: "Hyper Transport", 21 0x09: "Vendor-Specific", 22 0x0a: "Debug port", 23 0x0b: "CompactPCI Central Resource Control", 24 0x0c: "Hot Plug", 25 0x0d: "Subsystem ID and Subsystem Vendor ID", 26 0x0e: "AGP 8x", 27 0x0f: "Secure Device", 28 0x10: "PCI Express", 29 0x11: "MSI-X", 30 0x13: "Conventional PCI Advanced Features", 31 0x14: "Enhanced Allocation", 32 0x15: "FPB", 33 } 34 35 @property 36 def name(self): 37 if self.id in self._cap_names_.keys(): 38 return self._cap_names_[self.id] 39 else: 40 return f"Reserved ({hex(self.id)})" 41 42class CapabilityListRegister(cdata.Struct, Capability): 43 _pack_ = 1 44 _fields_ = [ 45 ('id', ctypes.c_uint8), 46 ('next_cap_ptr', ctypes.c_uint8), 47 ] 48 49# Power Management (0x01) 50 51class PowerManagement(cdata.Struct, Capability): 52 _pack_ = 1 53 _fields_ = copy.copy(CapabilityListRegister._fields_) + [ 54 ('version', ctypes.c_uint16, 3), 55 ('pme_clock', ctypes.c_uint16, 1), 56 ('immediate_readiness_on_return_to_d0', ctypes.c_uint16, 1), 57 ('device_specific_initialization', ctypes.c_uint16, 1), 58 ('aux_current', ctypes.c_uint16, 3), 59 ('d1_support', ctypes.c_uint16, 1), 60 ('d2_support', ctypes.c_uint16, 1), 61 ('pme_support', ctypes.c_uint16, 5), 62 ('power_state', ctypes.c_uint16, 2), 63 ('reserved1', ctypes.c_uint16, 1), 64 ('no_soft_reset', ctypes.c_uint16, 1), 65 ('reserved2', ctypes.c_uint16, 4), 66 ('pme_en', ctypes.c_uint16, 1), 67 ('data_select', ctypes.c_uint16, 4), 68 ('data_scale', ctypes.c_uint16, 2), 69 ('pme_status', ctypes.c_uint16, 1), 70 ('reserved3', ctypes.c_uint8, 6), 71 ('undefined', ctypes.c_uint8, 2), 72 ('data', ctypes.c_uint8), 73 ] 74 75def parse_power_management(buf, cap_ptr): 76 return PowerManagement.from_buffer_copy(buf, cap_ptr) 77 78# MSI (0x05) 79 80def MSI_factory(field_list): 81 class MSI(cdata.Struct, Capability): 82 _pack_ = 1 83 _fields_ = copy.copy(CapabilityListRegister._fields_) + [ 84 ('msi_enable', ctypes.c_uint16, 1), 85 ('multiple_message_capable', ctypes.c_uint16, 3), 86 ('multiple_message_enable', ctypes.c_uint16, 3), 87 ('address_64bit', ctypes.c_uint16, 1), 88 ('per_vector_masking_capable', ctypes.c_uint16, 1), 89 ('reserved', ctypes.c_uint16, 7), 90 ] + field_list 91 92 return MSI 93 94def msi_field_list(addr): 95 field_list = list() 96 msgctrl = MSI_factory([]).from_address(addr) 97 98 if msgctrl.address_64bit == 1: 99 field_list.append(('message_address', ctypes.c_uint64)) 100 else: 101 field_list.append(('message_address', ctypes.c_uint32)) 102 103 field_list.append(('message_data', ctypes.c_uint16)) 104 105 if msgctrl.per_vector_masking_capable: 106 field_list.append(('reserved', ctypes.c_uint16)) 107 field_list.append(('mask_bits', ctypes.c_uint32)) 108 field_list.append(('pending_bits', ctypes.c_uint32)) 109 110 return field_list 111 112def parse_msi(buf, cap_ptr): 113 addr = ctypes.addressof(buf) + cap_ptr 114 field_list = msi_field_list(addr) 115 return MSI_factory(field_list).from_buffer_copy(buf, cap_ptr) 116 117# MSI-X (0x11) 118 119class MSIX(cdata.Struct, Capability): 120 _pack_ = 1 121 _fields_ = copy.copy(CapabilityListRegister._fields_) + [ 122 ('table_size_z', ctypes.c_uint16, 10), 123 ('reserved', ctypes.c_uint16, 3), 124 ('function_mask', ctypes.c_uint16, 1), 125 ('msix_enable', ctypes.c_uint16, 1), 126 ('table_bir', ctypes.c_uint32, 3), 127 ('table_offset_z', ctypes.c_uint32, 29), 128 ('pba_bir', ctypes.c_uint32, 3), 129 ('pba_offset_z', ctypes.c_uint32, 29), 130 ] 131 132 @property 133 def table_size(self): 134 return self.table_size_z + 1 135 136 @property 137 def table_offset(self): 138 return self.table_offset_z << 3 139 140 @property 141 def pba_offset(self): 142 return self.pba_offset_z << 3 143 144def parse_msix(buf, cap_ptr): 145 return MSIX.from_buffer_copy(buf, cap_ptr) 146 147# Module API 148 149capability_parsers = { 150 0x1: parse_power_management, 151 0x5: parse_msi, 152 0x11: parse_msix, 153} 154 155def capabilities(data, cap_ptr): 156 buf = ctypes.create_string_buffer(data, len(data)) 157 158 acc = list() 159 while cap_ptr != 0: 160 caplist = CapabilityListRegister.from_buffer_copy(buf, cap_ptr) 161 if caplist.id in capability_parsers.keys(): 162 acc.append(capability_parsers[caplist.id](buf, cap_ptr)) 163 else: 164 acc.append(caplist) 165 cap_ptr = caplist.next_cap_ptr 166 167 return acc 168