Files
dts-scripts/include/hal/dts-hal.sh
Michał Iwanicki 8e7a660783 dts-scripts: Enhance mocking for purposes of profile comparison
common-mock-func: set dmidecode to return 0 by default

if test variable isn't defined this likely means that this value
doesn't matter for test purposes.

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock-func: cbmem shouldn't exit in failure in normal check

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock-func: cbfstool: allow specifying fmap regions in binary

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

dts-functions: set_flashrom_update_params: don't use $1 directly

It's hard to know exactly what this argument is for otherwise as this
function isn't documented

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

deploy: don't use mktemp so profile arguments are always the same

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

dts-boot: create TEMP_DIR

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: cbfstool: return 1 if not coreboot

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

cbfstool: don't mock when we check update binary config

Except in some cases where we want to possibly test for identical
config between update binary and current firmware.

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

dasharo-deploy: add 2>>ERR_LOG_FILE redirection

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: cbmem: check if coreboot

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

dts-functions: download_keys: use 'mkdir -p' so it doesn't error out

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: add cbmem_common_mock which returns error if not coreboot

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: add msrtool and mei-amt-check common mock

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: ifdtool: call original ifdtool if used with update binary

Fixes situation on QEMU that results in user not being asked whether to
continue flashing without ME even though we want to test with ME enabled

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: flashrom: verify internal chip use

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: add bootsplash presence mocking

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: both flashrom and cbfstool allow writing over existing file

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: bootsplash region might exist without custom bootsplash

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

check_if_me_disabled: Don't use mocked functions with 'grep -q'

Otherwise grep will break pipe immediately after matching searched
string which will result in mocked function being immediately stopped
even before finishing. It also results in e.g. tool wrapper not writing
used command to profile

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: Add separate ME disabled and ME HAP disabled

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: add cbfstool smmstore mocking

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

common-mock: simplify fuse mock

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

dts-functions: check_if_boot_guard_enabled: hide rdmsr output

remove non-existent command, likely pasted by mistake

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>

include: hal: add cbfstool_add_firmware_section_mock

Signed-off-by: Daniil Klimuk <daniil.klimuk@3mdeb.com>

use dont_mock instead of cbfstool_add_firmware_section_mock

Signed-off-by: Daniil Klimuk <daniil.klimuk@3mdeb.com>

common-mock: rename regions to _regions

Signed-off-by: Michał Iwanicki <michal.iwanicki@3mdeb.com>
2025-08-27 14:45:01 +02:00

278 lines
10 KiB
Bash

#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2024 3mdeb <contact@3mdeb.com>
#
# SPDX-License-Identifier: Apache-2.0
#
# This is a Hardware Abstraction Layer for DTS. The goal of this layer -
# separate all hardware-related code from DTS code to improve readability,
# scalability and testing.
#
# For testing, every hardware-specific tool must utilize DTS_TESTING
# variable, which is declared in dts-environment and set by user. If DTS_TESTING
# is not "true" - HAL communicates with hardware and firmware via specific tools
# otherwise it uses mocking functions and tool_wrapper to emulate behaviour of
# some of the tools.
#
# Real HAL is placed in $DTS_HAL* (* means that, apart from common HAL funcs.
# there could be, in future, files with platform-specific HAL funcs) and the
# Tests HAL is placed in $DTS_MOCK* (* means that, apart from common mocks,
# there could be, in future, files with platform-specific mocking functions).
# shellcheck disable=SC2034
# shellcheck source=../../include/hal/common-mock-func.sh
source $DTS_MOCK_COMMON
# Set tools wrappers:
DASHARO_ECTOOL="tool_wrapper dasharo_ectool"
FLASHROM="tool_wrapper flashrom"
DMIDECODE="tool_wrapper dmidecode"
IFDTOOL="tool_wrapper ifdtool"
SETPCI="tool_wrapper setpci"
# Emulating to eliminate false negatives, because it might fail on QEMU:
CBMEM="tool_wrapper cbmem"
CBFSTOOL="tool_wrapper cbfstool"
# Emulating to eliminate false negatives, because it fails on QEMU:
SUPERIOTOOL="tool_wrapper superiotool"
# Emulating to eliminate false negatives, because it fails on QEMU:
ECTOOL="tool_wrapper ectool"
# Emulating to eliminate false negatives, because it fails on QEMU:
MSRTOOL="tool_wrapper msrtool"
# Emulating to eliminate false negatives, because it fails on QEMU:
MEI_AMT_CHECK="tool_wrapper mei-amt-check"
# Emulating to eliminate false negatives, because it fails on QEMU:
INTELMETOOL="tool_wrapper intelmetool"
# Emulating, so no to probe every time testing is done
HW_PROBE="tool_wrapper hw-probe"
DMESG="tool_wrapper dmesg"
DCU="tool_wrapper dcu"
FUTILITY="tool_wrapper futility"
IOTOOLS="tool_wrapper iotools"
FSREAD_TOOL="tool_wrapper fsread_tool"
CAP_UPD_TOOL="tool_wrapper cap_upd_tool"
LSCPU="tool_wrapper lscpu"
# System commands:
POWEROFF="tool_wrapper poweroff"
REBOOT="tool_wrapper reboot"
RDMSR="tool_wrapper rdmsr"
LSPCI="tool_wrapper lspci"
LSUSB="tool_wrapper lsusb"
################################################################################
# Tools wrapper.
################################################################################
tool_wrapper() {
# Usage: tool_wrapper TOOL_NAME MOCK_FUNC_NAME TOOL_ARGS
#
# TOOL_NAME: the name of the tool being wrapped
# MOCK_FUNC_NAME: the name of mocking function (optional, check comments
# below for more inf.)
# TOOL_ARGS: the arguments that the tool gets if being called, for example
# for dmidecode -s system-vendor it will be "-s system-vendor".
#
# This function is a bridge between common DTS logic and hardware-specific DTS
# logic or functions. There is two paths a call to this function can be
# redirected to: real HAL for running on real platform and Tests HAL for testing
# on QEMU (depends on whether the var. DTS_TESTING is set or not).
#
# The real HAL are the real tools e.g. cbfstool, etc.. The testing HAL are the
# mocking functions. There are several types of mocking functions, with every
# type having a specific name syntax:
#
# FUNCTIONNAME_mock(){...}: mocking functions specific for every platform, those
# are stored in $DTS_MOCK_PLATFORM file which is sourced at the beginning of
# this file.
# TOOLNAME_FUNCTIONNAME_mock(){...}: mocking functions common for all platforms
# but specific for some tool, those are stored in $DTS_MOCK_COMMON file, which
# is being sourced at the beginning of this file.
# TOOLNAME_common_mock(){...}: standard mocking functions for every tool that
# are common for all platforms, those are stored in $DTS_MOCK_COMMON file, which
# is being sourced at the beginning of this file.
# common_mock(){...}: common mocking function, in case we need to use mocking
# function for a tool but we do not care about its output.
#
# This tool wrapper should only be used with tools which communicate with
# hardware or firmware (read or write, etc.).
#
# TODO: this wrapper deals with arguments as well as with stdout, stderr, and $?
# redirection, but it does not read and redirect stdin (this is not used in any
# mocking functions or tools right now).
# Gets toolname, e.g. poweroff, dmidecode. etc.:
local _tool="$1"
# Gets mocking function name:
local _mock_func="$2"
# It checks if _mock_func contains smth with _mock at the end, if not -
# mocking function is not provided and some common mocking func. will be used
# instead:
if ! echo "$_mock_func" | grep "_mock" &>/dev/null; then
unset _mock_func
shift 1
else
shift 2
fi
# Other arguments for this function are the arguments which are sent to a tool
# e.g. -s system-vendor for dmidecode, etc.:
local _arguments=("$@")
if [ -n "$DTS_TESTING" ]; then
# This is the order of calling mocking functions:
# 1) dont_mock - use original command
# 2) FUNCTIONNAME_mock;
# 3) TOOLNAME_FUNCTIONNAME_mock;
# 4) TOOLNAME_common_mock;
# 5) common_mock - last resort.
if [ "$_mock_func" = "dont_mock" ]; then
dont_mock "$_tool" "${_arguments[@]}"
elif [ -n "$_mock_func" ] && type $_mock_func &>/dev/null; then
$_mock_func "${_arguments[@]}"
elif type ${_tool}_${_mock_func} &>/dev/null; then
${_tool}_${_mock_func} "${_arguments[@]}"
elif type ${_tool}_common_mock &>/dev/null; then
${_tool}_common_mock "${_arguments[@]}"
else
common_mock $_tool
fi
else
# If not testing - call tool with the arguments instead:
$_tool "${_arguments[@]}"
fi
# !! When modifying this function, make sure this is return value of wrapped
# tool (real of mocked)
ret=$?
echo "${_tool} ${_arguments[*]} $ret" >>/tmp/logs/profile
echo "${BASH_SOURCE[1]}:${FUNCNAME[1]}:${BASH_LINENO[0]} ${_tool} ${_arguments[*]} $ret" >>/tmp/logs/debug_profile
return $ret
}
################################################################################
# Other funcs.
################################################################################
check_for_opensource_firmware() {
echo "Checking for Open Source Embedded Controller firmware..."
$DASHARO_ECTOOL check_for_opensource_firm_mock info >/dev/null 2>>"$ERR_LOG_FILE"
return $?
}
fsread_tool() {
# This func is an abstraction for proper handling of fs hardware-specific (e.g.
# checking devtmpfs, or sysfs, or some other fs that changes its state due to
# changes in hardware and/or firmware) reads by tool_wrapper.
#
# This function does not have arguments in common understanding, it takes a
# command, that is reading smth from some fs, and its arguments as an only
# argument. E.g. if you want to check tty1 device presence:
#
# fsread_tool test -f /dev/tty1
local _command="$1"
shift
$_command "$@"
return $?
}
cap_upd_tool() {
# This func is an abstraction for proper handling of UEFI Capsule Update driver
# writing by the tool_wrapper. arguments: capsule update file path, e.g.:
#
# capsule_update_tool /tmp/firm.cap
local _capsule="$1"
cat "$_capsule" >"$CAP_UPD_DEVICE"
return $?
}
check_if_heci_present() {
# FIXME: what if HECI is not device 16.0?
$FSREAD_TOOL test -d /sys/class/pci_bus/0000:00/device/0000:00:16.0
return $?
}
check_me_op_mode() {
# Checks ME Current Operation Mode at offset 0x40 bits 19:16:
local _mode
_mode="$($SETPCI check_me_op_mode_mock -s 00:16.0 42.B 2>>"$ERR_LOG_FILE" | cut -c2-)"
echo "$_mode"
return 0
}
check_if_uefi() {
# Check if current firmware has UEFI payload. Returns 0 on success, otherwise
# returns 1.
grep -q 'UEFI' <(echo "${DASHARO_FLAVOR}") && return 0
# Additional check is useful sometimes:
$FSREAD_TOOL test -d "/sys/firmware/efi" && return 0
return 1
}
check_if_seabios() {
# Check if current firmware has SeaBIOS payload. Returns 0 on success,
# otherwise returns 1.
grep -q 'SeaBIOS' <(echo "${DASHARO_FLAVOR}") && return 0
# Additional check is useful sometimes:
tmp_rom="$TEMP_DIR/rom_seabios_check"
config="$TEMP_DIR/config"
# Get current firmware:
$FLASHROM flashrom_read_firm_mock -p "$PROGRAMMER_BIOS" ${FLASH_CHIP_SELECT} -r "$tmp_rom" >>"$FLASH_INFO_FILE" 2>>"$ERR_LOG_FILE"
if [ -f "$tmp_rom" ]; then
# extract config
$CBFSTOOL read_bios_conffile_mock "$tmp_rom" extract -n config -f "$config" >/dev/null 2>>"$ERR_LOG_FILE"
grep -q "CONFIG_PAYLOAD_SEABIOS=y" "$config" 2>>"${ERR_LOG_FILE}" && return 0
fi
return 1
}
check_for_transition() {
# This function checks for possible transition for current hardware and
# firmware configuration flows and returns:
# * In case no transition flows sare available: return code 1 and empty
# stdout.
# * In case at least one transition flow is available: return code 0 and the
# transition flow name on stdout.
#
# Check the end of the function for stdout formatting.
local _is_uefi="false"
local _is_seabios="false"
local _transition_list=()
# The transition is only possible from Dasharo firmware:
check_if_dasharo || return 1
check_if_uefi && _is_uefi="true"
check_if_seabios && _is_seabios="true"
if [[ "$_is_uefi" == "true" ]]; then
# Dasharo (coreboot+UEFI) is installed, check possible transitions:
if [[ "$COREBOOT_UEFI_TO_SLIM_BOOTLOADER_UEFI_TRANSITION" == "true" ]]; then
_transition_list+=("Dasharo (coreboot+UEFI) to Dasharo (Slim Bootloader+UEFI)")
fi
fi
if [[ "$_is_seabios" == "true" ]]; then
# Dasharo (coreboot+SeaBIOS) is installed, check possible transitions:
if [[ "$COREBOOT_SEABIOS_TO_COREBOOT_UEFI_TRANSITION" == "true" ]]; then
_transition_list+=("Dasharo (coreboot+SeaBIOS) to Dasharo (coreboot+UEFI)")
fi
fi
# This loop will make this function return a string that contains a string
# that contains possible transitions separated by a new line, so it can be
# easily reconstructed back to an array with
# readarray -t transitions < <(check_for_transition)
for transition in "${_transition_list[@]}"; do
echo "$transition"
done
[[ -n "${_transition_list[*]}" ]] && return 0 || return 1
}