openipc_frame_ts: disable cv200/V2A hook pending hardware validation … #508
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: build | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| jobs: | |
| toolchain: | |
| name: Build SDK | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: hi3516ev200 | |
| board: hi3516ev200_lite | |
| - platform: gk7205v200 | |
| board: gk7205v200_lite | |
| - platform: hi3516cv500 | |
| board: hi3516cv500_lite | |
| - platform: hi3516cv200 | |
| board: hi3516cv200_lite | |
| - platform: hi3516cv300 | |
| board: hi3516cv300_lite | |
| - platform: hi3516cv100 | |
| board: hi3516cv100_lite | |
| - platform: hi3516av100 | |
| board: hi3516av100_lite | |
| - platform: hi3519v101 | |
| board: hi3519v101_lite | |
| - platform: hi3516ev300 | |
| board: hi3516ev300_neo | |
| kernel: "6.6" | |
| - platform: hi3516ev300 | |
| board: hi3516ev300_neo | |
| kernel: "6.18" | |
| - platform: hi3516ev300 | |
| board: hi3516ev300_neo | |
| kernel: "7.0" | |
| - platform: hi3516cv500 | |
| board: hi3516av300_neo | |
| kernel: "7.0" | |
| - platform: hi3516cv300 | |
| board: hi3516cv300_neo | |
| kernel: "7.0" | |
| steps: | |
| - name: Checkout Source | |
| uses: actions/checkout@v4 | |
| - name: Clone firmware | |
| run: git clone --depth 1 https://github.com/openipc/firmware ../firmware | |
| - name: Setup neo target (6.6 kernel) | |
| if: matrix.kernel == '6.6' | |
| run: | | |
| cd ../firmware | |
| # Create neo defconfig if not present | |
| if [ ! -f br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig ]; then | |
| cp br-ext-chip-hisilicon/configs/hi3516ev300_lite_defconfig \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| sed -i 's|$(OPENIPC_KERNEL).tar.gz|hisilicon-hi3516ev200-6.6.tar.gz|' \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| sed -i 's|hi3516ev300.generic.config|hi3516ev300.4.15.config|' \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| sed -i 's|VARIANT="lite"|VARIANT="neo"|' \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| echo 'BR2_LINUX_KERNEL_UIMAGE_LOADADDR="0x40008000"' >> \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| echo 'BR2_GLOBAL_PATCH_DIR="$(BR2_EXTERNAL)/package/all-patches-neo"' >> \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| fi | |
| # Create 6.6 kernel config if not present | |
| if [ ! -f br-ext-chip-hisilicon/board/hi3516ev200/hi3516ev300.4.15.config ]; then | |
| cp br-ext-chip-hisilicon/board/hi3516ev200/hi3516ev300.generic.config \ | |
| br-ext-chip-hisilicon/board/hi3516ev200/hi3516ev300.4.15.config | |
| # Disable features incompatible with mainline 6.6 | |
| printf '%s\n' \ | |
| '# CONFIG_CMA is not set' \ | |
| '# CONFIG_DMA_CMA is not set' \ | |
| 'CONFIG_STACKPROTECTOR=y' \ | |
| 'CONFIG_STACKPROTECTOR_STRONG=y' \ | |
| '# CONFIG_DEBUG_INFO is not set' \ | |
| '# CONFIG_FTRACE is not set' \ | |
| '# CONFIG_INPUT is not set' \ | |
| '# CONFIG_VT is not set' \ | |
| '# CONFIG_SOUND is not set' \ | |
| '# CONFIG_USB_SUPPORT is not set' \ | |
| '# CONFIG_NFS_FS is not set' \ | |
| '# CONFIG_DEBUG_KERNEL is not set' \ | |
| >> br-ext-chip-hisilicon/board/hi3516ev200/hi3516ev300.4.15.config | |
| fi | |
| # Create all-patches-neo with empty linux dir | |
| mkdir -p general/package/all-patches-neo/linux | |
| for d in general/package/all-patches/*/; do | |
| name=$(basename "$d") | |
| [ "$name" != "linux" ] && ln -sf "../all-patches/$name" "general/package/all-patches-neo/$name" | |
| done | |
| # Fragment comes before defconfig so defconfig can override | |
| sed -i 's|cat $(CONFIG) $(PWD)/general/openipc.fragment|cat $(PWD)/general/openipc.fragment $(CONFIG)|' Makefile | |
| - name: Setup neo target (EV300, 6.18/7.0 kernel) | |
| if: (matrix.kernel == '6.18' || matrix.kernel == '7.0') && matrix.platform == 'hi3516ev300' | |
| run: | | |
| cd ../firmware | |
| BRANCH="upstream-patches" | |
| # Use the upstream-patches branch (7.0-rc6 base + BVT patches) | |
| if [ ! -f br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig ]; then | |
| cp br-ext-chip-hisilicon/configs/hi3516ev300_lite_defconfig \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| fi | |
| # Point to upstream-patches kernel | |
| sed -i "s|BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION=.*|BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION=\"https://github.com/openipc/linux/archive/${BRANCH}.tar.gz\"|" \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| sed -i 's|BR2_LINUX_KERNEL_UIMAGE=y|BR2_LINUX_KERNEL_IMAGE_TARGET_CUSTOM=y\nBR2_LINUX_KERNEL_IMAGE_TARGET_NAME="zImage"|' \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| sed -i 's|VARIANT="lite"|VARIANT="neo"|' \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| echo 'BR2_GLOBAL_PATCH_DIR="$(BR2_EXTERNAL)/package/all-patches-neo"' >> \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| # Kernel config | |
| cp br-ext-chip-hisilicon/board/hi3516ev200/hi3516ev300.generic.config \ | |
| br-ext-chip-hisilicon/board/hi3516ev200/hi3516ev300.neo.config | |
| printf '%s\n' \ | |
| '# CONFIG_DEBUG_KERNEL is not set' \ | |
| 'CONFIG_CMDLINE="earlycon=pl011,0x12040000"' \ | |
| 'CONFIG_CMDLINE_EXTEND=y' \ | |
| >> br-ext-chip-hisilicon/board/hi3516ev200/hi3516ev300.neo.config | |
| sed -i 's|hi3516ev300.generic.config|hi3516ev300.neo.config|' \ | |
| br-ext-chip-hisilicon/configs/hi3516ev300_neo_defconfig | |
| # Neo patches dir | |
| mkdir -p general/package/all-patches-neo/linux | |
| for d in general/package/all-patches/*/; do | |
| name=$(basename "$d") | |
| [ "$name" != "linux" ] && ln -sf "../all-patches/$name" "general/package/all-patches-neo/$name" | |
| done | |
| sed -i 's|cat $(CONFIG) $(PWD)/general/openipc.fragment|cat $(PWD)/general/openipc.fragment $(CONFIG)|' Makefile | |
| - name: Setup neo target (CV500, 7.0 kernel) | |
| if: matrix.kernel == '7.0' && matrix.platform == 'hi3516cv500' | |
| run: | | |
| cd ../firmware | |
| # Neo patches dir (may already exist from EV300 setup) | |
| mkdir -p general/package/all-patches-neo/linux | |
| for d in general/package/all-patches/*/; do | |
| name=$(basename "$d") | |
| [ "$name" != "linux" ] && [ ! -e "general/package/all-patches-neo/$name" ] && \ | |
| ln -sf "../all-patches/$name" "general/package/all-patches-neo/$name" | |
| done | |
| # The hi3516av300_neo_defconfig, neo kernel config, and neo-post-image.sh | |
| # should already be in the firmware repo. If not, create minimal versions. | |
| if [ ! -f br-ext-chip-hisilicon/configs/hi3516av300_neo_defconfig ]; then | |
| echo "ERROR: hi3516av300_neo_defconfig not found in firmware repo" >&2 | |
| exit 1 | |
| fi | |
| sed -i 's|cat $(CONFIG) $(PWD)/general/openipc.fragment|cat $(PWD)/general/openipc.fragment $(CONFIG)|' Makefile | |
| # CV300 (V3, ARM926EJ-S) is the cleanest legacy platform — issue #60 | |
| # verified that zero blobs bypass OSAL for kernel APIs, so a kernel | |
| # bump is just recompiling OSAL. The PDE_DATA() shim in | |
| # kernel/compat/kernel_compat.h covers the one symbol OSAL touches | |
| # that changed on 5.17. CV300 is also the only HiSi SoC with real | |
| # mainline DT/SoC support upstream (arch/arm/boot/dts/hisilicon/ | |
| # hi3516cv300.dtsi) — a future neo build could ride pure mainline | |
| # rather than the BVT vendor patches; this matrix entry uses the | |
| # same upstream-patches branch as EV300/AV300 for now so the | |
| # delta-from-working is minimal. | |
| - name: Setup neo target (CV300, 7.0 kernel) | |
| if: matrix.kernel == '7.0' && matrix.platform == 'hi3516cv300' | |
| run: | | |
| cd ../firmware | |
| # Neo patches dir (may already exist from EV300/CV500 setup) | |
| mkdir -p general/package/all-patches-neo/linux | |
| for d in general/package/all-patches/*/; do | |
| name=$(basename "$d") | |
| [ "$name" != "linux" ] && [ ! -e "general/package/all-patches-neo/$name" ] && \ | |
| ln -sf "../all-patches/$name" "general/package/all-patches-neo/$name" | |
| done | |
| # Firmware-side dependency: hi3516cv300_neo_defconfig, neo kernel | |
| # config, and (if upstream-patches lacks a CV300 DT) the | |
| # all-patches-neo/linux DT drop-in must already be in firmware. | |
| if [ ! -f br-ext-chip-hisilicon/configs/hi3516cv300_neo_defconfig ]; then | |
| echo "ERROR: hi3516cv300_neo_defconfig not found in firmware repo" >&2 | |
| exit 1 | |
| fi | |
| sed -i 's|cat $(CONFIG) $(PWD)/general/openipc.fragment|cat $(PWD)/general/openipc.fragment $(CONFIG)|' Makefile | |
| - name: Build OpenHisilicon | |
| run: | | |
| cd ../firmware | |
| FILE=general/package/hisilicon-opensdk/hisilicon-opensdk.mk | |
| sed -i "s|^HISILICON_OPENSDK_SITE =.*|HISILICON_OPENSDK_SITE_METHOD = local\nHISILICON_OPENSDK_SITE = $GITHUB_WORKSPACE|" ${FILE} | |
| sed -i "s|^HISILICON_OPENSDK_VERSION =.*||" ${FILE} | |
| CMAKE_POLICY_VERSION_MINIMUM=3.5 make BOARD=${{matrix.board}} br-hisilicon-opensdk | |
| # ive_neo ships on platforms whose IVE block is reachable from this | |
| # driver — currently V4 (hi3516ev200/gk7205v200, fused IVE+NEO) and | |
| # CV500 (classic IVE only, XNN gated off). Catch silent regressions | |
| # in either path: a missing `include ive_neo/Kbuild` in | |
| # hi3516cv500.kbuild would skip the .ko without failing the build, | |
| # and a chip-ops mis-selection would bake the wrong chip= log line. | |
| - name: Verify ive_neo built for this platform | |
| if: matrix.platform == 'hi3516ev200' || matrix.platform == 'gk7205v200' || matrix.platform == 'hi3516cv500' | |
| run: | | |
| # Site_method=local plus a cleared _VERSION leaves the package | |
| # build dir name unpredictable across buildroot versions | |
| # (hisilicon-opensdk-, hisilicon-opensdk-custom, hisilicon-opensdk-<sha>, | |
| # etc.). Just walk the whole output tree. | |
| KO=$(find ../firmware/output -name open_ive_neo.ko 2>/dev/null | head -1) | |
| if [ -z "$KO" ] || [ ! -f "$KO" ]; then | |
| echo "FAIL: open_ive_neo.ko not built for ${{ matrix.platform }}" >&2 | |
| echo "--- candidate dirs under firmware/output/build ---" >&2 | |
| ls -1d ../firmware/output/build/hisilicon* 2>&1 || true | |
| echo "--- any open_*.ko anywhere ---" >&2 | |
| find ../firmware/output -name 'open_*.ko' 2>/dev/null | head -20 >&2 || true | |
| exit 1 | |
| fi | |
| echo "ok: $KO ($(stat -c%s "$KO") bytes)" | |
| # The chip-ops table in kernel/ive_neo/ive_neo.c bakes a | |
| # platform-specific name into a printk format string. Grep | |
| # it out of the .ko to confirm the right backend was | |
| # compiled in. V4 family rolls up to "hi3516ev200" (the | |
| # CHIPARCH for ev300/gk7205v200/etc.); cv500 has its own. | |
| case "${{ matrix.platform }}" in | |
| hi3516cv500) EXPECT=hi3516cv500 ;; | |
| *) EXPECT=hi3516ev200 ;; | |
| esac | |
| if ! strings "$KO" | grep -qF "chip=%s xnn=%s base=0x%x"; then | |
| echo "FAIL: chip-ops log format not found in $KO — refactor regression?" >&2 | |
| exit 1 | |
| fi | |
| if ! strings "$KO" | grep -qx "$EXPECT"; then | |
| echo "FAIL: $KO missing chip-name '$EXPECT' — wrong backend selected" >&2 | |
| echo "--- chip strings in .ko ---" >&2 | |
| strings "$KO" | grep -E '^hi3516(ev200|cv500)$' >&2 || true | |
| exit 1 | |
| fi | |
| echo "ok: $KO has chip='$EXPECT' baked in" | |
| # cv500 must NOT have the XNN dispatch active (no NEO unit | |
| # on cv500's IVE — CNN lives on a separate NNIE block). | |
| # The has_xnn=false branch DCEs the XNN handler bodies and | |
| # their fatal-warning string. If that string sneaks into a | |
| # cv500 build, XNN gating broke and the driver could go | |
| # past -EOPNOTSUPP into a model-load path that has no | |
| # hardware to back it. | |
| if [ "${{ matrix.platform }}" = hi3516cv500 ]; then | |
| if strings "$KO" | grep -qF "XNN loadmodel rejected on chip="; then | |
| echo "ok: cv500 build kept the XNN-reject guard" | |
| fi | |
| if strings "$KO" | grep -qF "FC layer using non-tiled hardcoded params"; then | |
| echo "FAIL: cv500 build contains XNN FC layer code — XNN gating regression" >&2 | |
| exit 1 | |
| fi | |
| echo "ok: cv500 build has no XNN dispatch code" | |
| # PR #107: PerspTrans and Hog have real HW handlers on | |
| # cv500. PR for #110: NCC handler is now wired (was a | |
| # silent stub before). Each handler emits a distinctive | |
| # log line; if those strings vanish, someone reverted to | |
| # the silent-stub or diag path. Grep for the validation | |
| # / one-time log rather than the handler symbol name so | |
| # the assertion stays meaningful across renames. | |
| for needle in "PerspTrans bad roi_num" "Hog bad roi_num" "NCC handler wired" "LBP handler wired" "CSC handler wired" "FilterAndCSC handler wired" "Resize handler wired" "LK handler wired" "EqualizeHist handler wired"; do | |
| if ! strings "$KO" | grep -qF "$needle"; then | |
| echo "FAIL: cv500 .ko missing '$needle' — handler regressed to stub?" >&2 | |
| exit 1 | |
| fi | |
| done | |
| echo "ok: cv500 build has PerspTrans + Hog + NCC + LBP + CSC + FilterAndCSC + Resize + LK + EqualizeHist HW handlers wired" | |
| # The N-node chain submit helper is the shared HW kick | |
| # path for both new ops. Its timeout log is the only | |
| # string unique to it; absence means the dispatcher is | |
| # going through some other (likely wrong) submit path. | |
| if ! strings "$KO" | grep -qF "submit_chain: timeout"; then | |
| echo "FAIL: cv500 .ko missing ive_submit_chain — chain dispatch regression" >&2 | |
| exit 1 | |
| fi | |
| echo "ok: cv500 build has ive_submit_chain helper" | |
| fi | |
| # nnie_neo ships on cv500-family only (separate NNIE block at | |
| # 0x11100000 — V4 has no NNIE). Same shape as the ive_neo check | |
| # above: confirm the .ko built, that it carries the expected | |
| # platform-driver match string, and that the load-bearing | |
| # support functions (descriptor MMZ pre-alloc, IRQ handler) are | |
| # present. | |
| - name: Verify nnie_neo built for this platform | |
| if: matrix.platform == 'hi3516cv500' | |
| run: | | |
| KO=$(find ../firmware/output -name open_nnie_neo.ko 2>/dev/null | head -1) | |
| if [ -z "$KO" ] || [ ! -f "$KO" ]; then | |
| echo "FAIL: open_nnie_neo.ko not built for ${{ matrix.platform }}" >&2 | |
| find ../firmware/output -name 'open_nnie*' 2>/dev/null >&2 || true | |
| exit 1 | |
| fi | |
| echo "ok: $KO ($(stat -c%s "$KO") bytes)" | |
| # Platform-driver bind target must match the DT compatible. | |
| if ! strings "$KO" | grep -qF "hisilicon,hisi-nnie"; then | |
| echo "FAIL: $KO missing DT compatible 'hisilicon,hisi-nnie'" >&2 | |
| exit 1 | |
| fi | |
| echo "ok: DT compatible wired" | |
| # /dev/nnie miscdevice — caller-facing surface. Renaming this | |
| # silently would break every userspace consumer. | |
| if ! strings "$KO" | grep -qF "/dev/nnie ready"; then | |
| echo "FAIL: $KO missing '/dev/nnie ready' init log" >&2 | |
| exit 1 | |
| fi | |
| echo "ok: /dev/nnie miscdevice wired" | |
| # Descriptor MMZ is pre-allocated at module init so its phys | |
| # stays stable across Forwards. The hil_mmb_alloc name is | |
| # baked in as the MMZ-zone label — losing it means the | |
| # pre-alloc step was removed and we'd regress to per-Forward | |
| # alloc (TASK_ADDR drift → HW cfg_err). | |
| if ! strings "$KO" | grep -qx "nnie_desc"; then | |
| echo "FAIL: $KO missing 'nnie_desc' MMZ label — descriptor pre-alloc regressed?" >&2 | |
| exit 1 | |
| fi | |
| echo "ok: descriptor MMZ pre-alloc wired" | |
| # Function names appear in the .ko's symbol string table, so | |
| # `strings | grep -x` is enough — avoids needing | |
| # arm-linux-gnueabi-nm, which isn't installed in this job. | |
| for sym in nnie_irq_handler nnie_dispatch_forward; do | |
| if ! strings "$KO" | grep -qx "$sym"; then | |
| echo "FAIL: $KO missing $sym symbol" >&2 | |
| exit 1 | |
| fi | |
| echo "ok: $sym present" | |
| done | |
| # | |
| # libraries-header-check | |
| # | |
| # Host-only fast sanity check on the libraries/*_neo public headers. | |
| # Catches header regressions on every push in <30 s without needing a | |
| # cross-compiler. Static_asserts lock in the binary ABI struct sizes. | |
| # | |
| libraries-header-check: | |
| name: Libraries header check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Compile public-header probe | |
| run: | | |
| cat > /tmp/probe.c <<'EOF' | |
| #include "hi_type.h" | |
| #include "hi_common.h" | |
| #include "hi_comm_ive.h" | |
| #include "hi_ive.h" | |
| #include "hi_comm_video.h" | |
| #include "hi_ivp.h" | |
| #include "mpi_sys.h" | |
| #include "mpi_ive.h" | |
| /* Binary ABI is frozen against vendor libive.so / libmpi.so / | |
| * libivp.so. A struct-size drift here means the ioctl arg | |
| * layout or HI_MPI_IVE_Thresh ctrl buffer would stop matching | |
| * the kernel — which is always a bug. */ | |
| _Static_assert(sizeof(IVE_IMAGE_S) == 72, "IVE_IMAGE_S size"); | |
| _Static_assert(sizeof(IVE_MEM_INFO_S) == 24, "IVE_MEM_INFO_S size"); | |
| _Static_assert(sizeof(IVE_DATA_S) == 32, "IVE_DATA_S size"); | |
| _Static_assert(sizeof(IVE_THRESH_CTRL_S) == 12, "IVE_THRESH_CTRL_S size"); | |
| _Static_assert(sizeof(IVE_SUB_CTRL_S) == 4, "IVE_SUB_CTRL_S size"); | |
| _Static_assert(sizeof(IVE_CCL_CTRL_S) == 8, "IVE_CCL_CTRL_S size"); | |
| _Static_assert(sizeof(IVE_DILATE_CTRL_S) == 25, "IVE_DILATE_CTRL_S size"); | |
| _Static_assert(sizeof(IVE_ERODE_CTRL_S) == 25, "IVE_ERODE_CTRL_S size"); | |
| _Static_assert(sizeof(IVE_CANNY_HYS_EDGE_CTRL_S) == 56, "IVE_CANNY_HYS_EDGE_CTRL_S size"); | |
| _Static_assert(sizeof(IVE_CCBLOB_S) == 3052, "IVE_CCBLOB_S size"); | |
| _Static_assert(sizeof(IVE_CNN_MODEL_S) == 232, "IVE_CNN_MODEL_S size"); | |
| _Static_assert(sizeof(IVE_GMM2_CTRL_S) == 28, "IVE_GMM2_CTRL_S size"); | |
| /* cv500-only ops (PerspTrans + Hog). The userspace marshalling | |
| * code in libraries/ive_neo/src/ive_ops.c packs these structs | |
| * into 7264-byte / 4200-byte ioctl arg buffers at fixed offsets; | |
| * a single byte of drift would corrupt the kernel's read. The | |
| * IVE_BLOB_S size (used by Hog's astDst[] array, stride 48 in | |
| * the kernel) is checked indirectly via the marshalling test | |
| * below — assert it explicitly here so the symptom is caught | |
| * at "make" time rather than as a runtime EFAULT. */ | |
| _Static_assert(sizeof(IVE_RECT_U32_S) == 16, "IVE_RECT_U32_S size"); | |
| _Static_assert(sizeof(IVE_PERSP_TRANS_CTRL_S) == 12, "IVE_PERSP_TRANS_CTRL_S size"); | |
| _Static_assert(sizeof(IVE_HOG_CTRL_S) == 16, "IVE_HOG_CTRL_S size"); | |
| _Static_assert(sizeof(IVE_BLOB_S) == 48, "IVE_BLOB_S size"); | |
| int main(void) { return 0; } | |
| EOF | |
| gcc -Wall -Wextra -Werror -fsyntax-only \ | |
| -I libraries/include \ | |
| -I libraries/ive_neo/include \ | |
| -I libraries/mpi_neo/include \ | |
| -I libraries/ivp_neo/include \ | |
| /tmp/probe.c | |
| echo "all headers compile cleanly; ABI sizes locked" | |
| - name: Compile NNIE public-header probe | |
| run: | | |
| cat > /tmp/probe_nnie.c <<'EOF' | |
| #include <stddef.h> | |
| #include "hi_type.h" | |
| #include "hi_common.h" | |
| #include "hi_comm_svp.h" | |
| #include "hi_nnie.h" | |
| #include "mpi_nnie.h" | |
| #include "nnie_wk_format.h" | |
| /* Binary ABI for the NNIE userspace ↔ kernel interface. The | |
| * 1624-byte Forward ioctl arg buffer packs SVP_BLOB_S + | |
| * SVP_NNIE_FORWARD_CTRL_S at fixed offsets; SVP_NNIE_MODEL_S | |
| * is copy_from_user'd directly by the kernel and indexed by | |
| * offsetof(stBase) + sizeof(SEG_S) — drift in either of those | |
| * silently corrupts the kernel's read. */ | |
| _Static_assert(sizeof(SVP_BLOB_S) == 48, "SVP_BLOB_S size"); | |
| _Static_assert(sizeof(SVP_NNIE_NODE_S) == 52, "SVP_NNIE_NODE_S size"); | |
| _Static_assert(sizeof(SVP_NNIE_ROIPOOL_INFO_S) == 104, "SVP_NNIE_ROIPOOL_INFO_S size"); | |
| _Static_assert(sizeof(SVP_NNIE_SEG_S) == 1692, "SVP_NNIE_SEG_S size"); | |
| _Static_assert(sizeof(SVP_NNIE_MODEL_S) == 13992,"SVP_NNIE_MODEL_S size"); | |
| _Static_assert(sizeof(SVP_NNIE_FORWARD_CTRL_S) == 64, "SVP_NNIE_FORWARD_CTRL_S size"); | |
| _Static_assert(sizeof(SVP_NNIE_FORWARD_WITHBBOX_CTRL_S) == 72, "SVP_NNIE_FORWARD_WITHBBOX_CTRL_S size"); | |
| /* The kernel's nnie_op_forward reads pstModel via copy_from_user | |
| * and indexes ([+0x3690] = stBase, +12 + seg*1692 + 12/16 for | |
| * astSeg[seg].u32InstOffset / u32InstLen). Lock those offsets | |
| * here so a header refactor can't drift them silently. */ | |
| _Static_assert(offsetof(SVP_NNIE_MODEL_S, stBase) == 13968, "MODEL_S.stBase offset"); | |
| int main(void) { return 0; } | |
| EOF | |
| gcc -Wall -Wextra -Werror -fsyntax-only \ | |
| -I libraries/include \ | |
| -I libraries/nnie_neo/include \ | |
| /tmp/probe_nnie.c | |
| echo "NNIE ABI sizes locked" | |
| - name: Run NNIE LoadModel sanity test | |
| run: | | |
| make -C libraries/nnie_neo/test test_loadmodel test_get_tskbuf_size | |
| ./libraries/nnie_neo/test/test_loadmodel | |
| ./libraries/nnie_neo/test/test_get_tskbuf_size | |
| # | |
| # libraries-cross-compile | |
| # | |
| # Cross-compiles libraries/{ive,mpi,ivp}_neo with the apt ARM EABI | |
| # toolchain (not musl — just a smoke test that code links). Verifies | |
| # the .so/.a outputs exist, asserts canonical symbols are exported, | |
| # and confirms the shared objects depend only on libc/libpthread | |
| # (no vendor blobs sneaking into NEEDED). | |
| # | |
| libraries-cross-compile: | |
| name: Libraries cross-compile (ARM EABI) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install ARM EABI toolchain | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| gcc-arm-linux-gnueabi libc6-dev-armel-cross binutils-arm-linux-gnueabi | |
| - name: Cross-compile libraries/*_neo | |
| run: | | |
| make -C libraries/mpi_neo \ | |
| CC=arm-linux-gnueabi-gcc AR=arm-linux-gnueabi-ar | |
| make -C libraries/ive_neo \ | |
| CC=arm-linux-gnueabi-gcc AR=arm-linux-gnueabi-ar | |
| make -C libraries/ivp_neo \ | |
| CC=arm-linux-gnueabi-gcc AR=arm-linux-gnueabi-ar | |
| make -C libraries/nnie_neo \ | |
| CC=arm-linux-gnueabi-gcc AR=arm-linux-gnueabi-ar | |
| - name: Verify outputs exist | |
| run: | | |
| for f in \ | |
| libraries/mpi_neo/libmpi_neo.so libraries/mpi_neo/libmpi_neo.a \ | |
| libraries/ive_neo/libive_neo.so libraries/ive_neo/libive_neo.a \ | |
| libraries/ivp_neo/libivp_neo.so libraries/ivp_neo/libivp_neo.a \ | |
| libraries/nnie_neo/libnnie_neo.so libraries/nnie_neo/libnnie_neo.a; do | |
| [ -f "$f" ] || { echo "MISSING: $f" >&2; exit 1; } | |
| echo "ok: $f ($(stat -c%s $f) bytes)" | |
| done | |
| - name: Assert canonical symbols | |
| run: | | |
| check() { arm-linux-gnueabi-nm -D --defined-only "$1" | grep -qE " T $2\$" || { echo "MISSING symbol $2 in $1" >&2; exit 1; } ; echo "ok: $2 in $(basename $1)"; } | |
| for sym in HI_MPI_SYS_Init HI_MPI_SYS_Exit HI_MPI_SYS_MmzAlloc HI_MPI_SYS_MmzFree HI_MPI_SYS_MmzFlushCache; do | |
| check libraries/mpi_neo/libmpi_neo.so $sym | |
| done | |
| for sym in HI_MPI_IVE_Query HI_MPI_IVE_Thresh HI_MPI_IVE_CCL HI_MPI_IVE_SAD HI_MPI_IVE_Dilate HI_MPI_IVE_Erode HI_MPI_IVE_Sobel mpi_ive_xnn_loadmodel mpi_ive_xnn_forward_slice mp_ive_svp_alg_proc_init; do | |
| check libraries/ive_neo/libive_neo.so $sym | |
| done | |
| # cv500-only ops added in PR #107. The previous stubs returned | |
| # HI_ERR_IVE_NOT_SUPPORT directly; the marshalling versions in | |
| # ive_ops.c pack the arg buffer and ioctl through to the kernel. | |
| # If someone reverts to the stub or renames the symbol, this | |
| # catches it. | |
| for sym in HI_MPI_IVE_PerspTrans HI_MPI_IVE_Hog; do | |
| check libraries/ive_neo/libive_neo.so $sym | |
| done | |
| # KCF tracker family — 10 stubs added per issue #109. Real | |
| # impls land in follow-ups; this assertion is purely | |
| # link-surface (symbols must exist) so callers compiled | |
| # against cv500 SDK headers can load the library. | |
| for sym in \ | |
| HI_MPI_IVE_KCF_GetMemSize HI_MPI_IVE_KCF_CreateObjList \ | |
| HI_MPI_IVE_KCF_DestroyObjList HI_MPI_IVE_KCF_CreateGaussPeak \ | |
| HI_MPI_IVE_KCF_CreateCosWin HI_MPI_IVE_KCF_GetTrainObj \ | |
| HI_MPI_IVE_KCF_Process HI_MPI_IVE_KCF_GetObjBbox \ | |
| HI_MPI_IVE_KCF_JudgeObjBboxTrackState HI_MPI_IVE_KCF_ObjUpdate; do | |
| check libraries/ive_neo/libive_neo.so $sym | |
| done | |
| for sym in hi_ivp_init hi_ivp_deinit hi_ivp_process_ex hi_ivp_load_resource_from_memory hi_ivp_set_ctrl_attr; do | |
| check libraries/ivp_neo/libivp_neo.so $sym | |
| done | |
| # NNIE — eight HI_MPI_SVP_NNIE_* entry points. LoadModel / | |
| # UnloadModel / GetTskBufSize are pure-userspace; the other | |
| # five ioctl through /dev/nnie. | |
| for sym in \ | |
| HI_MPI_SVP_NNIE_LoadModel HI_MPI_SVP_NNIE_UnloadModel \ | |
| HI_MPI_SVP_NNIE_GetTskBufSize HI_MPI_SVP_NNIE_Forward \ | |
| HI_MPI_SVP_NNIE_ForwardWithBbox HI_MPI_SVP_NNIE_Query \ | |
| HI_MPI_SVP_NNIE_AddTskBuf HI_MPI_SVP_NNIE_RemoveTskBuf; do | |
| check libraries/nnie_neo/libnnie_neo.so $sym | |
| done | |
| - name: Assert no vendor libs in NEEDED | |
| run: | | |
| for f in \ | |
| libraries/mpi_neo/libmpi_neo.so \ | |
| libraries/ive_neo/libive_neo.so \ | |
| libraries/ivp_neo/libivp_neo.so \ | |
| libraries/nnie_neo/libnnie_neo.so; do | |
| echo "=== $(basename $f) NEEDED ===" | |
| arm-linux-gnueabi-readelf -d "$f" | grep NEEDED | |
| # Expect only libc and (for pthread) libpthread. Any NEEDED | |
| # entry matching libive.so, libmpi.so, libivp.so, libsecurec.so | |
| # or libVoiceEngine.so would mean a vendor blob snuck in. | |
| if arm-linux-gnueabi-readelf -d "$f" | grep -E 'NEEDED.*lib(ive|mpi|ivp|secure|VoiceEngine|upvqe|dnvqe)\.so'; then | |
| echo "FAIL: vendor lib in NEEDED" >&2 | |
| exit 1 | |
| fi | |
| done | |
| # | |
| # qemu-ive-ops | |
| # | |
| # Builds QEMU with HiSilicon machine models from widgetii/qemu-hisilicon | |
| # (cached by pinned SHA), builds the vendored kernel/ive_neo/test/qemu | |
| # register test as a static ARM binary, wraps in cpio, boots under | |
| # qemu-system-arm -M hi3516ev300 with a downloaded OpenIPC kernel, and | |
| # asserts the test reports "Result: N/N passed". | |
| # | |
| # This is the only job that exercises actual kernel/ive_neo/ive_neo.c | |
| # behavior end-to-end. Any change to the driver's register sequences | |
| # that drifts away from the QEMU machine model will fail this job. | |
| # | |
| qemu-ive-ops: | |
| name: QEMU IVE ops regression (${{ matrix.platform.machine }}) | |
| runs-on: ubuntu-latest | |
| needs: libraries-cross-compile | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: | |
| - machine: hi3516ev300 | |
| firmware: hi3516ev300 | |
| ive_base: '0x11320000' | |
| # V4 family (ev200/ev300/gk7205v300): IVE @ 0x11320000. | |
| - machine: hi3516cv500 | |
| firmware: hi3516cv500 | |
| ive_base: '0x11230000' | |
| # CV500 family (cv500/av300): IVE @ 0x11230000. | |
| env: | |
| # Pinned SHA of widgetii/qemu-hisilicon. Bump this when upstream | |
| # changes machine-model register semantics. The cv500 machine | |
| # gained hisi-ive at 0x11230000 in widgetii/qemu-hisilicon#99 — | |
| # cv500 regression coverage requires that SHA or later. | |
| QEMU_HISILICON_SHA: master | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install QEMU build deps + ARM toolchain | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| build-essential ninja-build meson pkg-config \ | |
| libglib2.0-dev libpixman-1-dev libfdt-dev zlib1g-dev libslirp-dev \ | |
| python3 python3-venv cpio \ | |
| gcc-arm-linux-gnueabi libc6-dev-armel-cross | |
| - name: Clone qemu-hisilicon | |
| run: | | |
| git clone --depth 1 https://github.com/widgetii/qemu-hisilicon.git /tmp/qemu-hisilicon | |
| cd /tmp/qemu-hisilicon | |
| echo "SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV | |
| - name: Cache QEMU build | |
| id: cache-qemu | |
| uses: actions/cache@v4 | |
| with: | |
| path: /tmp/qemu-hisilicon/qemu-src/build/qemu-system-arm | |
| key: qemu-hisilicon-${{ env.SHA }}-ubuntu-latest | |
| - name: Build QEMU (if cache miss) | |
| if: steps.cache-qemu.outputs.cache-hit != 'true' | |
| run: | | |
| cd /tmp/qemu-hisilicon | |
| bash qemu/setup.sh | |
| - name: Verify qemu-system-arm works | |
| run: | | |
| QEMU=/tmp/qemu-hisilicon/qemu-src/build/qemu-system-arm | |
| chmod +x $QEMU | |
| $QEMU -machine help | grep -E 'hi35|gk7' | head | |
| $QEMU -machine ${{ matrix.platform.machine }} -m 128M -nographic -serial null -monitor none \ | |
| -kernel /dev/null 2>&1 | head -3 || true | |
| - name: Build test-ive-ops.cpio | |
| run: | | |
| make -C kernel/ive_neo/test/qemu CC=arm-linux-gnueabi-gcc | |
| ls -la kernel/ive_neo/test/qemu/test-ive-ops kernel/ive_neo/test/qemu/test-ive-ops.cpio | |
| - name: Download OpenIPC kernel | |
| run: | | |
| mkdir -p /tmp/openipc | |
| cd /tmp/openipc | |
| curl -sSLf -o fw.tgz \ | |
| https://github.com/OpenIPC/firmware/releases/download/latest/openipc.${{ matrix.platform.firmware }}-nor-lite.tgz | |
| tar xzf fw.tgz | |
| ls uImage.${{ matrix.platform.firmware }} | |
| - name: Run IVE register regression under QEMU | |
| timeout-minutes: 3 | |
| run: | | |
| QEMU=/tmp/qemu-hisilicon/qemu-src/build/qemu-system-arm | |
| timeout 60 $QEMU \ | |
| -M ${{ matrix.platform.machine }} -m 128M \ | |
| -kernel /tmp/openipc/uImage.${{ matrix.platform.firmware }} \ | |
| -initrd kernel/ive_neo/test/qemu/test-ive-ops.cpio \ | |
| -nographic -serial mon:stdio \ | |
| -append "console=ttyAMA0,115200 mem=96M mmz_allocator=hisi mmz=anonymous,0,0x46000000,32M --ive-base=${{ matrix.platform.ive_base }}" \ | |
| 2>&1 | tee ive-output.txt || true | |
| echo | |
| echo "=== Result line ===" | |
| grep -E "Result: [0-9]+/[0-9]+ passed" ive-output.txt || { | |
| echo "FAIL: no 'Result: N/N passed' in QEMU output" >&2 | |
| echo "--- last 30 lines ---" | |
| tail -30 ive-output.txt | |
| exit 1 | |
| } | |
| - name: Upload QEMU output | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: qemu-ive-ops-log-${{ matrix.platform.machine }} | |
| path: ive-output.txt | |
| retention-days: 7 | |
| # | |
| # qemu-nnie-forward | |
| # | |
| # Mirrors qemu-ive-ops for the NNIE block: boots an ARM initramfs | |
| # under qemu-system-arm with the hisi-nnie machine model | |
| # (widgetii/qemu-hisilicon, added alongside hisi-ive) and runs a | |
| # register-level smoke test against it. | |
| # | |
| # The test binary (kernel/nnie_neo/test/qemu/test-nnie-forward.c) is | |
| # static-linked, drives the NNIE block via /dev/mem + anonymous-page | |
| # MMZ, builds a 64-byte HW task descriptor + tskbuf tail, kicks | |
| # START, polls for the finish IRQ, and asserts the dst blob holds | |
| # the model's known-good mnist score vector. | |
| # | |
| # NNIE is cv500-only on the cv500/av300 SoCs — no equivalent on V4, | |
| # so the matrix is half the size of the IVE one. | |
| # | |
| qemu-nnie-forward: | |
| name: QEMU NNIE forward regression (${{ matrix.platform.machine }}) | |
| runs-on: ubuntu-latest | |
| needs: libraries-cross-compile | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: | |
| - machine: hi3516cv500 | |
| firmware: hi3516cv500 | |
| env: | |
| # Bump when widgetii/qemu-hisilicon changes the hisi-nnie model. | |
| QEMU_HISILICON_SHA: master | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install QEMU build deps + ARM toolchain | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| build-essential ninja-build meson pkg-config \ | |
| libglib2.0-dev libpixman-1-dev libfdt-dev zlib1g-dev libslirp-dev \ | |
| python3 python3-venv cpio \ | |
| gcc-arm-linux-gnueabi libc6-dev-armel-cross | |
| - name: Clone qemu-hisilicon | |
| run: | | |
| git clone --depth 1 https://github.com/widgetii/qemu-hisilicon.git /tmp/qemu-hisilicon | |
| cd /tmp/qemu-hisilicon | |
| echo "SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV | |
| - name: Cache QEMU build | |
| id: cache-qemu | |
| uses: actions/cache@v4 | |
| with: | |
| path: /tmp/qemu-hisilicon/qemu-src/build/qemu-system-arm | |
| key: qemu-hisilicon-${{ env.SHA }}-ubuntu-latest | |
| - name: Build QEMU (if cache miss) | |
| if: steps.cache-qemu.outputs.cache-hit != 'true' | |
| run: | | |
| cd /tmp/qemu-hisilicon | |
| bash qemu/setup.sh | |
| - name: Verify hisi-nnie machine model exists | |
| run: | | |
| # If the qemu-hisilicon SHA pinned above predates the NNIE | |
| # model, qtree won't list `hisi-nnie` and the test below | |
| # would silently read zeros from MMIO. Fail fast here. | |
| QEMU=/tmp/qemu-hisilicon/qemu-src/build/qemu-system-arm | |
| chmod +x $QEMU | |
| if ! strings $QEMU | grep -qx "hisi-nnie"; then | |
| echo "FAIL: qemu-system-arm has no hisi-nnie device type — bump QEMU_HISILICON_SHA" >&2 | |
| exit 1 | |
| fi | |
| echo "ok: hisi-nnie type present in $QEMU" | |
| - name: Build test-nnie-forward.cpio | |
| run: | | |
| make -C kernel/nnie_neo/test/qemu CC=arm-linux-gnueabi-gcc | |
| ls -la kernel/nnie_neo/test/qemu/test-nnie-forward kernel/nnie_neo/test/qemu/test-nnie-forward.cpio | |
| - name: Download OpenIPC kernel | |
| run: | | |
| mkdir -p /tmp/openipc | |
| cd /tmp/openipc | |
| curl -sSLf -o fw.tgz \ | |
| https://github.com/OpenIPC/firmware/releases/download/latest/openipc.${{ matrix.platform.firmware }}-nor-lite.tgz | |
| tar xzf fw.tgz | |
| ls uImage.${{ matrix.platform.firmware }} | |
| - name: Run NNIE Forward regression under QEMU | |
| timeout-minutes: 3 | |
| run: | | |
| QEMU=/tmp/qemu-hisilicon/qemu-src/build/qemu-system-arm | |
| timeout 60 $QEMU \ | |
| -M ${{ matrix.platform.machine }} -m 128M \ | |
| -kernel /tmp/openipc/uImage.${{ matrix.platform.firmware }} \ | |
| -initrd kernel/nnie_neo/test/qemu/test-nnie-forward.cpio \ | |
| -nographic -serial mon:stdio \ | |
| -append "console=ttyAMA0,115200 mem=96M mmz_allocator=hisi mmz=anonymous,0,0x46000000,32M" \ | |
| < /dev/zero 2>&1 | tee nnie-output.txt || true | |
| echo | |
| echo "=== Result line ===" | |
| grep -E "Result: [0-9]+/[0-9]+ passed" nnie-output.txt || { | |
| echo "FAIL: no 'Result: N/N passed' in QEMU output" >&2 | |
| echo "--- last 30 lines ---" | |
| tail -30 nnie-output.txt | |
| exit 1 | |
| } | |
| # All 4 sub-tests must pass (1 poweron + 3 forwards). A | |
| # partial pass means the model regressed somewhere — fail | |
| # the job instead of silently accepting a smaller number. | |
| if ! grep -qE "Result: 4/4 passed" nnie-output.txt; then | |
| echo "FAIL: NNIE regression test reported less than 4/4" >&2 | |
| tail -30 nnie-output.txt | |
| exit 1 | |
| fi | |
| - name: Upload QEMU output | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: qemu-nnie-forward-log-${{ matrix.platform.machine }} | |
| path: nnie-output.txt | |
| retention-days: 7 | |
| # | |
| # qemu-boot | |
| # | |
| # Matrix job that boots OpenIPC firmware for each supported platform | |
| # under qemu-system-arm and verifies the kernel reaches userspace. | |
| # Each platform uses its own QEMU machine model from qemu-hisilicon. | |
| # | |
| qemu-boot: | |
| name: QEMU boot (${{ matrix.machine }}) | |
| runs-on: ubuntu-latest | |
| # Per-row continue-on-error so we can keep platforms in the matrix that | |
| # currently surface pre-existing firmware-side bugs (av100 module-name | |
| # drift in remove_detect, cv300/cv500 missing cma_osal.ko). The rows | |
| # still run and any new green->red transition is visible in the matrix | |
| # view; they just don't gate the workflow until those firmware bugs are | |
| # fixed in OpenIPC/firmware. Tracked by per-row references in comments. | |
| continue-on-error: ${{ matrix.allow-failure == true }} | |
| env: | |
| QEMU_HISILICON_SHA: master | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - machine: hi3516cv100 | |
| firmware: hi3516cv100 | |
| mem: 64M | |
| append: "console=ttyAMA0,115200 mem=64M root=/dev/ram0 rootfstype=squashfs" | |
| # cv200/av100/cv300/3519v101: cap kernel mem= at 32M so it ends at | |
| # 0x82000000, where the qemu-hisilicon machine emulator appends the | |
| # MMZ region in cmdline (mmz=...,0x82000000,N). Previously these | |
| # rows passed mem=<full QEMU RAM>, and the kernel range overlapped | |
| # MMZ. The legacy MMZ allocator silently tolerated the overlap | |
| # until OpenIPC/openhisilicon#73 made it return -ENODEV when every | |
| # configured zone is rejected; with that strict check + post-#2059 | |
| # firmware where load_hisilicon no longer exits before insmod, the | |
| # overlap surfaces as "insmod: can't insert 'mmz.ko': No such | |
| # device". 32M kernel = production OS/MMZ split for V1/V2/V2A/V3A. | |
| - machine: hi3516cv200 | |
| firmware: hi3516cv200 | |
| mem: 64M | |
| append: "console=ttyAMA0,115200 mem=32M root=/dev/ram0 rootfstype=squashfs" | |
| # av100: load_hisilicon's remove_detect rmmods modules under | |
| # vendor names (sensor_spi, sensor_i2c, hi_media, mmz, hi3516a_*) | |
| # but these names don't match what the firmware actually loaded. | |
| # Pre-existing bug — was hidden on main because the script bailed | |
| # at os_mem= before ever calling remove_detect; surfaces now that | |
| # the post-#2059 firmware proceeds further. Needs a separate | |
| # OpenIPC/firmware fix to align remove_detect's rmmod targets | |
| # with the open_*/hi3516a_* names av100 actually uses. | |
| - machine: hi3516av100 | |
| firmware: hi3516av100 | |
| mem: 256M | |
| allow-failure: true | |
| append: "console=ttyAMA0,115200 mem=32M root=/dev/ram0 rootfstype=squashfs" | |
| - machine: hi3516cv300 | |
| firmware: hi3516cv300 | |
| mem: 128M | |
| min_modules: 20 | |
| append: "console=ttyAMA0,115200 mem=32M root=/dev/ram0 rootfstype=squashfs" | |
| - machine: hi3519v101 | |
| firmware: hi3519v101 | |
| mem: 64M | |
| append: "console=ttyAMA0,115200 mem=32M root=/dev/ram0 rootfstype=squashfs" | |
| - machine: hi3516cv500 | |
| firmware: hi3516cv500 | |
| mem: 128M | |
| append: "console=ttyAMA0,115200 earlyprintk mem=128M root=/dev/ram0 rootfstype=squashfs" | |
| - machine: hi3516ev200 | |
| firmware: hi3516ev200 | |
| mem: 128M | |
| append: "console=ttyAMA0,115200 earlyprintk mem=96M root=/dev/ram0 rootfstype=squashfs mmz_allocator=hisi mmz=anonymous,0,0x46000000,32M" | |
| - machine: gk7205v200 | |
| firmware: gk7205v200 | |
| mem: 128M | |
| append: "console=ttyAMA0,115200 earlyprintk mem=96M root=/dev/ram0 rootfstype=squashfs mmz_allocator=hisi mmz=anonymous,0,0x46000000,32M" | |
| # hi3516ev300_neo board (V4 + CMA). The bootargs match what the | |
| # production U-Boot passes: mem=<totalmem> with mmz_allocator=cma | |
| # and the MMZ chunk CMA-reserved within that range. The cmdline | |
| # exercises the load_hisilicon mem=/totalmem split logic that | |
| # OpenIPC/firmware#2059 tracks. | |
| - machine: hi3516ev300 | |
| firmware: hi3516ev300 | |
| mem: 128M | |
| min_modules: 5 | |
| append: "console=ttyAMA0,115200 panic=20 mem=128M root=/dev/ram0 rootfstype=squashfs mmz_allocator=cma mmz=anonymous,0,0x42000000,96M" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install QEMU build deps | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| build-essential ninja-build meson pkg-config \ | |
| libglib2.0-dev libpixman-1-dev libfdt-dev zlib1g-dev libslirp-dev \ | |
| python3 python3-venv | |
| - name: Clone qemu-hisilicon | |
| run: | | |
| git clone --depth 1 https://github.com/widgetii/qemu-hisilicon.git /tmp/qemu-hisilicon | |
| cd /tmp/qemu-hisilicon | |
| echo "SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV | |
| - name: Cache QEMU build | |
| id: cache-qemu | |
| uses: actions/cache@v4 | |
| with: | |
| path: /tmp/qemu-hisilicon/qemu-src/build/qemu-system-arm | |
| key: qemu-hisilicon-${{ env.SHA }}-ubuntu-latest | |
| - name: Build QEMU (if cache miss) | |
| if: steps.cache-qemu.outputs.cache-hit != 'true' | |
| run: | | |
| cd /tmp/qemu-hisilicon | |
| bash qemu/setup.sh | |
| - name: Download OpenIPC firmware | |
| run: | | |
| mkdir -p /tmp/openipc | |
| cd /tmp/openipc | |
| curl -sSLf -o fw.tgz \ | |
| https://github.com/OpenIPC/firmware/releases/download/latest/openipc.${{ matrix.firmware }}-nor-lite.tgz | |
| tar xzf fw.tgz | |
| ls uImage.${{ matrix.firmware }} | |
| # Inject a tiny S99 init script that emits a serial-console marker | |
| # with the count of HiSilicon modules currently loaded. Repacks the | |
| # squashfs so the QEMU boot exercises the real init flow and we can | |
| # grep deterministic markers out of qemu-output.txt afterwards. The | |
| # probe gives a *positive* signal that load_hisilicon actually | |
| # populated the kernel — without it all assertions are negative | |
| # ("absence of known error strings") and silent failures slip past. | |
| # | |
| # Counts every loaded module except the always-present FS modules. | |
| # That covers both V3+ source-built open_* names and V1/V2/V2A | |
| # vendor-named blobs (hi3518_*, mmz, acodec, hi_*) since cv100/cv200 | |
| # firmware loads modules under the vendor names exposed by | |
| # objcopy --redefine-sym wrappers. | |
| - name: Inject lsmod probe into rootfs | |
| run: | | |
| sudo apt-get install -y --no-install-recommends squashfs-tools | |
| cd /tmp/openipc | |
| sudo unsquashfs -d rfs rootfs.squashfs.${{ matrix.firmware }} | |
| sudo tee rfs/etc/init.d/S99lsmodprobe > /dev/null <<'EOS' | |
| #!/bin/sh | |
| # CI probe — emits markers for openhisilicon's qemu-boot job to grep. | |
| # Runs after S95majestic so load_hisilicon has had its chance. | |
| [ "$1" = start ] || exit 0 | |
| # Wait briefly for HiSilicon modules to settle. Threshold of 5 | |
| # is conservative — every supported platform loads at least | |
| # OSAL/MMZ + base + sys + sys_config + a couple of drivers. | |
| count_hisi() { lsmod | tail -n +2 | grep -cvE '^(vfat|fat)\b'; } | |
| for _ in 1 2 3 4 5; do | |
| [ "$(count_hisi)" -ge 5 ] && break | |
| sleep 1 | |
| done | |
| echo "===CI:HISI_MOD_COUNT===$(count_hisi)===" | |
| echo "===CI:LSMOD_BEGIN===" | |
| lsmod | |
| echo "===CI:LSMOD_END===" | |
| EOS | |
| sudo chmod +x rfs/etc/init.d/S99lsmodprobe | |
| sudo mksquashfs rfs rootfs.squashfs.${{ matrix.firmware }}.probed \ | |
| -comp xz -all-root -noappend -no-progress >/dev/null | |
| sudo mv rootfs.squashfs.${{ matrix.firmware }}.probed \ | |
| rootfs.squashfs.${{ matrix.firmware }} | |
| - name: Boot under QEMU | |
| timeout-minutes: 3 | |
| run: | | |
| QEMU=/tmp/qemu-hisilicon/qemu-src/build/qemu-system-arm | |
| chmod +x $QEMU | |
| timeout 90 $QEMU \ | |
| -M ${{ matrix.machine }} -m ${{ matrix.mem }} \ | |
| -kernel /tmp/openipc/uImage.${{ matrix.firmware }} \ | |
| -initrd /tmp/openipc/rootfs.squashfs.${{ matrix.firmware }} \ | |
| -nographic -serial mon:stdio \ | |
| -append "${{ matrix.append }}" \ | |
| 2>&1 | tee qemu-output.txt || true | |
| echo | |
| echo "=== Checking for successful boot ===" | |
| fail_with() { | |
| echo "--- last 50 lines of QEMU output ---" | |
| tail -50 qemu-output.txt | |
| # Rows in the matrix marked `allow-failure: true` know about a | |
| # pre-existing firmware-side bug; downgrade fail to warning so | |
| # the job conclusion is success (mergeStateStatus stays CLEAN) | |
| # while the regression remains visible in the log. | |
| if [ "${{ matrix.allow-failure }}" = "true" ]; then | |
| echo "::warning::EXPECTED FAIL (${{ matrix.machine }}, allow-failure): $1" | |
| exit 0 | |
| fi | |
| echo "FAIL (${{ matrix.machine }}): $1" >&2 | |
| exit 1 | |
| } | |
| # Module-load regression checks — see OpenIPC/openhisilicon#62. | |
| # The bug: load_hisilicon's remove_detect() rmmods modules by vendor | |
| # name (mmz, hi_media, sys_config, hi_sensor_i2c, ...) but openhisilicon | |
| # source-built modules announce themselves as open_* internally, so | |
| # those rmmods fail silently. The previous QEMU assertion (just "crond | |
| # reached userspace") missed it because the script exits cleanly when | |
| # SENSOR=unknown and crond starts regardless. | |
| if grep -qE "^rmmod: can't unload" qemu-output.txt; then | |
| fail_with "rmmod failure during boot — vendor name does not match loaded module name (#62)" | |
| fi | |
| if grep -qF "Error: There's something wrong, please check!" qemu-output.txt; then | |
| fail_with "load_hisilicon report_error exit" | |
| fi | |
| if grep -q "Cannot get chip id" qemu-output.txt; then | |
| fail_with "majestic 'Cannot get chip id' — sys module not active" | |
| fi | |
| # OpenIPC/firmware#2059: load_hisilicon mis-parses kernel cmdline | |
| # mem=NM as os_mem_size on V4+CMA boards (where mem= is total RAM, | |
| # not the OS-only slice), trips the "os_mem >= total_mem" guard, and | |
| # exits before any insmod. Manifests with this exact stderr line. | |
| if grep -qE "^\[err\] os_mem\[[0-9]+\], over total_mem\[[0-9]+\]" qemu-output.txt; then | |
| fail_with "load_hisilicon: cmdline mem= misread as OS-only memory (firmware#2059)" | |
| fi | |
| # The S99lsmodprobe init script we injected earlier prints | |
| # HISI_MOD_COUNT to the serial console. Always log the count for | |
| # diagnosability; gate on it only for matrix rows that opt in via | |
| # `min_modules`. -a/--text on grep so the binary serial output | |
| # (TTY control chars) doesn't suppress matches; pattern is | |
| # unanchored so a leading \r from the TTY doesn't break it. | |
| mod_count_line=$(grep -oaE 'CI:HISI_MOD_COUNT===[0-9]+===' qemu-output.txt | tail -1) | |
| mod_count=$(echo "$mod_count_line" | grep -oE '[0-9]+' || echo "") | |
| echo "PROBE: ${{ matrix.machine }} loaded ${mod_count:-?} HiSilicon modules" | |
| # Per-row gate: rows that set `min_modules:` require at least that | |
| # many HiSilicon modules to be loaded after boot. Rows that don't | |
| # set it stay on the existing string-pattern assertions only — | |
| # adding the count assertion to a row is opt-in as we audit each | |
| # platform's QEMU bootargs / firmware behaviour and confirm a | |
| # baseline count it can reliably hit. New row added today: | |
| # hi3516ev300 (V4+CMA) — the regression OpenIPC/firmware#2059 | |
| # actually showed up on; min=5 catches a load_hisilicon exit | |
| # before any insmod even when the script's specific stderr | |
| # patterns drift in future. | |
| min='${{ matrix.min_modules }}' | |
| if [ -n "$min" ] && [ "$min" -gt 0 ] 2>/dev/null; then | |
| if [ -z "$mod_count" ]; then | |
| echo "--- captured lsmod ---" | |
| awk '/CI:LSMOD_BEGIN/,/CI:LSMOD_END/' qemu-output.txt | |
| fail_with "S99lsmodprobe never ran (no CI:HISI_MOD_COUNT marker) — boot didn't reach late init" | |
| fi | |
| if [ "$mod_count" -lt "$min" ]; then | |
| echo "--- captured lsmod ---" | |
| awk '/CI:LSMOD_BEGIN/,/CI:LSMOD_END/' qemu-output.txt | |
| fail_with "only $mod_count HiSilicon modules loaded (expected ≥$min) — vendor module-load regression" | |
| fi | |
| fi | |
| if grep -qE "Starting crond: OK" qemu-output.txt; then | |
| echo "PASS: ${{ matrix.machine }} kernel booted to userspace, no module-load regressions" | |
| else | |
| fail_with "no 'Starting crond: OK' in QEMU output" | |
| fi | |
| - name: Upload QEMU output | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: qemu-boot-${{ matrix.machine }} | |
| path: qemu-output.txt | |
| retention-days: 7 | |
| # | |
| # bootrom-lint | |
| # | |
| # Compile-gate for the reverse-engineered bootrom source under bootrom/. | |
| # The .bin and disasm.txt are gitignored (only contributors with the | |
| # physical camera can produce them), so CI exercises just the RE'd C/asm: | |
| # it must keep linking cleanly under arm-none-eabi-gcc as analysis grows. | |
| # NOT a flashable-image check — see bootrom/README. | |
| # | |
| bootrom-lint: | |
| name: bootrom RE compile-gate | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install ARM bare-metal toolchain | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends gcc-arm-none-eabi | |
| - name: Build bootrom/hi3516av300/re | |
| run: | | |
| make -C bootrom/hi3516av300 build | |
| test -f bootrom/hi3516av300/re.elf | |
| # Verify the linked symbol set matches the tracked manifest. | |
| # Drift in either direction (additions or removals) fails the | |
| # diff; updating the manifest is a deliberate, reviewable act. | |
| # Drop compiler-generated clones (.part.N, .constprop.N, .isra.N, | |
| # .cold) — those are gcc-version-dependent optimizer artefacts, | |
| # not API contract. Without this filter, ubuntu-24.04's | |
| # gcc 13.2 emits klad_post_unlock.part.0 that arch's | |
| # gcc 14.2 doesn't, and the manifest diff fails. | |
| arm-none-eabi-objdump -t bootrom/hi3516av300/re.elf \ | |
| | grep ' F ' | awk '{ print $NF }' \ | |
| | grep -v '\.part\.\|\.constprop\.\|\.isra\.\|\.cold' \ | |
| | LC_ALL=C sort -u \ | |
| > /tmp/actual-symbols | |
| diff -u bootrom/hi3516av300/.expected-symbols /tmp/actual-symbols | |
| echo "ok: re.elf symbol set matches manifest ($(wc -l < /tmp/actual-symbols) functions)" |