Skip to content

ambarella: s3l: enable CONFIG_LBDAF in s3l.generic.config - #2763

Merged
openipc-ai merged 1 commit into
OpenIPC:masterfrom
DeathCamel58:s3l-enable-lbdaf
Nov 6, 2026
Merged

openipc-ai merged 1 commit into
OpenIPC:masterfrom
DeathCamel58:s3l-enable-lbdaf

Conversation

@DeathCamel58

Copy link
Copy Markdown
Contributor

Problem

The prebuilt S3L osdrv kernel modules (ambarella-osdrv-s3l: iav.ko, dsp.ko, ...) were built
against Ambarella's ambarella_s3l_defconfig, which has CONFIG_LBDAF=y. s3l.generic.config has
it disabled.

CONFIG_LBDAF makes blkcnt_t 64-bit, which grows struct inode and moves every field after
i_blocks. For example, inode.i_cdev is at 0x110 with LBDAF and 0x108 without. iav.ko is compiled
for the LBDAF layout, so on a kernel built from the generic config the modules load fine, but every
ioctl on /dev/iav oopses on a NULL file->private_data.

Fix

Set CONFIG_LBDAF=y in br-ext-chip-ambarella/board/s3l/s3l.generic.config, matching the config the
modules were built with.

Testing

On an Amcrest IP4M-1026E (Ambarella S3L, OV4689), 3.10.104 kernel built from s3l.generic.config,
with the prebuilt S3L modules:

  • before: insmod succeeds; the first /dev/iav ioctl oopses (NULL private_data)
  • after: DSP microcode boots, VIN/MIPI comes up, and stream 0 encodes valid 1920x1080 H.264
    (decoded with ffprobe)

I measured the offsets with a throwaway module built against each config, and compared the result with
the layout iav.ko uses. The vermagic (3.10.104 preempt mod_unload ARMv7) is unchanged.

The prebuilt S3L osdrv modules (ambarella-osdrv-s3l: iav.ko, dsp.ko, ...)
were built against Ambarella's ambarella_s3l_defconfig, which has
CONFIG_LBDAF=y. s3l.generic.config had it disabled. LBDAF makes blkcnt_t
64-bit, which grows struct inode and moves the fields after i_blocks:
inode.i_cdev is at 0x110 with LBDAF and 0x108 without.

iav.ko is compiled for the LBDAF layout, so with the generic config every
ioctl on /dev/iav oopses on a NULL file->private_data. With CONFIG_LBDAF=y
the offsets match and the IAV stack (DSP boot, VIN, H.264 encode) works on
an S3L board.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Match S3L kernel layout to prebuilt osdrv modules

🐞 Bug fix ⚙️ Configuration changes 🕐 Less than 10 minutes

Grey Divider

AI Description

• Enable CONFIG_LBDAF in the S3L generic kernel config to match prebuilt osdrv modules.
• Restore compatible inode offsets so /dev/iav ioctls work without a kernel oops.
Diagram

graph TD
  Config["S3L generic config"] --> Kernel["Kernel inode layout"] --> Modules["Prebuilt osdrv modules"] --> Ioctl["IAV ioctls"]
Loading
High-Level Assessment

Match the kernel configuration used to build the prebuilt modules. Rebuilding those modules against the generic config would also align layouts, but is less practical when the supplied modules are prebuilt.

Files changed (1) +1 / -1

Bug fix (1) +1 / -1
s3l.generic.configEnable LBDAF for S3L kernel ABI compatibility +1/-1

Enable LBDAF for S3L kernel ABI compatibility

• Sets CONFIG_LBDAF=y so the generic S3L kernel uses the inode layout expected by prebuilt osdrv modules. This addresses the reported /dev/iav ioctl oops caused by mismatched offsets.

br-ext-chip-ambarella/board/s3l/s3l.generic.config

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

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can turn these tips off under Display > Comment parts

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@openipc-ai openipc-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix is correct and addresses the real cause.

Verified

  • iav_open in the shipped iav.ko is ldr r3, [r0, #0x110]; str r3, [r1, #0x70], so it takes file->private_data from inode+0x110, as you describe.
  • CONFIG_LBDAF=y is set in ambarella_s3l_defconfig, both in the Ambarella SDK and on the ambarella-s3l kernel branch, and in the SDK's built s3lm_olive config.
  • The vermagic doesn't record this option, which is why the modules load and only fail at the first ioctl.
  • I compared s3l.generic.config with the SDK's built config. 117 options differ, and I checked them against the SDK kernel headers and the symbols the modules import. I found no other mismatch that reaches the modules. CONFIG_NET_NS changes struct sock, but iav_netlink.c treats the socket as opaque.

Not verified here

  • I didn't measure 0x108 on a kernel without LBDAF; that is your number, though it fits the NULL private_data oops.
  • CI doesn't build S3L, so your build is the only proof that this compiles.
  • Nobody has measured the kernel growth on the 8 MB target that s3l_lite_defconfig sets. It should be small, but it's worth checking.

Separate from this PR, not blockers

  • general/package/legacy/ambarella-osdrv-s3l/files/script/load_ambarella is 0 bytes, so S70vendor loads nothing and a stock image won't bring the drivers up.
  • s3l_lite_defconfig sets BR2_cortex_a7 and BR2_ARM_FPU_NEON_VFPV4, but the cpuinfo in OpenIPC/ipctool#266 shows a Cortex-A9 with vfpv3. I haven't tested this, but binaries built that way could hit illegal instructions.

A request: could you also run current ipctool on this camera? OpenIPC/ipctool#267 added Ambarella detection, and sensor probing hasn't run on hardware yet. With this kernel and the video stream running, your setup is the best test we have. Please post the result on OpenIPC/ipctool#266.

@openipc-ai
openipc-ai merged commit 3c416eb into OpenIPC:master Nov 6, 2026
29 checks passed
@DeathCamel58

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review. On the open points:

  • 0x108: confirmed on OpenIPC's own s3l.generic.config (same 3.10.104 tree, OpenIPC toolchain gcc 13.3), with a probe object built against each config:

    • LBDAF off: offsetof(struct inode, i_cdev) = 0x108, sizeof(struct inode) = 0x120, sizeof(blkcnt_t) = 4
    • LBDAF on: 0x110, 0x128, 8

    i_blocks is at 0x58 in both, so the 8-byte shift is just blkcnt_t growing. iav_open in the shipped iav.ko is ldr r3, [r0, #0x110], i.e. the LBDAF-on layout.

  • It builds: both configs build cleanly with the OpenIPC toolchain, and the LBDAF kernel is what all the hardware testing ran on.

  • Kernel growth on the 8 MB target: the s3l.generic.config kernel with CONFIG_KERNEL_XZ (as s3l_lite_defconfig uses) goes from 1,619,008 to 1,621,432 bytes vmlinux text grows by 5,576 bytes, data/bss unchanged. That leaves about 460 KiB free in the 2,048 KiB kernel slot of the 8 MB layout (about 400 KiB with the 1,983 KiB fixed-slot variant).

@openipc-ai

Copy link
Copy Markdown
Collaborator

Thanks, that closes all three points. The 8-byte shift, the build, and the headroom (about 460 KiB, or 400 KiB with the fixed slot) are what I was missing.

The two follow-ups from my review are now in #2764, and I have a few questions, since it hasn't run on a camera:

  1. Microcode: how did you get the DSP to boot? Did you use load_ucode, and where did orccode.bin, orcme.bin and default_binary.bin come from? They aren't in ambarella-osdrv-s3l.
  2. Module order: did you load things by hand? If your order or arguments differ from iav, sensor, microcode, or you needed anything beyond ov4689_mipi defaults, that's what I'd like to match.
  3. Toolchain: toolchain.ambarella-s3l.tgz isn't published, so which one did you use? If it was built from the old defconfig, its libraries target the Cortex-A7. Could you try ambarella: s3l: build for the Cortex-A9, load the drivers at boot #2764 with one rebuilt from the new config?

@DeathCamel58

Copy link
Copy Markdown
Contributor Author

1. Microcode. Yes, load_ucode. Both it and the three files come from this unit's own stock firmware, not from the OpenIPC package (ucode (S3L) version = 2017/2/22 267837.266889). The stock load_ucode is a glibc armhf binary, so it wouldn't run on the musl s3l_lite image anyway. I'll send a PR with an open-source replacement, which leaves only the three .bin files as the licensing question.

2. Module order. By hand, via a dev script: insmod hw_timer dsp imgproc vout iav ov4689_mipi in that order, then mknod /dev/iav c 248 0 and /dev/ucode c 248 1, then load_ucode. That's the same order as your script. I checked the pieces it relies on on this image: /proc/devices has 248 iav and 248 ucode, and modules.dep resolves iav to hw_timer imgproc vout dsp. ov4689_mipi needs no arguments (VIN mode 0, 2688×1512, 4-lane MIPI).

Beyond the module load, these were needed for video on this board, and they're not in s3l_lite yet:

  • Device tree: iavmem must be the full carve-out, <0x07800000 0x18800000> on this 512 MiB board (the DSP's memory budget depends on it). The sensor reset (XSHUTDN) here is GPIO 107, active low, not the reference board's GPIO 21.
  • A 3A process must hold a session on the IAV image netlink channel (protocol 20, 128-byte messages) before ENABLE_PREVIEW, and keep draining GET_STATISTICS; otherwise VCAP stops.
  • The VIN must run at exactly 30 fps (VIN_SET_FPS with 512 MHz / 30 = 0x0104aaab). The OV4689 mode defaults to 29.97, which makes iav.ko set a fractional-rate flag in the DSP's VCAP setup word, and with this microcode no YUV is written at all (luma 0).
  • An ISP configuration has to be in the IMG buffer before preview starts. So far I've used one captured from the stock firmware.

I'm planning to PR all of these as I get them working: the board's device tree, the microcode loader, the 3A/VIN setup, and an Ambarella S3L HAL for Divinus as the streamer (there's no Majestic build for S3L). The ISP configuration and the microcode are vendor data, so for those I'll only send the code that loads them; how they're shipped is the same licensing question.

Status: H.264 streams after a warm reboot. A cold power-on boot doesn't stream yet (the DSP never programs the VIN controller); I'm still on that.

3. Toolchain. My own Buildroot internal toolchain from a board defconfig: BR2_cortex_a9, BR2_ARM_ENABLE_VFP/NEON, BR2_ARM_EABIHF, glibc, gcc 13.3 (arm-openipc-linux-gnueabihf). glibc/armhf was chosen so the stock Ambarella userspace (load_ucode, the 3A libraries) runs unmodified. I can try #2764 with a musl toolchain rebuilt from the new s3l_lite_defconfig once the open-source loader is in.

@openipc-ai

Copy link
Copy Markdown
Collaborator

This is very useful, thanks. Your order, node numbers and modules.dep check match load_ambarella in #2764, so I've recorded them there as hardware evidence for the bring-up, and marked the rest as untested on this image.

Your list also corrects what I wrote in #2764: the microcode is only one of several missing pieces, so the PR now says the script alone won't give video. I've made the nodes root-only and handled a stored sensor=unknown. ov4689 still resolves to ov4689_mipi with no arguments, as you found.

An open-source load_ucode would remove the glibc blocker, so a PR for that is welcome, along with the device tree, the 3A/VIN setup and the Divinus HAL.

One decision to check: #2764 and #2765 target musl with softfp (BR2_ARM_EABI), because that's what s3l_lite already uses. Your build is glibc armhf so that the stock 3A libraries run. If your open-source work ends up needing those libraries, then s3l_lite itself would have to change, and the published toolchain with it. That's easy to redo with a named rebuild, so tell us which way you're heading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants