mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-19 13:49:39 +00:00
category based on different log levels: 1) If the board inspector show CRITICAL error messages, it means that the board inspector tool exit instantly and generate no file. 2) If the board inspector show ERROR messages, it means that the board inspector will generate the board XML file successfully, but several conditions may result in ACRN build or boot failure cannot continue execution due to the error. 3) If the board inspector show WARNING messages, this means that the board inspector generate the board XML file successfully, but this board XML file is lack of some feature which could still boot ACRN but loss some features. 4) If the board inspector show INFO messages, this means that board inspector printed out some normal information. 5) If the board inspector show DEBUG messages, this means that it is used to debug board inspector workflow. v1->v2 1. Keep the assertions or exceptions under the acpiparser, inspectorlib and smbiosparser directory. 2. Exit after all check_deps() completes instead of any unsatisfied dep is identified. 3. As Amy's advice, Replace the message "{board_xml} saved successfully" with the message "SUCCESS: Board configuration file <file name> generated successfully and saved to <path>." To do: Print all messages using the colored font. Tracked-On: #6689 Reviewed-by: Junjie Mao <junjie.mao@intel.com> Signed-off-by: Kunhui-Li <kunhuix.li@intel.com>
668 lines
26 KiB
Python
668 lines
26 KiB
Python
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
|
#
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
#
|
|
|
|
import os
|
|
import sys
|
|
import subprocess # nosec
|
|
import shutil
|
|
from collections import defaultdict
|
|
import dmar
|
|
import parser_lib
|
|
import logging
|
|
|
|
SYS_PATH = ['/proc/cpuinfo', '/sys/firmware/acpi/tables/', '/sys/devices/system/cpu/']
|
|
|
|
ACPI_OP = {
|
|
'AML_ZERO_OP':0x00,
|
|
'AML_ONE_OP':0x01,
|
|
'AML_ALIAS_OP':0x06,
|
|
'AML_NAME_OP':0x08,
|
|
'AML_BYTE_OP':0x0a,
|
|
'AML_WORD_OP':0x0b,
|
|
'AML_DWORD_OP':0x0c,
|
|
'AML_PACKAGE_OP':0x12,
|
|
'AML_VARIABLE_PACKAGE_OP':0x13,
|
|
}
|
|
|
|
SPACE_ID = {
|
|
0:'SPACE_SYSTEM_MEMORY',
|
|
1:'SPACE_SYSTEM_IO',
|
|
2:'SPACE_PCI_CONFIG',
|
|
3:'SPACE_Embedded_Control',
|
|
4:'SPACE_SMBUS',
|
|
10:'SPACE_PLATFORM_COMM',
|
|
0x7F:'SPACE_FFixedHW',
|
|
}
|
|
|
|
FACP_OFF = {
|
|
'facs_addr':36,
|
|
'reset_addr':116,
|
|
'reset_value':128,
|
|
'pm1a_evt':148,
|
|
'pm1b_evt':160,
|
|
'pm1a_cnt':172,
|
|
'pm1b_cnt':184,
|
|
}
|
|
|
|
class SxPkg:
|
|
"""This is Sx state structure for power"""
|
|
def __init__(self):
|
|
# This is Sx state structure for power
|
|
self.val_pm1a = ''
|
|
self.val_pm1b = ''
|
|
self.reserved = ''
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self.val_pm1a = ''
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self.val_pm1a = ''
|
|
|
|
class GasType:
|
|
"""This is generic address structure for power"""
|
|
def __init__(self):
|
|
self.space_id_8b = 0
|
|
self.bit_width_8b = 0
|
|
self.bit_offset_8b = 0
|
|
self.access_size_8b = 0
|
|
self.address_64b = 0
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self.space_id_8b = ''
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self.space_id_8b = ''
|
|
|
|
class PxPkg:
|
|
"""This is Px state structure for power"""
|
|
def __init__(self):
|
|
self.core_freq = 0
|
|
self.power = 0
|
|
self.trans_latency = 0
|
|
self.bus_latency = 0
|
|
self.control = 0
|
|
self.status = 0
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self.power = ''
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self.power = ''
|
|
|
|
class ResetReg:
|
|
"""This is Reset Registers meta data"""
|
|
def __init__(self):
|
|
self.reset_reg_addr = 0
|
|
self.reset_reg_space_id = 0
|
|
self.reset_reg_val = 0
|
|
|
|
def style_check_1(self):
|
|
"""Style check if have public method"""
|
|
self.reset_reg_val = ''
|
|
|
|
def style_check_2(self):
|
|
"""Style check if have public method"""
|
|
self.reset_reg_val = ''
|
|
|
|
DWORD_LEN = 4
|
|
PACK_TYPE_LEN = 12
|
|
WAKE_VECTOR_OFFSET_32 = 12
|
|
WAKE_VECTOR_OFFSET_64 = 24
|
|
MCFG_OFFSET = 4
|
|
MCFG_ENTRY1_OFFSET = 60
|
|
MCFG_ENTRY0_BASE_OFFSET = 44
|
|
|
|
S3_PKG = SxPkg()
|
|
S5_PKG = SxPkg()
|
|
PackedGas = GasType
|
|
PackedCx = GasType
|
|
|
|
|
|
def store_cpu_info(sysnode, config):
|
|
"""This will get CPU information
|
|
:param sysnode: the path to get cpu information, like: /proc/cpuifo
|
|
:param config: file pointer that opened for writing board config information
|
|
"""
|
|
with open(sysnode, 'r') as f_node:
|
|
line = f_node.readline()
|
|
while line:
|
|
if len(line.split(':')) >= 2:
|
|
if line.split(':')[0].strip() == "model name":
|
|
model_name = line.split(':')[1].strip()
|
|
print('\t"{0}"'.format(model_name), file=config)
|
|
break
|
|
line = f_node.readline()
|
|
|
|
|
|
def write_reset_reg(rst_reg_addr, rst_reg_space_id, rst_reg_val, config):
|
|
"""Write reset register info
|
|
:param rst_reg_addr: reset register address
|
|
:param rst_reg_space_id: reset register space id
|
|
:param rst_reg_val: reset register value
|
|
:param config: file pointer that opened for writing board config information
|
|
"""
|
|
print("\t{0}".format("<RESET_REGISTER_INFO>"), file=config)
|
|
print("\t#define RESET_REGISTER_ADDRESS 0x{:0>2X}UL".format(
|
|
rst_reg_addr), file=config)
|
|
print("\t#define RESET_REGISTER_SPACE_ID {0}".format(
|
|
SPACE_ID[rst_reg_space_id]), file=config)
|
|
print("\t#define RESET_REGISTER_VALUE {0}U".format(
|
|
rst_reg_val), file=config)
|
|
print("\t{0}\n".format("</RESET_REGISTER_INFO>"), file=config)
|
|
|
|
|
|
def get_vector_reset(sysnode, config):
|
|
"""This will get reset register value
|
|
:param sysnode: the system node of Px power state, like:/sys/firmware/acpi/tables/FACP
|
|
:param config: file pointer that opened for writing board config information
|
|
"""
|
|
reset_reg = ResetReg()
|
|
for key, offset in FACP_OFF.items():
|
|
with open(sysnode, 'rb') as f_node:
|
|
f_node.seek(offset, 0)
|
|
if key == 'facs_addr':
|
|
packed_data = f_node.read(DWORD_LEN)
|
|
packed_data_32 = int.from_bytes(packed_data, 'little')+WAKE_VECTOR_OFFSET_32
|
|
packed_data_64 = int.from_bytes(packed_data, 'little')+WAKE_VECTOR_OFFSET_64
|
|
print("\t{0}".format("<WAKE_VECTOR_INFO>"), file=config)
|
|
print("\t#define WAKE_VECTOR_32 0x{:0>2X}UL".format(
|
|
packed_data_32), file=config)
|
|
print("\t#define WAKE_VECTOR_64 0x{:0>2X}UL".format(
|
|
packed_data_64), file=config)
|
|
print("\t{0}\n".format("</WAKE_VECTOR_INFO>"), file=config)
|
|
elif key == 'reset_addr':
|
|
packed_data = f_node.read(PACK_TYPE_LEN)
|
|
reset_reg.reset_reg_space_id = packed_data[0]
|
|
reset_reg.reset_reg_addr = int.from_bytes(packed_data[4:11], 'little')
|
|
elif key == 'reset_value':
|
|
packed_data = f_node.read(1)
|
|
reset_reg.reset_reg_val = hex(packed_data[0])
|
|
|
|
write_reset_reg(reset_reg.reset_reg_addr, reset_reg.reset_reg_space_id,
|
|
reset_reg.reset_reg_val, config)
|
|
|
|
|
|
def read_pm_sstate(sysnode, config):
|
|
"""This will read Px state of power
|
|
:param sysnode: the system node of Px power state, like:/sys/firmware/acpi/tables/FACP
|
|
:param config: file pointer that opened for writing board config information
|
|
"""
|
|
get_vector_reset(sysnode, config)
|
|
print("\t{0}".format("<PM_INFO>"), file=config)
|
|
for key, offset in FACP_OFF.items():
|
|
with open(sysnode, 'rb') as f_node:
|
|
f_node.seek(offset, 0)
|
|
|
|
packed_data = f_node.read(PACK_TYPE_LEN)
|
|
PackedGas.space_id_8b = packed_data[0]
|
|
PackedGas.bit_width_8b = packed_data[1]
|
|
PackedGas.bit_offset_8b = packed_data[2]
|
|
PackedGas.access_size_8b = packed_data[3]
|
|
PackedGas.address_64b = int.from_bytes(packed_data[4:11], 'little')
|
|
|
|
if key == 'pm1a_evt':
|
|
print("\t#define PM1A_EVT_SPACE_ID {0}".format(
|
|
SPACE_ID[PackedGas.space_id_8b]), file=config)
|
|
print("\t#define PM1A_EVT_BIT_WIDTH {0}U".format(
|
|
hex(PackedGas.bit_width_8b)), file=config)
|
|
print("\t#define PM1A_EVT_BIT_OFFSET {0}U".format(
|
|
hex(PackedGas.bit_offset_8b)), file=config)
|
|
print("\t#define PM1A_EVT_ADDRESS {0}UL".format(
|
|
hex(PackedGas.address_64b)), file=config)
|
|
print("\t#define PM1A_EVT_ACCESS_SIZE {0}U".format(
|
|
hex(PackedGas.access_size_8b)), file=config)
|
|
elif key == 'pm1a_cnt':
|
|
print("\t#define PM1A_CNT_SPACE_ID {0}".format(
|
|
SPACE_ID[PackedGas.space_id_8b]), file=config)
|
|
print("\t#define PM1A_CNT_BIT_WIDTH {0}U".format(
|
|
hex(PackedGas.bit_width_8b)), file=config)
|
|
print("\t#define PM1A_CNT_BIT_OFFSET {0}U".format(
|
|
hex(PackedGas.bit_offset_8b)), file=config)
|
|
print("\t#define PM1A_CNT_ADDRESS {0}UL".format(
|
|
hex(PackedGas.address_64b)), file=config)
|
|
print("\t#define PM1A_CNT_ACCESS_SIZE {0}U".format(
|
|
hex(PackedGas.access_size_8b)), file=config)
|
|
elif key == 'pm1b_evt':
|
|
print("\t#define PM1B_EVT_SPACE_ID {0}".format(
|
|
SPACE_ID[PackedGas.space_id_8b]), file=config)
|
|
print("\t#define PM1B_EVT_BIT_WIDTH {0}U".format(
|
|
hex(PackedGas.bit_width_8b)), file=config)
|
|
print("\t#define PM1B_EVT_BIT_OFFSET {0}U".format(
|
|
hex(PackedGas.bit_offset_8b)), file=config)
|
|
print("\t#define PM1B_EVT_ADDRESS {0}UL".format(
|
|
hex(PackedGas.address_64b)), file=config)
|
|
print("\t#define PM1B_EVT_ACCESS_SIZE {0}U".format(
|
|
hex(PackedGas.access_size_8b)), file=config)
|
|
elif key == 'pm1b_cnt':
|
|
print("\t#define PM1B_CNT_SPACE_ID {0}".format(
|
|
SPACE_ID[PackedGas.space_id_8b]), file=config)
|
|
print("\t#define PM1B_CNT_BIT_WIDTH {0}U".format(
|
|
hex(PackedGas.bit_width_8b)), file=config)
|
|
print("\t#define PM1B_CNT_BIT_OFFSET {0}U".format(
|
|
hex(PackedGas.bit_offset_8b)), file=config)
|
|
print("\t#define PM1B_CNT_ADDRESS {0}UL".format(
|
|
hex(PackedGas.address_64b)), file=config)
|
|
print("\t#define PM1B_CNT_ACCESS_SIZE {0}U".format(
|
|
hex(PackedGas.access_size_8b)), file=config)
|
|
print("\t{0}\n".format("</PM_INFO>"), file=config)
|
|
|
|
|
|
def if_sx_name(sx_name, f_node):
|
|
"""If sx name in this field
|
|
:param sx_name: Sx name in DSDT of apci table, like _s3_, _s5_
|
|
:param f_node: f_node: file pointer that opened for reading sx from
|
|
"""
|
|
need_break = need_continue = 0
|
|
name_buf = f_node.read(4)
|
|
if not name_buf:
|
|
need_break = True
|
|
return (need_break, need_continue)
|
|
|
|
try:
|
|
if name_buf.decode('ascii').find(sx_name) != -1:
|
|
pass
|
|
else:
|
|
need_continue = True
|
|
except ValueError:
|
|
need_continue = True
|
|
return (need_break, need_continue)
|
|
else:
|
|
pass
|
|
|
|
return (need_break, need_continue)
|
|
|
|
|
|
def read_sx_locate(sx_name, f_node):
|
|
"""Read the location of sx
|
|
:param sx_name: Sx name in DSDT of apci table, like _s3_, _s5_
|
|
:param f_node: file pointer that opened for sx reading from
|
|
"""
|
|
need_continue = need_break = pkg_len = 0
|
|
|
|
(need_break, need_continue) = if_sx_name(sx_name, f_node)
|
|
|
|
tmp_char = f_node.read(1)
|
|
if not tmp_char:
|
|
need_break = True
|
|
return (need_break, need_continue, pkg_len)
|
|
if hex(int.from_bytes(tmp_char, 'little')) != hex(ACPI_OP['AML_PACKAGE_OP']):
|
|
need_continue = True
|
|
return (need_break, need_continue, pkg_len)
|
|
|
|
pkg_len = f_node.read(1)
|
|
if not pkg_len:
|
|
need_break = True
|
|
return (need_break, need_continue, pkg_len)
|
|
if int.from_bytes(pkg_len, 'little') < 5 or int.from_bytes(pkg_len, 'little') > 28:
|
|
need_continue = True
|
|
return (need_break, need_continue, pkg_len)
|
|
|
|
return (need_break, need_continue, pkg_len)
|
|
|
|
|
|
def decode_sx_pkg(pkg_len, f_node):
|
|
"""Parse and decode the sx pkg
|
|
:param pkg_len: the length of sx package read from f_node
|
|
:param f_node: file pointer that opened for sx reading from
|
|
"""
|
|
pkg_val_pm1a = pkg_val_pm1b = pkg_val_resv = need_break = 0
|
|
pkg_buf = f_node.read(int.from_bytes(pkg_len, 'little'))
|
|
if hex(pkg_buf[1]) == ACPI_OP['AML_ZERO_OP'] or \
|
|
hex(pkg_buf[1]) == hex(ACPI_OP['AML_ONE_OP']):
|
|
pkg_val_pm1a = pkg_buf[1]
|
|
if hex(pkg_buf[2]) == hex(ACPI_OP['AML_ZERO_OP']) or \
|
|
hex(pkg_buf[2]) == hex(ACPI_OP['AML_ONE_OP']):
|
|
pkg_val_pm1b = pkg_buf[2]
|
|
pkg_val_resv = pkg_buf[3:5]
|
|
elif hex(pkg_buf[2]) == hex(ACPI_OP['AML_BYTE_OP']):
|
|
pkg_val_pm1b = pkg_buf[3]
|
|
pkg_val_resv = pkg_buf[4:6]
|
|
else:
|
|
need_break = True
|
|
return (pkg_val_pm1a, pkg_val_pm1b, pkg_val_resv, need_break)
|
|
|
|
elif hex(pkg_buf[1]) == hex(ACPI_OP['AML_BYTE_OP']):
|
|
pkg_val_pm1a = pkg_buf[2]
|
|
if hex(pkg_buf[3]) == hex(ACPI_OP['AML_ZERO_OP']) or \
|
|
hex(pkg_buf[3]) == hex(ACPI_OP['AML_ONE_OP']):
|
|
pkg_val_pm1b = pkg_buf[3]
|
|
pkg_val_resv = pkg_buf[4:6]
|
|
elif hex(pkg_buf[3]) == hex(ACPI_OP['AML_BYTE_OP']):
|
|
pkg_val_pm1b = pkg_buf[4]
|
|
pkg_val_resv = pkg_buf[5:7]
|
|
else:
|
|
need_break = True
|
|
return (pkg_val_pm1a, pkg_val_pm1b, pkg_val_resv, need_break)
|
|
else:
|
|
need_break = True
|
|
|
|
return (pkg_val_pm1a, pkg_val_pm1b, pkg_val_resv, need_break)
|
|
|
|
|
|
def set_default_sx_value(sx_name, config):
|
|
print("\t/* {} is not supported by BIOS */".format(sx_name), file=config)
|
|
print("\t#define {}_PKG_VAL_PM1A 0x0U".format(sx_name), file=config)
|
|
print("\t#define {}_PKG_VAL_PM1B 0x0U".format(sx_name), file=config)
|
|
print("\t#define {}_PKG_RESERVED 0x0U".format(sx_name), file=config)
|
|
|
|
|
|
def read_pm_sdata(sysnode, sx_name, config):
|
|
"""This will read pm Sx state of power
|
|
:param sysnode: the system node of Sx power state, like:/sys/firmware/acpi/tables/DSDT
|
|
:param sx_name: Sx name in DSDT of apci table, like _s3_, _s5_
|
|
:param config: file pointer that opened for writing board config information
|
|
"""
|
|
with open(sysnode, 'rb') as f_node:
|
|
while True:
|
|
inc = f_node.read(1)
|
|
if inc:
|
|
if hex(int.from_bytes(inc, 'little')) != hex(ACPI_OP['AML_NAME_OP']):
|
|
continue
|
|
|
|
(need_break, need_continue, pkg_len) = read_sx_locate(sx_name, f_node)
|
|
if need_break:
|
|
# BIOS dose not support for SX, set it to default value
|
|
s_name = ''
|
|
if 'S3' in sx_name:
|
|
s_name = 'S3'
|
|
else:
|
|
s_name = 'S5'
|
|
|
|
set_default_sx_value(s_name, config)
|
|
break
|
|
if need_continue:
|
|
continue
|
|
|
|
# decode sx pkg
|
|
(pkg_val_pm1a, pkg_val_pm1b, pkg_val_resv, need_break) =\
|
|
decode_sx_pkg(pkg_len, f_node)
|
|
if need_break:
|
|
break
|
|
|
|
else:
|
|
break
|
|
|
|
if sx_name == '_S3_':
|
|
S3_PKG.val_pm1a = pkg_val_pm1a
|
|
S3_PKG.val_pm1b = pkg_val_pm1b
|
|
S3_PKG.reserved = pkg_val_resv
|
|
print("\t#define S3_PKG_VAL_PM1A {0}".format(
|
|
hex(S3_PKG.val_pm1a))+'U', file=config)
|
|
print("\t#define S3_PKG_VAL_PM1B {0}".format(
|
|
S3_PKG.val_pm1b)+'U', file=config)
|
|
print("\t#define S3_PKG_RESERVED {0}".format(
|
|
hex(int.from_bytes(S3_PKG.reserved, 'little')))+'U', file=config)
|
|
|
|
if sx_name == "_S5_":
|
|
S5_PKG.val_pm1a = pkg_val_pm1a
|
|
S5_PKG.val_pm1b = pkg_val_pm1b
|
|
S5_PKG.reserved = pkg_val_resv
|
|
print("\t#define S5_PKG_VAL_PM1A {0}".format(
|
|
hex(S5_PKG.val_pm1a))+'U', file=config)
|
|
print("\t#define S5_PKG_VAL_PM1B {0}".format(
|
|
S5_PKG.val_pm1b)+'U', file=config)
|
|
print("\t#define S5_PKG_RESERVED {0}".format(
|
|
hex(int.from_bytes(S5_PKG.reserved, 'little')))+'U', file=config)
|
|
|
|
|
|
def get_value_after_str(cstate_str, hw_type):
|
|
""" Get the value after cstate string """
|
|
idx = 0
|
|
cstate_list = cstate_str.split()
|
|
for idx_hw_type, val in enumerate(cstate_list):
|
|
if val.find(hw_type) != -1:
|
|
idx = idx_hw_type
|
|
break
|
|
|
|
return cstate_list[idx + 1]
|
|
|
|
|
|
def store_cx_data(sysnode1, sysnode2, config):
|
|
"""This will get Cx data of power and store it to PackedCx
|
|
:param sysnode1: the path of cx power state driver
|
|
:param sysnode2: the path of cpuidle
|
|
:param config: file pointer that opened for writing board config information
|
|
"""
|
|
i = 0
|
|
state_cpus = {}
|
|
try:
|
|
with open(sysnode1, 'r') as acpi_idle:
|
|
idle_driver = acpi_idle.read(32)
|
|
|
|
if idle_driver.find("acpi_idle") == -1:
|
|
logging.info("Failed to collect processor power states because the current CPU idle driver " \
|
|
"does not expose C-state data. If you need ACPI C-states in post-launched VMs, append 'idle=nomwait' " \
|
|
"to the kernel command line in GRUB config file.")
|
|
if idle_driver.find("intel_idle") == 0:
|
|
logging.info("Failed to collect processor power states because the current CPU idle driver " \
|
|
"does not expose C-state data. If you need ACPI C-states in post-launched VMs, append 'idle=nomwait' " \
|
|
"to the kernel command line in GRUB config file.")
|
|
else:
|
|
logging.info("Failed to collect processor power states because the platform does not provide " \
|
|
"C-state data. If you need ACPI C-states in post-launched VMs, enable C-state support in BIOS.")
|
|
print("\t/* Cx data is not available */", file=config)
|
|
return
|
|
except IOError:
|
|
logging.info("Failed to collect processor power states because CPU idle PM support is disabled " \
|
|
"in the current kernel. If you need ACPI C-states in post-launched VMs, rebuild the current kernel " \
|
|
"with CONFIG_CPU_IDLE set to 'y' or 'm'.")
|
|
print("\t/* Cx data is not available */", file=config)
|
|
return
|
|
|
|
files = os.listdir(sysnode2)
|
|
for d_path in files:
|
|
if os.path.isdir(sysnode2+d_path):
|
|
state_cpus[d_path] = sysnode2+d_path
|
|
|
|
state_cpus = sorted(state_cpus.keys())
|
|
del state_cpus[0]
|
|
cpu_state = ['desc', 'latency', 'power']
|
|
acpi_hw_type = ['HLT', 'MWAIT', 'IOPORT']
|
|
|
|
cx_state = defaultdict(dict)
|
|
c_cnt = 1
|
|
for state in state_cpus:
|
|
i += 1
|
|
for item in cpu_state:
|
|
cx_data_file = open(sysnode2+state+'/'+item, 'r')
|
|
cx_state[state][item] = cx_data_file.read().strip()
|
|
cx_state[state]['type'] = i
|
|
|
|
PackedCx.space_id_8b = SPACE_ID[0x7F]
|
|
if cx_state[state][cpu_state[0]].find(acpi_hw_type[0]) != -1:
|
|
PackedCx.bit_width_8b = 0
|
|
PackedCx.bit_offset_8b = 0
|
|
PackedCx.access_size_8b = 0
|
|
PackedCx.address_64b = 0
|
|
elif cx_state[state][cpu_state[0]].find(acpi_hw_type[1]) != -1:
|
|
PackedCx.bit_width_8b = 1
|
|
PackedCx.bit_offset_8b = 2
|
|
PackedCx.access_size_8b = 1
|
|
addr_val = get_value_after_str(cx_state[state][cpu_state[0]], acpi_hw_type[1])
|
|
PackedCx.address_64b = addr_val
|
|
elif cx_state[state][cpu_state[0]].find(acpi_hw_type[2]) != -1:
|
|
PackedCx.space_id_8b = SPACE_ID[1]
|
|
PackedCx.bit_width_8b = 8
|
|
PackedCx.bit_offset_8b = 0
|
|
PackedCx.access_size_8b = 0
|
|
addr_val = get_value_after_str(cx_state[state][cpu_state[0]], acpi_hw_type[2])
|
|
PackedCx.address_64b = addr_val
|
|
print("\t{{{{{}, 0x{:0>2X}U, 0x{:0>2X}U, 0x{:0>2X}U, ".format(
|
|
PackedCx.space_id_8b, PackedCx.bit_width_8b, PackedCx.bit_offset_8b,
|
|
PackedCx.access_size_8b), file=config, end="")
|
|
print("0x{:0>2X}UL}}, 0x{:0>2X}U, 0x{:0>2X}U, 0x{:0>2X}U}},\t/* C{} */".format(
|
|
int(str(PackedCx.address_64b), 16),
|
|
cx_state[state]['type'], int(cx_state[state][cpu_state[1]]),
|
|
int(cx_state[state][cpu_state[2]]), c_cnt), file=config)
|
|
c_cnt += 1
|
|
|
|
|
|
def store_px_data(sysnode, config):
|
|
"""This will get Px data of power and store it to px data
|
|
:param sysnode: the path of system power state, such as: /sys/devices/system/cpu/
|
|
:param config: file pointer that opened for writing board config information
|
|
"""
|
|
px_tmp = PxPkg()
|
|
px_data = {}
|
|
|
|
try:
|
|
with open(sysnode+'cpu0/cpufreq/scaling_driver', 'r') as f_node:
|
|
freq_driver = f_node.read()
|
|
if freq_driver.find("acpi-cpufreq") == -1:
|
|
logging.info("The Px data for ACRN relies on acpi-cpufreq driver but it is not found, ")
|
|
if freq_driver.find("intel_pstate") == 0:
|
|
logging.info("please add intel_pstate=disable in kernel cmdline to fall back to acpi-cpufreq driver")
|
|
else:
|
|
logging.info("Enable ACPI Pstate in BIOS.")
|
|
print("\t/* Px data is not available */", file=config)
|
|
return
|
|
except IOError:
|
|
logging.info("No scaling_driver found.", warn=True)
|
|
print("\t/* Px data is not available */", file=config)
|
|
return
|
|
|
|
try:
|
|
with open(sysnode+'cpufreq/boost', 'r') as f_node:
|
|
boost = f_node.read()
|
|
except IOError:
|
|
boost = 0
|
|
logging.info("Enable CPU turbo in BIOS.")
|
|
|
|
with open(sysnode + 'cpu0/cpufreq/scaling_available_frequencies', 'r') as f_node:
|
|
freqs = f_node.read()
|
|
with open(sysnode + 'cpu0/cpufreq/cpuinfo_transition_latency') as f_node:
|
|
latency = int(f_node.read().strip())
|
|
latency = latency//1000
|
|
|
|
i = 0
|
|
p_cnt = 0
|
|
for freq in freqs.split():
|
|
if boost != 0 and i == 0:
|
|
try:
|
|
subprocess.check_call('/usr/sbin/rdmsr 0x1ad', shell=True, stdout=subprocess.PIPE)
|
|
except subprocess.CalledProcessError:
|
|
logging.debug("MSR 0x1ad not support in this platform!")
|
|
return
|
|
|
|
res = subprocess.Popen('/usr/sbin/rdmsr 0x1ad', shell=True,
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
|
result = res.stdout.readline().strip()
|
|
#max_ratio_cpu = result[-2:]
|
|
ctl_state = int(result[-2:], 16) << 8
|
|
i += 1
|
|
else:
|
|
ctl_state = int(freq)//100000 << 8
|
|
|
|
px_tmp.core_freq = int(int(freq) / 1000)
|
|
px_tmp.power = 0
|
|
px_tmp.trans_latency = latency
|
|
px_tmp.bus_latency = latency
|
|
px_tmp.control = ctl_state
|
|
px_tmp.status = ctl_state
|
|
px_data[freq] = px_tmp
|
|
print("\t{{0x{:0>2X}UL, 0x{:0>2X}UL, 0x{:0>2X}UL, ".format(
|
|
px_data[freq].core_freq, px_data[freq].power,
|
|
px_data[freq].trans_latency), file=config, end="")
|
|
print("0x{:0>2X}UL, 0x{:0>6X}UL, 0x{:0>6X}UL}},\t/* P{} */".format(
|
|
px_data[freq].bus_latency, px_data[freq].control,
|
|
px_data[freq].status, p_cnt), file=config)
|
|
|
|
p_cnt += 1
|
|
|
|
def store_mmcfg_base_data(mmcfg_node, config):
|
|
|
|
print("\t/* PCI mmcfg base of MCFG */", file=config)
|
|
with open(mmcfg_node, 'rb') as mmcfg:
|
|
mmcfg.read(MCFG_OFFSET)
|
|
mmcfg_len_obj = mmcfg.read(DWORD_LEN)
|
|
mmcfg_len_int = int.from_bytes(mmcfg_len_obj, 'little')
|
|
|
|
if mmcfg_len_int > MCFG_ENTRY1_OFFSET:
|
|
logging.debug("Multiple PCI segment groups is not supported!")
|
|
return
|
|
|
|
mmcfg.seek(MCFG_ENTRY0_BASE_OFFSET, 0)
|
|
mmcfg_base_addr_obj = mmcfg.read(DWORD_LEN)
|
|
mmcfg_base_addr = int.from_bytes(mmcfg_base_addr_obj, 'little')
|
|
print("\t#define DEFAULT_PCI_MMCFG_BASE {}UL".format(hex(mmcfg_base_addr)), file=config)
|
|
|
|
|
|
def read_tpm_data(config):
|
|
'''get TPM information from ACPI tables
|
|
:param config: file pointer that opened for writing board config information
|
|
:return:
|
|
'''
|
|
try:
|
|
acpi_table_output = subprocess.check_output('ls -l /sys/firmware/acpi/tables/'.split()).decode('utf8')
|
|
except:
|
|
acpi_table_output = ''
|
|
if 'TPM2' in acpi_table_output:
|
|
print("\tTPM2", file=config)
|
|
else:
|
|
print("\t/* no TPM device */", file=config)
|
|
|
|
|
|
def gen_acpi_info(config):
|
|
"""This will parser the sys node form SYS_PATH and generate ACPI info
|
|
:param config: file pointer that opened for writing board config information
|
|
"""
|
|
read_pm_sstate(SYS_PATH[1] + 'FACP', config)
|
|
|
|
print("{0}".format("\t<S3_INFO>"), file=config)
|
|
read_pm_sdata(SYS_PATH[1] + 'DSDT', '_S3_', config)
|
|
print("{0}".format("\t</S3_INFO>\n"), file=config)
|
|
|
|
print("{0}".format("\t<S5_INFO>"), file=config)
|
|
read_pm_sdata(SYS_PATH[1] + 'DSDT', '_S5_', config)
|
|
print("{0}".format("\t</S5_INFO>\n"), file=config)
|
|
|
|
print("{0}".format("\t<DRHD_INFO>"), file=config)
|
|
dmar.write_dmar_data(SYS_PATH[1] + 'DMAR', config)
|
|
print("{0}".format("\t</DRHD_INFO>\n"), file=config)
|
|
|
|
print("{0}".format("\t<CPU_BRAND>"), file=config)
|
|
store_cpu_info(SYS_PATH[0], config)
|
|
print("{0}".format("\t</CPU_BRAND>\n"), file=config)
|
|
|
|
print("{0}".format("\t<CX_INFO>"), file=config)
|
|
store_cx_data(SYS_PATH[2]+'cpuidle/current_driver', SYS_PATH[2]+'cpu0/cpuidle/', config)
|
|
print("{0}".format("\t</CX_INFO>\n"), file=config)
|
|
|
|
print("{0}".format("\t<PX_INFO>"), file=config)
|
|
store_px_data(SYS_PATH[2], config)
|
|
print("{0}".format("\t</PX_INFO>\n"), file=config)
|
|
|
|
print("{0}".format("\t<MMCFG_BASE_INFO>"), file=config)
|
|
store_mmcfg_base_data(SYS_PATH[1] + 'MCFG', config)
|
|
print("{0}".format("\t</MMCFG_BASE_INFO>\n"), file=config)
|
|
|
|
print("{0}".format("\t<TPM_INFO>"), file=config)
|
|
read_tpm_data(config)
|
|
print("{0}".format("\t</TPM_INFO>\n"), file=config)
|
|
|
|
|
|
def generate_info(board_file):
|
|
"""This will generate ACPI info from board file
|
|
:param board_file: this is the file which stores the hardware board information
|
|
"""
|
|
# Generate board info
|
|
with open(board_file, 'a+') as config:
|
|
gen_acpi_info(config)
|
|
|
|
# get the PTCT/RTCT table from native environment
|
|
out_dir = os.path.dirname(board_file)
|
|
if os.path.isfile(SYS_PATH[1] + 'PTCT'):
|
|
shutil.copy(SYS_PATH[1] + 'PTCT', out_dir if out_dir != "" else "./")
|
|
logging.info("PTCT table has been saved to {} successfully!".format(os.path.join(out_dir, 'PTCT')))
|
|
if os.path.isfile(SYS_PATH[1] + 'RTCT'):
|
|
shutil.copy(SYS_PATH[1] + 'RTCT', out_dir if out_dir != "" else "./")
|
|
logging.info("RTCT table has been saved to {} successfully!".format(os.path.join(out_dir, 'RTCT')))
|