diff --git a/misc/config_tools/target/acpiparser/__init__.py b/misc/config_tools/target/acpiparser/__init__.py new file mode 100644 index 000000000..1f28c6cfd --- /dev/null +++ b/misc/config_tools/target/acpiparser/__init__.py @@ -0,0 +1,30 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import sys + +from acpiparser.apic import APIC +from acpiparser.asf import ASF +from acpiparser.dmar import DMAR +from acpiparser.facp import FACP +from acpiparser.rtct import RTCT + +def parse_table(signature, path=None): + if not path: + path = f"/sys/firmware/acpi/tables/{signature}" + signature = signature.rstrip("!") + fn = getattr(sys.modules[f"acpiparser.{signature.lower()}"], signature) + return fn(path) + +def make_parser(signature): + def parse(path=None): + return parse_table(signature, path) + return parse + +parse_apic = make_parser('APIC') +parse_asf = make_parser('ASF!') +parse_dmar = make_parser('DMAR') +parse_facp = make_parser('FACP') +parse_rtct = make_parser('RTCT') diff --git a/misc/config_tools/target/acpiparser/_utils.py b/misc/config_tools/target/acpiparser/_utils.py new file mode 100644 index 000000000..720ea7ddb --- /dev/null +++ b/misc/config_tools/target/acpiparser/_utils.py @@ -0,0 +1,68 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import ctypes + +import acpiparser.cdata as cdata +import acpiparser.unpack as unpack + +class TableHeader(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('signature', ctypes.c_char * 4), + ('length', ctypes.c_uint32), + ('revision', ctypes.c_ubyte), + ('checksum', ctypes.c_ubyte), + ('oemid', ctypes.c_char * 6), + ('oemtableid', ctypes.c_char * 8), + ('oemrevision', ctypes.c_uint32), + ('creatorid', ctypes.c_char * 4), + ('creatorrevision', ctypes.c_uint32), + ] + +ASID_SYSTEM_MEMORY = 0 +ASID_SYSTEM_IO = 1 +ASID_PCI_CFG_SPACE = 2 +ASID_EMBEDDED_CONTROLLER = 3 +ASID_SMBUS = 4 +ASID_PCC = 0xA +ASID_FFH = 0x7F + +def _asid_str(asid): + if asid >= 0xC0 and asid <= 0xff: + return 'OEM Defined' + _asid = { + ASID_SYSTEM_MEMORY: 'System Memory', + ASID_SYSTEM_IO: 'System IO', + ASID_PCI_CFG_SPACE: 'PCI Configuration Space', + ASID_EMBEDDED_CONTROLLER: 'Embedded Controller', + ASID_SMBUS: 'SMBus', + ASID_PCC: 'Platform Communications Channel (PCC)', + ASID_FFH: 'Functional Fixed Hardware', + } + return _asid.get(asid, 'Reserved') + +_access_sizes = { + 0: 'Undefined', + 1: 'Byte access', + 2: 'Word access', + 3: 'Dword access', + 4: 'Qword access', +} + +class GAS(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('address_space_id', ctypes.c_uint8), + ('register_bit_width', ctypes.c_uint8), + ('register_bit_offset', ctypes.c_uint8), + ('access_size', ctypes.c_uint8), + ('address', ctypes.c_uint64), + ] + + _formats = { + 'address_space_id' : unpack.format_function("{:#x}", _asid_str), + 'access_size' : unpack.format_table("{}", _access_sizes), + } diff --git a/misc/config_tools/target/acpiparser/apic.py b/misc/config_tools/target/acpiparser/apic.py new file mode 100644 index 000000000..86bc75f56 --- /dev/null +++ b/misc/config_tools/target/acpiparser/apic.py @@ -0,0 +1,281 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import ctypes +import copy + +import acpiparser.cdata as cdata +import acpiparser.unpack as unpack +from acpiparser._utils import TableHeader + +class APICSubtable(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('subtype', ctypes.c_uint8), + ('length', ctypes.c_uint8), + ] + +class local_apic_flags_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('enabled', ctypes.c_uint32, 1), + ] + +class local_apic_flags(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint32), + ('bits', local_apic_flags_bits), + ] + +class APICSubtableLocalApic(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(APICSubtable._fields_) + [ + ('proc_id', ctypes.c_uint8), + ('apic_id', ctypes.c_uint8), + ('flags', local_apic_flags), + ] + +class APICSubtableIOApic(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(APICSubtable._fields_) + [ + ('io_apic_id', ctypes.c_uint8), + ('reserved', ctypes.c_uint8), + ('io_apic_addr', ctypes.c_uint32), + ('global_sys_int_base', ctypes.c_uint32), + ] + +mps_inti_polarity = { + 0b00: 'Conforms to bus specifications', + 0b01: 'Active high', + 0b11: 'Active low', +} + +mps_inti_trigger_mode = { + 0b00: 'Conforms to bus specifications', + 0b01: 'Edge-triggered', + 0b11: 'Level-triggered', +} + +class APICSubtable_int_flags_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('polarity', ctypes.c_uint16, 2), + ('trigger_mode', ctypes.c_uint16, 2), + ] + _formats = { + 'polarity': unpack.format_table("{}", mps_inti_polarity), + 'trigger_mode': unpack.format_table("{}", mps_inti_trigger_mode), + } + +class APICSubtable_int_flags(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint16), + ('bits', APICSubtable_int_flags_bits), + ] + +class APICSubtableNmiIntSrc(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(APICSubtable._fields_) + [ + ('flags', APICSubtable_int_flags), + ('global_sys_interrupt', ctypes.c_uint32), + ] + +class APICSubtableLocalApicNmi(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(APICSubtable._fields_) + [ + ('proc_id', ctypes.c_uint8), + ('flags', APICSubtable_int_flags), + ('lint_num', ctypes.c_uint8), + ] + +class APICSubtableIntSrcOverride(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(APICSubtable._fields_) + [ + ('bus', ctypes.c_uint8), + ('source', ctypes.c_uint8), + ('global_sys_interrupt', ctypes.c_uint32), + ('flags', APICSubtable_int_flags) + ] + +class APICSubtableLocalx2Apic(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(APICSubtable._fields_) + [ + ('reserved', ctypes.c_uint16), + ('x2apicid', ctypes.c_uint32), + ('flags', local_apic_flags), + ('uid', ctypes.c_uint32), + ] + +class APICSubtableLocalx2ApicNmi(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(APICSubtable._fields_) + [ + ('flags', APICSubtable_int_flags), + ('uid', ctypes.c_uint32), + ('lint_num', ctypes.c_uint8), + ('reserved', ctypes.c_uint8 * 3), + ] + +_performance_interrupt_mode = { + 0: 'Level-triggered', + 1: 'Edge-triggered', +} + +class APICSubtableLocalGIC_flags_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('enabled', ctypes.c_uint32, 1), + ('performance_interrupt_mode', ctypes.c_uint32, 1), + ] + _formats = { + 'performance_interrupt_mode': unpack.format_table("{}", mps_inti_polarity), + } + +class APICSubtableLocalGIC_flags(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint32), + ('bits', APICSubtableLocalGIC_flags_bits), + ] + +class APICSubtableLocalGIC(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(APICSubtable._fields_) + [ + ('reserved', ctypes.c_uint16), + ('gic_id', ctypes.c_uint32), + ('uid', ctypes.c_uint32), + ('flags', APICSubtableLocalGIC_flags), + ('parking_protocol_version', ctypes.c_uint32), + ('performance_interrupt_gsiv', ctypes.c_uint32), + ('parked_address', ctypes.c_uint64), + ('physical_base_adddress', ctypes.c_uint64), + ] + +class APICSubtableLocalGICDistributor(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(APICSubtable._fields_) + [ + ('reserved1', ctypes.c_uint16), + ('gic_id', ctypes.c_uint32), + ('physical_base_adddress', ctypes.c_uint64), + ('system_vector_base', ctypes.c_uint32), + ('reserved2', ctypes.c_uint32), + ] + +def APICSubtableUnknown_factory(_len): + class APICSubtableUnknown(cdata.Struct): + _pack_ = 1 + _fields_ = APICSubtable._fields_ + [ + ('data', ctypes.c_uint8 * _len), + ] + return APICSubtableUnknown + +MADT_TYPE_LOCAL_APIC = 0 +MADT_TYPE_IO_APIC = 1 +MADT_TYPE_INT_SRC_OVERRIDE = 2 +MADT_TYPE_NMI_INT_SRC = 3 +MADT_TYPE_LOCAL_APIC_NMI = 4 +MADT_TYPE_LOCAL_X2APIC = 9 +MADT_TYPE_LOCAL_X2APIC_NMI = 0xA +MADT_TYPE_LOCAL_GIC = 0xB +MADT_TYPE_LOCAL_GIC_DISTRIBUTOR = 0xC + +class APIC_table_flags_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('pcat_compat', ctypes.c_uint32, 1), + ] + +class APIC_table_flags(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint32), + ('bits', APIC_table_flags_bits), + ] + +def apic_factory(field_list): + class subtables(cdata.Struct): + _pack_ = 1 + _fields_ = field_list + + def __iter__(self): + for f in self._fields_: + yield getattr(self, f[0]) + + class APIC_v3(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('header', TableHeader), + ('local_apic_address', ctypes.c_uint32), + ('flags', APIC_table_flags), + ('interrupt_controller_structures', subtables), + ] + + @property + def procid_apicid(self): + procid_apicid_dict = {} + for subtable in self.interrupt_controller_structures: + # accumulate the dictionary + if subtable.subtype == MADT_TYPE_LOCAL_APIC: + if subtable.flags.bits.enabled == 1: + procid_apicid_dict[subtable.proc_id] = subtable.apic_id + return procid_apicid_dict + + @property + def uid_x2apicid(self): + uid_x2apicid_dict = {} + for subtable in self.interrupt_controller_structures: + # accumulate the dictionary + if subtable.subtype == MADT_TYPE_LOCAL_X2APIC: + if subtable.flags.bits.enabled == 1: + uid_x2apicid_dict[subtable.uid] = subtable.x2apicid + return uid_x2apicid_dict + + return APIC_v3 + +def apic_subtable_list(addr, length): + end = addr + length + field_list = list() + subtable_num = 0 + while addr < end: + subtable_num += 1 + subtable = APICSubtable.from_address(addr) + addr += subtable.length + if subtable.subtype == MADT_TYPE_LOCAL_APIC: + cls = APICSubtableLocalApic + elif subtable.subtype == MADT_TYPE_IO_APIC: + cls = APICSubtableIOApic + elif subtable.subtype == MADT_TYPE_INT_SRC_OVERRIDE: + cls = APICSubtableIntSrcOverride + elif subtable.subtype == MADT_TYPE_NMI_INT_SRC: + cls = APICSubtableNmiIntSrc + elif subtable.subtype == MADT_TYPE_LOCAL_APIC_NMI: + cls = APICSubtableLocalApicNmi + elif subtable.subtype == MADT_TYPE_LOCAL_X2APIC: + cls = APICSubtableLocalx2Apic + elif subtable.subtype == MADT_TYPE_LOCAL_X2APIC_NMI: + cls = APICSubtableLocalx2ApicNmi + elif subtable.subtype == MADT_TYPE_LOCAL_GIC: + cls = APICSubtableLocalGIC + elif subtable.subtype == MADT_TYPE_LOCAL_GIC_DISTRIBUTOR: + cls = APICSubtableLocalGICDistributor + else: + cls = APICSubtableUnknown_factory(subtable.length - ctypes.sizeof(APICSubtable)) + field_list.append( ('subtable{}'.format(subtable_num), cls) ) + return field_list + +def APIC(val): + """Create class based on decode of an APIC table from filename.""" + preamble_length = ctypes.sizeof(apic_factory(list())) + data = open(val, mode='rb').read() + buf = ctypes.create_string_buffer(data, len(data)) + addr = ctypes.addressof(buf) + hdr = TableHeader.from_address(addr) + subtable_list = apic_subtable_list(addr + preamble_length, hdr.length - preamble_length) + return apic_factory(subtable_list).from_buffer_copy(data) diff --git a/misc/config_tools/target/acpiparser/asf.py b/misc/config_tools/target/acpiparser/asf.py new file mode 100644 index 000000000..0602fbe37 --- /dev/null +++ b/misc/config_tools/target/acpiparser/asf.py @@ -0,0 +1,278 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import ctypes +import copy + +import acpiparser.cdata as cdata +from acpiparser._utils import TableHeader + +class ASFSubtable(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('record_type', ctypes.c_uint8, 7), + ('last_record', ctypes.c_uint8, 1), + ('reserved', ctypes.c_uint8), + ('record_length', ctypes.c_uint16), + ] + +def ASF_subtable_unknown_factory(data_len): + class ASFSubtableUnknown(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(ASFSubtable._fields_) + [ + ('data', ctypes.c_uint8 * data_len), + ] + return ASFSubtableUnknown + +class ASF_info_flags_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('smbus_support', ctypes.c_uint8, 1), + ] + +class ASF_info_flags(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint8), + ('bits', ASF_info_flags_bits), + ] + +class fixed_smbus_address(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('ASF_compliant_device', ctypes.c_uint8, 1), + ('address', ctypes.c_uint8, 7), + ] + +class ASF_info_record(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(ASFSubtable._fields_) + [ + ('min_watchdog_reset_value', ctypes.c_uint8), + ('min_pollng_interval', ctypes.c_uint8), + ('system_id', ctypes.c_uint16), + ('iana_manufacturer_id', ctypes.c_uint8 * 4), + ('flags', ASF_info_flags), + ('reserved2', ctypes.c_uint8 * 3), + ] + +class ASF_ALERTDATA(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('device_address', ctypes.c_uint8), + ('command', ctypes.c_uint8), + ('data_mask', ctypes.c_uint8), + ('compare_value', ctypes.c_uint8), + ('event_sensor_type', ctypes.c_uint8), + ('event_type', ctypes.c_uint8), + ('event_offset', ctypes.c_uint8), + ('event_source_type', ctypes.c_uint8), + ('event_severity', ctypes.c_uint8), + ('sendor_number', ctypes.c_uint8), + ('entity', ctypes.c_uint8), + ('entity_instance', ctypes.c_uint8), + ] + +def ASF_alrt_factory(num_alerts): + class ASF_ALRT(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(ASFSubtable._fields_) + [ + ('assertion_event_mask', ctypes.c_uint8), + ('deassertion_event_mask', ctypes.c_uint8), + ('number_alerts', ctypes.c_uint8), + ('array_element_length', ctypes.c_uint8), + ('device_array', ASF_ALERTDATA * num_alerts), + ] + return ASF_ALRT + +class ASF_CONTROLDATA(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('function', ctypes.c_uint8), + ('device_address', ctypes.c_uint8), + ('command', ctypes.c_uint8), + ('data_value', ctypes.c_uint8), + ] + +def ASF_rctl_factory(num_controls): + class ASF_RCTL(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(ASFSubtable._fields_) + [ + ('number_controls', ctypes.c_uint8), + ('array_element_length', ctypes.c_uint8), + ('reserved2', ctypes.c_uint16), + ('control_array', ASF_CONTROLDATA * num_controls), + ] + return ASF_RCTL + +class ASF_boot_options_capabilities_1_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('firmware_verbosity_screen_blank', ctypes.c_uint8, 1), + ('power_button_lock', ctypes.c_uint8, 1), + ('reset_button_lock', ctypes.c_uint8, 1), + ('reserved_4_3', ctypes.c_uint8, 2), + ('lock_keyboard', ctypes.c_uint8, 1), + ('sleep_button_lock', ctypes.c_uint8, 1), + ('reserved_7', ctypes.c_uint8, 1), + ] + +class ASF_boot_options_capabilities_1(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint8), + ('bits', ASF_boot_options_capabilities_1_bits), + ] + +class ASF_boot_options_capabilities_2_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('reserved_2_0', ctypes.c_uint8, 3), + ('user_password_bypass', ctypes.c_uint8, 1), + ('forced_progress_events', ctypes.c_uint8, 1), + ('firmware_verbosity_verbose', ctypes.c_uint8, 1), + ('firmware_verbosity_quiet', ctypes.c_uint8, 1), + ('configuration_data_reset', ctypes.c_uint8, 1), + ] + +class ASF_boot_options_capabilities_2(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint8), + ('bits', ASF_boot_options_capabilities_2_bits), + ] + +class ASF_special_commands_2_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('force_pxe_boot', ctypes.c_uint8, 1), + ('force_hard_drive_boot', ctypes.c_uint8, 1), + ('force_hard_drive_safe_mode_boot', ctypes.c_uint8, 1), + ('force_diagnostic_boot', ctypes.c_uint8, 1), + ('force_cd_dvd_boot', ctypes.c_uint8, 1), + ('reserved', ctypes.c_uint8, 3), + ] + +class ASF_special_commands_2(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint8), + ('bits', ASF_special_commands_2_bits), + ] + +class ASF_system_capabilities_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('power_cycle_reset_only_on_secure_port', ctypes.c_uint8, 1), + ('power_down_only_on_secure_port', ctypes.c_uint8, 1), + ('power_on_only_on_secure_port', ctypes.c_uint8, 1), + ('reset_only_on_secure_port', ctypes.c_uint8, 1), + ('power_cycle_reset_on_compat_or_secure_port', ctypes.c_uint8, 1), + ('power_down_on_compat_or_secure_port', ctypes.c_uint8, 1), + ('power_on_via_compat_or_secure_port', ctypes.c_uint8, 1), + ('reset_only_on_compat_or_secure_port', ctypes.c_uint8, 1), + ] + +class ASF_system_capabilities(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint8), + ('bits', ASF_system_capabilities_bits), + ] + +class ASF_rmcp(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(ASFSubtable._fields_) + [ + ('boot_options_capabilities_1', ASF_boot_options_capabilities_1), + ('boot_options_capabilities_2', ASF_boot_options_capabilities_2), + ('boot_options_capabilities_3', ctypes.c_uint8), + ('boot_options_capabilities_4', ctypes.c_uint8), + ('special_commands_1', ctypes.c_uint8), + ('special_commands_2', ASF_special_commands_2), + ('system_capabilities', ASF_system_capabilities), + ('completion_code', ctypes.c_uint8), + ('iana', ctypes.c_uint8 * 4), + ('special_command', ctypes.c_uint8), + ('special_command_parameter', ctypes.c_uint8 * 2), + ('boot_options', ctypes.c_uint8 * 2), + ('oem_parameters', ctypes.c_uint8 * 2), + ] + +def ASF_addr_record_factory(num_devices): + + class ASF_addr_record(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(ASFSubtable._fields_) + [ + ('seeprom_address', ctypes.c_uint8), + ('num_devices', ctypes.c_uint8), + ('fixed_smbus_addresses', fixed_smbus_address * num_devices), + ] + return ASF_addr_record + +def ASF_factory(field_list): + class subtables(cdata.Struct): + _pack_ = 1 + _fields_ = field_list + + def __iter__(self): + for f in self._fields_: + yield getattr(self, f[0]) + + class ASF_v1(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('header', TableHeader), + ('information_records', subtables), + ] + + return ASF_v1 + +ASF_INFO = 0 +ASF_ALRT = 1 +ASF_RCTL = 2 +ASF_RMCP = 3 +ASF_ADDR = 4 + +def ASF_subtable_list(addr, length): + end = addr + length + field_list = list() + subtable_num = 0 + ASF_addr_record_base_len = ctypes.sizeof(ASF_addr_record_factory(0)) + ASF_alrt_base = ASF_alrt_factory(0) + ASF_rctl_base = ASF_rctl_factory(0) + while addr < end: + subtable_num += 1 + subtable = ASFSubtable.from_address(addr) + if subtable.record_type == ASF_INFO: + cls = ASF_info_record + elif subtable.record_type == ASF_ALRT: + num_alerts = ASF_alrt_base.from_address(addr).number_alerts + cls = ASF_alrt_factory(num_alerts) + elif subtable.record_type == ASF_RCTL: + num_controls = ASF_rctl_base.from_address(addr).number_controls + cls = ASF_rctl_factory(num_controls) + elif subtable.record_type == ASF_RMCP: + cls = ASF_rmcp + elif subtable.record_type == ASF_ADDR: + cls = ASF_addr_record_factory(subtable.record_length - ASF_addr_record_base_len) + else: + cls = (subtable.record_length - ctypes.sizeof(ASFSubtable)) + addr += subtable.record_length + field_list.append( ('subtable{}'.format(subtable_num), cls) ) + return field_list + +def ASF(val): + """Create class based on decode of an ASF! table from filename.""" + base_length = ctypes.sizeof(ASF_factory(list())) + data = open(val, mode='rb').read() + buf = ctypes.create_string_buffer(data, len(data)) + addr = ctypes.addressof(buf) + hdr = TableHeader.from_address(addr) + field_list = ASF_subtable_list(addr + base_length, hdr.length - base_length) + return ASF_factory(field_list).from_buffer_copy(data) diff --git a/misc/config_tools/target/acpiparser/bitfields.py b/misc/config_tools/target/acpiparser/bitfields.py new file mode 100644 index 000000000..828f79e59 --- /dev/null +++ b/misc/config_tools/target/acpiparser/bitfields.py @@ -0,0 +1,69 @@ +# Copyright (c) 2015, Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Helper functions to work with bitfields. + +Documentation frequently writes bitfields as the inclusive range [msb:lsb]; +this module provides functions to work with bitfields using msb and lsb rather +than manually computing shifts and masks from those.""" + +def bitfield_max(msb, lsb=None): + """Return the largest value that fits in the bitfield [msb:lsb] (or [msb] if lsb is None)""" + if lsb is None: + lsb = msb + return (1 << (msb - lsb + 1)) - 1 + +def bitmask(msb, lsb=None): + """Creates a mask with bits [msb:lsb] (or [msb] if lsb is None) set.""" + if lsb is None: + lsb = msb + return bitfield_max(msb, lsb) << lsb + +def bitfield(value, msb, lsb=None): + """Shift value to fit in the bitfield [msb:lsb] (or [msb] if lsb is None). + + Raise OverflowError if value does not fit in that bitfield.""" + if lsb is None: + lsb = msb + if value > bitfield_max(msb, lsb): + if msb == lsb: + field = "[{0}]".format(msb) + else: + field = "[{0}:{1}]".format(msb, lsb) + raise OverflowError("Value {value:#x} too big for bitfield {field}".format(**locals())) + return value << lsb + +def getbits(value, msb, lsb=None): + """From the specified value, extract the bitfield [msb:lsb] (or [msb] if lsb is None)""" + if lsb is None: + lsb = msb + return (value >> lsb) & bitfield_max(msb, lsb) + +def setbits(value, fieldvalue, msb, lsb=None): + """In the specified value, set the bitfield [msb:lsb] (or [msb] if lsb is None) to fieldvalue""" + value &= ~bitmask(msb, lsb) + value |= bitfield(fieldvalue, msb, lsb) + return value diff --git a/misc/config_tools/target/acpiparser/cdata.py b/misc/config_tools/target/acpiparser/cdata.py new file mode 100644 index 000000000..f81507553 --- /dev/null +++ b/misc/config_tools/target/acpiparser/cdata.py @@ -0,0 +1,201 @@ +# Copyright (c) 2015, Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""bits.cdata module.""" + +from __future__ import print_function +import binascii +import ctypes +import textwrap +import uuid + +def print_fields(cls): + with ttypager.page(): + print("{}".format(cls.__name__)) + print("{:20s} {:6} {:6}".format('field', 'length', 'offset')) + for f in cls._fields_: + a = getattr(cls, f[0]) + print("{:20s} {:6} {:6}".format(f[0], a.size, a.offset)) + +def to_bytes(var): + return (ctypes.c_char * ctypes.sizeof(var)).from_buffer(var).raw + +_CTYPES_HEX_TYPES = ( + ctypes.c_void_p, + ctypes.c_uint8, ctypes.c_uint16, ctypes.c_uint32, ctypes.c_uint64, + ctypes.c_ubyte, ctypes.c_ushort, ctypes.c_uint, ctypes.c_ulong, ctypes.c_ulonglong, +) + +class c_base(object): + """Base class for ctypes structures and unions.""" + @staticmethod + def _formatval(t, val): + if val is not None and t in _CTYPES_HEX_TYPES: + return "{:#x}".format(val) + if issubclass(t, ctypes.Array): + if issubclass(t._type_, (ctypes.c_char, ctypes.c_wchar)): + return "'{}'".format(val) + else: + return "[{}]".format(", ".join(Struct._formatval(t._type_, item) for item in val)) + return "{}".format(val) + + + def _formatter(self, field): + name = field[0] + t = field[1] + val = getattr(self, name) + if hasattr(self, '_formats'): + f = self._formats.get(name, None) + if f: + return f(val) + if issubclass(t, (Struct, Union)): + val._indent = self._indent + return str(val) + if issubclass(t, ctypes.Array): + if issubclass(t._type_, (Struct, Union)): + s = "[" + for item in val: + item._indent = self._indent + " " + s += "\n" + str(item) + s += "]" + return s + return self._formatval(t, val) + + _indent = "" + + def _wrap(self, str, indent=True): + line_len = 77 - len(self._indent + ' ') + _wrapper = textwrap.TextWrapper(width=line_len, initial_indent=self._indent, subsequent_indent=self._indent + ' ') + _wrapper_indentall = textwrap.TextWrapper(width=line_len, initial_indent=self._indent + ' ', subsequent_indent=self._indent + ' ') + def __wrap(): + wrapper = _wrapper + for line in str.split("\n"): + # Preserve blank lines, for which wrapper emits an empty list + if not line: + yield "" + for wrapped_line in wrapper.wrap(line): + yield wrapped_line + if indent: + wrapper = _wrapper_indentall + return '\n'.join(__wrap()) + + def preface_field(self, field): + a = getattr(self.__class__, field[0]) + return "ofs={} ".format(a.offset) + + def bitfield_info(self, field): + a = getattr(self.__class__, field[0]) + bit_count = a.size >> 16 + lo_bit = a.size & 0xFFFF + hi_bit = lo_bit + bit_count - 1 + return bit_count, hi_bit, lo_bit + + def preface_bitfield(self, field): + bit_count, hi_bit, lo_bit = self.bitfield_info(field) + if bit_count > 1: + return "bits[{}:{}]=".format(hi_bit, lo_bit) + if bit_count == 1: + return "bit[{}]=".format(lo_bit) + return "" + + def __str__(self): + self._indent += " " + s = "{}({})".format(self.__class__.__name__, "".join("\n{}{}={}{}".format(self.preface_field(field), field[0], self.preface_bitfield(field), self._formatter(field)) for field in self._fields_)) + self._indent = "" + return self._wrap(s) + +class Struct(ctypes.Structure, c_base): + """Base class for ctypes structures.""" + def __hash__(self): + buf = (ctypes.c_uint8 * ctypes.sizeof(self)).from_buffer(self) + return binascii.crc32(buf) + + def __cmp__(self, other): + return cmp(hash(self), hash(other)) + +class Union(ctypes.Union, c_base): + """Base class for ctypes unions.""" + def __hash__(self): + buf = (ctypes.c_uint8 * ctypes.sizeof(self)).from_buffer(self) + return binascii.crc32(buf) + + def __cmp__(self, other): + return cmp(hash(self), hash(other)) + +class GUID(Struct): + _fields_ = [ + ('Data', ctypes.c_ubyte * 16), + ] + + def __init__(self, *args, **kwargs): + """Create a GUID. Accepts any arguments the uuid.UUID constructor + would accept. Also accepts an instance of uuid.UUID, either as the + first argument or as a keyword argument "uuid". As with other + ctypes structures, passing no parameters yields a zero-initialized + structure.""" + u = kwargs.get("uuid") + if u is not None: + self.uuid = u + elif not(args) and not(kwargs): + self.uuid = uuid.UUID(int=0) + elif args and isinstance(args[0], uuid.UUID): + self.uuid = args[0] + else: + self.uuid = uuid.UUID(*args, **kwargs) + + def _get_uuid(self): + return uuid.UUID(bytes_le=to_bytes(self)) + + def _set_uuid(self, u): + ctypes.memmove(ctypes.addressof(self), ctypes.c_char_p(u.bytes_le), ctypes.sizeof(self)) + + uuid = property(_get_uuid, _set_uuid) + + def __cmp__(self, other): + if isinstance(other, GUID): + return cmp(self.uuid, other.uuid) + if isinstance(other, uuid.UUID): + return cmp(self.uuid, other) + return NotImplemented + + def __hash__(self): + return hash(self.uuid) + + def __repr__(self): + return "GUID({})".format(self.uuid) + + def __str__(self): + return "{}".format(self.uuid) + +def _format_guid(val): + try: + import efi + guid_str = efi.known_uuids.get(val.uuid, None) + except: + guid_str = None + if guid_str: + return '{} ({})'.format(val, guid_str) + return '{}'.format(val) diff --git a/misc/config_tools/target/acpiparser/dmar.py b/misc/config_tools/target/acpiparser/dmar.py new file mode 100644 index 000000000..3179138bd --- /dev/null +++ b/misc/config_tools/target/acpiparser/dmar.py @@ -0,0 +1,252 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import ctypes +import copy + +import acpiparser.cdata as cdata +from acpiparser._utils import TableHeader + +class DMARSubtable(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('subtype', ctypes.c_uint16), + ('length', ctypes.c_uint16), + ] + +class DMARDeviceScopePath(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('pci_device', ctypes.c_uint8), + ('pci_function', ctypes.c_uint8), + ] + +def DMARDeviceScope_factory(num_dev_scope_path): + class DMARDeviceScope(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('type', ctypes.c_uint8), + ('length', ctypes.c_uint8), + ('reserved', ctypes.c_uint16), + ('enumeration_id', ctypes.c_uint8), + ('start_bus_number', ctypes.c_uint8), + ('paths', DMARDeviceScopePath * num_dev_scope_path), + ] + return DMARDeviceScope + +def dmar_device_scope_list(addr, length): + end = addr + length + field_list = list() + subtable_num = 0 + base_len_DMARDeviceScope = ctypes.sizeof(DMARDeviceScope_factory(0)) + len_DMARDeviceScopePath = ctypes.sizeof(DMARDeviceScopePath) + while addr < end: + subtable_num += 1 + subtable = DMARDeviceScope_factory(0).from_address(addr) + num_dev_scope_path = (subtable.length - base_len_DMARDeviceScope) // len_DMARDeviceScopePath + cls = DMARDeviceScope_factory(num_dev_scope_path) + addr += subtable.length + field_list.append( ('subtable{}'.format(subtable_num), cls) ) + return field_list + +class drhd_flags_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('include_pci_all', ctypes.c_uint8, 1), + ] + +class drhd_flags(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint8), + ('bits', drhd_flags_bits), + ] + +def DMARSubtableDRHD_factory(field_list): + + class subtables(cdata.Struct): + _pack_ = 1 + _fields_ = field_list + + def __iter__(self): + for f in self._fields_: + yield getattr(self, f[0]) + + class DMARSubtableDRHD(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(DMARSubtable._fields_) + [ + ('flags', drhd_flags), + ('reserved', ctypes.c_uint8), + ('segment_number', ctypes.c_uint16), + ('base_address', ctypes.c_uint64), + ('device_scopes', subtables) + ] + return DMARSubtableDRHD + +def DMARSubtableRMRR_factory(field_list): + + class subtables(cdata.Struct): + _pack_ = 1 + _fields_ = field_list + + def __iter__(self): + for f in self._fields_: + yield getattr(self, f[0]) + + class DMARSubtableRMRR(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(DMARSubtable._fields_) + [ + ('reserved', ctypes.c_uint16), + ('segment_number', ctypes.c_uint16), + ('base_address', ctypes.c_uint64), + ('limit_address', ctypes.c_uint64), + ('device_scopes', subtables), + ] + + return DMARSubtableRMRR + +class atsr_flags_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('all_ports', ctypes.c_uint8, 1), + ] + +class atsr_flags(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint8), + ('bits', atsr_flags_bits), + ] + +def DMARSubtableATSR_factory(field_list): + + class subtables(cdata.Struct): + _pack_ = 1 + _fields_ = field_list + + def __iter__(self): + for f in self._fields_: + yield getattr(self, f[0]) + + class DMARSubtableATSR(cdata.Struct): + _pack = 1 + _fields_ = copy.copy(DMARSubtable._fields_) + [ + ('flags', atsr_flags), + ('reserved', ctypes.c_uint8), + ('segment_number', ctypes.c_uint16), + ('device_scopes', subtables), + ] + return DMARSubtableATSR + +class DMARSubtableRHSA(cdata.Struct): + _pack = 1 + _fields_ = copy.copy(DMARSubtable._fields_) + [ + ('reserved', ctypes.c_uint32), + ('base_address', ctypes.c_uint64), + ('proximity_domain', ctypes.c_uint32), + ] + +def DMARSubTableANDD_factory(obj_name_len): + class DMARSubTableANDD(cdata.Struct): + _pack = 1 + _fields_ = copy.copy(DMARSubtable._fields_) + [ + ('reserved', ctypes.c_uint8 * 3), + ('device_num', ctypes.c_uint8), + ('object_name', ctypes.c_char * obj_name_len), + ] + return DMARSubTableANDD + +def DMARSubtableUnknown_factory(data_len): + class DMARSubtableUnknown(cdata.Struct): + _pack = 1 + _fields_ = copy.copy(DMARSubtable._fields_) + [ + ('data', ctypes.c_uint8 * data_len), + ] + return DMARSubtableUnknown + +ACPI_DMAR_TYPE_DRHD = 0 +ACPI_DMAR_TYPE_RMRR = 1 +ACPI_DMAR_TYPE_ATSR = 2 +ACPI_DMAR_TYPE_RHSA = 3 +ACPI_DMAR_TYPE_ANDD = 4 + +def dmar_subtable_list(addr, length): + end = addr + length + field_list = list() + subtable_num = 0 + base_len_DRHD = ctypes.sizeof(DMARSubtableDRHD_factory(list())) + base_len_RMRR = ctypes.sizeof(DMARSubtableRMRR_factory(list())) + base_len_ATSR = ctypes.sizeof(DMARSubtableATSR_factory(list())) + base_len_ANDD = ctypes.sizeof(DMARSubTableANDD_factory(0)) + while addr < end: + subtable_num += 1 + subtable = DMARSubtable.from_address(addr) + if subtable.subtype == ACPI_DMAR_TYPE_DRHD: + next_field_list = dmar_device_scope_list(addr + base_len_DRHD, subtable.length - base_len_DRHD) + cls = DMARSubtableDRHD_factory(next_field_list) + elif subtable.subtype == ACPI_DMAR_TYPE_RMRR: + next_field_list = dmar_device_scope_list(addr + base_len_RMRR, subtable.length - base_len_RMRR) + cls = DMARSubtableRMRR_factory(next_field_list) + elif subtable.subtype == ACPI_DMAR_TYPE_ATSR: + next_field_list = dmar_device_scope_list(addr + base_len_ATSR, subtable.length - base_len_ATSR) + cls = DMARSubtableATSR_factory(next_field_list) + elif subtable.subtype == ACPI_DMAR_TYPE_RHSA: + cls = DMARSubtableRHSA + elif subtable.subtype == ACPI_DMAR_TYPE_ANDD: + cls = DMARSubTableANDD_factory(subtable.length - base_len_ANDD) + else: + cls = DMARSubtableUnknown_factory(subtable.length - ctypes.sizeof(DMARSubtable)) + addr += subtable.length + field_list.append( ('subtable{}'.format(subtable_num), cls) ) + return field_list + +class dmar_flags_bits(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('intr_remap', ctypes.c_uint8, 1), + ('x2apic_opt_out', ctypes.c_uint8, 1), + ] + +class dmar_flags(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint8), + ('bits', dmar_flags_bits), + ] + +def dmar_factory(field_list): + + class subtables(cdata.Struct): + _pack_ = 1 + _fields_ = field_list + + def __iter__(self): + for f in self._fields_: + yield getattr(self, f[0]) + + class DMAR_v1(cdata.Struct): + _pack = 1 + _fields_ = [ + ('header', TableHeader), + ('host_addr_width', ctypes.c_uint8), + ('flags', ctypes.c_uint8), + ('reserved', ctypes.c_uint8 * 10), + ('remapping_structures', subtables), + ] + + return DMAR_v1 + +def DMAR(val): + """Create class based on decode of an DMAR table from filename.""" + base_length = ctypes.sizeof(dmar_factory(list())) + data = open(val, mode='rb').read() + buf = ctypes.create_string_buffer(data, len(data)) + addr = ctypes.addressof(buf) + hdr = TableHeader.from_address(addr) + field_list = dmar_subtable_list(addr + base_length, hdr.length - base_length) + return dmar_factory(field_list).from_buffer_copy(data) diff --git a/misc/config_tools/target/acpiparser/facp.py b/misc/config_tools/target/acpiparser/facp.py new file mode 100644 index 000000000..1d9faaf0c --- /dev/null +++ b/misc/config_tools/target/acpiparser/facp.py @@ -0,0 +1,373 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import ctypes +import copy + +import acpiparser.cdata as cdata +import acpiparser.unpack as unpack +from acpiparser._utils import TableHeader, GAS + +_preferred_pm_profile = { + 0: 'Unspecified', + 1: 'Desktop', + 2: 'Mobile', + 3: 'Workstation', + 4: 'Enterprise Server', + 5: 'SOHO Server', + 6: 'Appliance PC', + 7: 'Performance Server', + 8: 'Tablet' +} + +class facp_flags_bits_v1(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('wbinvd', ctypes.c_uint32, 1), + ('wbinvd_flush', ctypes.c_uint32, 1), + ('proc_c1', ctypes.c_uint32, 1), + ('p_lvl2_up', ctypes.c_uint32, 1), + ('pwr_button', ctypes.c_uint32, 1), + ('slp_button', ctypes.c_uint32, 1), + ('fix_rtc', ctypes.c_uint32, 1), + ('rtc_s4', ctypes.c_uint32, 1), + ('tmr_val_ext', ctypes.c_uint32, 1), + ('dck_cap', ctypes.c_uint32, 1), + ] + +class facp_flags_v1(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint32), + ('bits', facp_flags_bits_v1), + ] + +class FACP_v1(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('header', TableHeader), + ('firmware_ctrl', ctypes.c_uint32), + ('dsdt', ctypes.c_uint32), + ('int_model', ctypes.c_uint8), + ('reserved0', ctypes.c_uint8), + ('sci_int', ctypes.c_uint16), + ('smi_cmd', ctypes.c_uint32), + ('acpi_enable', ctypes.c_uint8), + ('acpi_disable', ctypes.c_uint8), + ('s4bios_req', ctypes.c_uint8), + ('reserved1', ctypes.c_uint8), + ('pm1a_evt_blk', ctypes.c_uint32), + ('pm1b_evt_blk', ctypes.c_uint32), + ('pm1a_cnt_blk', ctypes.c_uint32), + ('pm1b_cnt_blk', ctypes.c_uint32), + ('pm2_cnt_blk', ctypes.c_uint32), + ('pm_tmr_blk', ctypes.c_uint32), + ('gpe0_blk', ctypes.c_uint32), + ('gpe1_blk', ctypes.c_uint32), + ('pm1_evt_len', ctypes.c_uint8), + ('pm1_cnt_len', ctypes.c_uint8), + ('pm2_cnt_len', ctypes.c_uint8), + ('pm_tmr_len', ctypes.c_uint8), + ('gpe0_blk_len', ctypes.c_uint8), + ('gpe1_blk_len', ctypes.c_uint8), + ('gpe1_base', ctypes.c_uint8), + ('reserved2', ctypes.c_uint8), + ('p_lvl2_lat', ctypes.c_uint16), + ('p_lvl3_lat', ctypes.c_uint16), + ('flush_size', ctypes.c_uint16), + ('flush_stride', ctypes.c_uint16), + ('duty_offset', ctypes.c_uint8), + ('duty_width', ctypes.c_uint8), + ('day_alrm', ctypes.c_uint8), + ('mon_alrm', ctypes.c_uint8), + ('century', ctypes.c_uint8), + ('reserved3', ctypes.c_uint8 * 3), + ('flags', facp_flags_v1), + ] + +class facp_flags_bits_v3(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(facp_flags_bits_v1._fields_) + [ + ('reset_reg_sup', ctypes.c_uint32, 1), + ('sealed_case', ctypes.c_uint32, 1), + ('headless', ctypes.c_uint32, 1), + ('cpu_sw_slp', ctypes.c_uint32, 1), + ('pci_exp_wak', ctypes.c_uint32, 1), + ('use_platform_clock', ctypes.c_uint32, 1), + ('s4_rtc_sts_valid', ctypes.c_uint32, 1), + ('remote_power_on_capable', ctypes.c_uint32, 1), + ('force_apic_cluster_mode', ctypes.c_uint32, 1), + ('force_apic_physical_destination_mode', ctypes.c_uint32, 1), + ] + +class facp_flags_v3(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint32), + ('bits', facp_flags_bits_v3), + ] + +class facp_iapc_arch_bits_v3(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('legacy_devices', ctypes.c_uint16, 1), + ('8042', ctypes.c_uint16, 1), + ('vga_not_present', ctypes.c_uint16, 1), + ('msi_not_supported', ctypes.c_uint16, 1), + ] + +class facp_iapc_arch_v3(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint16), + ('bits', facp_iapc_arch_bits_v3), + ] + +class FACP_v3(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('header', TableHeader), + ('firmware_ctrl', ctypes.c_uint32), + ('dsdt', ctypes.c_uint32), + ('reserved0', ctypes.c_uint8), + ('preferred_pm_profile', ctypes.c_uint8), + ('sci_int', ctypes.c_uint16), + ('smi_cmd', ctypes.c_uint32), + ('acpi_enable', ctypes.c_uint8), + ('acpi_disable', ctypes.c_uint8), + ('s4bios_req', ctypes.c_uint8), + ('pstate_cnt', ctypes.c_uint8), + ('pm1a_evt_blk', ctypes.c_uint32), + ('pm1b_evt_blk', ctypes.c_uint32), + ('pm1a_cnt_blk', ctypes.c_uint32), + ('pm1b_cnt_blk', ctypes.c_uint32), + ('pm2_cnt_blk', ctypes.c_uint32), + ('pm_tmr_blk', ctypes.c_uint32), + ('gpe0_blk', ctypes.c_uint32), + ('gpe1_blk', ctypes.c_uint32), + ('pm1_evt_len', ctypes.c_uint8), + ('pm1_cnt_len', ctypes.c_uint8), + ('pm2_cnt_len', ctypes.c_uint8), + ('pm_tmr_len', ctypes.c_uint8), + ('gpe0_blk_len', ctypes.c_uint8), + ('gpe1_blk_len', ctypes.c_uint8), + ('gpe1_base', ctypes.c_uint8), + ('cst_cnt', ctypes.c_uint8), + ('p_lvl2_lat', ctypes.c_uint16), + ('p_lvl3_lat', ctypes.c_uint16), + ('flush_size', ctypes.c_uint16), + ('flush_stride', ctypes.c_uint16), + ('duty_offset', ctypes.c_uint8), + ('duty_width', ctypes.c_uint8), + ('day_alrm', ctypes.c_uint8), + ('mon_alrm', ctypes.c_uint8), + ('century', ctypes.c_uint8), + ('iapc_boot_arch', facp_iapc_arch_v3), + ('reserved1', ctypes.c_uint8), + ('flags', facp_flags_v3), + ('reset_reg', GAS), + ('reset_value', ctypes.c_uint8), + ('reserved2', ctypes.c_uint8 * 3), + ('x_firmware_ctrl', ctypes.c_uint64), + ('x_dsdt', ctypes.c_uint64), + ('x_pm1a_evt_blk', GAS), + ('x_pm1b_evt_blk', GAS), + ('x_pm1a_cnt_blk', GAS), + ('x_pm1b_cnt_blk', GAS), + ('x_pm2_cnt_blk', GAS), + ('x_pm_tmr_blk', GAS), + ('x_gpe0_blk', GAS), + ('x_gpe1_blk', GAS), + ] + + _formats = { + 'preferred_pm_profile': unpack.format_table("{}", _preferred_pm_profile), + } + +class facp_iapc_arch_bits_v4(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(facp_iapc_arch_bits_v3._fields_) + [ + ('pcie_aspm_controls', ctypes.c_uint16, 1), + ] + +class facp_iapc_arch_v4(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint16), + ('bits', facp_iapc_arch_bits_v4), + ] + +class FACP_v4(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('header', TableHeader), + ('firmware_ctrl', ctypes.c_uint32), + ('dsdt', ctypes.c_uint32), + ('reserved0', ctypes.c_uint8), + ('preferred_pm_profile', ctypes.c_uint8), + ('sci_int', ctypes.c_uint16), + ('smi_cmd', ctypes.c_uint32), + ('acpi_enable', ctypes.c_uint8), + ('acpi_disable', ctypes.c_uint8), + ('s4bios_req', ctypes.c_uint8), + ('pstate_cnt', ctypes.c_uint8), + ('pm1a_evt_blk', ctypes.c_uint32), + ('pm1b_evt_blk', ctypes.c_uint32), + ('pm1a_cnt_blk', ctypes.c_uint32), + ('pm1b_cnt_blk', ctypes.c_uint32), + ('pm2_cnt_blk', ctypes.c_uint32), + ('pm_tmr_blk', ctypes.c_uint32), + ('gpe0_blk', ctypes.c_uint32), + ('gpe1_blk', ctypes.c_uint32), + ('pm1_evt_len', ctypes.c_uint8), + ('pm1_cnt_len', ctypes.c_uint8), + ('pm2_cnt_len', ctypes.c_uint8), + ('pm_tmr_len', ctypes.c_uint8), + ('gpe0_blk_len', ctypes.c_uint8), + ('gpe1_blk_len', ctypes.c_uint8), + ('gpe1_base', ctypes.c_uint8), + ('cst_cnt', ctypes.c_uint8), + ('p_lvl2_lat', ctypes.c_uint16), + ('p_lvl3_lat', ctypes.c_uint16), + ('flush_size', ctypes.c_uint16), + ('flush_stride', ctypes.c_uint16), + ('duty_offset', ctypes.c_uint8), + ('duty_width', ctypes.c_uint8), + ('day_alrm', ctypes.c_uint8), + ('mon_alrm', ctypes.c_uint8), + ('century', ctypes.c_uint8), + ('iapc_boot_arch', facp_iapc_arch_v4), + ('reserved1', ctypes.c_uint8), + ('flags', facp_flags_v3), + ('reset_reg', GAS), + ('reset_value', ctypes.c_uint8), + ('reserved2', ctypes.c_uint8 * 3), + ('x_firmware_ctrl', ctypes.c_uint64), + ('x_dsdt', ctypes.c_uint64), + ('x_pm1a_evt_blk', GAS), + ('x_pm1b_evt_blk', GAS), + ('x_pm1a_cnt_blk', GAS), + ('x_pm1b_cnt_blk', GAS), + ('x_pm2_cnt_blk', GAS), + ('x_pm_tmr_blk', GAS), + ('x_gpe0_blk', GAS), + ('x_gpe1_blk', GAS), + ] + + _formats = { + 'preferred_pm_profile': unpack.format_table("{}", _preferred_pm_profile), + } + +class facp_flags_bits_v5(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(facp_flags_bits_v3._fields_) + [ + ('hw_reduced_acpi', ctypes.c_uint32, 1), + ('low_power_s0_idle_capable', ctypes.c_uint32, 1), + ] + +class facp_flags_v5(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint32), + ('bits', facp_flags_bits_v5), + ] + +class facp_iapc_arch_bits_v5(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(facp_iapc_arch_bits_v4._fields_) + [ + ('cmos_rtc_not_present', ctypes.c_uint16, 1), + ] + +class facp_iapc_arch_v5(cdata.Union): + _pack_ = 1 + _anonymous_ = ("bits",) + _fields_ = [ + ('data', ctypes.c_uint16), + ('bits', facp_iapc_arch_bits_v5), + ] + +class FACP_v5(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('header', TableHeader), + ('firmware_ctrl', ctypes.c_uint32), + ('dsdt', ctypes.c_uint32), + ('reserved0', ctypes.c_uint8), + ('preferred_pm_profile', ctypes.c_uint8), + ('sci_int', ctypes.c_uint16), + ('smi_cmd', ctypes.c_uint32), + ('acpi_enable', ctypes.c_uint8), + ('acpi_disable', ctypes.c_uint8), + ('s4bios_req', ctypes.c_uint8), + ('pstate_cnt', ctypes.c_uint8), + ('pm1a_evt_blk', ctypes.c_uint32), + ('pm1b_evt_blk', ctypes.c_uint32), + ('pm1a_cnt_blk', ctypes.c_uint32), + ('pm1b_cnt_blk', ctypes.c_uint32), + ('pm2_cnt_blk', ctypes.c_uint32), + ('pm_tmr_blk', ctypes.c_uint32), + ('gpe0_blk', ctypes.c_uint32), + ('gpe1_blk', ctypes.c_uint32), + ('pm1_evt_len', ctypes.c_uint8), + ('pm1_cnt_len', ctypes.c_uint8), + ('pm2_cnt_len', ctypes.c_uint8), + ('pm_tmr_len', ctypes.c_uint8), + ('gpe0_blk_len', ctypes.c_uint8), + ('gpe1_blk_len', ctypes.c_uint8), + ('gpe1_base', ctypes.c_uint8), + ('cst_cnt', ctypes.c_uint8), + ('p_lvl2_lat', ctypes.c_uint16), + ('p_lvl3_lat', ctypes.c_uint16), + ('flush_size', ctypes.c_uint16), + ('flush_stride', ctypes.c_uint16), + ('duty_offset', ctypes.c_uint8), + ('duty_width', ctypes.c_uint8), + ('day_alrm', ctypes.c_uint8), + ('mon_alrm', ctypes.c_uint8), + ('century', ctypes.c_uint8), + ('iapc_boot_arch', facp_iapc_arch_v5), + ('reserved1', ctypes.c_uint8), + ('flags', facp_flags_v5), + ('reset_reg', GAS), + ('reset_value', ctypes.c_uint8), + ('reserved2', ctypes.c_uint8 * 3), + ('x_firmware_ctrl', ctypes.c_uint64), + ('x_dsdt', ctypes.c_uint64), + ('x_pm1a_evt_blk', GAS), + ('x_pm1b_evt_blk', GAS), + ('x_pm1a_cnt_blk', GAS), + ('x_pm1b_cnt_blk', GAS), + ('x_pm2_cnt_blk', GAS), + ('x_pm_tmr_blk', GAS), + ('x_gpe0_blk', GAS), + ('x_gpe1_blk', GAS), + ('sleep_control_reg', GAS), + ('sleep_status_reg', GAS), + ] + + _formats = { + 'preferred_pm_profile': unpack.format_table("{}", _preferred_pm_profile), + } + +def FACP(val): + """Create class based on decode of an FACP table from filename.""" + data = open(val, mode='rb').read() + buf = ctypes.create_string_buffer(data, len(data)) + addr = ctypes.addressof(buf) + hdr = TableHeader.from_address(addr) + if hdr.revision < 3: + cls = FACP_v1 + elif hdr.revision == 3: + cls = FACP_v3 + elif hdr.revision == 4: + cls = FACP_v4 + else: + cls = FACP_v5 + return cls.from_buffer_copy(data) diff --git a/misc/config_tools/target/acpiparser/rtct.py b/misc/config_tools/target/acpiparser/rtct.py new file mode 100644 index 000000000..99b1ee6aa --- /dev/null +++ b/misc/config_tools/target/acpiparser/rtct.py @@ -0,0 +1,130 @@ +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import ctypes +import copy + +import acpiparser.cdata as cdata +from acpiparser._utils import TableHeader + +class RTCTSubtable(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('size', ctypes.c_uint16), + ('format', ctypes.c_uint16), + ('type', ctypes.c_uint32), + ] + +class RTCTSubtableRTCMBinary(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(RTCTSubtable._fields_) + [ + ('address', ctypes.c_uint64), + ('size', ctypes.c_uint32), + ] + +def RTCTSubtableWRCL3Waymasks_factory(data_len): + class RTCTSubtableWRCL3Waymasks(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(RTCTSubtable._fields_) + [ + ('waskmask', ctypes.c_uint32 * (data_len // 4)), + ] + return RTCTSubtableWRCL3Waymasks + +def RTCTSubtableGTL3Waymasks_factory(data_len): + class RTCTSubtableGTL3Waymasks(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(RTCTSubtable._fields_) + [ + ('waskmask', ctypes.c_uint32 * (data_len // 4)), + ] + return RTCTSubtableGTL3Waymasks + +def RTCTSubtableSoftwareSRAM_factory(data_len): + class RTCTSubtableSoftwareSRAM(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(RTCTSubtable._fields_) + [ + ('cache_level', ctypes.c_uint32), + ('base', ctypes.c_uint64), + ('ways', ctypes.c_uint32), + ('size', ctypes.c_uint32), + ('apic_id_tbl', ctypes.c_uint32 * ((data_len - 20) // 4)), + ] + return RTCTSubtableSoftwareSRAM + +def RTCTSubtableMemoryHierarchyLatency_factory(data_len): + class RTCTSubtableMemoryHierarchyLatency(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(RTCTSubtable._fields_) + [ + ('hierarchy', ctypes.c_uint32), + ('clock_cycles', ctypes.c_uint32), + ('apic_id_tbl', ctypes.c_uint32 * ((data_len - 8) // 4)), + ] + return RTCTSubtableMemoryHierarchyLatency + +def RTCTSubtableUnknown_factory(data_len): + class RTCTSubtableUnknown(cdata.Struct): + _pack_ = 1 + _fields_ = copy.copy(RTCTSubtable._fields_) + [ + ('data', ctypes.c_uint8 * data_len), + ] + return RTCTSubtableUnknown + +ACPI_RTCT_TYPE_RTCM_BINARY = 2 +ACPI_RTCT_TYPE_WRC_L3Waymasks = 3 +ACPI_RTCT_TYPE_GT_L3Waymasks = 4 +ACPI_RTCT_TYPE_SoftwareSRAM = 5 +ACPI_RTCT_TYPE_Memory_Hierarchy_Latency = 9 + +def rtct_subtable_list(addr, length): + end = addr + length + field_list = list() + subtable_num = 0 + while addr < end: + subtable_num += 1 + subtable = RTCTSubtable.from_address(addr) + data_len = subtable.size - ctypes.sizeof(RTCTSubtable) + if subtable.type == ACPI_RTCT_TYPE_RTCM_BINARY: + cls = RTCTSubtableRTCMBinary + elif subtable.type == ACPI_RTCT_TYPE_WRC_L3Waymasks: + cls = RTCTSubtableWRCL3Waymasks_factory(data_len) + elif subtable.type == ACPI_RTCT_TYPE_GT_L3Waymasks: + cls = RTCTSubtableGTL3Waymasks_factory(data_len) + elif subtable.type == ACPI_RTCT_TYPE_SoftwareSRAM: + cls = RTCTSubtableSoftwareSRAM_factory(data_len) + elif subtable.type == ACPI_RTCT_TYPE_Memory_Hierarchy_Latency: + cls = RTCTSubtableMemoryHierarchyLatency_factory(data_len) + else: + cls = RTCTSubtableUnknown_factory(data_len) + addr += subtable.size + field_list.append( ('subtable{}'.format(subtable_num), cls) ) + return field_list + +def rtct_factory(field_list): + + class subtables(cdata.Struct): + _pack_ = 1 + _fields_ = field_list + + def __iter__(self): + for f in self._fields_: + yield getattr(self, f[0]) + + class RTCT(cdata.Struct): + _pack_ = 1 + _fields_ = [ + ('header', TableHeader), + ('entries', subtables), + ] + + return RTCT + +def RTCT(val): + """Create class based on decode of an RTCT table from filename.""" + base_length = ctypes.sizeof(rtct_factory(list())) + data = open(val, mode='rb').read() + buf = ctypes.create_string_buffer(data, len(data)) + addr = ctypes.addressof(buf) + hdr = TableHeader.from_address(addr) + field_list = rtct_subtable_list(addr + base_length, hdr.length - base_length) + return rtct_factory(field_list).from_buffer_copy(data) diff --git a/misc/config_tools/target/acpiparser/unpack.py b/misc/config_tools/target/acpiparser/unpack.py new file mode 100644 index 000000000..dedbc9d8d --- /dev/null +++ b/misc/config_tools/target/acpiparser/unpack.py @@ -0,0 +1,202 @@ +# Copyright (c) 2013, Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""unpack module.""" + +from collections import OrderedDict +import struct + +class UnpackError(Exception): + pass + +class Unpackable(object): + def __init__(self, data, offset=0, size=None): + self.data = data + data_size = len(data) + if offset > data_size: + raise UnpackError("Unpackable.__init__: offset={} but len(data)={}".format(offset, data_size)) + self.offset = offset + if size is None: + self.size = data_size + else: + self.size = offset + size + if self.size > data_size: + raise UnpackError("Unpackable.__init__: offset+size={} but len(data)={}".format(self.size, data_size)) + + def _check_unpack(self, size): + if self.offset + size > self.size: + raise UnpackError("Unpackable: Attempted to unpack {} bytes, but only {} bytes remaining".format(size, self.size - self.offset)) + + def skip(self, size): + self._check_unpack(size) + self.offset += size + + def unpack(self, fmt): + try: + l = struct.calcsize(fmt) + self._check_unpack(l) + value = struct.unpack_from(fmt, self.data, self.offset) + self.offset += l + return value + except struct.error as e: + raise UnpackError("Unpackable.unpack: " + str(e)) + + def unpack_one(self, fmt): + return self.unpack(fmt)[0] + + def unpack_peek(self, fmt): + try: + l = struct.calcsize(fmt) + self._check_unpack(l) + return struct.unpack_from(fmt, self.data, self.offset) + except struct.error as e: + raise UnpackError("Unpackable.unpack_peek: " + str(e)) + + def unpack_peek_one(self, fmt): + return self.unpack_peek(fmt)[0] + + def unpack_peek_raw(self, size): + """Peek at the specified number of bytes as a str""" + self._check_unpack(size) + return self.data[self.offset:self.offset+size] + + def unpack_peek_rest(self): + """Peek at the remainder of the unpackable as a str""" + return self.data[self.offset:self.size] + + def unpack_raw(self, size): + """Unpack the specified number of bytes as a str""" + self._check_unpack(size) + old_offset = self.offset + self.offset += size + return self.data[old_offset:self.offset] + + def unpack_rest(self): + """Return the remainder of the unpackable as a str""" + offset = self.offset + self.offset = self.size + return self.data[offset:self.size] + + def unpack_unpackable(self, size): + """Unpack the specified number of bytes as an Unpackable""" + u = Unpackable(self.data, self.offset, size) + self.offset += size + return u + + def at_end(self): + return self.offset == self.size + +class StructError(Exception): + pass + +class Struct(object): + def __init__(self): + self.fields = OrderedDict() + + @classmethod + def unpack(cls, u): + s = cls() + for field in cls._unpack(u): + s.add_field(*field) + return s + + def add_field(self, name, value, fmt=None): + if hasattr(self, name): + raise StructError("Internal error: Duplicate Struct field name {}".format(name)) + if fmt is None: + if isinstance(value, (int, long)) and not isinstance(value, bool): + fmt = "{:#x}".format + else: + fmt = "{!r}".format + elif isinstance(fmt, str): + fmt = fmt.format + elif not callable(fmt): + raise StructError("Internal error: Expected a format string or callable, but got: {}".format(fmt)) + setattr(self, name, value) + self.fields[name] = fmt + + def format_field(self, name): + return self.fields[name](getattr(self, name)) + + def __repr__(self): + return "{}({})".format(self.__class__.__name__, ", ".join("{}={}".format(k, self.format_field(k)) for k in self.fields.iterkeys())) + + def __iter__(self): + return (getattr(self, k) for k in self.fields.iterkeys()) + + def __eq__(self, other): + if type(self) is not type(other): + return NotImplemented + return self.fields.keys() == other.fields.keys() and all(getattr(self, name) == getattr(other, name) for name in self.fields.iterkeys()) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(tuple((name, getattr(self, name)) for name in self.fields.iterkeys())) + +def format_each(fmt_one): + def f(it): + return "({})".format(", ".join(fmt_one.format(i) for i in it)) + return f + +format_each_hex = format_each("{:#x}") + +def format_table(fmt, table, default='Reserved'): + def f(value): + return "{} ({})".format(fmt.format(value), table.get(value, default)) + return f + +def format_function(fmt, function): + def f(value): + return "{} ({})".format(fmt.format(value), function(value)) + return f + +def reserved_None(fmt="{!r}"): + def f(value): + if value is None: + return "Reserved" + return fmt.format(value) + return f + +def unpack_all(u, structs, *args): + """Keep constructing structs from the unpackable u until it runs out of data. + + structs should consist of a list of Struct subclasses to be tried in order. + Each of them should return None from their constructor if they're not the + correct type to unpack the next chunk of data. Any catch-all generic + structure should apepar last in the list. Raises a StructError if no + struct matches.""" + def _substructs(): + while not u.at_end(): + for s in structs: + temp = s(u, *args) + if temp is not None: + yield temp + break + else: + raise StructError("Internal error: unable to unpack any structure at byte {} of unpackable".format(u.offset)) + return tuple(_substructs())