mirror of
https://github.com/chipsec/chipsec
synced 2026-06-08 13:31:00 +00:00
79f81bbb7e
This commit fixes whitespace errors from checkers W291,W292,W293,W391. They were found by running "flake8 ." from the root directory. These are errors defined by "pycodestyle", and have no functional impact to the source. This is simply fixing whitespace as defined by CHIPSEC's .flake8 configuration. background: https://www.flake8rules.com/rules/W291.html https://www.flake8rules.com/rules/W292.html https://www.flake8rules.com/rules/W293.html https://www.flake8rules.com/rules/W391.html tool versions: flake8 v7.3.0, python v3.13.11 Signed-off-by: William Leara <william.leara@dell.com>
626 lines
26 KiB
Python
626 lines
26 KiB
Python
# CHIPSEC: Platform Security Assessment Framework
|
|
# Copyright (c) 2010-2021, Intel Corporation
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; Version 2.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# Contact information:
|
|
# chipsec@intel.com
|
|
#
|
|
|
|
"""
|
|
HAL component providing access to and decoding of ACPI tables
|
|
"""
|
|
|
|
__version__ = '0.1'
|
|
|
|
import struct
|
|
from collections import defaultdict, namedtuple
|
|
from collections.abc import Callable
|
|
from typing import Dict, List, Tuple, Optional, Union, TYPE_CHECKING
|
|
|
|
from chipsec.library.defines import bytestostring
|
|
from chipsec.library.exceptions import UnimplementedAPIError
|
|
from chipsec.library.file import read_file
|
|
from chipsec.library import acpi_tables
|
|
from chipsec.hal.hal_base import HALBase
|
|
from chipsec.hal.common.uefi import UEFI
|
|
from chipsec.library.logger import logger, print_buffer_bytes
|
|
from chipsec.library.acpi_tables import ACPI_TABLE
|
|
|
|
if TYPE_CHECKING:
|
|
from ctypes import Array
|
|
|
|
# ACPI Table Header Format
|
|
ACPI_TABLE_HEADER_FORMAT = '=4sIBB6s8sI4sI'
|
|
ACPI_TABLE_HEADER_SIZE = struct.calcsize(ACPI_TABLE_HEADER_FORMAT) # 36
|
|
assert 36 == ACPI_TABLE_HEADER_SIZE
|
|
|
|
|
|
class ACPI_TABLE_HEADER(namedtuple('ACPI_TABLE_HEADER', 'Signature Length Revision Checksum OEMID OEMTableID OEMRevision CreatorID CreatorRevision')):
|
|
__slots__ = ()
|
|
|
|
def __str__(self) -> str:
|
|
return f""" Table Header
|
|
------------------------------------------------------------------
|
|
Signature : {self.Signature}
|
|
Length : 0x{self.Length:08X}
|
|
Revision : 0x{self.Revision:02X}
|
|
Checksum : 0x{self.Checksum:02X}
|
|
OEM ID : {self.OEMID}
|
|
OEM Table ID : {self.OEMTableID}
|
|
OEM Revision : 0x{self.OEMRevision:08X}
|
|
Creator ID : {self.CreatorID}
|
|
Creator Revision : 0x{self.CreatorRevision:08X}
|
|
"""
|
|
|
|
|
|
ACPI_TABLE_SIG_SIZE = 0x4
|
|
|
|
ACPI_TABLE_SIG_ROOT = 'ROOT'
|
|
ACPI_TABLE_SIG_RSDP = 'RSDP'
|
|
ACPI_TABLE_SIG_RSDT = 'RSDT'
|
|
ACPI_TABLE_SIG_XSDT = 'XSDT'
|
|
ACPI_TABLE_SIG_FACP = 'FACP'
|
|
ACPI_TABLE_SIG_FACS = 'FACS'
|
|
ACPI_TABLE_SIG_DSDT = 'DSDT'
|
|
ACPI_TABLE_SIG_SSDT = 'SSDT'
|
|
ACPI_TABLE_SIG_PSDT = 'PSDT'
|
|
ACPI_TABLE_SIG_APIC = 'APIC'
|
|
ACPI_TABLE_SIG_SBST = 'SBST'
|
|
ACPI_TABLE_SIG_ECDT = 'ECDT'
|
|
ACPI_TABLE_SIG_SRAT = 'SRAT'
|
|
ACPI_TABLE_SIG_SLIC = 'SLIC'
|
|
ACPI_TABLE_SIG_SLIT = 'SLIT'
|
|
ACPI_TABLE_SIG_BOOT = 'BOOT'
|
|
ACPI_TABLE_SIG_CPEP = 'CPEP'
|
|
ACPI_TABLE_SIG_DBGP = 'DBGP'
|
|
ACPI_TABLE_SIG_ETDT = 'ETDT'
|
|
ACPI_TABLE_SIG_HPET = 'HPET'
|
|
ACPI_TABLE_SIG_MCFG = 'MCFG'
|
|
ACPI_TABLE_SIG_SPCR = 'SPCR'
|
|
ACPI_TABLE_SIG_SPMI = 'SPMI'
|
|
ACPI_TABLE_SIG_TCPA = 'TCPA'
|
|
ACPI_TABLE_SIG_WDAT = 'WDAT'
|
|
ACPI_TABLE_SIG_WDRT = 'WDRT'
|
|
ACPI_TABLE_SIG_WSPT = 'WSPT'
|
|
ACPI_TABLE_SIG_WDDT = 'WDDT'
|
|
ACPI_TABLE_SIG_ASF = 'ASF!'
|
|
ACPI_TABLE_SIG_MSEG = 'MSEG'
|
|
ACPI_TABLE_SIG_DMAR = 'DMAR'
|
|
ACPI_TABLE_SIG_UEFI = 'UEFI'
|
|
ACPI_TABLE_SIG_FPDT = 'FPDT'
|
|
ACPI_TABLE_SIG_PCCT = 'PCCT'
|
|
ACPI_TABLE_SIG_MSDM = 'MSDM'
|
|
ACPI_TABLE_SIG_BATB = 'BATB'
|
|
ACPI_TABLE_SIG_BGRT = 'BGRT'
|
|
ACPI_TABLE_SIG_LPIT = 'LPIT'
|
|
ACPI_TABLE_SIG_ASPT = 'ASPT'
|
|
ACPI_TABLE_SIG_FIDT = 'FIDT'
|
|
ACPI_TABLE_SIG_HEST = 'HEST'
|
|
ACPI_TABLE_SIG_BERT = 'BERT'
|
|
ACPI_TABLE_SIG_ERST = 'ERST'
|
|
ACPI_TABLE_SIG_EINJ = 'EINJ'
|
|
ACPI_TABLE_SIG_TPM2 = 'TPM2'
|
|
ACPI_TABLE_SIG_WSMT = 'WSMT'
|
|
ACPI_TABLE_SIG_DBG2 = 'DBG2'
|
|
ACPI_TABLE_SIG_NHLT = 'NHLT'
|
|
ACPI_TABLE_SIG_MSCT = 'MSCT'
|
|
ACPI_TABLE_SIG_RASF = 'RASF'
|
|
ACPI_TABLE_SIG_OEM1 = 'OEM1'
|
|
ACPI_TABLE_SIG_OEM2 = 'OEM2'
|
|
ACPI_TABLE_SIG_OEM3 = 'OEM3'
|
|
ACPI_TABLE_SIG_OEM4 = 'OEM4'
|
|
ACPI_TABLE_SIG_NFIT = 'NFIT'
|
|
|
|
ACPI_TABLES: Dict[str, Callable] = {
|
|
ACPI_TABLE_SIG_ROOT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_RSDT: acpi_tables.RSDT,
|
|
ACPI_TABLE_SIG_XSDT: acpi_tables.XSDT,
|
|
ACPI_TABLE_SIG_FACP: acpi_tables.FADT,
|
|
ACPI_TABLE_SIG_FACS: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_DSDT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_SSDT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_PSDT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_APIC: acpi_tables.APIC,
|
|
ACPI_TABLE_SIG_SBST: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_ECDT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_SRAT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_SLIC: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_SLIT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_BOOT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_CPEP: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_DBGP: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_ETDT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_HPET: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_MCFG: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_SPCR: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_TCPA: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_WDAT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_WDRT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_WSPT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_WDDT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_ASF: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_MSEG: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_DMAR: acpi_tables.DMAR,
|
|
ACPI_TABLE_SIG_UEFI: acpi_tables.UEFI_TABLE,
|
|
ACPI_TABLE_SIG_FPDT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_PCCT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_MSDM: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_BATB: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_BGRT: acpi_tables.BGRT,
|
|
ACPI_TABLE_SIG_LPIT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_ASPT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_FIDT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_HEST: acpi_tables.HEST,
|
|
ACPI_TABLE_SIG_BERT: acpi_tables.BERT,
|
|
ACPI_TABLE_SIG_ERST: acpi_tables.ERST,
|
|
ACPI_TABLE_SIG_EINJ: acpi_tables.EINJ,
|
|
ACPI_TABLE_SIG_TPM2: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_WSMT: acpi_tables.WSMT,
|
|
ACPI_TABLE_SIG_DBG2: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_NHLT: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_MSCT: acpi_tables.MSCT,
|
|
ACPI_TABLE_SIG_RASF: acpi_tables.RASF,
|
|
ACPI_TABLE_SIG_SPMI: acpi_tables.SPMI,
|
|
ACPI_TABLE_SIG_OEM1: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_OEM2: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_OEM3: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_OEM4: acpi_tables.ACPI_TABLE,
|
|
ACPI_TABLE_SIG_NFIT: acpi_tables.NFIT
|
|
}
|
|
|
|
########################################################################################################
|
|
#
|
|
# RSDP
|
|
#
|
|
########################################################################################################
|
|
|
|
RSDP_GUID_ACPI2_0 = '8868E871-E4F1-11D3-BC22-0080C73C8881'
|
|
RSDP_GUID_ACPI1_0 = 'EB9D2D31-2D88-11D3-9A16-0090273FC14D'
|
|
ACPI_RSDP_SIG = 'RSD PTR '
|
|
|
|
########################################################################################################
|
|
#
|
|
# ACPI HAL Component
|
|
#
|
|
########################################################################################################
|
|
|
|
|
|
class ACPI(HALBase):
|
|
def __init__(self, cs):
|
|
super(ACPI, self).__init__(cs)
|
|
self.uefi = UEFI(self.cs)
|
|
self.tableList: Dict[str, List[int]] = defaultdict(list)
|
|
self.get_ACPI_table_list()
|
|
|
|
def read_RSDP(self, rsdp_pa: int) -> acpi_tables.RSDP:
|
|
rsdp_buf = self.cs.hals.memory.read_physical_mem(rsdp_pa, acpi_tables.ACPI_RSDP_SIZE)
|
|
rsdp = acpi_tables.RSDP()
|
|
rsdp.parse(rsdp_buf)
|
|
if rsdp.Revision >= 0x2:
|
|
rsdp_buf = self.cs.hals.memory.read_physical_mem(rsdp_pa, acpi_tables.ACPI_RSDP_EXT_SIZE)
|
|
rsdp = acpi_tables.RSDP()
|
|
rsdp.parse(rsdp_buf)
|
|
return rsdp
|
|
|
|
#
|
|
# Check RSDP in Extended BIOS Data Area
|
|
#
|
|
def _find_RSDP_in_EBDA(self) -> Tuple[Optional[acpi_tables.RSDP], Optional[int]]:
|
|
rsdp_pa = None
|
|
rsdp = None
|
|
logger().log_hal('[acpi] searching RSDP in EBDA...')
|
|
ebda_ptr_addr = 0x40E
|
|
ebda_addr = struct.unpack('<H', self.cs.hals.memory.read_physical_mem(ebda_ptr_addr, 2))[0] << 4
|
|
if ebda_addr > 0x400 and ebda_addr < 0xA0000:
|
|
membuf = self.cs.hals.memory.read_physical_mem(ebda_addr, 0xA0000 - ebda_addr)
|
|
pos = bytestostring(membuf).find(ACPI_RSDP_SIG)
|
|
if -1 != pos:
|
|
rsdp_pa = ebda_addr + pos
|
|
rsdp = self.read_RSDP(rsdp_pa)
|
|
if rsdp.is_RSDP_valid():
|
|
logger().log_hal(f'[acpi] found RSDP in EBDA at: 0x{rsdp_pa:016X}')
|
|
else:
|
|
rsdp_pa = None
|
|
return rsdp, rsdp_pa
|
|
|
|
#
|
|
# Search RSDP in legacy BIOS E/F segments (0xE0000 - 0xFFFFF)
|
|
#
|
|
def _find_RSDP_in_legacy_BIOS_segments(self) -> Tuple[Optional[acpi_tables.RSDP], Optional[int]]:
|
|
rsdp_pa = None
|
|
rsdp = None
|
|
membuf = self.cs.hals.memory.read_physical_mem(0xE0000, 0x20000)
|
|
membuf = bytestostring(membuf)
|
|
pos = bytestostring(membuf).find(ACPI_RSDP_SIG)
|
|
if -1 != pos:
|
|
rsdp_pa = 0xE0000 + pos
|
|
rsdp = self.read_RSDP(rsdp_pa)
|
|
if rsdp.is_RSDP_valid():
|
|
logger().log_hal(f'[acpi] Found RSDP in BIOS E/F segments: 0x{rsdp_pa:016X}')
|
|
else:
|
|
rsdp_pa = None
|
|
return rsdp, rsdp_pa
|
|
|
|
#
|
|
# Search for RSDP in the EFI memory (EFI Configuration Table)
|
|
#
|
|
def _find_RSDP_in_EFI_config_table(self) -> Tuple[Optional[acpi_tables.RSDP], Optional[int]]:
|
|
rsdp_pa = None
|
|
rsdp = None
|
|
logger().log_hal('[acpi] Searching RSDP pointers in EFI Configuration Table...')
|
|
(isFound, _, ect, _) = self.uefi.find_EFI_Configuration_Table()
|
|
if isFound and (ect is not None):
|
|
if RSDP_GUID_ACPI2_0 in ect.VendorTables:
|
|
rsdp_pa = ect.VendorTables[RSDP_GUID_ACPI2_0]
|
|
logger().log_hal(f'[acpi] ACPI 2.0+ RSDP {{{RSDP_GUID_ACPI2_0}}} in EFI Config Table: 0x{rsdp_pa:016X}')
|
|
elif RSDP_GUID_ACPI1_0 in ect.VendorTables:
|
|
rsdp_pa = ect.VendorTables[RSDP_GUID_ACPI1_0]
|
|
logger().log_hal('[acpi] ACPI 1.0 RSDP {{{RSDP_GUID_ACPI1_0}}} in EFI Config Table: 0x{rsdp_pa:016X}')
|
|
|
|
if rsdp_pa:
|
|
rsdp = self.read_RSDP(rsdp_pa)
|
|
if rsdp.is_RSDP_valid():
|
|
logger().log_hal(f'[acpi] Found RSDP in EFI Config Table: 0x{rsdp_pa:016X}')
|
|
else:
|
|
rsdp_pa = None
|
|
return rsdp, rsdp_pa
|
|
|
|
#
|
|
# Search for RSDP in all EFI memory
|
|
#
|
|
def _find_RSDP_in_EFI(self) -> Tuple[Optional[acpi_tables.RSDP], Optional[int]]:
|
|
rsdp_pa = None
|
|
rsdp = None
|
|
logger().log_hal("[acpi] Searching all EFI memory for RSDP (this may take a minute).")
|
|
CHUNK_SZ = 1024 * 1024 # 1MB
|
|
(smram_base, _, _) = self.cs.hals.cpu.get_SMRAM()
|
|
pa = smram_base - CHUNK_SZ
|
|
while pa > CHUNK_SZ:
|
|
membuf = self.cs.hals.memory.read_physical_mem(pa, CHUNK_SZ)
|
|
pos = bytestostring(membuf).find(ACPI_RSDP_SIG)
|
|
if -1 != pos:
|
|
rsdp_pa = pa + pos
|
|
logger().log_hal(f"[acpi] Found '{ACPI_RSDP_SIG}' signature at 0x{rsdp_pa:16X}. Checking if valid RSDP.")
|
|
rsdp = self.read_RSDP(rsdp_pa)
|
|
if rsdp.is_RSDP_valid():
|
|
logger().log_hal(f'[acpi] Found RSDP in EFI memory: 0x{rsdp_pa:016X}')
|
|
break
|
|
pa -= CHUNK_SZ
|
|
return rsdp, rsdp_pa
|
|
|
|
#
|
|
# Searches for Root System Description Pointer (RSDP) in various locations for legacy/EFI systems
|
|
#
|
|
def find_RSDP(self) -> Tuple[Optional[int], Optional[acpi_tables.RSDP]]:
|
|
rsdp, rsdp_pa = self._find_RSDP_in_EBDA()
|
|
|
|
if rsdp_pa is None:
|
|
rsdp, rsdp_pa = self._find_RSDP_in_legacy_BIOS_segments()
|
|
|
|
if rsdp_pa is None:
|
|
rsdp, rsdp_pa = self._find_RSDP_in_EFI_config_table()
|
|
|
|
if rsdp_pa is None:
|
|
rsdp, rsdp_pa = self._find_RSDP_in_EFI()
|
|
|
|
if rsdp is not None:
|
|
logger().log_hal(str(rsdp))
|
|
|
|
return (rsdp_pa, rsdp)
|
|
|
|
RsdtXsdt = Union[acpi_tables.RSDT, acpi_tables.XSDT]
|
|
#
|
|
# Retrieves System Description Table (RSDT or XSDT) either from RSDP or using OS API
|
|
#
|
|
def get_SDT(self, search_rsdp: bool = True) -> Tuple[bool, Optional[int], Optional[RsdtXsdt], Optional[ACPI_TABLE_HEADER]]:
|
|
is_xsdt = False
|
|
sdt_pa = None
|
|
sdt_header = None
|
|
sdt_buf = b''
|
|
if search_rsdp:
|
|
(_, rsdp) = self.find_RSDP()
|
|
if rsdp is not None:
|
|
if 0x0 == rsdp.Revision:
|
|
sdt_pa = rsdp.RsdtAddress
|
|
is_xsdt = False
|
|
elif 0x2 == rsdp.Revision:
|
|
sdt_pa = rsdp.XsdtAddress
|
|
is_xsdt = True
|
|
else:
|
|
return (False, None, None, None)
|
|
found_str = 'XSDT' if is_xsdt else 'RSDT'
|
|
logger().log_hal(f'[acpi] Found {found_str} at PA: 0x{sdt_pa:016X}')
|
|
sdt_header_buf = self.cs.hals.memory.read_physical_mem(sdt_pa, ACPI_TABLE_HEADER_SIZE)
|
|
sdt_header = self._parse_table_header(sdt_header_buf)
|
|
sdt_buf = self.cs.hals.memory.read_physical_mem(sdt_pa, sdt_header.Length)
|
|
else:
|
|
sdt_pa = None
|
|
if logger().HAL:
|
|
logger().log("[acpi] Reading RSDT/XSDT using OS API...")
|
|
(sdt_buf, is_xsdt) = self.get_ACPI_SDT()
|
|
sdt_header = self._parse_table_header(sdt_buf[:ACPI_TABLE_HEADER_SIZE])
|
|
|
|
sdt_contents = sdt_buf[ACPI_TABLE_HEADER_SIZE:]
|
|
sdt = ACPI_TABLES[ACPI_TABLE_SIG_XSDT if is_xsdt else ACPI_TABLE_SIG_RSDT]()
|
|
sdt.parse(sdt_contents)
|
|
return (is_xsdt, sdt_pa, sdt, sdt_header)
|
|
|
|
|
|
|
|
def get_ACPI_SDT(self) -> Tuple[Optional['Array'], bool]:
|
|
sdt = self.cs.helper.get_acpi_table('XSDT') # FirmwareTableID_XSDT
|
|
xsdt = sdt is not None
|
|
if not xsdt:
|
|
sdt = self.get_acpi_table('RSDT') # FirmwareTableID_RSDT
|
|
return sdt, xsdt
|
|
|
|
#
|
|
# Populates a list of ACPI tables available on the system
|
|
#
|
|
def get_ACPI_table_list(self) -> Dict[str, List[int]]:
|
|
try:
|
|
# 1. If didn't work, try using get_ACPI_table if a helper implemented
|
|
# reading ACPI tables via native API which some OS may provide
|
|
# raise UnimplementedAPIError("asdf")
|
|
logger().log_hal("[acpi] Trying to enumerate ACPI tables using get_ACPI_table...")
|
|
for table_name in self.cs.helper.enum_ACPI_tables():
|
|
self.tableList[table_name.decode("utf-8")].append(0)
|
|
except UnimplementedAPIError:
|
|
# 2. Try to extract ACPI table(s) from physical memory
|
|
# read_physical_mem can be implemented using both
|
|
# CHIPSEC kernel module and OS native API
|
|
logger().log_hal("[acpi] Trying to enumerate ACPI tables from physical memory...")
|
|
# find RSDT/XSDT table
|
|
(is_xsdt, sdt_pa, sdt, sdt_header) = self.get_SDT()
|
|
# cache RSDT/XSDT in the list of ACPI tables
|
|
if (sdt_pa is not None) and (sdt_header is not None):
|
|
self.tableList[bytestostring(sdt_header.Signature)].append(sdt_pa)
|
|
if sdt is not None:
|
|
self.get_table_list_from_SDT(sdt, is_xsdt)
|
|
self.get_DSDT_from_FADT()
|
|
|
|
return self.tableList
|
|
|
|
#
|
|
# Gets table list from entries in RSDT/XSDT
|
|
#
|
|
def get_table_list_from_SDT(self, sdt: RsdtXsdt, is_xsdt: bool) -> None:
|
|
logger().log_hal(f'[acpi] Getting table list from entries in {"XSDT" if is_xsdt else "RSDT"}')
|
|
for a in sdt.Entries:
|
|
_sig = self.cs.hals.memory.read_physical_mem(a, ACPI_TABLE_SIG_SIZE)
|
|
_sig = bytestostring(_sig)
|
|
if _sig not in ACPI_TABLES.keys():
|
|
if logger().HAL:
|
|
logger().log_warning(f'Unknown ACPI table signature: {_sig}')
|
|
self.tableList[_sig].append(a)
|
|
|
|
#
|
|
# Gets DSDT from FADT
|
|
#
|
|
def get_DSDT_from_FADT(self) -> None:
|
|
logger().log_hal('[acpi] Getting DSDT from FADT')
|
|
|
|
if ACPI_TABLE_SIG_FACP in self.tableList:
|
|
(_, parsed_fadt_content, _, _) = self.get_parse_ACPI_table('FACP')[0]
|
|
else:
|
|
if logger().HAL:
|
|
found_table = 'XSDT' if ACPI_TABLE_SIG_XSDT in self.tableList else 'RSDT'
|
|
logger().log_warning(f'Cannot find FADT in {found_table}')
|
|
return
|
|
|
|
dsdt_address_to_use = parsed_fadt_content.get_DSDT_address_to_use()
|
|
|
|
if dsdt_address_to_use is None:
|
|
dsdt_address = parsed_fadt_content.dsdt
|
|
x_dsdt_address = parsed_fadt_content.x_dsdt
|
|
if logger().HAL:
|
|
logger().log_error('Unable to determine the correct DSDT address')
|
|
if logger().HAL:
|
|
logger().log_error(f' DSDT address = 0x{dsdt_address:08X}')
|
|
if logger().HAL:
|
|
address_str = f'{x_dsdt_address:16X}' if x_dsdt_address is not None else 'Not found'
|
|
logger().log_error(f' X_DSDT address = 0x{address_str}')
|
|
return
|
|
|
|
self.tableList[ACPI_TABLE_SIG_DSDT].append(dsdt_address_to_use)
|
|
|
|
#
|
|
# Checks is ACPI table with <name> is available on the system
|
|
#
|
|
def is_ACPI_table_present(self, name: str) -> bool:
|
|
return (name in self.tableList)
|
|
|
|
#
|
|
# Prints a list of ACPI tables available on the system
|
|
#
|
|
def print_ACPI_table_list(self) -> None:
|
|
if len(self.tableList) == 0:
|
|
logger().log_error("Couldn't get a list of ACPI tables")
|
|
else:
|
|
logger().log_hal('[acpi] Found the following ACPI tables:')
|
|
for tableName in sorted(self.tableList.keys()):
|
|
table_values_str = ', '.join([f'0x{addr:016X}' for addr in self.tableList[tableName]])
|
|
logger().log(f' - {tableName}: {table_values_str}')
|
|
|
|
#
|
|
# Retrieves contents of ACPI table from memory or from file
|
|
#
|
|
def get_parse_ACPI_table(self, name: str, isfile: bool = False) -> List['ParseTable']:
|
|
acpi_tables = self.get_ACPI_table(name, isfile)
|
|
return [self._parse_table(name, table_header_blob, table_blob) for (table_header_blob, table_blob) in acpi_tables if table_header_blob is not None]
|
|
|
|
def get_ACPI_table(self, name: str, isfile: bool = False) -> List[Tuple[bytes, bytes]]:
|
|
acpi_tables_data: List[bytes] = []
|
|
if isfile:
|
|
acpi_tables_data.append(read_file(name))
|
|
else:
|
|
try:
|
|
# 1. Try to extract ACPI table(s) using get_ACPI_table if a helper implemented
|
|
# reading ACPI tables via native API which some OS may provide
|
|
logger().log_hal("[acpi] trying to extract ACPI table using get_ACPI_table...")
|
|
t_data = self.cs.helper.get_ACPI_table(name)
|
|
if t_data is None:
|
|
raise TypeError
|
|
acpi_tables_data.append(t_data)
|
|
except UnimplementedAPIError:
|
|
# 2. If didn't work, try scrubbing physical memory
|
|
# read_physical_mem can be implemented using both
|
|
# CHIPSEC kernel module and OS native API
|
|
logger().log_hal('[acpi] trying to extract ACPI table from physical memory...')
|
|
for table_address in self.tableList[name]:
|
|
t_size = self.cs.hals.memory.read_physical_mem_dword(table_address + 4)
|
|
t_data = self.cs.hals.memory.read_physical_mem(table_address, t_size)
|
|
acpi_tables_data.append(t_data)
|
|
|
|
acpi_tables = []
|
|
for data in acpi_tables_data:
|
|
acpi_tables.append((data[: ACPI_TABLE_HEADER_SIZE], data[ACPI_TABLE_HEADER_SIZE:]))
|
|
|
|
return acpi_tables
|
|
|
|
#
|
|
# Dumps contents of ACPI table
|
|
#
|
|
def dump_ACPI_table(self, name: str, isfile: bool = False) -> None:
|
|
acpi_tables = self.get_parse_ACPI_table(name, isfile)
|
|
for acpi_table in acpi_tables:
|
|
(table_header, table, table_header_blob, table_blob) = acpi_table
|
|
logger().log("==================================================================")
|
|
logger().log(f'ACPI Table: {name}')
|
|
logger().log("==================================================================")
|
|
# print table header
|
|
logger().log(str(table_header))
|
|
print_buffer_bytes(table_header_blob)
|
|
# print table contents
|
|
logger().log('')
|
|
logger().log(str(table))
|
|
print_buffer_bytes(table_blob)
|
|
logger().log('')
|
|
|
|
# --------------------------------------------------------------------
|
|
# Internal ACPI table parsing functions
|
|
# --------------------------------------------------------------------
|
|
|
|
ParseTable = Tuple[ACPI_TABLE_HEADER, Optional[ACPI_TABLE], bytes, bytes]
|
|
|
|
def _parse_table(self, name: str, table_header_blob: bytes, table_blob: bytes) -> ParseTable:
|
|
table_header = self._parse_table_header(table_header_blob)
|
|
table = self._parse_table_contents(name, table_blob, table_header_blob)
|
|
return (table_header, table, table_header_blob, table_blob)
|
|
|
|
def _parse_table_header(self, header: bytes) -> ACPI_TABLE_HEADER:
|
|
acpi_table_hdr = ACPI_TABLE_HEADER(*struct.unpack_from(ACPI_TABLE_HEADER_FORMAT, header))
|
|
logger().log_hal(str(acpi_table_hdr))
|
|
return acpi_table_hdr
|
|
|
|
def _parse_table_contents(self, signature: str, contents: bytes, header: bytes) -> Optional[ACPI_TABLE]:
|
|
table = None
|
|
if ACPI_TABLES.__contains__(signature):
|
|
logger().log_hal(f'{signature}')
|
|
if 'BERT' in signature:
|
|
BootRegionLen = struct.unpack('<L', contents[0:4])[0]
|
|
BootRegionAddr = struct.unpack('<Q', contents[4:12])[0]
|
|
bootRegion = self.cs.hals.memory.read_physical_mem(BootRegionAddr, BootRegionLen)
|
|
table = (ACPI_TABLES[signature])(bootRegion)
|
|
elif 'NFIT' in signature:
|
|
table = (ACPI_TABLES[signature])(header)
|
|
else:
|
|
table = (ACPI_TABLES[signature])()
|
|
table.parse(contents)
|
|
return table
|
|
|
|
# ========================================================================
|
|
# ACPI OperationRegion Analysis
|
|
# ========================================================================
|
|
|
|
def list_operation_regions(self, include_ssdt: bool = True, include_crs: bool = True) -> List[Dict]:
|
|
"""
|
|
Extract all OperationRegion definitions and optionally _CRS resource descriptors from DSDT/SSDTs.
|
|
|
|
Args:
|
|
include_ssdt: Include SSDT tables (default True)
|
|
include_crs: Include _CRS method resource descriptors (default True)
|
|
|
|
Returns:
|
|
List of dicts: [{name, space_type, space_type_name, base, length, source}, ...]
|
|
where source is "OperationRegion" or "CRS Descriptor"
|
|
"""
|
|
from chipsec.library.acpi_aml_parser import parse_operation_regions, CRSResourceParser
|
|
|
|
regions = []
|
|
tables_to_parse = []
|
|
aml_tables = [] # Keep track of AML content for CRS extraction
|
|
|
|
# Add DSDT
|
|
try:
|
|
if ACPI_TABLE_SIG_DSDT in self.tableList:
|
|
dsdt_tables = self.get_ACPI_table(ACPI_TABLE_SIG_DSDT)
|
|
if dsdt_tables:
|
|
for dsdt_header, dsdt_content in dsdt_tables:
|
|
full_table = dsdt_header + dsdt_content
|
|
tables_to_parse.append(full_table)
|
|
aml_tables.append(dsdt_content)
|
|
except Exception as e:
|
|
logger().log_warning(f"[acpi] Error reading DSDT: {e}")
|
|
|
|
# Add SSDTs if requested
|
|
if include_ssdt:
|
|
try:
|
|
if ACPI_TABLE_SIG_SSDT in self.tableList:
|
|
ssdt_tables = self.get_ACPI_table(ACPI_TABLE_SIG_SSDT)
|
|
if ssdt_tables:
|
|
for ssdt_header, ssdt_content in ssdt_tables:
|
|
full_table = ssdt_header + ssdt_content
|
|
tables_to_parse.append(full_table)
|
|
aml_tables.append(ssdt_content)
|
|
except Exception as e:
|
|
logger().log_warning(f"[acpi] Error reading SSDTs: {e}")
|
|
|
|
if not tables_to_parse:
|
|
return regions
|
|
|
|
# Parse OperationRegion definitions
|
|
regions = parse_operation_regions(tables_to_parse)
|
|
|
|
# Extract _CRS resource descriptors if enabled
|
|
if include_crs and aml_tables:
|
|
try:
|
|
for aml_content in aml_tables:
|
|
# Extract _CRS buffer from each AML table
|
|
crs_buffer = CRSResourceParser.extract_crs_buffer(aml_content)
|
|
if crs_buffer:
|
|
# Decode resource descriptors
|
|
resources = CRSResourceParser.decode_crs_buffer(crs_buffer)
|
|
for res in resources:
|
|
if 'address' in res:
|
|
# Convert CRS resource to region dict format
|
|
crs_region = {
|
|
'name': f"CRS_{res.get('type', 'Unknown')}_{res['address']:08X}",
|
|
'space_type': 0 if res.get('type', '').startswith('Memory') else 1,
|
|
'space_type_name': 'SystemMemory' if res.get('type', '').startswith('Memory') else 'SystemIO',
|
|
'base': res['address'],
|
|
'length': res['size'],
|
|
'source': 'CRS Descriptor'
|
|
}
|
|
regions.append(crs_region)
|
|
except Exception as e:
|
|
logger().log_warning(f"[acpi] Error extracting _CRS resources: {e}")
|
|
|
|
return regions
|
|
|
|
|
|
|
|
haldata = {"arch":[HALBase.MfgIds.Any], 'name': {'acpi':'ACPI'}}
|