Skip to content

boards/sim,qemu: switch all nsh defconfigs to nxinit entrypoint - #19984

Open
JianyuWang0623 wants to merge 3 commits into
apache:masterfrom
JianyuWang0623:nxinit-qemu-sim-all-nsh-defconfigs
Open

boards/sim,qemu: switch all nsh defconfigs to nxinit entrypoint#19984
JianyuWang0623 wants to merge 3 commits into
apache:masterfrom
JianyuWang0623:nxinit-qemu-sim-all-nsh-defconfigs

Conversation

@JianyuWang0623

@JianyuWang0623 JianyuWang0623 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Switch every defconfig whose init entry point was nsh_main on the three
locally verifiable boards — sim (80), qemu-armv8a (18), qemu-armv7a (6),
104 defconfigs total — to nxinit (init_main), matching the esp32p4/esp32s3
boards' existing nxinit configs. With nxinit, nsh no longer is init; it
runs as a console sh service started by init.rc.

Split into three commits, one per board:

  • boards/sim: switch all nsh defconfigs to nxinit entrypoint
  • boards/arm64/qemu-armv8a: switch all nsh defconfigs to nxinit entrypoint
  • boards/arm/qemu-armv7a: switch all nsh defconfigs to nxinit entrypoint

Per defconfig (regenerated with make savedefconfig):

  • CONFIG_INIT_ENTRYPOINT="init_main"
  • CONFIG_SYSTEM_NXINIT=y plus its Kconfig deps: CONFIG_EXPERIMENTAL,
    CONFIG_LIBC_EXECFUNCS, CONFIG_SCHED_CHILD_STATUS
    (which depends on CONFIG_SCHED_HAVE_PARENT)
  • CONFIG_ETC_ROMFS=y / CONFIG_FS_ROMFS=y to ship init.rc via ROMFS

Each board gains an src/etc/init.d/init.rc (registers a console sh
service guarded by CONFIG_SYSTEM_NSH; on init starts it), shipped through
ROMFS in both the Make build (RCSRCS) and CMake (nuttx_add_romfs()), both
gated on CONFIG_ETC_ROMFS && CONFIG_SYSTEM_NXINIT so non-nxinit configs are
unaffected. Omitting this ROMFS plumbing causes a link-time
undefined reference to romfs_img.

Five sim configs (dynconns, module, module32, sotest, sotest32)
had CONFIG_BINFMT_DISABLE=y, which blocks CONFIG_LIBC_EXECFUNCS and thus
CONFIG_SYSTEM_NXINIT. Since nxinit spawns services via exec(), binfmt is
re-enabled in those five; they still build cleanly.

Impact

  • Only affects the sim, qemu-armv7a, and qemu-armv8a boards' defconfigs
    plus each board's src/{Makefile,CMakeLists.txt} and new init.rc. No
    common code, no other boards, no NSH behavior change: nsh still owns the
    interactive console, now as an nxinit service rather than as init itself.
  • The new ROMFS wiring is conditional on CONFIG_ETC_ROMFS && CONFIG_SYSTEM_NXINIT;
    existing non-nxinit builds of these boards are byte-for-byte unchanged.
  • CONFIG_SYSTEM_NXINIT currently depends on EXPERIMENTAL.

Testing

Verified against apache/master (before = nsh_main) vs this branch
(after = init_main), building each config and, where a config reaches an
interactive nsh> prompt, capturing ps. Toolchains: host gcc (sim),
aarch64-none-elf + qemu-system-aarch64 (armv8a), arm-none-eabi +
qemu-system-arm (armv7a); SMP configs booted with -smp 4.

Runtime ps (before/after) — init task changes from nsh_main to
init_main, and nsh (sh) is spawned as its child service:

sim:nsh (host)
  BEFORE  nsh> ps
      4     4     0 100 FIFO  Task  Running            ... nsh_main
  AFTER   nsh> ps
      4     4     0 100 FIFO  Task  Waiting  Semaphore ... init_main
      5     5     4 100 FIFO  Task  Running            ... sh

qemu-armv8a:nsh (qemu-system-aarch64)
  BEFORE  3  3  0 100 RR  Task  Running            ... nsh_main
  AFTER   3  3  0 100 RR  Task  Waiting  Semaphore ... init_main
          4  4  3 100 RR  Task  Running            ... sh

qemu-armv8a:nsh_smp (-smp 4)
  AFTER   4  4  0 -- 100 RR  Task  Waiting  Semaphore ... init_main
          5  5  4  0 100 RR  Task  Running            ... sh

qemu-armv7a:nsh (qemu-system-arm)
  BEFORE  3  3  0 100 RR  Task  Running            ... nsh_main
  AFTER   3  3  0 100 RR  Task  Waiting  Semaphore ... init_main
          4  4  3 100 RR  Task  Running            ... sh

qemu-armv7a:smp (-smp 4)
  AFTER   6  6  0 -- 100 RR  Task  Waiting  Semaphore ... init_main
          7  7  6  1 100 RR  Task  Running            ... sh

ps before/after confirmed for the 55 configs that reach an interactive nsh
prompt (sim: 41, qemu: 14). Configs that don't drop to an nsh prompt
(app-entrypoint like nx/foc/can, remoteproc rpproxy/rpserver pairs, graphics,
gdbstub waiting for a debugger) were build-verified instead; those are not
ps-observable by design.

Build note: 18 configs (sim: adb, alsa, bastest, duktape, login, lua, minmea,
mnemofs, nand, nimble, posix_spawn, romfs, rust, smartfs, windows;
qemu-armv8a: nsh_fiq, nsh_gicv2; qemu-armv7a: full) fail to build in my local
environment due to missing external dependencies (clang, lua headers,
third-party libs). These fail identically on unmodified apache/master, i.e.
they are pre-existing environment limitations unrelated to this change and
build fine in upstream CI.

@JianyuWang0623
JianyuWang0623 force-pushed the nxinit-qemu-sim-all-nsh-defconfigs branch from 98c3f7d to edfdb17 Compare August 27, 2026 09:11
@github-actions github-actions Bot added Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. Board: arm Board: arm64 Board: simulator labels Aug 27, 2026
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

qemu-armv8a

  • Code: .rodata +3,208 B, .text +136 B, .text.cmd_exec +140 B, .text.cmd_set +52 B, .text.exec_module +28 B, .text.group_allocate -16 B, .text.group_drop -8 B, .text.group_leave +8 B, .text.nsh_builtin +64 B, .text.nsh_initialize +44 B, .text.nx_start +4 B, .text.nx_start_task +80 B, .text.nxsched_waitpid +388 B, .text.nxsig_action +52 B, .text.nxsig_kill +16 B, .text.nxsig_notification +24 B, .text.nxsig_queue +20 B, .text.nxsig_tcbdispatch -36 B, .text.nxsig_tgkill +16 B, .text.nxsig_timedwait -16 B, .text.nxsig_wait_irq +16 B, .text.nxtask_exithook +108 B, .text.nxthread_setup_scheduler +168 B, .text.sighand_test +208 B, .text.user_main +60 B, .text.waitpid_test +300 B (+5.5%, 337,508 B)
  • Data: .bss.g_kthread_group -24 B, .bss.g_sigpool +192 B (+0.7%, 77,526 B)
    No memory changes detected for:
  • arduino-mega2560
  • esp32-devkitc
  • hifive1-revb
  • mirtoo
  • qemu-intel64
  • rx65n-rsk2mb
  • s698pm-dkit
  • stm32-nucleo-f103rb

Comment thread boards/sim/sim/sim/configs/adb/defconfig Outdated
Comment thread boards/sim/sim/sim/configs/adb/defconfig Outdated
Comment thread boards/sim/sim/sim/configs/adb/defconfig Outdated
@JianyuWang0623
JianyuWang0623 force-pushed the nxinit-qemu-sim-all-nsh-defconfigs branch 2 times, most recently from 6caf831 to 3b78f74 Compare August 27, 2026 11:27
@github-actions github-actions Bot added Size: L The size of the change in this PR is large and removed Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. labels Aug 27, 2026
@JianyuWang0623
JianyuWang0623 force-pushed the nxinit-qemu-sim-all-nsh-defconfigs branch 13 times, most recently from 4edfa97 to 842c568 Compare August 30, 2026 03:36
@JianyuWang0623
JianyuWang0623 marked this pull request as ready for review August 30, 2026 03:38
Comment thread boards/sim/sim/sim/src/etc/init.d/init.rc Outdated
Comment thread boards/sim/sim/sim/src/CMakeLists.txt Outdated
@JianyuWang0623
JianyuWang0623 force-pushed the nxinit-qemu-sim-all-nsh-defconfigs branch from 2b6eaa2 to 0b0c010 Compare August 31, 2026 04:14
Switch every sim/sim/sim defconfig whose init entry point was nsh_main
(81 configs) to nxinit (init_main). nsh now runs as a "console sh"
service started by init.rc instead of being the top-level init task.

Each switched defconfig only gains the nxinit-essential keys (minimal
delta, regenerated so no unrelated options leak in):
- CONFIG_INIT_ENTRYPOINT="init_main"
- CONFIG_SYSTEM_NXINIT=y plus its Kconfig deps that were not already set:
  CONFIG_EXPERIMENTAL, CONFIG_LIBC_EXECFUNCS, CONFIG_SCHED_CHILD_STATUS
  (which depends on CONFIG_SCHED_HAVE_PARENT)
- CONFIG_ETC_ROMFS=y / CONFIG_FS_ROMFS=y to ship init.rc via ROMFS
No stack-size overrides are added: INIT_STACKSIZE and SYSTEM_NSH_STACKSIZE
keep their Kconfig defaults (DEFAULT_TASK_STACKSIZE).

Five configs (dynconns, module, module32, sotest, sotest32) had
CONFIG_BINFMT_DISABLE=y, which blocks CONFIG_LIBC_EXECFUNCS and thus
CONFIG_SYSTEM_NXINIT. Since nxinit spawns services via exec(), binfmt is
re-enabled in those five. They also enable CONFIG_LIBC_ENVPATH but did
not ship a /bin, so nxinit's posix_spawnp("sh") resolved via PATH and
failed with ENOENT ("Error Starting service 'console': 2"). Add
CONFIG_FS_BINFS=y and CONFIG_PATH_INITIAL="/bin" to those five so the
builtin apps are visible under /bin and the console service starts,
matching the working sim:nsh config.

citest additionally needed CONFIG_ETC_ROMFSDEVNO=1 (matching the 53
other sim configs that already set it to avoid the same collision):
sim_registerblockdevice() (arch/sim/src/sim/sim_blockdevice.c, called
from up_initialize() before any application task starts) unconditionally
registers a FAT ramdisk at /dev/ram0 whenever CONFIG_FS_FAT is set, and
citest is one of the few switched configs with CONFIG_FS_FAT=y that had
never overridden CONFIG_ETC_ROMFSDEVNO away from its Kconfig default of
0. With nxinit, romdisk_register() for /etc's ROMFS at that same minor
number then fails with -EEXIST and nxinit aborts with "Error Opening
/etc/init.d/init.rc" since it treats a missing init.rc as fatal;
nsh_main() silently tolerated the identical failure before this switch
(logs "init: open failed: 2" and continues to the nsh prompt), so the
/etc mount had actually never worked for citest even pre-nxinit.

Add boards/sim/sim/sim/src/etc/init.d/init.rc (registers a "console sh"
service guarded by CONFIG_SYSTEM_NSH; "on init" starts it), shipped via
ROMFS in the Make build (RCSRCS) and CMake (nuttx_add_romfs()), both gated
on CONFIG_ETC_ROMFS && CONFIG_SYSTEM_NXINIT so non-nxinit configs are
unaffected.

Switching to nxinit means nsh_main()/nsh_initialize() (which used to run
/etc/init.d/rc.sysinit and rcS to mount /tmp and /data) no longer runs on
the 46 sim configs that already had CONFIG_ETC_ROMFS=y on master, so
those mounts would otherwise be lost. /bin (binfs) and /proc (procfs) are
unaffected: sim_bringup() (called from board_late_initialize(), before
any entrypoint task starts, independent of nsh_main/init_main) already
mounts them unconditionally on master, and this PR does not touch that
file. init.rc's "on init" action reproduces the rest of rc.sysinit's
mounts (tmpfs, or the FAT-backed /tmp ramdisk via mkrd + mkfatfs + mount
for boards without CONFIG_FS_TMPFS) plus the hostfs /data mount that
used to live in rcS, inlining them directly instead of spawning the
standalone rcsysinit/rcS scripts through a "service ... oneshot"
indirection. nxinit has no builtin mount/mkrd/mkfatfs yet, so each of
these commands still resolves through its posix_spawnp() sh fallback;
that limitation is unchanged by this PR and is left for a follow-up
(either a native mount() call in sim_bringup() for the fixed-path
mounts, or a builtin in nxinit). The 6 configs that also enable
CONFIG_FS_FAT get an explicit CONFIG_ETC_FATDEVNO=2 so this ramdisk does
not collide with /etc's romdisk.

Now that every sim nsh_main + CONFIG_ETC_ROMFS config has switched to
nxinit, boards/sim/sim/sim/src/etc/init.d/rcS and rc.sysinit are dead
code: no remaining sim config calls nsh_initialize() (the only caller of
those scripts), so remove both files outright and drop them from
boards/sim/sim/sim/src/CMakeLists.txt and Makefile's ROMFS RCSRCS list
(init.rc is shipped instead, as above, only when CONFIG_SYSTEM_NXINIT=y).
The remaining ROMFS-enabled sim configs whose entrypoint isn't nsh_main
(nxlines/nxwm/toybox) never executed rcS/rc.sysinit either (their
entrypoint never calls nsh_initialize()), so they lose nothing but a few
unused bytes from their ROMFS image; sim:nxlines was rebuilt to confirm
it still links and boots with an empty etc/init.d/.

init.rc also uses <nuttx/macro.h>'s CONCATENATE() instead of a local
CONCAT_()/CONCAT() pair, matching existing NuttX convention instead of
duplicating a macro the tree already provides.

sim:windows and sim:windows64 are excluded and stay on nsh_main: they
build under MSVC in CI, and CONFIG_SYSTEM_NXINIT pulls in
apps/system/nxinit/action.c, which fails to compile under MSVC because
list_peek_head_type() (include/nuttx/list.h) uses a GCC
statement-expression the MSVC C compiler does not support; separately,
CONFIG_ETC_ROMFS routes them through nuttx_add_romfs()'s POSIX-shell
genromfs/xxd/sed custom build step, which cmd.exe cannot parse either.
Both are pre-existing MSVC gaps in nxinit/the ROMFS CMake helper, not
something this defconfig-only PR should fix, so these two configs are
left on nsh_main.

Testing (sim, host gcc): sim:nsh boots into nxinit with nsh spawned as
its service (init task = init_main, sh = its child), and now shows the
same five mount points as master (/bin /data /etc /proc /tmp), matching
the pre-switch nsh_main baseline; /data round-trips a write to the host
filesystem. The five binfmt configs reach an interactive nsh prompt
(help/uname work) instead of failing to start the console; e.g.
sim:module:
  BEFORE fix:  Error Starting service 'console': 2
  AFTER  fix:  nsh> help / nsh> uname -a  (console up)
init.rc's inlined FAT/tmpfs mount was verified by preprocessing it with
cpp under both CONFIG_FS_FAT and CONFIG_FS_TMPFS: the expansion matches
rc.sysinit's mount sequence (CONCATENATE() resolves to /dev/ram2, etc.).
sim:citest specifically verified with gdb: before the ETC_ROMFSDEVNO fix,
register_blockdriver("/dev/ram0") is called twice (once by
sim_registerblockdevice() from up_initialize(), once by nx_romfsetc());
the second call returns -EEXIST and nxinit aborts. After setting
CONFIG_ETC_ROMFSDEVNO=1, sim:citest boots cleanly into nxinit (ps shows
init_main waiting on its child sh, matching sim:nsh), and the compiled
ROMFS image contains only init.rc. sim:nxlines, a non-nxinit
CONFIG_ETC_ROMFS config, was rebuilt to confirm it still links and boots
with the now-empty etc/init.d/ (rcS/rc.sysinit removed).

Assisted-by: opencode-agent/claude-sonnet-5
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Switch every qemu-armv8a defconfig whose init entry point was nsh_main
(20 configs) to nxinit (init_main). nsh now runs as a "console sh"
service started by init.rc instead of being the top-level init task.

Each switched defconfig only gains the nxinit-essential keys (minimal
delta):
- CONFIG_INIT_ENTRYPOINT="init_main"
- CONFIG_SYSTEM_NXINIT=y plus its Kconfig deps not already set:
  CONFIG_EXPERIMENTAL, CONFIG_LIBC_EXECFUNCS, CONFIG_SCHED_CHILD_STATUS
  (depends on CONFIG_SCHED_HAVE_PARENT)
- CONFIG_ETC_ROMFS=y / CONFIG_FS_ROMFS=y to ship init.rc via ROMFS
No stack-size overrides: INIT_STACKSIZE/SYSTEM_NSH_STACKSIZE keep their
Kconfig defaults (DEFAULT_TASK_STACKSIZE, already 8192 on this board).

Add boards/arm64/qemu/qemu-armv8a/src/etc/init.d/init.rc, shipped via
ROMFS in the Make build (RCSRCS) and CMake (nuttx_add_romfs()), gated on
CONFIG_ETC_ROMFS && CONFIG_SYSTEM_NXINIT. Omitting this ROMFS plumbing
causes a link-time "undefined reference to romfs_img".

Testing: qemu-armv8a:nsh (and nsh_smp with -smp 4) boot into nxinit with
nsh spawned as its service (init task = init_main, sh = its child),
confirmed via qemu-system-aarch64 in an earlier run of this series. The
removed explicit 8192 stack sizes equal this board's DEFAULT_TASK_STACKSIZE,
so runtime behavior is unchanged. Note: current apache/master fails to
build locally this round (empty -Wstack-usage=, missing libfdt.h)
independent of this change.

Assisted-by: opencode-agent/claude-opus-4-8
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Switch every qemu-armv7a defconfig whose init entry point was nsh_main
(6 configs: full, gdbstub, nsh, rpproxy, rpserver, smp) to nxinit
(init_main). nsh now runs as a "console sh" service started by init.rc
instead of being the top-level init task.

Each switched defconfig only gains the nxinit-essential keys (minimal
delta):
- CONFIG_INIT_ENTRYPOINT="init_main"
- CONFIG_SYSTEM_NXINIT=y plus its Kconfig deps not already set:
  CONFIG_EXPERIMENTAL, CONFIG_LIBC_EXECFUNCS, CONFIG_SCHED_CHILD_STATUS
  (depends on CONFIG_SCHED_HAVE_PARENT)
- CONFIG_ETC_ROMFS=y / CONFIG_FS_ROMFS=y to ship init.rc via ROMFS
No stack-size overrides: INIT_STACKSIZE/SYSTEM_NSH_STACKSIZE keep their
Kconfig defaults.

Add boards/arm/qemu/qemu-armv7a/src/etc/init.d/init.rc, shipped via ROMFS
in the Make build (RCSRCS) and CMake (nuttx_add_romfs()), gated on
CONFIG_ETC_ROMFS && CONFIG_SYSTEM_NXINIT.

Testing: qemu-armv7a:nsh (and smp with -smp 4) boot into nxinit with nsh
spawned as its service (init task = init_main, sh = its child), confirmed
via qemu-system-arm in an earlier run of this series. Note: current
apache/master fails to build qemu-armv7a locally this round (missing
arm_timer.h) independent of this change.

Assisted-by: opencode-agent/claude-opus-4-8
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
@JianyuWang0623
JianyuWang0623 force-pushed the nxinit-qemu-sim-all-nsh-defconfigs branch from 0b0c010 to 4f38002 Compare August 31, 2026 04:32
@JianyuWang0623

Copy link
Copy Markdown
Contributor Author

The sim:citest config in this PR sets CONFIG_INIT_ENTRYPOINT=init_main (nxinit), but the external NTFC test suite that the Linux (sim-01) CI job runs (apache/nuttx-ntfc-testing, release-0.0.1 branch) hard-requires CONFIG_INIT_ENTRYPOINT == "nsh_main" in ntfc.yaml:

requirements:
  - ["CONFIG_DEBUG_SYMBOLS", True]
  - ["CONFIG_SYSTEM_NSH", True]
  - ["CONFIG_INIT_ENTRYPOINT", "nsh_main"]

ntfc's _kv_validate() does a strict scalar != comparison against this value, so any config using init_main (including sim:citest after this PR) fails that requirement check before the actual test cases even run.

Fix: apache/nuttx-ntfc-testing#6 drops the obsolete CONFIG_INIT_ENTRYPOINT requirement line entirely.

Why it's safe to drop: the Nuttx_System NTFC suite (e.g. arch/nsh/test_arch_nsh_integration.py) only does sendCommand(cmd, expected_output)-style nsh prompt interaction checks; it never inspects the init task's name or PID, so it behaves identically whether nsh is the init task (nsh_main) or spawned as an nxinit service (init_main). The requirement predates nxinit and no longer reflects a real constraint. Both released ntfc versions (0.0.1/0.0.2) were checked — _kv_validate has no list/multi-value support, so relaxing the value to ["nsh_main","init_main"] isn't viable; removing the line is the correct fix.

Note: apache/nuttx-ntfc-testing#6 is open and marked ready for review; a full NTFC run under init_main as attached verification evidence is still pending. Once it merges into release-0.0.1 (the branch the CI job actually clones), sim:citest here should pass the NTFC requirements check.

@JianyuWang0623

JianyuWang0623 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

@xiaoxiang781216
@acassis
@lupyuen
@linguini1
@szafonimateusz-mi
We may need to publish a new release‑0.0.2 for https://github.com/apache/nuttx-ntfc-testing.

git clone -b release-0.0.1 https://github.com/apache/nuttx-ntfc-testing && break

@JianyuWang0623

Copy link
Copy Markdown
Contributor Author

The nuttx-ntfc-testing release-0.0.2 tag bump needed to fix the sim:citest CONFIG_INIT_ENTRYPOINT failure seen here is up in:

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

Labels

Board: arm Board: arm64 Board: simulator Size: L The size of the change in this PR is large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants