Skip to content

load_novatek: give Linux 48M of a 128 MB board, not 32M - #2660

Merged
widgetii merged 2 commits into
masterfrom
novatek-osmem-48m
Oct 22, 2026
Merged

widgetii merged 2 commits into
masterfrom
novatek-osmem-48m

Conversation

@widgetii

Copy link
Copy Markdown
Member

Problem

128 MB NT98562/NT98566 boards are set up with osmem=32M, which leaves Linux 25 MB. On a clean boot that is about 3.4 MB free while majestic streams, just above majestic's 3.2 MB low-memory floor, so majestic admits one live viewer. Meanwhile HDAL keeps a 96 MB pool and leaves 41 MB of it unused. A 32M camera works, but only just, and any second viewer or web UI activity gets shed.

These boards keep the vendor U-Boot, which lays memory out from the osmem variable in its own environment, so there is no firmware-side default to change. load_novatek now moves it:

Hardware tested on

NT98566 + SC501AI (XM board, 128 MB, vendor U-Boot 2019.04), OpenIPC nt98566_lite nightly 2026-10-21, with this load_novatek placed in the overlay for the test. The original environment and script were restored afterwards.

Evidence

Before (saved environment):

osmem=32M
## Error: "osmem_default" not defined
bootargs=earlyprintk console=ttyS0,115200 mem=32M panic=20 nprofile_irq_duration=on root=/dev/mtdblock3 rootfstype=squashfs init=/init mtdparts=spi_nor.0:64k(loader),256k(boot),2048k(kernel),5120k(rootfs),-(rootfs_data) bootts=239453,764983 resume_addr=0x00200088 user_debug=0xff

After the first boot with this script (it still booted mem=32M and loaded HDAL normally):

osmem=48M
bootargs=earlyprintk console=ttyS0,115200 mem=48M panic=20 nprofile_irq_duration=on root=/dev/mtdblock3 rootfstype=squashfs init=/init mtdparts=spi_nor.0:64k(loader),256k(boot),2048k(kernel),5120k(rootfs),-(rootfs_data) bootts=239453,764983 resume_addr=0x00200088 user_debug=0xff
osmem_default=48M
daemon.info novatek: Linux memory set to 48M of 128M; applies at the next boot

The next boot (no further writes):

mem=48M
MemAvailable:      23024 kB
DDR1: paddr = 0x03000000, vaddr = 0x84000000, size = 0x05000000, Free = 0x01960000, MaxFreeBlk = 0x0195EFF0
17 kflow modules loaded; main encoder PULL 12 REL 12 (dark scene)
live_backlog_budget_bytes 9455200
live_backlog_sessions 7

For comparison, at 32M on a clean boot MemAvailable was 6.9 MB at idle, about 3.4 MB while streaming, and majestic admitted ~1 session. Earlier at 48M, a 60 s /video.mp4 together with a 20 s /mjpeg viewer ran with no shedding.

.github/scripts/test_load_novatek.sh (new; runs the real script under busybox): 19 checks pass. With the set_default_osmem call disabled, the migration checks fail.

Local make BOARD=nt98566_lite:

- uImage: [1902KB/2048KB]
- rootfs.squashfs: [4804KB/5120KB]

STRICT=1 test_shell_parse.sh, STRICT=1 test_strip_shell_comments.sh, ci-matrix.py --self-test and lint-workflow-shell.py pass. Adding the new test means editing ci-matrix.py, so this PR builds every board.

Scope

  • No kernel patches under general/package/all-patches/linux/ (those go to OpenIPC/linux)
  • No files specific to a single retail camera model (those go to OpenIPC/builder)
  • No probing or bring-up tooling (that goes to OpenIPC/ipctool)
  • Nothing under general/overlay/ or in a shared load_<vendor> script hardcodes a value specific to my board
  • Package sources come from an OpenIPC repository, and any version bump keeps at least the specificity of the pin it replaces (a new package should pin a full 40-character SHA)
  • No LD_PRELOAD, and no binaries that cannot be rebuilt from source
  • New code is selected by a defconfig, so CI actually builds it

128 MB NT9856x boards have been set up with osmem=32M, which leaves Linux
25 MB. That is one live viewer before majestic's low-memory floor sheds it,
and on a clean boot about 3.4 MB free while it streams, while HDAL keeps a
96 MB pool it does not fill (41 MB free). At osmem=48M the same NT98566 +
SC501AI runs with 23 MB available, majestic admits seven viewers, and HDAL
still keeps 25 MB free.

The vendor U-Boot reads osmem from its environment, so the firmware moves
it there: load_novatek, on a board whose device tree says 128 MB of DRAM,
with osmem unset or still at the old 32M, writes osmem=48M and the matching
mem= in bootargs in one fw_setenv --script write, effective at the next
boot -- the two can never be written apart, which would hang the kernel or
crash HDAL. A bootargs that carries the ${osmem} placeholder keeps it and
only osmem moves; a bootargs whose mem= already disagrees is not touched.
osmem_default marks the move done, so a split an owner picks afterwards is
left alone. Nothing changes on any other board or setting.

.github/scripts/test_load_novatek.sh runs the real script under busybox
with a faked device tree, command line and environment, and checks every
case that must write and every case that must not, check_mem's guard
included.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Give Linux 48M on eligible 128 MB Novatek boards

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Migrate eligible 128 MB boards from 32M to 48M of Linux memory at the next boot.
• Keep U-Boot’s osmem and saved mem= aligned while preserving owner-selected settings.
• Test migration and safety guards under BusyBox, and run the tests in CI.
Diagram

graph TD
  A["Novatek boot"] --> B["Memory guard"] --> C{"Layout safe?"}
  C -->|No| D["Repair bootargs"]
  C -->|Yes| E{"Migration eligible?"}
  E -->|Yes| F["Batch environment write"] --> G["Load HDAL"]
  E -->|No| G
Loading
High-Level Assessment

The guarded, one-time U-Boot environment migration fits boards that retain vendor U-Boot. Changing only the kernel’s mem= limit would risk disagreement with HDAL’s layout, while changing a firmware-side default would not control the vendor environment. Review the eligibility checks and write-failure behavior carefully because the settings persist across boots.

Files changed (5) +193 / -5

Enhancement (1) +53 / -4
load_novatekMigrate eligible 128 MB boards to osmem=48M +53/-4

Migrate eligible 128 MB boards to osmem=48M

• Reuses a device-tree memory reader in the existing guard and adds a one-time migration before HDAL module loading. Eligible boards receive osmem=48M, a matching saved mem= when needed, and an osmem_default marker through one fw_setenv script invocation; other layouts and owner-selected settings are left alone.

general/package/novatek-osdrv-nt9856x/files/script/load_novatek

Tests (1) +137 / -0
test_load_novatek.shExercise Novatek memory migration under BusyBox +137/-0

Exercise Novatek memory migration under BusyBox

• Sources the real boot script with simulated device-tree data, command lines, and U-Boot environment commands. Checks batch migration, placeholder handling, owner settings, no-write cases, and the existing unsafe-memory guard.

.github/scripts/test_load_novatek.sh

Documentation (1) +1 / -0
CLAUDE.mdDocument the Novatek test command +1/-0

Document the Novatek test command

• Adds the local test command and notes its BusyBox requirement.

CLAUDE.md

Other (2) +2 / -1
ci-matrix.pyClassify the Novatek test as an off-device script +1/-1

Classify the Novatek test as an off-device script

• Adds the new test to NO_BUILD_SCRIPTS so the CI selector recognizes it rather than treating the test file itself as an unknown build-affecting change.

.github/scripts/ci-matrix.py

shell-tests.ymlRun the Novatek test in shell-tests CI +1/-0

Run the Novatek test in shell-tests CI

• Runs the new test after the workflow installs BusyBox.

.github/workflows/shell-tests.yml

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. 128 MB Novatek cameras inherit a forced memory split 📘 Rule violation ≡ Correctness
Description
The new set_default_osmem hardcodes a 128 MB detection and migrates any unset or 32M
environment to 48M inside the shared load_novatek script. Every 128 MB NT9856x camera using this
family-wide script reaches that branch during boot, including deployed cameras whose board-specific
configuration was not intended to change.
Code

general/package/novatek-osdrv-nt9856x/files/script/load_novatek[R211-214]

+	[ "$dram" -eq 128 ] || return 0
+	[ -z "$(fw_printenv -n osmem_default 2>/dev/null)" ] || return 0
+	case "$(fw_printenv -n osmem 2>/dev/null)" in
+	"" | 32M) ;;
Evidence
The checklist prohibits device-specific values in a shared load_ script. The added function
specifically detects 128 MB DRAM, restricts migration to osmem unset or 32M, and writes 48M
plus osmem_default, so the new behavior is applied broadly to all matching cameras using this
shared script.

Rule 7: No device-specific values in generic configuration
general/package/novatek-osdrv-nt9856x/files/script/load_novatek[209-240]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The shared `load_novatek` script hardcodes a 128 MB, 32M-to-48M migration that changes the default memory layout for every matching Novatek camera instead of using a board-specific configuration seam.
## Fix Focus Areas
- general/package/novatek-osdrv-nt9856x/files/script/load_novatek[209-240]
## Recommended Fix
Remove the board-specific `128`, `32M`, and `48M` migration policy from the shared vendor script and place it in the appropriate per-board builder configuration or another board-specific seam. Keep the generic loader limited to behavior that applies across the SoC family, or derive the policy from explicit per-board configuration rather than hardcoded values.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. A second memory limit stays mismatched ✓ Resolved
Description
set_default_osmem accepts saved bootargs as soon as it finds one mem=32M token, then replaces
only that token without checking for another mem= value. If the saved arguments contain both
mem=32M and an owner-set mem=40M, the batch writes osmem=48M and marks the migration done
while leaving the disagreeing limit in place.
Code

general/package/novatek-osdrv-nt9856x/files/script/load_novatek[R223-224]

+	*" mem=32M "*)
+		bootargs=$(echo " $bootargs" | sed "s/ mem=32M/ mem=48M/; s/^ //")
Evidence
The new branch tests for the presence of a matching token, and its substitution changes only the
first occurrence; the batch then persists the result and completion marker. The existing
command-line guard likewise stops at the first memory token rather than establishing that memory
arguments are unique.

general/package/novatek-osdrv-nt9856x/files/script/load_novatek[218-237]
general/package/novatek-osdrv-nt9856x/files/script/load_novatek[174-185]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The migration changes saved bootargs containing `mem=32M` even when another `mem=` argument disagrees, leaving the saved memory settings inconsistent.
## Fix Focus Areas
- general/package/novatek-osdrv-nt9856x/files/script/load_novatek[218-229]
- .github/scripts/test_load_novatek.sh[119-120]
## Recommended Fix
Parse the saved bootargs into memory arguments and migrate only when exactly one memory argument is present and it is `mem=32M`. Add a no-write test for saved bootargs containing both `mem=32M` and a different `mem=` value.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. No-write tests pass after script crashes ✓ Resolved
Description
run() discards the BusyBox shell's exit status with || true, while lacks() checks only whether
a write was recorded. On a no-write case such as the 64 MB board, an unexpected early exit with an
empty calls log passes instead of revealing that the loader failed.
Code

.github/scripts/test_load_novatek.sh[R71-73]

+	(cd "$work" && SCRIPT="$script" CALLS="$work/calls" ENV_FILE="$work/env" \
+		LOAD_NOVATEK_DT="$work/dt" LOAD_NOVATEK_CMDLINE="$work/cmdline" \
+		busybox sh "$work/wrapper" >"$work/out" 2>&1) || true
Evidence
The runner unconditionally suppresses a nonzero status, and the no-write assertion inspects only the
calls file. The 64 MB case uses that assertion without checking whether the script completed.

.github/scripts/test_load_novatek.sh[60-80]
.github/scripts/test_load_novatek.sh[116-117]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test harness suppresses loader failures, allowing no-write cases to pass when the script exits before completing.
## Fix Focus Areas
- .github/scripts/test_load_novatek.sh[60-74]
- .github/scripts/test_load_novatek.sh[106-131]
## Recommended Fix
Capture each invocation's exit status. Require success for ordinary migration and no-write cases, while explicitly expecting the nonzero guard exit in the over-limit cases.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can ask Qodo to dismiss a finding you disagree with, with your reason on record

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread general/package/novatek-osdrv-nt9856x/files/script/load_novatek
Comment thread general/package/novatek-osdrv-nt9856x/files/script/load_novatek Outdated
Comment thread .github/scripts/test_load_novatek.sh Outdated
… that the script ran

A saved bootargs with a second mem= beside mem=32M was half-moved and marked done. The move now needs exactly one mem=, of the old split or the ${osmem} placeholder. The test records each run's exit status, so a case that must not write cannot pass because the script died first, and gains the two-mem= case.
@widgetii
widgetii merged commit fe83145 into master Oct 22, 2026
52 checks passed
@widgetii
widgetii deleted the novatek-osmem-48m branch October 22, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant