From ca6a9c0556211864fbc7c73b6ad42796289cd229 Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Tue, 25 Aug 2026 21:29:43 +0530 Subject: [PATCH 1/2] utils: add runtime device-tree capability helpers Signed-off-by: Srikanth Muppandam --- Runner/utils/functestlib.sh | 901 ++++++++++++++++++++++++++++++++++++ 1 file changed, 901 insertions(+) diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index da1202fc..76da20d6 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -3357,6 +3357,14 @@ dt_list_compatible_nodes() { esac dtlc_found=0 + if [ -r "${DT_COMPATIBLE_INDEX:-}" ]; then + if [ "$dtlc_mode" = "regex" ]; then + awk -F '|' -v pattern="$dtlc_pattern" '$2 ~ pattern { print $1; found=1 } END { exit !found }' "$DT_COMPATIBLE_INDEX" + else + awk -F '|' -v pattern="$dtlc_pattern" 'index($2, pattern) { print $1; found=1 } END { exit !found }' "$DT_COMPATIBLE_INDEX" + fi + return $? + fi dtlc_previous_root="" for dtlc_root in /proc/device-tree /sys/firmware/devicetree/base; do [ -d "$dtlc_root" ] || continue @@ -3394,6 +3402,159 @@ dt_list_compatible_nodes() { [ "$dtlc_found" -eq 1 ] } +############################################################################### +# dt_build_runtime_indexes +# Builds reusable enabled-compatible, provider-property, and platform-device +# indexes. The files are retained with the suite artifacts for diagnosis. +############################################################################### +dt_build_runtime_indexes() { + dbri_root="$1" + dbri_result_dir="$2" + dbri_compatible_file="$dbri_result_dir/dt_compatible_index.log" + dbri_property_file="$dbri_result_dir/dt_property_index.log" + dbri_platform_file="$dbri_result_dir/platform_device_index.log" + [ -d "$dbri_root" ] && [ -n "$dbri_result_dir" ] || return 3 + + : >"$dbri_compatible_file" + : >"$dbri_property_file" + : >"$dbri_platform_file" + + find "$dbri_root" -type f \( \ + -name compatible -o \ + -name numa-node-id -o \ + -name interrupt-controller -o \ + -name '#clock-cells' -o \ + -name '#reset-cells' -o \ + -name regulator-name -o \ + -name '#mbox-cells' -o \ + -name '#interconnect-cells' \ + \) 2>/dev/null | + while IFS= read -r dbri_path; do + dbri_node=$(dirname "$dbri_path") + dt_node_enabled "$dbri_node" || continue + dbri_property=$(basename "$dbri_path") + if [ "$dbri_property" = "compatible" ]; then + dbri_text=$(dt_property_text "$dbri_node" compatible 2>/dev/null || true) + [ -n "$dbri_text" ] || continue + printf '%s|%s\n' "$dbri_node" "$dbri_text" >>"$dbri_compatible_file" + else + printf '%s|%s\n' "$dbri_property" "$dbri_node" >>"$dbri_property_file" + fi + done + + for dbri_of_node in /sys/bus/platform/devices/*/of_node; do + [ -e "$dbri_of_node" ] || continue + dbri_node=$(readlink -f "$dbri_of_node" 2>/dev/null || true) + [ -n "$dbri_node" ] || continue + dbri_device=$(dirname "$dbri_of_node") + printf '%s|%s\n' "$dbri_node" "$dbri_device" >>"$dbri_platform_file" + done + + DT_COMPATIBLE_INDEX="$dbri_compatible_file" + DT_PROPERTY_INDEX="$dbri_property_file" + DT_PLATFORM_DEVICE_INDEX="$dbri_platform_file" + export DT_COMPATIBLE_INDEX DT_PROPERTY_INDEX DT_PLATFORM_DEVICE_INDEX + log_info "Runtime DT indexes: compatible=$(wc -l <"$dbri_compatible_file" | tr -d '[:space:]') property=$(wc -l <"$dbri_property_file" | tr -d '[:space:]') platform=$(wc -l <"$dbri_platform_file" | tr -d '[:space:]')" +} + +############################################################################### +# dt_hw_capability_parse_args [--area ] [--list-areas] [--help] +# Validates suite area selection and stores the comma-separated selection in +# DTRHC_AREAS. Returns 2 after printing informational output. +############################################################################### +dt_hw_capability_parse_args() { + DTRHC_AREAS="all" + while [ "$#" -gt 0 ]; do + case "$1" in + --area) + shift + [ "$#" -gt 0 ] || return 3 + DTRHC_AREAS="$1" + ;; + --list-areas) + printf '%s\n' "identity boot cpu-memory interrupts fabric storage usb pcie network multimedia remoteproc security health" + return 2 + ;; + --help|-h) + printf '%s\n' "Usage: ./run.sh [--area all|identity,boot,cpu-memory,interrupts,fabric,storage,usb,pcie,network,multimedia,remoteproc,security,health] [--list-areas]" + return 2 + ;; + *) + return 3 + ;; + esac + done + DTRHC_AREAS=$(printf '%s' "$DTRHC_AREAS" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]') + [ -n "$DTRHC_AREAS" ] || return 3 + dthc_remaining=$DTRHC_AREAS + while [ -n "$dthc_remaining" ]; do + dthc_area=${dthc_remaining%%,*} + case "$dthc_area" in + all|identity|boot|cpu-memory|interrupts|fabric|storage|usb|pcie|network|multimedia|remoteproc|security|health) + ;; + *) + return 3 + ;; + esac + [ "$dthc_remaining" = "$dthc_area" ] && break + dthc_remaining=${dthc_remaining#*,} + done + export DTRHC_AREAS +} + +# dt_hw_capability_area_enabled +# Returns success when all areas or the supplied area was selected. +dt_hw_capability_area_enabled() { + dthcae_area="$1" + case ",${DTRHC_AREAS:-all}," in + *,all,*|*,$dthcae_area,*) + return 0 + ;; + esac + return 1 +} + +# dt_summary_begin_area +# Snapshots shared result counters before one validation area runs. +dt_summary_begin_area() { + DTSUMMARY_RESULT_DIR="$1" + DTSUMMARY_AREA="$2" + DTSUMMARY_PASS_BEFORE=$TEST_RESULT_PASS_COUNT + DTSUMMARY_FAIL_BEFORE=$TEST_RESULT_FAIL_COUNT + DTSUMMARY_SKIP_BEFORE=$TEST_RESULT_SKIP_COUNT + [ -n "$DTSUMMARY_RESULT_DIR" ] && [ -n "$DTSUMMARY_AREA" ] || return 3 +} + +# dt_summary_end_area +# Appends the selected area's result delta to the retained summary artifact. +dt_summary_end_area() { + dtsummary_file="$DTSUMMARY_RESULT_DIR/dt_area_summary.tsv" + dtsummary_pass=$((TEST_RESULT_PASS_COUNT - DTSUMMARY_PASS_BEFORE)) + dtsummary_fail=$((TEST_RESULT_FAIL_COUNT - DTSUMMARY_FAIL_BEFORE)) + dtsummary_skip=$((TEST_RESULT_SKIP_COUNT - DTSUMMARY_SKIP_BEFORE)) + if [ "$dtsummary_fail" -gt 0 ]; then + dtsummary_result="FAIL" + elif [ "$dtsummary_pass" -gt 0 ]; then + dtsummary_result="PASS" + else + dtsummary_result="SKIP" + fi + printf '%s\t%s\t%s\t%s\t%s\n' "$DTSUMMARY_AREA" "$dtsummary_result" "$dtsummary_pass" "$dtsummary_fail" "$dtsummary_skip" >>"$dtsummary_file" +} + +# dt_summary_print +# Prints a compact area-level summary from the retained TSV artifact. +dt_summary_print() { + dtsummary_file="$1/dt_area_summary.tsv" + [ -r "$dtsummary_file" ] || return 1 + log_info "Device Tree Capability Summary" + printf '%-26s %-6s %5s %5s %5s\n' "Area" "Result" "Pass" "Fail" "Skip" + dtsummary_tab=$(printf '\t') + while IFS="$dtsummary_tab" read -r dtsummary_area dtsummary_result dtsummary_pass dtsummary_fail dtsummary_skip; do + printf '%-26s %-6s %5s %5s %5s\n' "$dtsummary_area" "$dtsummary_result" "$dtsummary_pass" "$dtsummary_fail" "$dtsummary_skip" + done <"$dtsummary_file" +} + # dt_list_qcom_icc_provider_nodes # Prints enabled Qualcomm DT nodes exposing #interconnect-cells, excluding the # virtual IPA providers intentionally ignored by the ICC core. Returns 0 when @@ -3458,6 +3619,15 @@ find_platform_device_for_dt_node() { fpdd_target_node=$(readlink -f "$fpdd_node_dir" 2>/dev/null || true) [ -n "$fpdd_target_node" ] || fpdd_target_node="$fpdd_node_dir" + if [ -r "${DT_PLATFORM_DEVICE_INDEX:-}" ]; then + fpdd_index_device=$(awk -F '|' -v node="$fpdd_target_node" '$1 == node { print $2; exit }' "$DT_PLATFORM_DEVICE_INDEX") + if [ -n "$fpdd_index_device" ]; then + printf '%s\n' "$fpdd_index_device" + return 0 + fi + return 1 + fi + for fpdd_of_node_link in /sys/bus/platform/devices/*/of_node; do [ -e "$fpdd_of_node_link" ] || continue fpdd_device_node=$(readlink -f "$fpdd_of_node_link" 2>/dev/null || true) @@ -3539,6 +3709,737 @@ dt_property_text() { tr '\000' ' ' <"$node_dir/$property" | tr -cd '[:print:] \n' | sed 's/[[:space:]]*$//' } +############################################################################### +# dt_runtime_root +# Prints the resolved runtime device-tree root, preferring sysfs over procfs. +############################################################################### +dt_runtime_root() { + dtrr_candidate="" + + for dtrr_candidate in /sys/firmware/devicetree/base /proc/device-tree; do + [ -d "$dtrr_candidate" ] || continue + dtrr_resolved=$(readlink -f "$dtrr_candidate" 2>/dev/null || true) + if [ -n "$dtrr_resolved" ] && [ -d "$dtrr_resolved" ]; then + printf '%s\n' "$dtrr_resolved" + else + printf '%s\n' "$dtrr_candidate" + fi + return 0 + done + + return 1 +} + +############################################################################### +# dt_node_enabled +# Returns success unless the node status explicitly disables or fails the node. +############################################################################### +dt_node_enabled() { + dtne_node_dir="$1" + [ -n "$dtne_node_dir" ] || return 3 + dtne_status=$(dt_property_text "$dtne_node_dir" status 2>/dev/null || true) + + case "$dtne_status" in + disabled|fail|failed) + return 1 + ;; + esac + + return 0 +} + +############################################################################### +# dt_count_enabled_cpu_nodes +# Prints the number of enabled CPU nodes described by the supplied DT root. +############################################################################### +dt_count_enabled_cpu_nodes() { + dtcec_root="$1" + dtcec_count=0 + [ -d "$dtcec_root/cpus" ] || { + printf '%s\n' 0 + return 1 + } + + for dtcec_type_file in "$dtcec_root"/cpus/*/device_type; do + [ -r "$dtcec_type_file" ] || continue + dtcec_type=$(tr -d '\000[:space:]' <"$dtcec_type_file" 2>/dev/null) + [ "$dtcec_type" = "cpu" ] || continue + dtcec_node=$(dirname "$dtcec_type_file") + dt_node_enabled "$dtcec_node" || continue + dtcec_count=$((dtcec_count + 1)) + done + + printf '%s\n' "$dtcec_count" + [ "$dtcec_count" -gt 0 ] +} + +############################################################################### +# dt_count_kernel_cpu_nodes +# Prints the number of CPU directories exposed by the running kernel. +############################################################################### +dt_count_kernel_cpu_nodes() { + find /sys/devices/system/cpu -maxdepth 1 -type d -name 'cpu[0-9]*' 2>/dev/null | + wc -l | + tr -d '[:space:]' +} + +############################################################################### +# dt_list_enabled_memory_nodes +# Prints enabled memory nodes directly below the supplied DT root. +############################################################################### +dt_list_enabled_memory_nodes() { + dtlem_root="$1" + dtlem_found=0 + [ -d "$dtlem_root" ] || return 3 + + for dtlem_node in "$dtlem_root"/memory@* "$dtlem_root"/memory; do + [ -d "$dtlem_node" ] || continue + dt_node_enabled "$dtlem_node" || continue + printf '%s\n' "$dtlem_node" + dtlem_found=1 + done + + [ "$dtlem_found" -eq 1 ] +} + +############################################################################### +# dt_list_enabled_reserved_memory_nodes +# Prints enabled reserved-memory child nodes from the supplied DT root. +############################################################################### +dt_list_enabled_reserved_memory_nodes() { + dtlrm_root="$1" + dtlrm_found=0 + dtlrm_reserved_root="$dtlrm_root/reserved-memory" + [ -d "$dtlrm_reserved_root" ] || return 1 + + for dtlrm_node in "$dtlrm_reserved_root"/*; do + [ -d "$dtlrm_node" ] || continue + dt_node_enabled "$dtlrm_node" || continue + printf '%s\n' "$dtlrm_node" + dtlrm_found=1 + done + + [ "$dtlrm_found" -eq 1 ] +} + +############################################################################### +# dt_profile_id +# Prints a stable profile identifier derived from the first root compatible. +############################################################################### +dt_profile_id() { + dtpi_root="$1" + dtpi_first_compatible="" + [ -d "$dtpi_root" ] || return 3 + + if [ -r "$dtpi_root/compatible" ]; then + dtpi_first_compatible=$(tr '\000' '\n' <"$dtpi_root/compatible" 2>/dev/null | sed -n '1p') + fi + [ -n "$dtpi_first_compatible" ] || return 1 + + printf '%s\n' "$dtpi_first_compatible" | + tr '[:upper:]' '[:lower:]' | + tr ',.-' '___' | + tr -cd '[:alnum:]_' +} + +############################################################################### +# dt_list_enabled_property_nodes +# Prints enabled DT node directories that expose the exact property name. +############################################################################### +dt_list_enabled_property_nodes() { + dtlep_root="$1" + dtlep_property="$2" + dtlep_file="" + [ -d "$dtlep_root" ] && [ -n "$dtlep_property" ] || return 3 + + if [ -r "${DT_PROPERTY_INDEX:-}" ]; then + awk -F '|' -v property="$dtlep_property" '$1 == property { print $2; found=1 } END { exit !found }' "$DT_PROPERTY_INDEX" + return $? + fi + + dtlep_file=$(mktemp "${TMPDIR:-/tmp}/dt-property.XXXXXX") || return 1 + find "$dtlep_root" -type f -name "$dtlep_property" 2>/dev/null | + while IFS= read -r dtlep_property_file; do + dtlep_node=$(dirname "$dtlep_property_file") + dt_node_enabled "$dtlep_node" || continue + printf '%s\n' "$dtlep_node" >>"$dtlep_file" + done + + if [ -s "$dtlep_file" ]; then + sort -u "$dtlep_file" + rm -f "$dtlep_file" + return 0 + fi + + rm -f "$dtlep_file" + return 1 +} + +############################################################################### +# dt_validate_property_provider