diff --git a/Runner/config/pkg_command_map.conf b/Runner/config/pkg_command_map.conf index 54d6e5607..f0231f39a 100755 --- a/Runner/config/pkg_command_map.conf +++ b/Runner/config/pkg_command_map.conf @@ -268,6 +268,9 @@ centos:pgrep=procps-ng centos:killall=psmisc centos:lsmod=kmod centos:modprobe=kmod +centos:dtc=dtc +centos:fdtdump=dtc +centos:diff=diffutils # --------------------------------------------------------------------------- # Common opkg provider mappings. @@ -548,7 +551,7 @@ centos:Sensors:ssc_drva_test=qcom-sensors-test-core qcom-sensors-test-apps debian:package-set:camera-nhx=camx-dkms camx-glymur libcamx-glymur1 camx-firmware-glymur camx-nhx ubuntu:package-set:camera-nhx=camx-dkms camx-glymur libcamx-glymur1 camx-firmware-glymur camx-nhx -centos:package-set:camera-nhx=camx-dkms camx-firmware-kodiak camx-kodiak libcamx-kodiak1 +centos:package-set:camera-nhx=camx-dkms camx-firmware-kodiak camx-kodiak libcamx-kodiak1 dtc diffutils # Qualcomm Iris video overlay. Debian and Ubuntu use the public DKMS package, # whose post-install flow builds, registers, and loads iris_vpu. CentOS uses diff --git a/Runner/suites/Multimedia/Camera/Camera_NHX/Camera_NHX_Setup.yaml b/Runner/suites/Multimedia/Camera/Camera_NHX/Camera_NHX_Setup.yaml new file mode 100644 index 000000000..2bb489082 --- /dev/null +++ b/Runner/suites/Multimedia/Camera/Camera_NHX/Camera_NHX_Setup.yaml @@ -0,0 +1,15 @@ +metadata: + name: camera_nhx_setup + format: "Lava-Test Test Definition 1.0" + description: "Read-only NHX runtime readiness, not camera capture validation" + os: + - yocto + scope: + - functional + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Multimedia/Camera/Camera_NHX + - ./run.sh --check-setup + - $REPO_PATH/Runner/utils/send-to-lava.sh Camera_NHX_Setup.res diff --git a/Runner/suites/Multimedia/Camera/Camera_NHX/Kodiak/README.md b/Runner/suites/Multimedia/Camera/Camera_NHX/Kodiak/README.md new file mode 100644 index 000000000..cc85063e8 --- /dev/null +++ b/Runner/suites/Multimedia/Camera/Camera_NHX/Kodiak/README.md @@ -0,0 +1,37 @@ +# Kodiak NHX configurations + +These four files configure `NativeHalX.OmniTest` through the NHX JSON launcher. +They are not nativehaltest command lines or proof that the installed native-HAL +binary provides an equivalent testcase. + +| JSON | Streams configured | +| --- | --- | +| `Preview_YUVNV12_MaxResolution_NHX.json` | One format-35 preview output | +| `Video_YUVNV12_MaxResolution_NHX.json` | One format-34 video output | +| `Prev_plus_Video_YUVNV12_MaxResolution_NHX.json` | Format-35 preview and format-34 video outputs | +| `Snapshot_YUVNV12_MaxResolution_NHX.json` | Format-35 preview and format-33 snapshot outputs | + +All four retain their existing camera `0`, 150-frame, maximum-resolution, +stream usage/data-space and image-dump settings. In particular, the video JSON +uses format 34 despite its historical YUVNV12 filename. Renaming or translating +these fields would change existing coverage and is not part of native-HAL +support. + +Use the original JSON route when an NHX wrapper is installed: + +```sh +./run.sh --json Preview_YUVNV12_MaxResolution_NHX.json --target Kodiak +./run.sh --json Video_YUVNV12_MaxResolution_NHX.json --target Kodiak +./run.sh --json Prev_plus_Video_YUVNV12_MaxResolution_NHX.json --target Kodiak +./run.sh --json Snapshot_YUVNV12_MaxResolution_NHX.json --target Kodiak +``` + +Run these commands from the parent `Camera_NHX` directory. The folder name is +an explicit JSON selection, not automatic board or camera detection. + +For a package exposing only `nativehaltest`, select a verified installed +`Suite.Case` using `--native-test` instead. The supplied native help has no +advertised JSON, stream-format, resolution, usage or data-space options, so the +runner does not claim equivalence to these four configurations. Do not combine +native selectors with `--json` or `--target`. Native camera selection defaults +to the client policy unless the operator passes `--native-camera`. diff --git a/Runner/suites/Multimedia/Camera/Camera_NHX/README_Camera_NHX.md b/Runner/suites/Multimedia/Camera/Camera_NHX/README_Camera_NHX.md index 8cd42f819..533ea6495 100644 --- a/Runner/suites/Multimedia/Camera/Camera_NHX/README_Camera_NHX.md +++ b/Runner/suites/Multimedia/Camera/Camera_NHX/README_Camera_NHX.md @@ -1,6 +1,6 @@ # Camera_NHX -Camera NHX validation test for the Qualcomm CAMX proprietary camera stack. This test runs the available NHX launcher (`nhx.sh` or `camera-nhx`), collects generated image dumps, validates dumps (existence + non-zero size), and produces a PASS/FAIL `.res` file suitable for LAVA gating. +Camera validation test for the Qualcomm CAMX proprietary camera stack. This test prefers the available NHX launcher (`nhx.sh` or `camera-nhx`) and also supports an explicitly selected native-HAL testcase. It collects generated image dumps, validates dumps (existence, non-zero size and checksums), and produces a PASS/FAIL `.res` file suitable for LAVA gating. The test supports the legacy/default NHX flow, optional target-specific JSON selection for preview, video, preview+video, and snapshot validation, and an opt-in desktop CAMX package and FIT-DTB selection flow. @@ -57,15 +57,68 @@ Snapshot JSON files are currently expected only for targets where the files are ## What this test does +### NHX setup-only check + +Run `./run.sh --check-setup` to validate NHX runtime prerequisites without +launching NHX, capturing frames, changing camera services, installing packages, +writing EFI variables or rebooting. This mode writes `Camera_NHX_Setup.res`, +not a capture PASS. `Camera_NHX_Setup.yaml` publishes that separate result. +Do not combine it with `--overlay`, `--fit-dtb`, `--json` or `--target`, including +`NHX_JSON`/`NHX_TARGET` environment selections. + +On CentOS it checks the core CAMX packages from the existing package map, +the image-provided `efivar` utility and the `camx` EFI selection. It also retains +cached DNF listings for EPEL, both Qualcomm repositories and both CLO +repositories. Missing repositories or cache are provisioning warnings, not a +reason to fail an already installed offline NHX runtime. No repository is added, +enabled or refreshed in this mode. Use the explicit `--overlay` mode below for +repository and package recovery. If configured repositories cannot supply +`efivar`, the image owner must provision it with matching libraries. This test +does not access private shares or use `--allowerasing`. + +The remaining checks reuse the normal launcher, checksum, active CAMX DT, +loaded camera module, ICP firmware and package prerequisites. Missing runtime +prerequisites produce SKIP, package-query failures/timeouts produce FAIL. +PASS confirms NHX readiness only, not physical sensor or capture functionality. +Inspect stdout and `logs/package-*.log`, `logs/repositories.log` and +`logs/repository-*.log` for the CentOS package/repository evidence. + +Direct NHX is intentionally not gated on GStreamer, QMMF, `sscrpcd` or an active +`qti-cam-server`. Those belong to camera-service/GStreamer integration testing. +An initially inactive or absent camera server stays that way. If NHX stops an +active camera server, it must restore it and verify the active state. A start +failure, timeout or inactive post-start state makes the final result FAIL. +Exit and signal cleanup attempt restoration only for the service owned by this +run. Service start is bounded to 60 seconds and its output is retained in +`logs/service-restore.log`. + +Examples: + +```sh +# Read-only readiness, no package/EFI/service mutations +./run.sh --check-setup + +# Execute NHX against the image-provided runtime +./run.sh + +# Existing explicit overlay preparation, manual reboot if requested by the log +./run.sh --overlay +``` + 1. **Environment setup** - Locates and sources `init_env` - Sources `functestlib.sh` and `lib_camera.sh` 2. **Dependency checks** - Uses `check_dependencies` from `functestlib.sh` to ensure required commands exist. + - Uses `dtc` when available. `diff` is optional and only compares checksums with a previous run, its absence does not skip NHX or weaken current-run dump validation. + - Requires one image-provided checksum utility, selected in order: `sha256sum`, `md5sum`, then `cksum`. It does not require all three. If none is available, the suite skips before launching NHX and identifies the missing prerequisite. + - The CentOS `--overlay` package set includes `dtc` and `diffutils`, which provide `dtc`, `fdtdump`, and `diff`. + - Direct runs without `--overlay` do not install packages. This dependency policy is the same on Yocto, Debian, Ubuntu and CentOS. 3. **CAMX proprietary prechecks** - - Device-tree presence checks for CAMX/camera patterns, including downstream CAMX-style nodes such as: + - Decodes the active runtime tree with `dtc -I fs -O dts` from the resolved `/sys/firmware/devicetree/base` or `/proc/device-tree` root. + - Uses enabled runtime nodes, rather than text matches in the full DTS, as authoritative CAMX evidence. Downstream CAMX-style nodes include: - `qcom,cam-sensor` - `qcom,cam-gmsl-sensor` - `qcom,cam-gmsl-deserializer` @@ -74,7 +127,8 @@ Snapshot JSON files are currently expected only for targets where the files are - `qcom,csiphy` - `qcom,cam-tpg1031` - `qcom,camera` - - `fdtdump` scan for camera-related nodes through `lib_camera.sh` + - Retains line-numbered camera and sensor matches from the decoded DTS for diagnosis. These artifacts can include disabled nodes and are not used alone to declare the CAMX runtime active. + - Uses `fdtdump` only as optional secondary raw-FDT evidence. - Camera kernel module detection and loaded-state validation, including `camera_x1e80100` on Glymur - ICP firmware presence check (`CAMERA_ICP`) - `dmesg` scan for camera warnings/errors @@ -94,40 +148,57 @@ Snapshot JSON files are currently expected only for targets where the files are - Dump list is non-empty - Each dump file exists - Each dump file size is greater than `0` bytes - - Optionally generates checksums (`sha256sum`, `md5sum`, or `cksum`) and writes checksum files. + - Generates checksums using the selected utility and writes checksum files. A failed checksum command fails validation even if it prints partial output. + - Previous-run comparison is advisory. Without `diff` it is omitted with an INFO message. A comparison error is reported separately from changed checksums and does not fail current-run validation. 6. **Result decision** - Parses `Final Report -> [X PASSED] [Y FAILED] [Z SKIPPED]` from the NHX log. - FAIL if: - Final Report is missing or unparseable + - The NHX launcher exits nonzero, even if a success summary was printed + - No NHX tests passed - NHX reports `FAILED > 0` - No dumps are detected - Any dump is missing or zero bytes - Dump checksum validation helper fails + - Restoration of a camera server stopped by the test fails - Writes final result to: `Camera_NHX.res` --- ## Desktop CAMX overlay flow -Yocto and meta-qcom LAVA images keep their image-provided camera stack and do -not use this flow. On Debian and Ubuntu, an explicit overlay request installs -the `camera-nhx` package set: +On Debian and Ubuntu, an explicit overlay request ensures the complete mapped +`camera-nhx` package set: ```text camx-dkms camx-glymur libcamx-glymur1 camx-firmware-glymur camx-nhx ``` -On CentOS Stream 10, the runner follows the Qualcomm overlay installation -guide and installs the Kodiak package set: +On CentOS Stream 10, the runner ensures the complete mapped Kodiak package set: ```text -camx-dkms camx-firmware-kodiak camx-kodiak libcamx-kodiak1 -``` - -Before a missing CentOS package is installed, the shared package provider -ensures EPEL and both `qualcomm-linux-aarch64` and -`qualcomm-linux-noarch` repositories are enabled, then refreshes DNF metadata. +camx-dkms camx-firmware-kodiak camx-kodiak libcamx-kodiak1 dtc diffutils +``` + +Ubuntu uses the APT sources already configured in the image. Debian may use the +Qualcomm `qli-staging` source. CentOS prepares EPEL and the Qualcomm Linux +`aarch64` and `noarch` RPM repositories plus `clo-rpm-aarch64` and +`clo-rpm-noarch` before installation, even when CAMX packages are already +installed. The CLO repositories use +`https://artifacts.codelinaro.org/artifactory/qli-rpm/centos/10/os/`, with +`aarch64/` and `noarch/` respectively, `priority=10` and `gpgcheck=0` as specified +by the LRH setup procedure. Missing CLO definitions are written to a separate +`qcom-testkit-camx-clo.repo` alongside the Qualcomm repository file. Existing +enabled definitions are preserved. Disabled or conflicting definitions require +administrator correction and are not overwritten. Metadata is refreshed and +both CLO package listings are verified, with diagnostics retained in +`logs/camx-*-repositories.log` and `logs/camx-*-packages.log`. +Yocto does not use +this package-recovery flow and continues with its image-provided camera stack. +The runner passes the complete mapped package list to the shared package +provider instead of selecting a suite-local subset. If package recovery fails, +the explicit `--overlay` request reports FAIL with the complete required list. `--overlay` requires the CAMX FIT DTB and uses the compatibility name `camx`. The same requirement can be stated explicitly with `--fit-dtb camx`: @@ -136,6 +207,17 @@ The same requirement can be stated explicitly with `--fit-dtb camx`: ./run.sh --overlay --fit-dtb camx ``` +On Debian, Ubuntu and CentOS, explicit overlay preparation first reuses an +installed `efivar` or recovers the mapped package from configured repositories. +It mounts `efivarfs` at the runtime EFI directory only if not already mounted, +then verifies the filesystem type. A newly created mount remains mounted. +Existing read-only mounts are restored to read-only after EFI write handling. +An unavailable EFI runtime produces SKIP, while package recovery or mount +failures produce FAIL with diagnostics. A failed `efivar` query or duplicate +`VendorDtbOverlays` variables produces FAIL, not a missing-variable SKIP. +Base and setup-only runs do not install +`efivar` or mount efivarfs. + The test discovers the platform's `VendorDtbOverlays` EFI variable with `efivar -l`. The EFI variable GUID is never supplied by the user or hardcoded in the test. It verifies that the payload is `camx` through `efivar -p`. If it @@ -153,10 +235,23 @@ reboot ``` If the CAMX FIT DTB is already selected, the test continues with runtime DT and -camera-module checks. A loaded board-specific camera module is used as runtime -evidence that the current boot has activated the CAMX camera stack. If EFI is -set to `camx` but the module is not loaded, the test records SKIP and asks the -operator to reboot manually. The test never issues a reboot itself. +camera-module checks. Qualcomm UEFI uses SoC and board identifiers from CDT to +select a compatible FIT configuration. Linux does not expose the firmware's +raw CDT decision directly, so the test validates its result: EFI selection, +the decoded active runtime tree, enabled downstream CamX nodes, and the loaded +board-specific camera module. This is generic across FIT-enabled Qualcomm SoCs +and does not hardcode a SoC-to-DTB table in the test. + +The implementation follows the public Qualcomm FIT metadata flow documented in +[`qcom-dtb-metadata`](https://github.com/qualcomm-linux/qcom-dtb-metadata/blob/main/Documentation.md) +and generated by the public +[`meta-qcom` FIT image class](https://github.com/qualcomm-linux/meta-qcom/blob/master/classes-recipe/dtb-fit-image.bbclass). + +If EFI is set to `camx` but the active runtime tree has no enabled downstream +CamX nodes, the test records FAIL because the selected FIT configuration and +the running tree disagree. If the enabled nodes are present but the camera +module is not loaded, the test records SKIP and asks the operator to reboot +manually. The test never issues a reboot itself. --- @@ -288,6 +383,89 @@ NHX JSON argument ## Command usage +### Native HAL executable mode + +Some CentOS CAMX packages provide only `nativehaltest`, for example +`camx-kodiak-1.0.45` installs `/usr/libexec/camx-kodiak/nativehaltest` without an +NHX wrapper. The runner preserves wrapper discovery order (`nhx.sh` in PATH, +`camera-nhx` in PATH, `/usr/libexec/camx/nhx.sh`). If no wrapper is found, it +discovers `nativehaltest` in PATH, then executable +`/usr/libexec/camx*/nativehaltest` files. This is a runtime path search, not a +board-to-path table. Symlinks to the same binary count once. Multiple different +binaries require `--native-bin /absolute/path/nativehaltest`. + +`--native-test Suite.Case` explicitly selects native mode even when a wrapper +exists. Use one exact testcase supported by the installed client, without +wildcards or multi-test filters. There is no guessed default testcase, automatic +full-binary run, or unadvertised test-list probe. Without a testcase, native +capture records an actionable SKIP. An unknown selected testcase, zero tests, +or a failed native CLI probe must not produce PASS. + +Native-only options have no environment-variable equivalents: + +- `--native-test NAME`: exact `Suite.Case`, required for native capture. +- `--native-bin PATH`: absolute executable override, otherwise runtime discovery. +- `--native-frames N`: 1 through 10000, default 150. The total time limit still applies. +- `--native-camera ID`: optional nonnegative integer camera ID. When omitted, + leave selection to the installed client, whose supplied help defaults to + physical cameras. Do not infer camera ID 0 from a board name. +- `--native-dump-dir PATH`: absolute directory where this binary writes images, + default `/var/cache/camera/nativehaltest`. This selects the validation root, + it does not redirect the binary output. Confirm the directory from client + documentation or an observed capture before overriding it. + +Readiness can discover and inspect the native CLI without running a testcase: + +```sh +./run.sh --check-setup +./run.sh --check-setup --native-bin /usr/libexec/camx-kodiak/nativehaltest +``` + +For capture, substitute a verified installed testcase for the placeholder below: + +```sh +native_case='Suite.Case' # Replace with a testcase supported by this client. +./run.sh --native-test "$native_case" +./run.sh --native-test "$native_case" --native-frames 60 --native-camera 0 +./run.sh --native-test "$native_case" --native-bin /usr/libexec/camx-kodiak/nativehaltest --native-dump-dir /var/cache/camera/nativehaltest +./run.sh --overlay --native-test "$native_case" +``` + +The example camera ID is an explicit operator choice, not a detected default. +Native capture flags cannot be combined with `--check-setup`, except the +read-only `--native-bin` discovery override. Native mode cannot consume +`--json`, `--target`, `NHX_JSON` or `NHX_TARGET`. An explicitly mixed request +fails before overlay preparation. If automatic discovery finds only the native +binary for a JSON request, the runner SKIPs and asks for an NHX wrapper instead +of silently replacing the requested stream configuration. + +The native CLI is checked with a bounded `--help` query. Execution uses the +advertised `--test` or `--gtest_filter` selector, `--frames`, `--p` for stdout +and `--timeout 5000` for per-buffer/result waits. A separate 180-second managed +deadline bounds the whole capture. No `--nid` option is passed, image dumping +must remain enabled. Raw output is retained in the normal run log and replayed +to stdout when execution ends. Help evidence is retained in +`logs/native-hal-help.log`. Capture timeout, nonzero exit and service-restoration +failure all fail the result. Interrupted native captures stop their owned +process and timeout watcher before camera-service restoration. + +Native result parsing requires exactly one matching `RUN` and `OK` testcase +marker plus either a completed single-test GoogleTest summary or one CAMX +`Final Report -> [1 PASSED] [0 FAILED] [0 SKIPPED]`. Missing or unfamiliar +result formats fail closed and require the retained native stdout for diagnosis, +they are not treated as successful capture. These formats still need +confirmation against a successful run of the installed CentOS binary. + +Native capture also uses the existing fresh-file, nonempty-file and checksum +diversity validation. It permits untagged `.yuv`, `.raw`, `.bin`, `.jpg` and +`.jpeg` images in the selected output directory and its subdirectories, while +ignoring logs and stale files. Wrapper dump discovery and invocation are +unchanged. No packages are installed for native base runs. The existing Yocto +YAML interface remains unchanged, native selections are direct-run options. + +The [Kodiak configuration notes](Kodiak/README.md) explain why its NHX JSONs +cannot be substituted with an arbitrary native-HAL testcase. + ```sh ./run.sh [--overlay] [--fit-dtb NAME] [--json JSON_FILE] [--target TARGET] [--help] ``` @@ -295,8 +473,9 @@ NHX JSON argument Options: ```text ---overlay Install the optional Camera NHX CAMX package set on Debian, - Ubuntu, or CentOS and ensure the CAMX FIT DTB is selected. +--overlay Verify the Camera NHX CAMX package set on Debian, Ubuntu, + or CentOS, install missing packages, and ensure the CAMX + FIT DTB is selected. --fit-dtb NAME Select NAME as the FIT DTB compatibility name for the next boot. Requires --overlay on a supported desktop distro. @@ -342,11 +521,17 @@ Created under the test directory: - Logs: - `logs/Camera_NHX_.log` + - `logs/Camera_NHX_dtc_.log` - `logs/dmesg_/dmesg_snapshot.log` - `logs/dmesg_/dmesg_errors.log` when matches exist - Out files: - `out/Camera_NHX_summary_.txt` + - `out/Camera_NHX_running_.dts`, when `dtc` is available + - `out/Camera_NHX_runtime_camera_.log`, when `dtc` is available + - `out/Camera_NHX_runtime_sensors_.log`, when `dtc` is available + - `out/Camera_NHX_enabled_camx_nodes_.log` + - `out/Camera_NHX_enabled_sensor_nodes_.log` - Dump list file, usually under `out/nhx_/` - Checksum file(s), when checksum tool/helper is available @@ -384,7 +569,9 @@ Run a selected NHX config: cat Camera_NHX.res ``` -This script is LAVA-friendly and exits `0` even on FAIL/SKIP; gating is via `.res`. +After environment initialization this script exits `0` on FAIL/SKIP, gating is +via `.res`. A missing repository bootstrap or result-initialization failure +exits nonzero instead of publishing stale results. --- @@ -456,18 +643,24 @@ run: - NHX `Final Report` parsed successfully - `FAILED=0` +- `PASSED` is greater than zero and the NHX launcher exited zero - Dump list is non-empty - All dump files exist and are non-zero - Dump checksum validation helper did not fail, when used +- Any camera server stopped by the test was successfully restored ### FAIL - Any PASS condition is not met +- Explicit `--overlay` package recovery fails or leaves the mapped package set incomplete +- EFI selects `camx`, but the active runtime DT has no enabled downstream CamX nodes - NHX reports one or more failed cases - Final Report is missing/unparseable - No dumps are detected - Any dump is missing or zero bytes - Dump checksum validation helper fails +- The NHX launcher exits nonzero or reports no passing tests +- A camera server stopped by the test cannot be restored to active state ### SKIP @@ -475,7 +668,7 @@ run: - The CAMX FIT DTB selection was written and a manual reboot is required before validation - EFI selects `camx`, but the board-specific camera module is not loaded, so a manual reboot is required - `VendorDtbOverlays` is unavailable when `--overlay` was requested -- `fdtdump` is not available or camera node evidence is inconclusive +- No enabled downstream CamX node exists when `camx` was not explicitly selected - Requested `--json` file is not found - Requested JSON filename is ambiguous and `--target` was not supplied - JSON staging under `/etc/camera/test/NHX` fails diff --git a/Runner/suites/Multimedia/Camera/Camera_NHX/run.sh b/Runner/suites/Multimedia/Camera/Camera_NHX/run.sh index c7f1cd78e..9c1801169 100755 --- a/Runner/suites/Multimedia/Camera/Camera_NHX/run.sh +++ b/Runner/suites/Multimedia/Camera/Camera_NHX/run.sh @@ -3,8 +3,6 @@ # SPDX-License-Identifier: BSD-3-Clause # Camera NHX validation -TESTNAME="Camera_NHX" - SCRIPT_DIR="$( cd "$(dirname "$0")" || exit 1 pwd @@ -20,12 +18,9 @@ while [ "$SEARCH" != "/" ]; do SEARCH=$(dirname "$SEARCH") done -RES_FILE="$SCRIPT_DIR/${TESTNAME}.res" - if [ -z "${INIT_ENV:-}" ]; then echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 - echo "$TESTNAME SKIP" >"$RES_FILE" 2>/dev/null || true - exit 0 + exit 1 fi if [ -z "${__INIT_ENV_LOADED:-}" ]; then @@ -34,6 +29,8 @@ if [ -z "${__INIT_ENV_LOADED:-}" ]; then __INIT_ENV_LOADED=1 fi +# shellcheck disable=SC1090 +. "$INIT_ENV" # shellcheck disable=SC1090,SC1091 . "$TOOLS/functestlib.sh" @@ -48,6 +45,10 @@ if [ -r "$TOOLS/lib_pkg_provider.sh" ]; then . "$TOOLS/lib_pkg_provider.sh" fi +TESTNAME="Camera_NHX" +RES_FILE="$SCRIPT_DIR/${TESTNAME}.res" +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 + LOG_DIR="$SCRIPT_DIR/logs" OUT_DIR="$SCRIPT_DIR/out" DUMP_DIR="/var/cache/camera/nativehaltest" @@ -60,6 +61,12 @@ SUMMARY_TXT="$OUT_DIR/${TESTNAME}_summary_${TS}.txt" DMESG_DIR="$LOG_DIR/dmesg_${TS}" CAMX_EFI_LOG="$LOG_DIR/${TESTNAME}_camx_efi_${TS}.log" CAMX_EFI_LIST_LOG="$LOG_DIR/${TESTNAME}_camx_efi_list_${TS}.log" +RUNNING_DTS="$OUT_DIR/${TESTNAME}_running_${TS}.dts" +RUNNING_DTS_WARNINGS="$LOG_DIR/${TESTNAME}_dtc_${TS}.log" +CAMERA_DTS_MATCHES="$OUT_DIR/${TESTNAME}_runtime_camera_${TS}.log" +SENSOR_DTS_MATCHES="$OUT_DIR/${TESTNAME}_runtime_sensors_${TS}.log" +CAMX_DT_NODES_LOG="$OUT_DIR/${TESTNAME}_enabled_camx_nodes_${TS}.log" +SENSOR_DT_NODES_LOG="$OUT_DIR/${TESTNAME}_enabled_sensor_nodes_${TS}.log" NHX_OUTDIR="$OUT_DIR/nhx_${TS}" @@ -76,11 +83,24 @@ fi CAM_SERVER_PRESENT=0 CAM_SERVER_SERVICE="" CAM_SERVER_STOPPED_FOR_TEST=0 +CAM_SERVER_RESTORE_FAILED=0 +NHX_CHECK_SETUP=0 NHX_JSON="${NHX_JSON:-}" NHX_TARGET="${NHX_TARGET:-}" NHX_RUNNER="" NHX_RUNNER_SOURCE="" +NHX_RUNNER_MODE="wrapper" +NATIVE_REQUESTED=0 +NATIVE_TEST="" +NATIVE_BIN="" +NATIVE_FRAMES=150 +NATIVE_FRAMES_EXPLICIT=0 +NATIVE_CAMERA="" +NATIVE_DUMP_OVERRIDE="" +NATIVE_SELECTOR="" +NATIVE_REPORT_VALID=1 +NATIVE_CAPTURE_ACTIVE=0 NHX_JSON_RESOLVED="" NHX_JSON_ARG="" OVERLAY_REQUESTED=0 @@ -90,12 +110,20 @@ CAMX_FIT_DTB_SELECTED=0 # shellcheck disable=SC2317 cleanup() { + trap - EXIT INT TERM + if [ "$NATIVE_CAPTURE_ACTIVE" -eq 1 ]; then + if ! nhx_stop_native_capture; then + test_result_finish "FAIL" "$TESTNAME interrupted native capture could not be stopped safely" + fi + fi if command -v efi_restore_efivarfs_ro >/dev/null 2>&1; then efi_restore_efivarfs_ro >/dev/null 2>&1 || true fi if [ "$CAM_SERVER_STOPPED_FOR_TEST" -eq 1 ] && [ "$CAM_SERVER_PRESENT" -eq 1 ]; then - systemd_service_start_safe "$CAM_SERVER_SERVICE" >/dev/null 2>&1 || true + if ! nhx_restore_camera_service "$CAM_SERVER_SERVICE" "$LOG_DIR"; then + test_result_finish "FAIL" "$TESTNAME FAIL camera-server restoration failed: $CAM_SERVER_SERVICE" + fi fi if [ -n "${MARKER:-}" ]; then @@ -103,16 +131,22 @@ cleanup() { fi } -trap 'cleanup' EXIT INT TERM +trap 'cleanup' EXIT +trap 'test_result_finish "FAIL" "$TESTNAME interrupted, restoring owned camera state"' INT TERM usage() { cat </dev/null 2>&1; then + test_result_finish "FAIL" "$TESTNAME requires the repository package-provider helper" + fi + if [ "$(pkg_detect_os_id)" = centos ]; then + nhx_check_rpm_setup "$LOG_DIR" + NHX_SETUP_RPM_STATUS=$? + case "$NHX_SETUP_RPM_STATUS" in + 0) + ;; + 2) + test_result_finish "SKIP" "$TESTNAME requires the image-provided CAMX core package set, inspect NHX-PACKAGE diagnostics" + ;; + *) + test_result_finish "FAIL" "$TESTNAME could not query CAMX packages, inspect NHX-PACKAGE diagnostics" + ;; + esac + if ! command -v efivar >/dev/null 2>&1; then + test_result_finish "SKIP" "$TESTNAME requires image-provided efivar and its matching libraries, private RPM installation is not automated" + fi + NHX_SETUP_EFI=$(efi_find_variable_by_name VendorDtbOverlays "$CAMX_EFI_LIST_LOG") + NHX_SETUP_EFI_STATUS=$? + if [ "$NHX_SETUP_EFI_STATUS" -ne 0 ] && [ "$NHX_SETUP_EFI_STATUS" -ne 2 ]; then + log_file_with_label "NHX-EFI" "$CAMX_EFI_LIST_LOG" + test_result_finish "FAIL" "$TESTNAME could not discover EFI variables, check efivar runtime libraries and EFI permissions" + fi + if [ -z "$NHX_SETUP_EFI" ] || ! efi_text_variable_matches "$NHX_SETUP_EFI" camx "$CAMX_EFI_LOG"; then + test_result_finish "SKIP" "$TESTNAME requires EFI VendorDtbOverlays=camx, provision the overlay and reboot before checking runtime readiness" + fi + fi +fi if [ "$OVERLAY_REQUESTED" -eq 1 ]; then if [ -z "$FIT_DTB_NAME" ]; then @@ -216,6 +387,8 @@ fi if [ "$OVERLAY_REQUESTED" -eq 1 ]; then for required_helper in \ pkg_provider_init \ + pkg_detect_os_id \ + pkg_lookup_package_set \ pkg_ensure_optional_package_set_present \ pkg_verify_package_set_installed; do if ! command -v "$required_helper" >/dev/null 2>&1; then @@ -226,23 +399,51 @@ if [ "$OVERLAY_REQUESTED" -eq 1 ]; then done pkg_provider_init + CAMX_OVERLAY_OS_ID="$(pkg_detect_os_id 2>/dev/null || true)" + CAMX_APT_SOURCE="none" + + if [ "$CAMX_OVERLAY_OS_ID" = "debian" ]; then + CAMX_APT_SOURCE="qli-staging" + fi + + if [ "$CAMX_OVERLAY_OS_ID" = centos ]; then + if ! pkg_prepare_camx_rpm_repositories "$LOG_DIR"; then + test_result_finish "FAIL" "$TESTNAME CAMX repository preparation failed, inspect EPEL, Qualcomm and CLO diagnostics" + fi + fi + + CAMX_PACKAGES="$(pkg_lookup_package_set camera-nhx 2>/dev/null || true)" + if [ -z "$CAMX_PACKAGES" ]; then + log_skip "$TESTNAME SKIP - no Camera NHX package-set mapping is available, os=${CAMX_OVERLAY_OS_ID:-unknown} action=add-a-camera-nhx-package-set-mapping-for-this-os" + echo "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi + + log_info "Ensuring complete Camera NHX package set, os=${CAMX_OVERLAY_OS_ID:-unknown} packages=$CAMX_PACKAGES" if ! pkg_ensure_optional_package_set_present \ camera-nhx \ - qli-staging \ + "$CAMX_APT_SOURCE" \ auto \ --overlay; then - log_fail "$TESTNAME FAIL - failed to ensure Camera NHX CAMX package set" + log_fail "$TESTNAME FAIL - --overlay package-set installation failed, os=${CAMX_OVERLAY_OS_ID:-unknown} packages=$CAMX_PACKAGES action=verify-configured-package-repositories-and-package-availability" + echo "$TESTNAME FAIL" >"$RES_FILE" + exit 0 + fi + + if ! pkg_verify_package_set_installed camera-nhx; then + log_fail "$TESTNAME FAIL - Camera NHX package recovery completed but the required package set is still incomplete, os=${CAMX_OVERLAY_OS_ID:-unknown} packages=$CAMX_PACKAGES" echo "$TESTNAME FAIL" >"$RES_FILE" exit 0 fi log_pass "Camera NHX CAMX package set is ready" - CAMX_OVERLAY_OS_ID="$(pkg_detect_os_id 2>/dev/null || true)" case "$CAMX_OVERLAY_OS_ID" in debian|ubuntu|centos) for required_helper in \ + pkg_ensure_command \ + efi_ensure_mounted \ efi_find_variable_by_name \ efi_text_variable_matches \ efi_write_text_variable \ @@ -254,20 +455,45 @@ if [ "$OVERLAY_REQUESTED" -eq 1 ]; then fi done - if ! CHECK_DEPS_NO_EXIT=1 check_dependencies efivar mount mktemp od tr sed grep sync awk; then + if ! pkg_ensure_command efivar; then + test_result_finish "FAIL" "$TESTNAME could not provide efivar from the configured package repositories. Check the mapped package and matching runtime libraries, private RPM replacement is not automated" + fi + + if ! CHECK_DEPS_RECOVER=0 CHECK_DEPS_NO_EXIT=1 check_dependencies efivar mount mktemp od tr sed grep sync awk; then log_skip "$TESTNAME SKIP - CAMX FIT DTB verification requires efivar and EFI runtime tools" echo "$TESTNAME SKIP" >"$RES_FILE" exit 0 fi + efi_ensure_mounted "$LOG_DIR/${TESTNAME}_efi_mount_${TS}.log" + CAMX_EFI_MOUNT_STATUS=$? + case "$CAMX_EFI_MOUNT_STATUS" in + 0) + ;; + 2) + test_result_finish "SKIP" "$TESTNAME requires EFI runtime support for CAMX overlay selection" + ;; + *) + test_result_finish "FAIL" "$TESTNAME could not prepare efivarfs for CAMX overlay selection" + ;; + esac + CAMX_DTB_OVERLAY_VARIABLE="$(efi_find_variable_by_name \ VendorDtbOverlays \ "$CAMX_EFI_LIST_LOG" 2>>"$CAMX_EFI_LOG")" - if [ -z "$CAMX_DTB_OVERLAY_VARIABLE" ]; then - log_skip "$TESTNAME SKIP - VendorDtbOverlays EFI variable was not found" - echo "$TESTNAME SKIP" >"$RES_FILE" - exit 0 - fi + CAMX_EFI_DISCOVERY_STATUS=$? + case "$CAMX_EFI_DISCOVERY_STATUS" in + 0) + ;; + 2) + test_result_finish "SKIP" "$TESTNAME requires firmware exposing the VendorDtbOverlays EFI variable" + ;; + *) + log_file_with_label "NHX-EFI" "$CAMX_EFI_LIST_LOG" + log_file_with_label "NHX-EFI" "$CAMX_EFI_LOG" + test_result_finish "FAIL" "$TESTNAME could not discover one VendorDtbOverlays variable, check efivar runtime libraries, EFI permissions and duplicate variables" + ;; + esac log_info "CAMX FIT DTB EFI variable selected: $CAMX_DTB_OVERLAY_VARIABLE" @@ -300,39 +526,62 @@ if [ "$OVERLAY_REQUESTED" -eq 1 ]; then ;; esac else - log_info "Camera NHX overlay package installation not requested" + log_info "Camera NHX overlay package recovery not requested" fi # ----------------------------------------------------------------------------- # Deps check # ----------------------------------------------------------------------------- -deps_list="date awk sed grep tee wc ls find stat rm tr head tail dmesg sort fdtdump mkfifo sha256sum md5sum cksum diff cp mkdir" -if ! check_dependencies "$deps_list"; then +deps_list="date awk sed grep tee wc ls find stat rm tr head tail sort mkfifo cp mkdir" +if ! CHECK_DEPS_RECOVER=0 check_dependencies "$deps_list"; then log_skip "$TESTNAME SKIP missing one or more dependencies: $deps_list" echo "$TESTNAME SKIP" >"$RES_FILE" exit 0 fi -NHX_RUNNER="$(command -v nhx.sh 2>/dev/null || true)" -if [ -n "$NHX_RUNNER" ] && [ -f "$NHX_RUNNER" ] && [ -x "$NHX_RUNNER" ]; then - NHX_RUNNER_SOURCE="PATH-nhx.sh" -else - NHX_RUNNER="$(command -v camera-nhx 2>/dev/null || true)" -fi - -if [ -z "$NHX_RUNNER_SOURCE" ] && \ - [ -n "$NHX_RUNNER" ] && [ -f "$NHX_RUNNER" ] && [ -x "$NHX_RUNNER" ]; then - NHX_RUNNER_SOURCE="PATH-camera-nhx" -elif [ -z "$NHX_RUNNER_SOURCE" ] && \ - [ -f "/usr/libexec/camx/nhx.sh" ] && [ -x "/usr/libexec/camx/nhx.sh" ]; then - NHX_RUNNER="/usr/libexec/camx/nhx.sh" - NHX_RUNNER_SOURCE="packaged-libexec" -elif [ -z "$NHX_RUNNER_SOURCE" ]; then - log_skip "$TESTNAME SKIP NHX launcher not found, provide executable nhx.sh or camera-nhx in PATH, or /usr/libexec/camx/nhx.sh" +CKSUM_TOOL="$(nhx_pick_cksum_tool)" +if [ -z "$CKSUM_TOOL" ]; then + log_skip "$TESTNAME SKIP checksum validation requires one image-provided utility: sha256sum, md5sum, or cksum" echo "$TESTNAME SKIP" >"$RES_FILE" exit 0 fi +if [ "$NATIVE_REQUESTED" -eq 0 ]; then + NHX_RUNNER=$(nhx_find_launcher wrapper "") + NHX_DISCOVERY_STATUS=$? +else + NHX_DISCOVERY_STATUS=2 +fi +if [ "$NHX_DISCOVERY_STATUS" -eq 2 ]; then + NHX_RUNNER_MODE=native + NHX_RUNNER=$(nhx_find_launcher native "$NATIVE_BIN") + NHX_DISCOVERY_STATUS=$? +fi +case "$NHX_DISCOVERY_STATUS" in + 0) + ;; + 2) + test_result_finish "SKIP" "$TESTNAME requires an executable nhx.sh, camera-nhx or nativehaltest from the image CAMX packages" + ;; + *) + test_result_finish "FAIL" "$TESTNAME could not select a unique executable, inspect discovery diagnostics or specify --native-bin" + ;; +esac +NHX_RUNNER_SOURCE="$NHX_RUNNER_MODE-runtime-discovery" + +if [ "$NHX_RUNNER_MODE" = native ]; then + if [ -n "$NHX_JSON$NHX_TARGET" ]; then + test_result_finish "SKIP" "$TESTNAME found nativehaltest but no NHX JSON launcher. Kodiak and other NHX JSON configurations require nhx.sh or camera-nhx, no native substitution was attempted" + fi + if ! NATIVE_SELECTOR=$(nhx_native_check_cli "$NHX_RUNNER" "$LOG_DIR" "$NATIVE_CAMERA"); then + log_file_with_label NATIVE-HAL-HELP "$LOG_DIR/native-hal-help.log" + test_result_finish "FAIL" "$TESTNAME installed nativehaltest lacks the required capture CLI or its help probe failed" + fi + if [ "$NHX_CHECK_SETUP" -eq 0 ] && [ -z "$NATIVE_TEST" ]; then + test_result_finish "SKIP" "$TESTNAME nativehaltest found at $NHX_RUNNER. Pass --native-test Suite.Case from this installed client, no supported safe default testcase has been established" + fi +fi + log_info "NHX runner selected, path=$NHX_RUNNER source=$NHX_RUNNER_SOURCE" # ----------------------------------------------------------------------------- @@ -340,51 +589,107 @@ log_info "NHX runner selected, path=$NHX_RUNNER source=$NHX_RUNNER_SOURCE" # ----------------------------------------------------------------------------- log_info "Checking CAMX proprietary prerequisites before running NHX" -log_info "DT check" +log_info "Decoding the active runtime device tree" -PATTERNS="qcom,cam-sensor qcom,cam-gmsl-sensor qcom,cam-gmsl-deserializer qcom,eeprom qcom,cci qcom,csiphy qcom,cam-tpg1031 qcom,camera qcom,cam camera_kt cam-req-mgr cam-cpas cam-jpeg cam-ife cam-icp camera0-thermal" -found_any=0 -missing_list="" - -for pat in $PATTERNS; do - out="$(dt_confirm_node_or_compatible "$pat" 2>/dev/null || true)" - if [ -n "$out" ]; then - printf '%s\n' "$out" - found_any=1 - else - if [ -n "$missing_list" ]; then - missing_list="$missing_list, $pat" - else - missing_list="$pat" +camx_dump_runtime_dts "$RUNNING_DTS" "$RUNNING_DTS_WARNINGS" +DTC_RC=$? +case "$DTC_RC" in + 0) + log_pass "Active runtime DT decoded to $RUNNING_DTS" + if [ -s "$RUNNING_DTS_WARNINGS" ]; then + log_warn "dtc reported non-fatal warnings, retained in $RUNNING_DTS_WARNINGS" fi - fi -done + ;; + 2) + : >"$RUNNING_DTS_WARNINGS" + : >"$CAMERA_DTS_MATCHES" + : >"$SENSOR_DTS_MATCHES" + log_warn "dtc is not available in the image, continuing with enabled runtime-node validation" + ;; + 3) + : >"$RUNNING_DTS_WARNINGS" + : >"$CAMERA_DTS_MATCHES" + : >"$SENSOR_DTS_MATCHES" + log_warn "No readable runtime device-tree root is exposed under procfs or sysfs" + ;; + *) + : >"$CAMERA_DTS_MATCHES" + : >"$SENSOR_DTS_MATCHES" + log_warn "Active runtime DT decoding failed, continuing with enabled runtime-node validation, inspect $RUNNING_DTS_WARNINGS" + ;; +esac -if [ "$found_any" -ne 1 ]; then - log_skip "$TESTNAME SKIP missing DT patterns $missing_list" - echo "$TESTNAME SKIP" >"$RES_FILE" - exit 0 +if [ "$DTC_RC" -eq 0 ]; then + camx_extract_runtime_dts_evidence \ + "$RUNNING_DTS" \ + "$CAMERA_DTS_MATCHES" \ + "$SENSOR_DTS_MATCHES" fi -log_info "fdtdump camera nodes sample" +RUNTIME_DT_ROOT="$(dt_runtime_root 2>/dev/null || true)" +RUNTIME_DT_MODEL="$(dt_property_text "$RUNTIME_DT_ROOT" model 2>/dev/null || true)" +RUNTIME_DT_COMPATIBLE="$(dt_property_text "$RUNTIME_DT_ROOT" compatible 2>/dev/null || true)" +log_info "Runtime DT model: ${RUNTIME_DT_MODEL:-unavailable}" +log_info "Runtime DT compatible: ${RUNTIME_DT_COMPATIBLE:-unavailable}" +if [ "$DTC_RC" -eq 0 ]; then + log_info "[CAMX-CDT-FIT] efi_camx_selected=$CAMX_FIT_DTB_SELECTED active_dt=$RUNNING_DTS" + log_info "Runtime camera DTS matches retained in $CAMERA_DTS_MATCHES" + log_info "Runtime sensor DTS matches retained in $SENSOR_DTS_MATCHES" +else + log_info "[CAMX-CDT-FIT] efi_camx_selected=$CAMX_FIT_DTB_SELECTED active_dt=unavailable" +fi -FDT_MATCHES="$(camx_fdtdump_has_cam_nodes 2>/dev/null)" -rc=$? -if [ "$rc" -ne 0 ]; then - if [ "$rc" -eq 2 ]; then - log_skip "$TESTNAME SKIP fdtdump not available" +CAMX_DT_NODES="$(camx_list_enabled_runtime_nodes)" +: >"$CAMX_DT_NODES_LOG" +if [ -n "$CAMX_DT_NODES" ]; then + printf '%s\n' "$CAMX_DT_NODES" >"$CAMX_DT_NODES_LOG" +fi +if [ -z "$CAMX_DT_NODES" ]; then + if [ "$CAMX_FIT_DTB_SELECTED" -eq 1 ]; then + log_fail "$TESTNAME FAIL - VendorDtbOverlays selects camx but the active runtime DT has no enabled downstream CamX nodes, verify the CDT-selected FIT configuration and rebooted image" + echo "$TESTNAME FAIL" >"$RES_FILE" else - log_skip "$TESTNAME SKIP fdtdump did not show conclusive camera nodes" + log_skip "$TESTNAME SKIP - the active runtime DT has no enabled downstream CamX nodes, provision a CAMX-capable FIT configuration selected by CDT" + echo "$TESTNAME SKIP" >"$RES_FILE" fi - echo "$TESTNAME SKIP" >"$RES_FILE" exit 0 fi -printf '%s\n' "$FDT_MATCHES" | while IFS= read -r l; do - if [ -n "$l" ]; then - log_info " $l" +printf '%s\n' "$CAMX_DT_NODES" | while IFS= read -r camx_node; do + if [ -n "$camx_node" ]; then + log_info "Enabled CamX DT node: $camx_node" fi done +log_pass "Active runtime DT exposes enabled downstream CamX nodes" + +SENSOR_DT_NODES="$(camx_list_enabled_sensor_nodes)" +: >"$SENSOR_DT_NODES_LOG" +if [ -n "$SENSOR_DT_NODES" ]; then + printf '%s\n' "$SENSOR_DT_NODES" >"$SENSOR_DT_NODES_LOG" + printf '%s\n' "$SENSOR_DT_NODES" | while IFS= read -r sensor_node; do + if [ -n "$sensor_node" ]; then + log_info "Enabled camera sensor DT node: $sensor_node" + fi + done +else + log_warn "No enabled IMX, OV, AR, CamX sensor, or GMSL sensor nodes were found, NHX execution remains authoritative" +fi + +log_info "Checking optional raw FDT camera evidence" + +FDT_MATCHES="$(camx_fdtdump_has_cam_nodes 2>/dev/null)" +rc=$? +if [ "$rc" -eq 0 ]; then + printf '%s\n' "$FDT_MATCHES" | while IFS= read -r fdt_match; do + if [ -n "$fdt_match" ]; then + log_info "Raw FDT camera match: $fdt_match" + fi + done +elif [ "$rc" -eq 2 ]; then + log_warn "Optional fdtdump command is unavailable, continuing with enabled runtime-node evidence" +else + log_warn "Optional raw FDT scan did not find camera nodes, continuing with enabled runtime DT evidence" +fi log_info "driver load checks" @@ -518,17 +823,8 @@ if [ "$SENSOR_COUNT" -lt 1 ]; then log_warn "No sensors reported by cam list continuing" fi -CKSUM_TOOL="" -if command -v nhx_pick_cksum_tool >/dev/null 2>&1; then - CKSUM_TOOL="$(nhx_pick_cksum_tool)" -else - if command -v sha256sum >/dev/null 2>&1; then - CKSUM_TOOL="sha256sum" - elif command -v md5sum >/dev/null 2>&1; then - CKSUM_TOOL="md5sum" - elif command -v cksum >/dev/null 2>&1; then - CKSUM_TOOL="cksum" - fi +if [ "$NHX_CHECK_SETUP" -eq 1 ]; then + test_result_finish "PASS" "$TESTNAME NHX runtime prerequisites are present. No capture, QMMF service or physical sensor functionality was tested" fi log_info "$TESTNAME starting" @@ -563,6 +859,7 @@ if [ -n "$CAM_SERVER_SERVICE" ]; then if systemd_service_is_active "$CAM_SERVER_SERVICE"; then log_info "Stopping active camera server before NHX launcher" + CAM_SERVER_STOPPED_FOR_TEST=1 if ! systemd_service_stop_safe "$CAM_SERVER_SERVICE"; then log_fail "$TESTNAME FAIL - unable to stop active $CAM_SERVER_SERVICE before NHX launcher" @@ -576,7 +873,6 @@ if [ -n "$CAM_SERVER_SERVICE" ]; then exit 0 fi - CAM_SERVER_STOPPED_FOR_TEST=1 CAM_SERVER_TS_AFTER_STOP="$(date '+%Y-%m-%d %H:%M:%S')" log_info "Camera server status after stop" @@ -598,7 +894,13 @@ fi # ----------------------------------------------------------------------------- : >"$MARKER" 2>/dev/null || touch "$MARKER" -if [ -n "$NHX_JSON" ]; then +if [ "$NHX_RUNNER_MODE" = native ]; then + NATIVE_CAPTURE_ACTIVE=1 + nhx_run_native "$NHX_RUNNER" "$NATIVE_SELECTOR" "$NATIVE_TEST" \ + "$NATIVE_FRAMES" "$NATIVE_CAMERA" "$RUN_LOG" "$LOG_DIR" + NHX_RC=$? + NATIVE_CAPTURE_ACTIVE=0 +elif [ -n "$NHX_JSON" ]; then if ! command -v nhx_resolve_json_file >/dev/null 2>&1; then log_skip "$TESTNAME SKIP nhx_resolve_json_file helper not available" echo "$TESTNAME SKIP" >"$RES_FILE" @@ -689,7 +991,7 @@ fi # ----------------------------------------------------------------------------- if [ "$CAM_SERVER_STOPPED_FOR_TEST" -eq 1 ]; then log_info "Restoring camera server after NHX launcher: $CAM_SERVER_SERVICE" - if systemd_service_start_safe "$CAM_SERVER_SERVICE"; then + if nhx_restore_camera_service "$CAM_SERVER_SERVICE" "$LOG_DIR"; then CAM_SERVER_STOPPED_FOR_TEST=0 CAM_SERVER_TS_AFTER_START="$(date '+%Y-%m-%d %H:%M:%S')" @@ -701,7 +1003,8 @@ if [ "$CAM_SERVER_STOPPED_FOR_TEST" -eq 1 ]; then systemd_service_stdout_since "Camera server AFTER restore (stdout since restore marker)" \ "$RUN_LOG" "$CAM_SERVER_TS_AFTER_START" "$CAM_SERVER_SERVICE" || true else - log_warn "Failed to restore camera server after NHX launcher: $CAM_SERVER_SERVICE" + CAM_SERVER_RESTORE_FAILED=1 + log_fail "Failed to restore camera server after NHX launcher: $CAM_SERVER_SERVICE" fi else log_info "Camera server state was not changed for NHX, skipping restore" @@ -715,7 +1018,7 @@ DUMP_VALIDATION_FAIL=0 mkdir -p "$NHX_OUTDIR" 2>/dev/null || true if command -v nhx_validate_dumps_and_checksums >/dev/null 2>&1; then - if ! nhx_validate_dumps_and_checksums "$DUMP_DIR" "$MARKER" "$NHX_OUTDIR" "$CHECKSUMS_TXT" "$CHECKSUMS_PREV"; then + if ! nhx_validate_dumps_and_checksums "$DUMP_DIR" "$MARKER" "$NHX_OUTDIR" "$CHECKSUMS_TXT" "$CHECKSUMS_PREV" "$NHX_RUNNER_MODE"; then DUMP_VALIDATION_FAIL=1 fi @@ -764,6 +1067,10 @@ TOTAL_BYTES=0 echo "NHX launcher exit code: $NHX_RC" echo "NHX runner: $NHX_RUNNER" echo "NHX runner source: $NHX_RUNNER_SOURCE" + echo "Execution mode: $NHX_RUNNER_MODE" + echo "Native testcase: ${NATIVE_TEST:-}" + echo "Native frames: $NATIVE_FRAMES" + echo "Native camera: ${NATIVE_CAMERA:-}" echo "NHX JSON requested: ${NHX_JSON:-}" echo "NHX target requested: ${NHX_TARGET:-}" echo "NHX JSON resolved: ${NHX_JSON_RESOLVED:-}" @@ -842,6 +1149,17 @@ if [ -n "$FINAL_LINE" ]; then SKIPPED="$(echo "$FINAL_LINE" | sed -n 's/.*\[\([0-9][0-9]*\) SKIPPED\].*/\1/p')" fi +if [ "$NHX_RUNNER_MODE" = native ]; then + if NATIVE_COUNTS=$(nhx_native_result_counts "$RUN_LOG" "$NATIVE_TEST"); then + read -r PASSED FAILED SKIPPED <"$RES_FILE" -else - log_fail "$TESTNAME FAIL $REASON" - echo "$TESTNAME FAIL" >"$RES_FILE" -fi - -exit 0 +test_result_finish "$RESULT" "$TESTNAME $RESULT $REASON" diff --git a/Runner/suites/Multimedia/Camera/Libcamera_cam/libcamera_test_README.md b/Runner/suites/Multimedia/Camera/Libcamera_cam/libcamera_test_README.md index 62f82ad85..43cf80a1b 100644 --- a/Runner/suites/Multimedia/Camera/Libcamera_cam/libcamera_test_README.md +++ b/Runner/suites/Multimedia/Camera/Libcamera_cam/libcamera_test_README.md @@ -7,10 +7,8 @@ This repository contains a **POSIX shell** test harness for exercising `libcamer ## What this test does 1. **Discovers repo context** (finds `init_env`, sources `functestlib.sh`, and the camera helpers `Runner/utils/camera/lib_camera.sh`). -2. **Checks DT readiness** using `dt_confirm_node_or_compatible` for: - - Sensor compatible(s), e.g. `sony,imx577` - - ISP / camera blocks, e.g. `isp`, `cam`, `camss` -3. **Lists available cameras** with `cam -l` (warning/error lines are tolerated when camera listing succeeds). +2. **Checks DT applicability** by discovering enabled Qualcomm CAMSS pipeline nodes in the runtime device tree. Disabled nodes from inactive overlays are ignored. +3. **Lists available cameras** with `cam -l`, retains the complete output, and accepts both `[0] ...` and `0: ...` camera entry formats. 4. **Captures frames** with `cam` for one or multiple indices, storing artifacts per camera under `OUT_DIR`. 5. **Validates output**: sequence continuity, content sanity (PPM/BIN), duplicate detection, and log scanning with noise suppression. 6. **Summarizes per‑camera PASS/FAIL**, with overall suite verdict and exit code. @@ -19,18 +17,17 @@ This repository contains a **POSIX shell** test harness for exercising `libcamer ## Requirements -- `cam` (from the `libcamera-tools` package on Debian and Ubuntu) +- Image-provided `cam` utility from libcamera - Standard tools: `awk`, `sed`, `grep`, `sort`, `cut`, `tr`, `wc`, `find`, `stat`, `head`, `tail`, `dd` - Optional: `sha256sum` or `md5sum` (for duplicate BIN detection) - **BusyBox compatibility**: - We avoid `find -printf` and `od -A` options (not available on BusyBox). -When `cam` is missing on an APT-based target, the shared package provider -refreshes package metadata, installs `libcamera-tools`, and verifies the -command before capture starts. Other images continue to use their configured -provider or image-provided command and report the missing dependency cleanly. +The test does not install packages at runtime. If `cam` or another required +utility is absent from the image, it reports a clean SKIP with the missing +prerequisite. -> The harness tolerates noisy `cam -l` / `cam -I` output (WARN/ERROR lines). It only requires that cameras and/or stream info are ultimately reported. +> The harness tolerates noisy `cam -l` / `cam -I` output when cameras and stream information are ultimately reported. A nonzero `cam -l` status or zero enumerated cameras is a failure when an enabled CAMSS pipeline is present. --- @@ -80,49 +77,51 @@ Examples: ## Device‑tree checks -The runner verifies DT node presence **before** capture: +The runner determines applicability **before** capture by finding enabled +Qualcomm compatibles containing `camss` in the live device tree. The shared +device-tree helper resolves runtime root symlinks, searches both standard roots, +and ignores nodes whose status is `disabled`, `fail`, or `failed`. -- First it looks for known sensor compatibles (e.g. `sony,imx577`). -- If the sensor isn’t found, it looks for ISP / camera nodes (e.g. `isp`, `cam`, `camss`). -- Matching entries are printed cleanly (name, path, compatible). - -If neither sensor nor ISP/camera blocks are found, the test **SKIPs** with a message: -``` -SKIP – No ISP/camera node/compatible found in DT -``` - -> On large DTs, this scan can take time. The log prints “Verifying the availability of DT nodes, this process may take some time.” +- No enabled CAMSS pipeline: **SKIP** because the upstream libcamera path is not applicable to the active image or overlay. +- Enabled CAMSS pipeline and `cam -l` fails: **FAIL** and retain the command output. +- Enabled CAMSS pipeline and `cam -l` reports zero cameras: **FAIL** because the image needs matching libcamera pipeline-handler and sensor support. --- ## IPA file workaround (simple pipeline) -On some builds, allocation may fail if `uncalibrated.yaml` exists for the `simple` IPA. The runner guards this by **renaming** it pre‑run: +On some builds, allocation may fail if `uncalibrated.yaml` exists for the +`simple` IPA. The runner temporarily moves it to a test-owned backup path: ```sh if [ -f /usr/share/libcamera/ipa/simple/uncalibrated.yaml ]; then mv /usr/share/libcamera/ipa/simple/uncalibrated.yaml \ - /usr/share/libcamera/ipa/simple/uncalibrated.yaml.bk + /usr/share/libcamera/ipa/simple/uncalibrated.yaml.qcom-testkit-backup fi ``` -It’s restored automatically at the end (if it was present). +The EXIT trap restores only the file moved by the current test run. If the +backup path already exists, the test leaves both files unchanged. --- ## Output & artifacts +Directly under `OUT_DIR`: + +- `cam-list-.log` – retained initial `cam -l` output used for applicability validation and index selection +- `summary.txt` – per‑camera PASS/FAIL + Per‑camera subfolder under `OUT_DIR`: + - `cam-run--camX.log` – raw cam output - `cam-info--camX.log` – `cam -l` and `cam -I` info - `frame-...` files (`.bin` or `.ppm`) – captured frames - `.file_seq_map.txt`, `.bytesused.txt`, etc. – validation sidecar files -- `summary.txt` – per‑camera PASS/FAIL -Console prints a **per‑camera** and **overall** summary. Exit codes: -- `0` PASS -- `1` FAIL -- `2` SKIP +Console prints a **per‑camera** and **overall** summary. +The `.res` file is the authoritative result. The runner exits `0` after +publishing PASS, FAIL, or SKIP so LAVA can consume that result. --- @@ -145,15 +144,11 @@ You can relax strictness with `--no-strict` (skips contiguous sequence enforceme --- -## Environment overrides +## Repository integration -- `INIT_ENV`: If set, the runner uses it instead of walking upward to find `init_env`. -- `LIBCAM_PATH`: If set, the runner sources this path for `lib_camera.sh` helper functions. -- Otherwise, the runner searches typical repo locations: - - `Runner/utils/camera/lib_camera.sh` - - `Runner/utils/lib_camera.sh` - - `utils/camera/lib_camera.sh` - - `utils/lib_camera.sh` +The runner locates `init_env` by walking upward from its own directory and +loads `Runner/utils/camera/lib_camera.sh` through the repository `TOOLS` path. +No environment override is required. --- @@ -161,7 +156,8 @@ You can relax strictness with `--no-strict` (skips contiguous sequence enforceme - **`cam -l` prints WARN/ERROR but lists cameras**: This is tolerated. The runner parses indices from the “Available cameras” section. - **BusyBox `find`/`od` compatibility**: We avoid GNU-only flags; if you see issues, ensure BusyBox provides the required applets mentioned above. -- **No DT matches**: Ensure your DT exposes sensor compatibles (e.g. `sony,imx577`) or ISP/camera nodes (`isp`, `cam`, `camss`). On dev boards, DT overlays may need to be applied. +- **No enabled CAMSS node**: Ensure the active runtime DT selects the upstream CAMSS image or overlay. Camera nodes from disabled overlays do not make this test applicable. +- **Enabled CAMSS but zero cameras**: Inspect `cam-list-.log` and provide a libcamera build with the matching Qualcomm pipeline handler and sensor support. - **Content flagged “near‑constant”**: This typically indicates all-same bytes in sampled regions. Verify the lens cap, sensor mode, or try `--args` with a smaller resolution/role to confirm live changes. - **IPA config missing**: See the **IPA file workaround** above. diff --git a/Runner/suites/Multimedia/Camera/Libcamera_cam/run.sh b/Runner/suites/Multimedia/Camera/Libcamera_cam/run.sh index e9f6c146a..438a29bb9 100755 --- a/Runner/suites/Multimedia/Camera/Libcamera_cam/run.sh +++ b/Runner/suites/Multimedia/Camera/Libcamera_cam/run.sh @@ -1,59 +1,46 @@ #!/bin/sh # Copyright (c) Qualcomm Technologies, Inc. -# SPDX-License-Identifier: BSD-3-Clause# libcamera 'cam' runner with strong post-capture validation (CLI-config only) -# ---------- Repo env + helpers (single-pass) ---------- -SCRIPT_DIR=$(cd "$(dirname "$0")" || exit 1; pwd) +# SPDX-License-Identifier: BSD-3-Clause +# libcamera 'cam' runner with strong post-capture validation (CLI-config only) + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + pwd +)" +INIT_ENV="" SEARCH="$SCRIPT_DIR" -INIT_ENV="${INIT_ENV:-}" -LIBCAM_PATH="${LIBCAM_PATH:-}" -# Find init_env quickly (single upward walk) -while [ "$SEARCH" != "/" ] && [ -z "$INIT_ENV" ]; do - [ -f "$SEARCH/init_env" ] && INIT_ENV="$SEARCH/init_env" && break - SEARCH=${SEARCH%/*} +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") done if [ -z "$INIT_ENV" ]; then - printf '%s\n' "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 exit 1 fi +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + # shellcheck disable=SC1090 . "$INIT_ENV" # shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" -# Prefer direct repo-root locations for lib_camera.sh -if [ -z "$LIBCAM_PATH" ]; then - REPO_ROOT=$(dirname "$INIT_ENV") - for cand in \ - "$REPO_ROOT/Runner/utils/camera/lib_camera.sh" \ - "$REPO_ROOT/utils/camera/lib_camera.sh" - do - [ -f "$cand" ] && { LIBCAM_PATH="$cand"; break; } - done -fi +LIBCAM_PATH="$TOOLS/camera/lib_camera.sh" -# Fallback upward walk only if still not found -if [ -z "$LIBCAM_PATH" ]; then - SEARCH="$SCRIPT_DIR" - while [ "$SEARCH" != "/" ]; do - for cand in \ - "$SEARCH/Runner/utils/camera/lib_camera.sh" \ - "$SEARCH/utils/camera/lib_camera.sh" - do - [ -f "$cand" ] && { LIBCAM_PATH="$cand"; break 2; } - done - SEARCH=${SEARCH%/*} - done -fi - -if [ -z "$LIBCAM_PATH" ]; then - if command -v log_error >/dev/null 2>&1; then - log_error "lib_camera.sh not found (searched under Runner/utils/camera and utils/camera)" - else - printf '%s\n' "ERROR: lib_camera.sh not found (searched under Runner/utils/camera and utils/camera)" >&2 - fi +if [ ! -r "$LIBCAM_PATH" ]; then + log_error "lib_camera.sh is unavailable at $LIBCAM_PATH" exit 1 fi @@ -62,7 +49,9 @@ fi TESTNAME="Libcamera_cam" RES_FILE="./${TESTNAME}.res" -: > "$RES_FILE" +if ! test_result_init "$TESTNAME" "$RES_FILE"; then + exit 1 +fi # ---------- Defaults (override via CLI only) ---------- CAM_INDEX="auto" # --index |all|n,m,k ; auto = first from `cam -l` @@ -78,8 +67,22 @@ DUP_MAX_RATIO="0.5" # --dup-max-ratio <0..1> BIN_TOL_PCT="5" # --bin-tol-pct PPM_SAMPLE_BYTES="65536" BIN_SAMPLE_BYTES="65536" +UNCALIB="/usr/share/libcamera/ipa/simple/uncalibrated.yaml" +UNCALIB_BACKUP="${UNCALIB}.qcom-testkit-backup" +UNCALIB_MOVED=0 + +# Restore test-owned runtime changes. +cleanup() { + if [ "$UNCALIB_MOVED" -eq 1 ]; then + libcam_restore_ipa_config "$UNCALIB" "$UNCALIB_BACKUP" || true + UNCALIB_MOVED=0 + fi +} + +trap 'cleanup' EXIT +trap 'exit 130' INT TERM -print_usage() { +usage() { cat </dev/null || true)" - if [ -n "$out" ]; then - printf '%s\n' "$out" - found_any=1 - else - [ -n "$missing_list" ] && missing_list="$missing_list, $pat" || missing_list="$pat" - fi -done - -if [ "$found_any" -eq 1 ]; then - log_info "DT nodes present (see matches above)." -else - log_skip "$TESTNAME SKIP – missing DT patterns: $missing_list" - echo "$TESTNAME SKIP" >"$RES_FILE" - exit 0 +log_info "Checking the runtime device tree for an enabled Qualcomm CAMSS pipeline" +CAMSS_NODES="$(libcam_list_enabled_pipeline_nodes 2>/dev/null || true)" + +if [ -z "$CAMSS_NODES" ]; then + test_result_finish \ + "SKIP" \ + "$TESTNAME SKIP - no enabled Qualcomm CAMSS pipeline exists in the runtime device tree, select an upstream CAMSS image or overlay" fi +printf '%s\n' "$CAMSS_NODES" | + while IFS= read -r camss_node; do + [ -n "$camss_node" ] || continue + log_info "Enabled CAMSS pipeline node: $camss_node" + done + # ---------- Dependencies ---------- -log_info "Ensuring the libcamera cam utility is available." -CHECK_DEPS_NO_EXIT=1 check_dependencies cam || { - log_error "cam utility remains unavailable after package recovery" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 1 -} -# nice-to-haves for validation -check_dependencies awk sed grep sort cut tr wc find stat head tail dd || true -(check_dependencies sha256sum || check_dependencies md5sum) || true +log_info "Checking image-provided libcamera dependencies" +if ! CHECK_DEPS_RECOVER=0 CHECK_DEPS_NO_EXIT=1 check_dependencies cam; then + test_result_finish \ + "SKIP" \ + "$TESTNAME SKIP - enabled CAMSS is present but the image does not provide the libcamera cam utility" +fi + +if ! CHECK_DEPS_RECOVER=0 CHECK_DEPS_NO_EXIT=1 \ + check_dependencies awk sed grep sort cut tr wc find stat head tail dd; then + test_result_finish \ + "SKIP" \ + "$TESTNAME SKIP - required image-provided shell utilities are unavailable" +fi + +if ! command -v sha256sum >/dev/null 2>&1 && + ! command -v md5sum >/dev/null 2>&1; then + log_info "No optional SHA-256 or MD5 utility is available, duplicate hash checks will be skipped" +fi # ---------- Setup ---------- mkdir -p "$OUT_DIR" 2>/dev/null || true RUN_TS="$(date -u +%Y%m%d-%H%M%S)" +CAM_LIST_LOG="${OUT_DIR%/}/cam-list-${RUN_TS}.log" log_info "Test: $TESTNAME" log_info "OUT_DIR=$OUT_DIR | COUNT=$CAPTURE_COUNT | SAVE_AS_PPM=$SAVE_AS_PPM" log_info "Extra args: ${CAM_EXTRA_ARGS:-}" # ---- IPA workaround: disable simple/uncalibrated to avoid buffer allocation failures ---- -UNCALIB="/usr/share/libcamera/ipa/simple/uncalibrated.yaml" -if [ -f "$UNCALIB" ]; then - log_info "Renaming $UNCALIB -> ${UNCALIB}.bk to avoid IPA buffer allocation issues" - mv "$UNCALIB" "${UNCALIB}.bk" 2>/dev/null || log_warn "Failed to rename $UNCALIB (continuing)" -elif [ -f "${UNCALIB}.bk" ]; then - # Already renamed in a previous run; just log for clarity - log_info "IPA workaround already applied: ${UNCALIB}.bk present" +if libcam_disable_ipa_config "$UNCALIB" "$UNCALIB_BACKUP"; then + UNCALIB_MOVED=1 +else + IPA_CONFIG_RC=$? + if [ "$IPA_CONFIG_RC" -eq 2 ]; then + log_info "IPA workaround is not applicable, config is absent: $UNCALIB" + fi fi # ---------- Sensor presence ---------- -SENSOR_COUNT="$(libcam_list_sensors_count 2>/dev/null)" -# harden: ensure numeric -case "$SENSOR_COUNT" in ''|*[!0-9]*) SENSOR_COUNT=0 ;; esac +if cam -l >"$CAM_LIST_LOG" 2>&1; then + CAM_LIST_RC=0 +else + CAM_LIST_RC=$? +fi + +sed 's/^/[cam -l] /' "$CAM_LIST_LOG" + +if [ "$CAM_LIST_RC" -ne 0 ]; then + test_result_finish \ + "FAIL" \ + "$TESTNAME FAIL - cam -l exited with status $CAM_LIST_RC although an enabled CAMSS pipeline is present, see $CAM_LIST_LOG" +fi + +SENSOR_COUNT="$(libcam_count_sensors_from_file "$CAM_LIST_LOG" 2>/dev/null || true)" +case "$SENSOR_COUNT" in + ''|*[!0-9]*) SENSOR_COUNT=0 ;; +esac log_info "[cam -l] detected ${SENSOR_COUNT} camera(s)" - + if [ "$SENSOR_COUNT" -lt 1 ]; then - log_skip "No sensors reported by 'cam -l' - marking SKIP" - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 + test_result_finish \ + "FAIL" \ + "$TESTNAME FAIL - enabled CAMSS pipeline detected but cam -l reported zero cameras, provide matching libcamera pipeline-handler and sensor support, see $CAM_LIST_LOG" fi - # ---------- Resolve indices (supports: auto | all | 0,2,5) ---------- -INDICES="$(libcam_resolve_indices "$CAM_INDEX")" +INDICES="$(libcam_resolve_indices "$CAM_INDEX" "$CAM_LIST_LOG")" if [ -z "$INDICES" ]; then - log_skip "No valid camera indices resolved (cam -l empty?) - SKIP" - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 + test_result_finish \ + "FAIL" \ + "$TESTNAME FAIL - cam -l reported cameras but no index matched --index $CAM_INDEX, see $CAM_LIST_LOG" fi log_info "Resolved indices: $INDICES" @@ -210,7 +275,7 @@ for IDX in $INDICES; do log_info "---- Camera idx: $IDX ----" { echo "== cam -l ==" - cam -l || true + cat "$CAM_LIST_LOG" echo echo "== cam -I (index $IDX) ==" cam -c "$IDX" -I || true @@ -257,11 +322,11 @@ for IDX in $INDICES; do [ $RC -eq 0 ] || { ANY_RC_NONZERO=1; PASS=0; } if [ "$PASS" -eq 1 ]; then - log_pass "[idx $IDX] PASS" + test_result_record "PASS" "Camera index $IDX capture and validation passed" PASS_LIST="$PASS_LIST $IDX" echo "cam$IDX PASS" >> "$OUT_DIR/summary.txt" else - log_fail "[idx $IDX] FAIL" + test_result_record "FAIL" "Camera index $IDX capture or validation failed, see $RUN_LOG" FAIL_LIST="$FAIL_LIST $IDX" echo "cam$IDX FAIL" >> "$OUT_DIR/summary.txt" OVERALL_PASS=0 @@ -293,11 +358,11 @@ done # ---------- Final verdict ---------- if [ "$OVERALL_PASS" -eq 1 ] && [ $ANY_RC_NONZERO -eq 0 ]; then - echo "$TESTNAME PASS" > "$RES_FILE" - log_pass "$TESTNAME PASS" - exit 0 + test_result_finish \ + "PASS" \ + "$TESTNAME PASS - all selected camera captures passed" else - echo "$TESTNAME FAIL" > "$RES_FILE" - log_fail "$TESTNAME FAIL" - exit 1 + test_result_finish \ + "FAIL" \ + "$TESTNAME FAIL - one or more selected camera captures failed, see $OUT_DIR/summary.txt" fi diff --git a/Runner/utils/camera/lib_camera.sh b/Runner/utils/camera/lib_camera.sh index 34e550a8e..7000ffbdb 100755 --- a/Runner/utils/camera/lib_camera.sh +++ b/Runner/utils/camera/lib_camera.sh @@ -1,14 +1,66 @@ #!/bin/sh # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -# SPDX-License-Identifier: BSD-3-Clause# Shared libcamera helpers +# SPDX-License-Identifier: BSD-3-Clause +# Shared libcamera helpers # ---------- Sensor & index helpers ---------- -# Return the number of sensors visible in `cam -l` +# libcam_list_indices_from_file +# List camera indices from retained `cam -l` output. +libcam_list_indices_from_file() { + libcam_indices_file="$1" + if [ ! -r "$libcam_indices_file" ]; then + printf '%s\n' "libcamera list output is not readable: $libcam_indices_file" >&2 + return 1 + fi + + awk ' + /^[[:space:]]*Available cameras:/ { + in_list=1 + next + } + !in_list { + next + } + /^[[:space:]]*\[[0-9]+\]/ { + index_value=$0 + sub(/^[[:space:]]*\[/, "", index_value) + sub(/\].*$/, "", index_value) + if (!seen[index_value]++) { + print index_value + } + next + } + /^[[:space:]]*[0-9]+:[[:space:]]/ { + index_value=$0 + sub(/^[[:space:]]*/, "", index_value) + sub(/:.*/, "", index_value) + if (!seen[index_value]++) { + print index_value + } + } + ' "$libcam_indices_file" +} + +# libcam_count_sensors_from_file +# Return the number of cameras in retained `cam -l` output. +libcam_count_sensors_from_file() { + libcam_count_file="$1" + if [ ! -r "$libcam_count_file" ]; then + printf '%s\n' "libcamera list output is not readable: $libcam_count_file" >&2 + return 1 + fi + + libcam_list_indices_from_file "$libcam_count_file" | + awk 'NF { count++ } END { print count + 0 }' +} + +# Return the number of sensors visible in `cam -l`. libcam_list_sensors_count() { out="$(cam -l 2>&1 || true)" printf '%s\n' "$out" | awk ' BEGIN { c=0; inlist=0 } - /^Available cameras:/ { inlist=1; next } + /^[[:space:]]*Available cameras:/ { inlist=1; next } + /^[[:space:]]*\[[0-9]+\]/ { if (inlist) c++ } /^[[:space:]]*[0-9]+:[[:space:]]/ { if (inlist) c++ } END { print c+0 } ' @@ -28,7 +80,12 @@ libcam_list_indices() { # Outputs space-separated indices to stdout. libcam_resolve_indices() { want="${1:-auto}" - all="$(libcam_list_indices | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g;s/^ //;s/ $//')" + list_file="${2:-}" + if [ -n "$list_file" ]; then + all="$(libcam_list_indices_from_file "$list_file" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g;s/^ //;s/ $//')" + else + all="$(libcam_list_indices | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g;s/^ //;s/ $//')" + fi [ -n "$all" ] || { printf '\n'; return 1; } case "$want" in @@ -51,6 +108,50 @@ libcam_resolve_indices() { esac } +# List enabled Qualcomm libcamera pipeline nodes from the runtime device tree. +libcam_list_enabled_pipeline_nodes() { + dt_list_compatible_nodes \ + '(^|[[:space:]])qcom,[^[:space:]]*camss([[:space:]]|$)' \ + regex +} + +# libcam_disable_ipa_config +# Moves one IPA config aside for a test-owned temporary workaround. +libcam_disable_ipa_config() { + libcam_ipa_config="$1" + libcam_ipa_backup="$2" + + [ -f "$libcam_ipa_config" ] || return 2 + if [ -e "$libcam_ipa_backup" ]; then + log_warn "IPA config backup already exists, leaving both files unchanged: $libcam_ipa_backup" + return 1 + fi + + if mv "$libcam_ipa_config" "$libcam_ipa_backup"; then + log_info "Temporarily moved IPA config to $libcam_ipa_backup" + return 0 + fi + + log_warn "Failed to move IPA config $libcam_ipa_config" + return 1 +} + +# libcam_restore_ipa_config +# Restores an IPA config moved by libcam_disable_ipa_config. +libcam_restore_ipa_config() { + libcam_ipa_config="$1" + libcam_ipa_backup="$2" + + [ -f "$libcam_ipa_backup" ] || return 0 + if mv "$libcam_ipa_backup" "$libcam_ipa_config"; then + log_info "Restored IPA config $libcam_ipa_config" + return 0 + fi + + log_warn "Failed to restore IPA config from $libcam_ipa_backup" + return 1 +} + # Pick first camera index (helper used by older paths) libcam_pick_cam_index() { want="${1:-auto}" @@ -558,8 +659,78 @@ camx_opkg_list_camx() { } # ----------------------------------------------------------------------------- -# DT camera nodes sample using fdtdump +# Runtime DT and camera overlay evidence # ----------------------------------------------------------------------------- +# camx_dump_runtime_dts +# Decodes the active runtime device tree from the resolved filesystem root. +camx_dump_runtime_dts() { + runtime_dts_output="$1" + runtime_dts_warnings="$2" + runtime_dts_root="" + + command -v dtc >/dev/null 2>&1 || return 2 + runtime_dts_root="$(dt_runtime_root 2>/dev/null || true)" + [ -n "$runtime_dts_root" ] || return 3 + + : >"$runtime_dts_warnings" + if ! dtc \ + -I fs \ + -O dts \ + -o "$runtime_dts_output" \ + "$runtime_dts_root" \ + 2>"$runtime_dts_warnings"; then + return 1 + fi + + [ -s "$runtime_dts_output" ] +} + +# camx_extract_runtime_dts_evidence +# Extracts line-numbered camera and sensor matches from a decoded runtime DTS. +camx_extract_runtime_dts_evidence() { + runtime_dts_input="$1" + runtime_camera_log="$2" + runtime_sensor_log="$3" + + [ -r "$runtime_dts_input" ] || return 1 + + if ! grep -nEi \ + 'camera|cam[-_@]|qcom,cam|cci(@|[-_])|csiphy' \ + "$runtime_dts_input" >"$runtime_camera_log"; then + : >"$runtime_camera_log" + fi + + if ! grep -nEi \ + 'imx[0-9]+|ov[0-9]+|ar[0-9]+|cam[_-]sensor|cam[_-]gmsl' \ + "$runtime_dts_input" >"$runtime_sensor_log"; then + : >"$runtime_sensor_log" + fi +} + +# camx_list_enabled_runtime_nodes +# Lists enabled downstream CamX nodes from the active runtime DT. +camx_list_enabled_runtime_nodes() { + { + dt_list_compatible_nodes \ + '(^|[[:space:]])qcom,cam[-_][^[:space:]]+([[:space:]]|$)' \ + regex 2>/dev/null || true + dt_list_compatible_nodes \ + '(^|[[:space:]])qcom,camera[^[:space:]]*([[:space:]]|$)' \ + regex 2>/dev/null || true + } | sort -u +} + +# camx_list_enabled_sensor_nodes +# Lists enabled physical or CamX sensor nodes from the active runtime DT. +camx_list_enabled_sensor_nodes() { + { + dt_list_compatible_nodes \ + '(^|[[:space:]])(sony,imx[^[:space:]]*|ovti,ov[^[:space:]]*|onsemi,ar[^[:space:]]*|onnn,ar[^[:space:]]*|aptina,ar[^[:space:]]*|qcom,cam[-_](sensor|gmsl[-_]sensor|gmsl[-_]deserializer))([[:space:]]|$)' \ + regex 2>/dev/null || true + } | sort -u +} + +# DT camera nodes sample using fdtdump. camx_fdtdump_has_cam_nodes() { command -v fdtdump >/dev/null 2>&1 || return 2 [ -r /sys/firmware/fdt ] || return 1 @@ -575,11 +746,305 @@ camx_fdtdump_has_cam_nodes() { # ---- NHX dump + checksum validation helpers (POSIX) ---- +# nhx_find_launcher mode override_path [libexec_root] +# Print one executable path. Wrapper discovery preserves the legacy order. +# Native discovery prefers an explicit path, then PATH, then camx* libexec +# directories without a SoC table. Resolve aliases before counting candidates. +# Return 0 for one executable, 2 when absent, 1 for invalid/ambiguous selection. +# Read-only discovery, diagnostics go to stderr. +nhx_find_launcher() { + nfl_mode="$1" + nfl_override="$2" + nfl_root="${3:-/usr/libexec}" + if [ "$nfl_mode" = wrapper ]; then + for nfl_name in nhx.sh camera-nhx; do + nfl_path=$(command -v "$nfl_name" 2>/dev/null || true) + if [ -n "$nfl_path" ] && [ -f "$nfl_path" ] && [ -x "$nfl_path" ]; then + printf '%s\n' "$nfl_path" + return 0 + fi + done + if [ -f "$nfl_root/camx/nhx.sh" ] && [ -x "$nfl_root/camx/nhx.sh" ]; then + printf '%s\n' "$nfl_root/camx/nhx.sh" + return 0 + fi + return 2 + fi + [ "$nfl_mode" = native ] || return 1 + if [ -n "$nfl_override" ]; then + case "$nfl_override" in + /*) + ;; + *) + printf '%s\n' 'Native binary override must be an absolute path' >&2 + return 1 + ;; + esac + if [ ! -f "$nfl_override" ] || [ ! -x "$nfl_override" ]; then + printf 'Native executable is unavailable: %s\n' "$nfl_override" >&2 + return 1 + fi + printf '%s\n' "$nfl_override" + return 0 + fi + nfl_path=$(command -v nativehaltest 2>/dev/null || true) + if [ -n "$nfl_path" ] && [ -f "$nfl_path" ] && [ -x "$nfl_path" ]; then + printf '%s\n' "$nfl_path" + return 0 + fi + nfl_selected="" + for nfl_path in "$nfl_root"/camx*/nativehaltest; do + if [ ! -f "$nfl_path" ] || [ ! -x "$nfl_path" ]; then + continue + fi + nfl_resolved=$(readlink -f "$nfl_path" 2>/dev/null) || return 1 + if [ -n "$nfl_selected" ] && [ "$nfl_resolved" != "$nfl_selected" ]; then + printf 'Multiple native HAL executables found: %s, %s. Select --native-bin\n' \ + "$nfl_selected" "$nfl_resolved" >&2 + return 1 + fi + nfl_selected="$nfl_resolved" + done + [ -n "$nfl_selected" ] || return 2 + printf '%s\n' "$nfl_selected" +} + +# nhx_native_check_cli binary log_directory camera_id +# Probe documented help only, never guess enumeration switches or run tests. +# Print the supported exact-test selector. Retain help output and bound the +# probe to 15 seconds. Return 1 for a broken client or missing required options. +nhx_native_check_cli() { + nncc_binary="$1" + nncc_logs="$2" + nncc_camera="$3" + if ! run_with_managed_timeout 15 "$nncc_logs" native-hal-help \ + "$nncc_binary" --help >"$nncc_logs/native-hal-help.log" 2>&1; then + printf 'Native HAL help query failed, inspect %s/native-hal-help.log\n' "$nncc_logs" >&2 + return 1 + fi + for nncc_option in --frames --timeout --p; do + if ! grep -Eq "^[[:space:]]*$nncc_option([[:space:]]|$)" "$nncc_logs/native-hal-help.log"; then + printf 'Native HAL does not advertise required option %s\n' "$nncc_option" >&2 + return 1 + fi + done + if [ -n "$nncc_camera" ] && \ + ! grep -Eq '^[[:space:]]*-c([[:space:]]|$)' "$nncc_logs/native-hal-help.log"; then + printf '%s\n' 'Native HAL does not advertise the requested camera override' >&2 + return 1 + fi + for nncc_option in --test --gtest_filter; do + if grep -Eq "^[[:space:]]*$nncc_option([[:space:]]|$)" "$nncc_logs/native-hal-help.log"; then + printf '%s\n' "$nncc_option" + return 0 + fi + done + printf '%s\n' 'Native HAL does not advertise an exact testcase selector' >&2 + return 1 +} + +# nhx_run_native binary selector testcase frames camera_id run_log log_directory +# Run one explicitly selected native testcase with dumps enabled and stdout +# logging. A 5-second per-buffer timeout is separate from the 180-second total +# deadline. Capture raw output, then replay it to stdout. No wrapper semantics +# or service state are changed here. Return the native process/timeout status. +nhx_run_native() { + nrn_binary="$1" + nrn_selector="$2" + nrn_test="$3" + nrn_frames="$4" + nrn_camera="$5" + nrn_log="$6" + nrn_directory="$7" + set -- "$nrn_binary" "$nrn_selector" "$nrn_test" \ + --frames "$nrn_frames" --timeout 5000 --p + if [ -n "$nrn_camera" ]; then + set -- "$@" -c "$nrn_camera" + fi + log_info "Running native HAL testcase=$nrn_test frames=$nrn_frames camera=${nrn_camera:-client-default-physical} total-timeout=180s" + run_with_managed_timeout 180 "$nrn_directory" native-hal-capture \ + "$@" >"$nrn_log" 2>&1 + nrn_status=$? + log_file_with_label NATIVE-HAL "$nrn_log" + return "$nrn_status" +} + +# nhx_native_result_counts run_log testcase +# Print PASSED FAILED SKIPPED counts for exactly one executed native testcase. +# Require matching RUN/OK markers and a completed single-test summary, either +# GoogleTest or CAMX Final Report. Reject zero/multiple tests, skips, conflicting +# summaries and unrelated successful testcases. No mutation, return 1 on gaps. +nhx_native_result_counts() { + awk -v selected="$2" ' + /\[[[:space:]]*RUN[[:space:]]*\]/ { + line=$0 + sub(/^.*\[[[:space:]]*RUN[[:space:]]*\][[:space:]]*/, "", line) + split(line, fields, /[[:space:]]+/) + runs++ + if (fields[1] != selected) bad=1 + } + /\[[[:space:]]*OK[[:space:]]*\]/ { + line=$0 + sub(/^.*\[[[:space:]]*OK[[:space:]]*\][[:space:]]*/, "", line) + split(line, fields, /[[:space:]]+/) + oks++ + if (fields[1] != selected) bad=1 + } + /\[[[:space:]]*(FAILED|SKIPPED)[[:space:]]*\]/ { bad=1 } + /\[==========\].*ran\./ { + line=$0 + sub(/^.*\[==========\][[:space:]]*/, "", line) + split(line, fields, /[[:space:]]+/) + if (fields[1] != 1) bad=1 + completed++ + } + /\[[[:space:]]*PASSED[[:space:]]*\]/ { + line=$0 + sub(/^.*\[[[:space:]]*PASSED[[:space:]]*\][[:space:]]*/, "", line) + split(line, fields, /[[:space:]]+/) + if (fields[1] != 1) bad=1 + summaries++ + } + /Final Report ->/ { + if ($0 !~ /\[1 PASSED\]/ || $0 !~ /\[0 FAILED\]/ || $0 !~ /\[0 SKIPPED\]/) bad=1 + reports++ + } + END { + if (bad || runs != 1 || oks != 1 || + !((completed == 1 && summaries == 1 && reports == 0) || + (reports == 1 && completed == 0 && summaries == 0))) exit 1 + print "1 0 0" + } + ' "$1" +} + +# nhx_stop_native_capture +# Stop only the command and watcher owned by an interrupted native capture. +# Call before restoring camera services, only while the caller-owned native +# capture flag is set. Reuse bounded process termination and reap children. +# Log to stdout, return 1 if an owned process cannot be stopped safely. +nhx_stop_native_capture() { + for nsnc_pid in "${MANAGED_TIMEOUT_WATCHER_PID:-}" "${MANAGED_TIMEOUT_CMD_PID:-}"; do + case "$nsnc_pid" in + "") + continue + ;; + 0|1|*[!0-9]*) + log_fail "Invalid owned native capture PID: $nsnc_pid" + return 1 + ;; + esac + if ! kill_process "$nsnc_pid"; then + log_fail "Could not stop owned native capture process $nsnc_pid, camera service will not be restarted while capture may remain active" + return 1 + fi + wait "$nsnc_pid" 2>/dev/null || true + done + for nsnc_file in "${MANAGED_TIMEOUT_SLEEP_PID_FILE:-}" "${MANAGED_TIMEOUT_MARKER_FILE:-}"; do + [ -n "$nsnc_file" ] || continue + rm -f "$nsnc_file" + done + return 0 +} + +# nhx_restore_camera_service service log_directory +# Restore a service stopped by NHX and verify it is active. The caller owns +# the initial-state decision. Retain bounded systemctl output and log errors. +# Return 0 when active, 1 on start, timeout or post-start state failure. +nhx_restore_camera_service() { + nrcs_service="$1" + nrcs_directory="$2" + if ! run_with_managed_timeout 60 "$nrcs_directory" nhx-service-restore \ + systemctl start "$nrcs_service" >"$nrcs_directory/service-restore.log" 2>&1; then + log_file_with_label NHX-SERVICE "$nrcs_directory/service-restore.log" + log_fail "Could not restore $nrcs_service after NHX, inspect service-restore.log and the service journal" + return 1 + fi + if ! systemd_service_is_active "$nrcs_service"; then + log_fail "Restored $nrcs_service is not active after start, inspect the service journal" + return 1 + fi + return 0 +} + +# nhx_check_rpm_setup log_directory +# Read-only CentOS setup check using the configured camera-nhx package map. +# Required CAMX packages are checked locally. Repository listings use cached +# metadata only, never install, refresh, enable a repository or access a share. +# Log missing repositories/cache as provisioning diagnostics, not a requirement +# for an already installed offline NHX runtime. Return 0 when core packages +# exist, 2 when required tools/packages are absent, 1 on execution failure. +nhx_check_rpm_setup() { + ncrs_directory="$1" + command -v rpm >/dev/null 2>&1 || return 2 + ncrs_packages=$(pkg_lookup_package_set camera-nhx) || return 2 + ncrs_missing=0 + ncrs_failed=0 + ncrs_count=0 + for ncrs_package in $ncrs_packages; do + case "$ncrs_package" in + camx-*|libcamx-*) + ncrs_count=$((ncrs_count + 1)) + if LC_ALL=C run_with_managed_timeout 20 "$ncrs_directory" nhx-rpm-query \ + rpm -q "$ncrs_package" >"$ncrs_directory/package-$ncrs_package.log" 2>&1; then + : + else + ncrs_status=$? + if [ "$ncrs_status" -eq 1 ] && grep -Fq 'is not installed' "$ncrs_directory/package-$ncrs_package.log"; then + ncrs_missing=1 + log_warn "NHX package prerequisite missing: $ncrs_package. Provision the matching CAMX package set in the image" + else + ncrs_failed=1 + log_fail "NHX package query failed: $ncrs_package rc=$ncrs_status, inspect the retained package log" + fi + fi + log_file_with_label NHX-PACKAGE "$ncrs_directory/package-$ncrs_package.log" + ;; + esac + done + if command -v dnf >/dev/null 2>&1; then + if run_with_managed_timeout 20 "$ncrs_directory" nhx-repolist \ + dnf -C repolist >"$ncrs_directory/repositories.log" 2>&1; then + log_file_with_label NHX-REPOSITORIES "$ncrs_directory/repositories.log" + for ncrs_repo in epel qualcomm-linux-aarch64 qualcomm-linux-noarch clo-rpm-aarch64 clo-rpm-noarch; do + if awk -v repo="$ncrs_repo" '$1 == repo { found=1 } END { exit !found }' \ + "$ncrs_directory/repositories.log"; then + if ! run_with_managed_timeout 20 "$ncrs_directory" nhx-repository-packages \ + dnf -C repo-pkgs "$ncrs_repo" list \ + >"$ncrs_directory/repository-$ncrs_repo.log" 2>&1; then + log_warn "Cached package listing unavailable for $ncrs_repo. Refresh its metadata during image provisioning" + elif ! awk '$1 ~ /\.(aarch64|noarch)$/ { found=1 } END { exit !found }' \ + "$ncrs_directory/repository-$ncrs_repo.log"; then + log_warn "Cached repository $ncrs_repo exposes no aarch64/noarch packages. Check repository provisioning" + fi + log_file_with_label NHX-REPOSITORY "$ncrs_directory/repository-$ncrs_repo.log" 25 + else + log_warn "LRH setup repository $ncrs_repo is not enabled. Provision it if CAMX package resolution requires it" + fi + done + else + log_file_with_label NHX-REPOSITORIES "$ncrs_directory/repositories.log" + log_warn "DNF cached repository inventory is unavailable. Installed CAMX package checks remain authoritative for offline NHX" + fi + else + log_warn "dnf is unavailable, repository provisioning cannot be checked" + fi + [ "$ncrs_count" -gt 0 ] || return 2 + [ "$ncrs_failed" -eq 0 ] || return 1 + [ "$ncrs_missing" -eq 0 ] || return 2 + return 0 +} + +# nhx_pick_cksum_tool +# Print the first available checksum utility, or an empty line when absent. +# Uses image-provided tools only, preferring SHA-256 over MD5 and POSIX cksum. nhx_pick_cksum_tool() { if command -v sha256sum >/dev/null 2>&1; then echo "sha256sum" elif command -v md5sum >/dev/null 2>&1; then echo "md5sum" + elif command -v cksum >/dev/null 2>&1; then + echo "cksum" else echo "" fi @@ -751,6 +1216,19 @@ nhx_collect_new_dumps() { : >"$out" + if [ "${4:-wrapper}" = native ]; then + # Native captures need not carry the NHX filename tag. Restrict collection + # to image formats under the selected output root, excluding logs/metadata. + if ! find "$ddir" -type f \ + \( -name '*.yuv' -o -name '*.raw' -o -name '*.bin' \ + -o -name '*.jpg' -o -name '*.jpeg' \) \ + -newer "$marker" >"$out"; then + return 1 + fi + sort -o "$out" "$out" + return $? + fi + # Prefer NHX-tagged files (matches your observed names) find "$ddir" -maxdepth 1 -type f \ \( -name '*NHX*.yuv' -o -name '*NHX*.raw' -o -name '*NHX*.bin' \) \ @@ -762,6 +1240,11 @@ nhx_collect_new_dumps() { fi } +# nhx_validate_dumps_and_checksums dump_dir marker_file log_dir [unused] [unused] [mode] +# Validate fresh nonempty dumps and checksum diversity, retaining file lists +# and checksums in log_dir. The previous-run diff is advisory and optional. +# Optional mode is native for untagged image formats including JPEG, otherwise +# keep legacy NHX collection. Log to stdout, return 1 on validation failure. nhx_validate_dumps_and_checksums() { # $1 = dump_dir, $2 = marker_file, $3 = log_dir ddir="$1" @@ -779,11 +1262,14 @@ nhx_validate_dumps_and_checksums() { fi if [ -z "$tool" ]; then - log_skip "No checksum tool found (sha256sum/md5sum). Skipping dump checksum validation." + log_skip "No checksum tool found (sha256sum/md5sum/cksum). Skipping dump checksum validation." return 0 fi - nhx_collect_new_dumps "$ddir" "$marker" "$list" + if ! nhx_collect_new_dumps "$ddir" "$marker" "$list" "${6:-wrapper}"; then + log_fail "Could not collect fresh captures from $ddir" + return 1 + fi if [ ! -s "$list" ]; then log_fail "No NHX dump files found in $ddir (after marker). Dump/checksum validation not exercised." @@ -804,8 +1290,11 @@ nhx_validate_dumps_and_checksums() { return 1 fi - # Produce "HASH FILE" lines, same as sha256sum/md5sum - line="$($tool "$f" 2>/dev/null)" + # Retain native output, HASH FILE or CRC SIZE FILE for POSIX cksum. + if ! line="$("$tool" "$f" 2>/dev/null)"; then + log_fail "Checksum tool failed for: $f" + return 1 + fi if [ -z "$line" ]; then log_fail "Checksum tool failed for: $f" return 1 @@ -839,10 +1328,19 @@ nhx_validate_dumps_and_checksums() { # Optional drift check: compare against previous run (same log dir) if [ -f "$prevsum" ]; then - if ! diff -q "$prevsum" "$sumfile" >/dev/null 2>&1; then - log_warn "Dump checksums changed vs previous run ($prevsum). This may be expected; keeping as WARN." + if command -v diff >/dev/null 2>&1; then + if diff -q "$prevsum" "$sumfile" >/dev/null 2>&1; then + log_info "Dump checksums match previous run ($prevsum)" + else + nhx_diff_status=$? + if [ "$nhx_diff_status" -eq 1 ]; then + log_warn "Dump checksums changed vs previous run ($prevsum). This may be expected, keeping as WARN." + else + log_warn "Previous-run checksum comparison could not complete, diff rc=$nhx_diff_status. Current-run dump validation is unaffected" + fi + fi else - log_info "Dump checksums match previous run ($prevsum)" + log_info "Previous-run checksum comparison omitted because optional diff is unavailable. Current-run dump validation is unaffected" fi fi diff --git a/Runner/utils/lib_pkg_provider.sh b/Runner/utils/lib_pkg_provider.sh index 52ee8a7f3..bbc743456 100755 --- a/Runner/utils/lib_pkg_provider.sh +++ b/Runner/utils/lib_pkg_provider.sh @@ -1820,6 +1820,121 @@ pkg_ensure_qualcomm_rpm_repository() { return 0 } +# pkg_prepare_camx_rpm_repositories log_directory +# Prepare the CentOS 10 CAMX repositories only for explicit NHX overlay use. +# Reuse EPEL/Qualcomm setup, then preserve existing CLO definitions or create +# missing definitions in a separate managed file. Never overwrite or enable +# an administrator's disabled repository. Refresh metadata even when all IDs +# exist, since the shared update marker may predate CLO configuration. +# Return 0 when ready or not CentOS, 1 on configuration/repository failure. +# Writes the managed repository file and package metadata, logs to stdout and +# the supplied existing log directory. Repository queries are bounded. +pkg_prepare_camx_rpm_repositories() { + pccr_logs="$1" + pccr_os="$(pkg_detect_os_id)" + [ "$pccr_os" = centos ] || return 0 + pccr_version="$(pkg_os_release_value VERSION_ID)" + pccr_arch="$(uname -m)" + if [ "${pccr_version%%.*}" != 10 ] || [ "$pccr_arch" != aarch64 ]; then + pkg_log_fail "CAMX CLO repository setup supports CentOS 10 aarch64, detected version=$pccr_version architecture=$pccr_arch" + return 1 + fi + pkg_prepare_qualcomm_rpm_overlay_repositories || return 1 + pccr_tool="$(pkg_rpm_tool)" + [ -n "$pccr_tool" ] || return 1 + pccr_repo_log="$pccr_logs/camx-enabled-repositories.log" + if ! run_with_managed_timeout 60 "$pccr_logs" camx-repolist \ + "$pccr_tool" -C repolist >"$pccr_repo_log" 2>&1; then + cat "$pccr_repo_log" + pkg_log_fail "Could not inspect enabled CAMX repositories, no CLO configuration changed. Inspect $pccr_repo_log" + return 1 + fi + case "$PKG_QCOM_RPM_REPO_FILE" in + */*) + pccr_directory=${PKG_QCOM_RPM_REPO_FILE%/*} + ;; + *) + pccr_directory=. + ;; + esac + pccr_file="$pccr_directory/qcom-testkit-camx-clo.repo" + pccr_missing="" + for pccr_id in clo-rpm-aarch64 clo-rpm-noarch; do + if awk '{print $1}' "$pccr_repo_log" | grep -Fxq "$pccr_id"; then + pkg_log_info "Preserving enabled CAMX repository $pccr_id" + continue + fi + for pccr_existing in "$pccr_directory"/*.repo; do + [ -f "$pccr_existing" ] || continue + if grep -Eq "^[[:space:]]*\\[$pccr_id\\][[:space:]]*([#;].*)?$" "$pccr_existing"; then + pkg_log_fail "CAMX repository $pccr_id is defined but not enabled or readable by DNF. Correct $pccr_existing before retrying, no existing configuration was overwritten" + return 1 + fi + done + pccr_missing="$pccr_missing $pccr_id" + done + if [ -n "$pccr_missing" ]; then + if [ -e "$pccr_file" ] || [ -L "$pccr_file" ]; then + pkg_log_fail "Refusing to overwrite existing incomplete CAMX repository file $pccr_file" + return 1 + fi + if ! pkg_can_install; then + pkg_log_fail "CAMX repository preparation requires root and enabled package recovery" + return 1 + fi + pccr_temp="$(mktemp "${TMPDIR:-/tmp}/camx-clo-repositories.XXXXXX")" || return 1 + for pccr_id in $pccr_missing; do + pccr_type=${pccr_id#clo-rpm-} + if ! printf '%s\n' \ + "[$pccr_id]" \ + "name=Qualcomm Linux CLO RPM Repository - CentOS Stream 10 ($pccr_type)" \ + "baseurl=https://artifacts.codelinaro.org/artifactory/qli-rpm/centos/10/os/$pccr_type/" \ + 'enabled=1' \ + 'gpgcheck=0' \ + 'priority=10' \ + '' >>"$pccr_temp"; then + rm -f "$pccr_temp" + return 1 + fi + done + if ! install -m 0644 "$pccr_temp" "$pccr_file"; then + rm -f "$pccr_temp" + pkg_log_fail "Could not create CAMX CLO repository file $pccr_file" + return 1 + fi + rm -f "$pccr_temp" + pkg_log_info "Created CAMX CLO repository definitions in $pccr_file" + fi + if ! run_with_managed_timeout 60 "$pccr_logs" camx-repolist \ + "$pccr_tool" -C repolist >"$pccr_repo_log" 2>&1; then + cat "$pccr_repo_log" + pkg_log_fail "Could not verify enabled CAMX repositories, inspect $pccr_repo_log" + return 1 + fi + for pccr_id in clo-rpm-aarch64 clo-rpm-noarch; do + if ! awk '{print $1}' "$pccr_repo_log" | grep -Fxq "$pccr_id"; then + pkg_log_fail "CAMX CLO repository is still unavailable: $pccr_id, inspect $pccr_directory" + return 1 + fi + done + rm -f "$PKG_RPM_UPDATED_MARK" || return 1 + pkg_rpm_update || return 1 + for pccr_id in clo-rpm-aarch64 clo-rpm-noarch; do + pccr_list_log="$pccr_logs/camx-$pccr_id-packages.log" + if ! run_with_managed_timeout 120 "$pccr_logs" "camx-$pccr_id-list" \ + "$pccr_tool" --refresh --disablerepo='*' --enablerepo="$pccr_id" \ + --setopt="$pccr_id.skip_if_unavailable=False" \ + repo-pkgs "$pccr_id" list >"$pccr_list_log" 2>&1; then + cat "$pccr_list_log" + pkg_log_fail "CAMX repository package listing failed: $pccr_id, inspect $pccr_list_log" + return 1 + fi + pkg_log_info "CAMX repository package listing retained in $pccr_list_log" + done + pkg_log_pass "CAMX RPM repositories are ready, EPEL, Qualcomm and both CLO repositories verified" + return 0 +} + # Refresh rpm metadata. pkg_rpm_update() { rpm_tool="$(pkg_rpm_tool || true)" diff --git a/Runner/utils/lib_system.sh b/Runner/utils/lib_system.sh index 36b5f14bf..0be3392c5 100755 --- a/Runner/utils/lib_system.sh +++ b/Runner/utils/lib_system.sh @@ -225,6 +225,38 @@ efi_mount_exists() { ' /proc/mounts 2>/dev/null } +# efi_ensure_mounted log_file +# Mount efivarfs on an EFI boot when absent, preserving an existing ro/rw +# mount. A mount created for overlay preparation is intentionally left mounted. +# Do not call from read-only setup validation. Return 0 when mounted as +# efivarfs, 2 when EFI runtime is absent, 1 on mount/type/verification failure. +efi_ensure_mounted() { + eem_log="$1" + if [ ! -d "$EFIVARFS_PATH" ]; then + log_warn "EFI runtime directory is unavailable: $EFIVARFS_PATH" + return 2 + fi + if ! efi_mount_exists; then + log_info "Mounting efivarfs at $EFIVARFS_PATH for overlay preparation" + if ! mount -t efivarfs none "$EFIVARFS_PATH" >"$eem_log" 2>&1; then + system_log_file_excerpt efivarfs-mount "$eem_log" 40 + log_fail "Could not mount efivarfs, check EFI runtime support and mount permissions" + return 1 + fi + fi + if command -v partition_get_mount_fstype >/dev/null 2>&1; then + eem_type=$(partition_get_mount_fstype "$EFIVARFS_PATH") + else + eem_type=$(awk -v p="$EFIVARFS_PATH" '$2 == p { print $3; exit }' /proc/mounts) + fi + if [ "$eem_type" != efivarfs ]; then + log_fail "Expected efivarfs at $EFIVARFS_PATH, detected ${eem_type:-no mounted filesystem}" + return 1 + fi + log_info "EFI runtime filesystem is ready at $EFIVARFS_PATH" + return 0 +} + # Return efivarfs mount options. # Prefer partition_get_mount_options from functestlib.sh when available. efi_mount_options() { @@ -348,6 +380,8 @@ efi_variable_list_contains() { # $2 - file that receives efivar list output # Diagnostic output is written to stderr so command-substitution callers only # receive the variable name. +# Return 0 for exactly one match, 2 for absence after a successful query, +# or 1 for command failure, invalid arguments or ambiguous matches. efi_find_variable_by_name() { efvbn_name="$1" efvbn_log_file="$2" @@ -376,6 +410,10 @@ efi_find_variable_by_name() { ' "$efvbn_log_file")" efvbn_count="$(printf '%s\n' "$efvbn_matches" | awk 'NF { count++ } END { print count + 0 }')" + if [ "$efvbn_count" -eq 0 ]; then + printf '%s\n' "EFI variable $efvbn_name is absent" >&2 + return 2 + fi if [ "$efvbn_count" -ne 1 ]; then printf '%s\n' "Expected one EFI variable named $efvbn_name, found $efvbn_count" >&2 return 1