esp_wifi_event_handler() held esp_wifi_lock() across the whole event
switch, including the esp_wlan_*_hook() calls
(WIFI_EVENT_STA_CONNECTED/_DISCONNECTED, WIFI_EVENT_AP_START/_STOP).
Those hooks reach netdev_lower_carrier_on()/_off(), which take the
per-device netdev_lock().
Every other path into esp_wifi_lock() acquires the two locks in the
opposite order -- the netdev ifdown path holds netdev_lock() around
its own call into esp_wifi_api_stop(), which calls esp_wifi_lock().
An application that disconnects Wi-Fi (wpa_driver_wext_disconnect()
immediately followed by wapi_set_ifdown()) races the resulting
WIFI_EVENT_STA_DISCONNECTED callback against its own ifdown call, and
the two lock orders wedge each other permanently.
Confirmed on real ESP32-S3 hardware (XIAO ESP32-S3,
CONFIG_ESPRESSIF_WIFI + CONFIG_PM + CONFIG_SCHED_TICKLESS): the
disconnecting task and the low-priority work-queue thread each waited
on a mutex held by the other (checked live via JTAG/GDB, not inferred
from code reading alone). Reproduced 4/4 times before this fix, 0/2
after.
Fix: esp_wifi_lock() is now taken only around the specific calls that
reach into the Wi-Fi driver API (esp_wifi_scan_event_parse(),
esp_wifi_set_ps()), never spanning a esp_wlan_*_hook() call --
netdev_lock() first (or absent), esp_wifi_lock() last, on every path.
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Assisted-by: Claude:claude-sonnet-5
att_find_info_rsp() computed the per-record stride with sizeof(info.i16)
and sizeof(info.i128), but "info" is a union of two pointers, so both
expressions evaluate to the pointer width instead of the size of the
record that the response format selects. The records are 4 octets for a
16-bit UUID and 18 octets for a 128-bit UUID, so the 128-bit path
advanced by 4 (or 8) octets per iteration while reading an 18-octet
record: handles and UUIDs were parsed from the wrong offsets and the walk
ran past the end of the received PDU. On 64-bit builds the 16-bit path
was wrong too.
Take the stride from the record structures, and require the response to
carry whole records before walking it, since the loop advances one record
at a time and a partial trailing record would be parsed as a whole one.
Ref: Core v6.0, Vol 3, Part F, 3.4.3.2 (ATT_FIND_INFORMATION_RSP)
Testing: sim:bluetooth builds with Make, no new warnings. Not yet
exercised at runtime; the scriptable controller that can inject a
malformed Find Information Response is added separately.
Signed-off-by: Alan C. Assis <acassis@gmail.com>
Assisted-by: Claude Code Opus 5
1. nxsched_process_timer: call clock_update_wall_time() under
CONFIG_CLOCK_TIMEKEEPING so that wall time is updated on timer
events during tickless operation.
2. clock_timekeeping_get_wall_time: call clock_update_wall_time()
before sampling the base and counter.
3. clock_timekeeping: allow overriding NTP_MAX_ADJUST with
CONFIG_CLOCK_ADJTIME_SLEWLIMIT_PPM if configured.
4. Use clock_t consistently for counter values in clock_timekeeping.c.
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
This patch addresses two issues in the single-timer capture/compare
tickless OS drivers for STM32 families (common m3m4 v1 for F1/F2/F3/F4/G4,
F7, H7, and WB):
1. Zero-period handling: when up_timer_start() is called with a zero or
negative duration (or period converts to 0 ticks), the driver now
enables the compare match interrupt and immediately fires an event
via EGR (CCxG), avoiding missed events or unexpected counter behavior.
2. Compare-match race condition: after programming CCR and enabling the
compare interrupt, a post-check validates whether the free-running
counter already reached or passed count + period during register
configuration. If elapsed, the interrupt is forced immediately via EGR,
preventing the counter from missing the match and hanging until a full
32-bit rollover (approx. 71 minutes at 1 MHz).
Verified on real hardware:
- STM32H743ZI (IED R550): validated with ping, sleep, and usleep.
- STM32G431KB (Nucleo-G431KB): validated with uptime, sleep, and usleep.
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
up_idlepm() (esp32s3_idle.c/esp32_idle.c/esp32s2_idle.c and the
shared risc-v esp_idle.c for esp32c3/esp32c6) has a recovery branch
that forces the domain back to PM_NORMAL when oldstate is not
PM_NORMAL and nothing is currently staying at it:
pm_stay(PM_IDLE_DOMAIN, PM_NORMAL);
pm_changestate(PM_IDLE_DOMAIN, PM_NORMAL);
newstate = PM_NORMAL;
pm_stay() here has no matching pm_relax() anywhere in any of the
four files. The first time this branch runs, the stay count for
PM_NORMAL never returns to 0, and pm_checkstate() (called
unconditionally right after this block) can never recommend
anything deeper than PM_NORMAL again for the rest of uptime -- the
idle loop keeps running, but the governor is permanently pinned at
full power, with no further light or deep sleep.
Confirmed on real ESP32-S3 hardware (XIAO ESP32-S3,
CONFIG_ESPRESSIF_WIFI + CONFIG_PM + CONFIG_SCHED_TICKLESS): reading
g_pmdomains[0] live via JTAG/GDB showed a "system" wakelock stuck at
state=PM_NORMAL, count=1, acquired a few seconds after boot (right
when Wi-Fi coming up briefly moves the domain off PM_NORMAL and this
branch then forces it back). Reproduced 4/4 times before this fix
(never a single PM_STANDBY transition or light-sleep-return log line
across a 40+ minute run), 0/4 after.
The trigger is timing-dependent (whether anything else already
holds PM_NORMAL at the moment this branch runs), which is likely why
it does not reproduce on every single boot.
Fix: release the stay right after the one pm_changestate() call it
exists to force, matching the comment already there ("Keep working
in normal stage") -- a one-shot nudge, not a standing hold.
Touching the switch statement right below the fix in all four files
exposed a pre-existing nxstyle violation (case labels indented level
with the switch's opening brace instead of one level in from it, per
NuttX style); reindented alongside since checkpatch lints the whole
file. esp32s3_idle.c also had two unrelated stray-indented lines
("Perform IDLE mode power management" / up_idlepm()) in up_idle();
fixed those too, same reason.
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Assisted-by: Claude:claude-sonnet-5
Moves EPWM initialization from am67_bringup.c to am67_pwm.c. Initialization
now requires only a function call in bringup.c.
Nxstyle checked, builds same.
Signed-off-by: Ulaş Sertan Kemeç <sertan.usk@gmail.com>
Record the EPWM0 and EPWM1 outputs in the board's Peripheral Support
list.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Ulaş Sertan Kemeç <sertan.usk@gmail.com>
Adds a PWM lower-half for EPWM0 and EPWM1, covering both output channels of
each. The CTRL_MMR EPWM clock enables are unlocked once during board bring-up.
t3-gem-o1 registers /dev/pwm0 and /dev/pwm1 with PWM_NCHANNELS=2.
Verified on t3-gem-o1: all four outputs (EPWM0 A+B, EPWM1 A+B) drive physical
pins, jumpered into a Linux GPIO input -- 50% and 20% duty read back at the
expected sample ratios, and gpiomon timed a 50 Hz half-period at 9.998-10.002
ms. examples/pwm starts and stops a 1 kHz train cleanly.
Assisted-by: Claude Code:claude-fable-5
Signed-off-by: Ulaş Sertan Kemeç <sertan.usk@gmail.com>
Document the purpose of the rv-virt elf and libcxx64 configurations and clarify that the LEDs used by leds64 are virtual/log-only and are not backed by a NuttX GPIO controller.
This addresses issue #20174.
mtdconfig_unregister_by_path() opened the device with file_open(),
which runs mtdconfig_open() and therefore holds dev->lock for the
whole lifetime of the temporary file reference. It then destroyed
the mutex and freed the private device structure while that
reference was still open, so the subsequent file_close() reached
mtdconfig_close(), which performs nxmutex_unlock() on freed memory.
Destroying a held mutex and unlocking it after free corrupt the heap;
on sim this crashes deterministically in the next allocation
(EXC_BAD_ACCESS in mm_malloc). Both file_close() and
unregister_driver() return values were also discarded and the
function unconditionally returned OK, masking legitimate errors.
Reorder the teardown to close -> unregister -> destroy/free and
propagate errors, so that:
- file_close() (driver close callback and inode release) runs while
the private device structure is still valid, releasing the
exclusive access taken by mtdconfig_open(),
- the private structure is destroyed and freed only after
unregister_driver() succeeds. On failure the inode (and with it
i_private) may still be referenced, so freeing would be wrong.
Returning the error also honors the documented API contract
(zero on success, negated errno on failure).
This matches the established close -> unregister -> teardown ordering
used by e.g. bchdev_unregister().
Verified with sim:configdata plus a register/unregister lifetime
exercise in examples/configdata: 934706/934706 checks pass with the
fix; with the fix stashed the same run dies with SIGSEGV right after
mtdconfig_unregister_by_path() returns.
Fixes: https://github.com/apache/nuttx/issues/20166
Signed-off-by: Arnav Sharma <2006arnavsharma@gmail.com>
pkt_poll() records the outgoing IOB in pkt_conn->pendiob so that any
synchronous TX tap or loopback executed during the driver callback can
skip delivering the packet back to the sending socket.
Previously, pendiob was never cleared upon TX completion and would
linger across transmissions as a dangling pointer. Because the IOB
pool is small and recycled quickly (LIFO), a subsequent incoming
packet from the network frequently reused the same IOB buffer address,
causing pkt_in() to drop legitimate RX packets as false self-echoes.
Drop the pendiob reference immediately after callback(dev) returns in
devif_poll_pkt_connections(), ensuring the pointer never outlives the
transmission cycle.
Also fixes a pre-existing nxstyle alignment issue in devif_poll.c
IPv6 version-check block (unrelated nerr() call), since this file is
now touched and CI enforces style on the whole file.
Suggested-by: zhhyu7
Assisted-by: Gemini:gemini-3.8-flash-medium
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
openeth_receive() (arch/xtensa/src/common/espressif/esp_openeth.c)
tracks the next expected RX descriptor in priv->cur_rx_desc, an int
initialized to 0 exactly once, in esp_openeth_initialize(). QEMU's
esp32s3 machine models the OpenCores MAC's DMA ring pointer as
resetting to descriptor 0 every time RXEN is toggled off and back on
(openeth_disable()/openeth_enable(), called from ifdown()/ifup()), but
nothing rewinds the driver's own index to match. On the very first
bring-up both start at 0, so nothing looks wrong; from the second
ifup() onward the two permanently disagree, openeth_receive() keeps
inspecting the wrong descriptor, finds it still marked "owned by HW"
(e=1), and silently drops the notification. This breaks all inbound
traffic on the interface, not just application sockets -- ARP replies
and ICMP echo replies are RX frames too, so ping breaks identically.
Re-run the same descriptor initialization esp_openeth_initialize()
does at boot -- re-arm every RX/TX descriptor, rewind
cur_rx_desc/cur_tx_desc to 0 -- inside openeth_ifup(), under the same
critical section that already toggles RXEN.
Board-independent code, and open_eth only exists as a QEMU peripheral,
so there is no real-hardware regression risk.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Aligns the qemu-i486 nsh and vga_fb defconfigs with the nxinit
migration already done for sim, rv-virt and qemu-armv8a:
- CONFIG_INIT_ENTRYPOINT/ENTRYNAME: "nsh_main" -> "init_main"
- CONFIG_SYSTEM_NXINIT=y, plus its dependencies CONFIG_EXPERIMENTAL,
CONFIG_LIBC_EXECFUNCS, CONFIG_SCHED_HAVE_PARENT and
CONFIG_SCHED_CHILD_STATUS
- CONFIG_ETC_ROMFS=y (CONFIG_FS_ROMFS was already enabled on both
configs)
- New boards/x86/qemu/qemu-i486/src/etc/init.d/init.rc, identical in
content to boards/arm64/qemu/qemu-armv8a/src/etc/init.d/init.rc
(service console sh + restart_period 1000, started from `on init`
under CONFIG_SYSTEM_NSH)
- src/Makefile: add init.rc to RCSRCS when CONFIG_ETC_ROMFS and
CONFIG_SYSTEM_NXINIT are both set, matching qemu-armv8a's
src/Makefile
This depends on the previous commit ("arch/x86: Add -P to CPP to
suppress linemarkers."): without it, the preprocessed init.rc that
Board.mk feeds to nxinit's parser at build time still contains GNU
linemarker lines and the parser rejects it with -EINVAL at boot. That
arch-level fix is otherwise independent and can be reverted on its
own without affecting other x86 boards.
qemu-i486 is 32-bit x86 with no romfs_img/romdisk_register/
romfs_boot/romfs_stub definitions anywhere under its board directory,
so it does not hit the romfs_img symbol collision that affects
qemu-intel64 (a separate board, tracked separately); i486 goes
straight from a clean ETC_ROMFS build to a working /etc mount.
Verified under QEMU (qemu-system-i386), both configs, host gcc -m32
(CROSSDEV is unset on Linux, ARCH_X86_M32=y already handles -m32):
nsh (-cpu 486 -m 2):
nsh> ps
TID PID PPID PRI POLICY TYPE NPX STATE EVENT SIGMASK STACK COMMAND
0 0 0 0 FIFO Kthread - Ready 0000000000000000 0002024 Idle_Task
2 2 0 100 FIFO Task - Waiting Semaphore 0000000000000000 0002004 init_main
3 3 2 100 FIFO Task - Running 0000000000000000 0002012 sh
nsh> mount
/etc type romfs
/proc type procfs
nsh> free
total used free maxused maxfree nused nfree name
572784 9680 563104 10048 563104 52 1 Umem
vga_fb (-cpu 486 -m 1024 -vga std -serial stdio -display none): same
init_main/sh parent-child relationship, /etc romfs mounted, `fb`
framebuffer test completes ("Test finished"); free shows
551696/86832/464864 total/used/free (heavier due to LCD framebuffer
allocations, still well clear of CONFIG_RAM_SIZE=1048576).
ostest (third config on this board, INIT_ENTRYPOINT="ostest_main")
is out of scope and left untouched.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
The x86 CPP definition used `gcc -E -x c` without `-P`, unlike every
other arch's Toolchain.defs (arm, risc-v, avr, mips, misoc, or1k, z16,
z80 all pass `-E -P -x c`). Without `-P`, cpp emits GNU linemarker
lines (e.g. `# 0 "file"`) into its preprocessed output.
boards/Board.mk's PREPROCESS macro runs RCSRCS init.rc files through
$(CPP) before feeding them to apps/system/nxinit's parser. The parser
(apps/system/nxinit/parser.c) matches each line against known section
keywords ("on", "service", ...) with strncmp(); a leading linemarker
line does not match any keyword and the parser returns -EINVAL, so
any board that preprocesses an nxinit init.rc under x86 fails to
parse it at boot.
Reproduced independently on the host toolchain: `gcc -E -x c` on a
minimal init.rc emits `# 0 "file"` lines; `gcc -E -P -x c` on the
same input produces clean `service`/`on` lines only.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
fdlist_extend() grows a task group's descriptor table to 'row' rows of
CONFIG_NFILE_DESCRIPTORS_PER_BLOCK entries each, and guards the growth
against OPEN_MAX:
if (CONFIG_NFILE_DESCRIPTORS_PER_BLOCK * (orig_rows + 1) > OPEN_MAX)
The check sizes the table at orig_rows + 1, which assumes the caller
only ever grows by a single block. The function then allocates 'row'
rows, so the two agree only for growth by one.
Callers do skip ahead. fdlist_dup3() asks for
fd2 / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + 1, fdlist_dupfile() for the
row holding minfd, and fdlist_copy() for the row holding a parent
descriptor it is duplicating. Any of those can request a row well past
orig_rows + 1.
Such a request passes the check and the function then allocates and
installs a table with more than OPEN_MAX descriptors. With the defaults
(8 per block, OPEN_MAX 256) a process holding one row that calls
dup2(fd, 400) ends up with 51 rows, or 408 descriptor slots, against a
256 limit.
Check the row actually being requested. For single-block growth
row == orig_rows + 1 and the comparison is unchanged.
Signed-off-by: AlmAck <gluca86@gmail.com>
Simpleboot on espressif changes the location of irom and drom segments
in the image. Instead of correcting in `map_rom_segments` a routine
is introduced that corrects the load addresses before calling
`map_rom_segments`.
Signed-off-by: Laczen JMS <laczenjms@gmail.com>
Record the WKUP_I2C0 master in the board's Peripheral Support list.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Ulaş Sertan Kemeç <sertan.usk@gmail.com>
Adds an I2C master driver for the AM67 I2C instances, completing transfers on
ARDY as the K3 controller signals.
Bring-up is deferred to the first transfer, because the Linux Device Manager
enables the I2C clocks late and touching the bus during early board init is not
safe here. The last reference drop clears the flag so the next transfer
re-initialises the hardware.
t3-gem-o1 registers WKUP_I2C0 as /dev/i2c2.
Verified on t3-gem-o1: i2c dev finds 0x30, 0x40, 0x51 and 0x68, the RTC at
0x68 reads a ticking BCD seconds register, repeated reads are consistent, and
NACK recovery returns the bus to a usable state.
Assisted-by: Claude Code:claude-fable-5
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Ulaş Sertan Kemeç <sertan.usk@gmail.com>
SDIR_template and MAKE_template did not pass TOPDIR to recursive makes.
When a sub-make needed to include $(TOPDIR)/Make.defs (e.g. to define
SDIR_template for further recursion), TOPDIR was missing and the include
silently failed via '-include'.
Pass TOPDIR in both templates so recursive makes have full access to the
build system configuration.
Signed-off-by: hanzhijian <hanzhijian@zepp.com>
esp32s3-devkit and esp32s3-eye both call board_spiflash_init() from
their bring-up to register the internal SPI flash MTD partition and
mount its file system; esp32s3-xiao never did, so CONFIG_ESP32S3_SPIFLASH
built but no /dev/... MTD partition or mount ever appeared -- same shape
of gap as the IMU, SD, console and Wi-Fi wiring already fixed for this
board.
Guarded with pm_stay(PM_IDLE_DOMAIN, PM_IDLE) the same way the Wi-Fi
bring-up below it is: flash operations run with the cache disabled and
can't tolerate PM_STANDBY's clock gating either.
Confirmed under QEMU's esp32s3 machine: LittleFS mounts, and a counter
file written to it survives a reboot.
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Assisted-by: Claude:claude-sonnet-5
esp32s3-devkit, esp32s3-ws-lcd128, lckfb-szpi-esp32s3 and esp32-devkitc
all call esp_openeth_initialize() from their bring-up to register the
openeth MAC (the NIC QEMU's esp32s3 machine provides); esp32s3-xiao
never did, so CONFIG_ESP32S3_OPENETH built but no wlan/eth netdev ever
registered under QEMU -- same shape of gap as the IMU, SD, console,
Wi-Fi and SPI-flash wiring already fixed for this board.
Confirmed under QEMU's esp32s3 machine, combined with the esp_openeth
RX-interrupt fix (merged in 88c8623ced): the netdev registers and the
guest genuinely sends and receives.
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Assisted-by: Claude:claude-sonnet-5
esp_openeth_initialize() (arch/xtensa/src/common/espressif/esp_openeth.c)
attaches the MAC interrupt with esp_setup_irq() but never calls
up_enable_irq(OPENETH_IRQ_MAC), unlike every other Espressif driver in
this tree. Left masked, openeth_isr_handler() never runs and received
frames are only picked up when the netdev work thread happens to run
for some other reason (a transmit). A guest can therefore send but
effectively not receive: ping still works because each request is
itself a transmit, while a socket blocked in recvfrom() waits on a
wake-up that never comes.
Confirmed with a GDB breakpoint counter on openeth_isr_handler():
zero hits before the fix, dozens after, under QEMU's esp32s3 machine
(the open_eth NIC it emulates). With the interrupt enabled, TCP
retransmits over a fixed test window dropped from 86 to 4.
Separately, openeth_ifdown() calls openeth_enable() right under a
comment that says "Disable TX and RX" -- it should call
openeth_disable(), which is what actually disables the two DMA
descriptor rings. Fixed alongside since it's the same function and
the same class of mistake.
Board-independent (arch/xtensa/src/common/espressif), not specific
to any one esp32s3 board; open_eth itself only exists as a QEMU
peripheral, so there's no real-hardware regression risk from either
change.
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Assisted-by: Claude:claude-sonnet-5
Convert failures from CLOCK_FD lookup and PTP_CLOCK_GETRES into the public
clock_getres() convention of returning ERROR and setting errno. This keeps
dynamic PTP clocks consistent with the other clock_getres() error paths.
Assisted-by: OpenAI Codex
Signed-off-by: yushuailong <yyyusl@qq.com>
Move the critical-monitor update after PID hash entry validation and keep
the scheduler critical section held so the TCB remains stable. Invalid or
stale PIDs now return -ESRCH instead of passing a NULL TCB to
nxsched_update_critmon().
Also add the declaration spacing required by nxstyle in the modified file.
Assisted-by: OpenAI Codex
Signed-off-by: yushuailong <yyyusl@qq.com>
Move RTC synchronization outside the non-timekeeping branch so setting
CLOCK_REALTIME also updates the RTC when CONFIG_CLOCK_TIMEKEEPING is
enabled. This prevents corrected wall time from reverting to an older RTC
value after restart.
Preserve the existing low-priority work queue path for RTC drivers that may
block while updating hardware.
Assisted-by: OpenAI Codex
Signed-off-by: yushuailong <yyyusl@qq.com>
Remove pending IRQ work before clearing its callback state, and guard the
worker callback against a concurrent detach. This prevents detached worked
IRQs from invoking a NULL function pointer.
Assisted-by: OpenAI Codex
Signed-off-by: yushuailong <yyyusl@qq.com>
Only call kthread_delete for a valid positive PID and always clear the IRQ
thread slot afterward. This makes detach on an unused IRQ, including a
repeated detach, a safe no-op instead of deleting the calling task.
Assisted-by: OpenAI Codex
Signed-off-by: yushuailong <yyyusl@qq.com>
Return ENOMEM and leave the IRQ detached when no custom work queue can be
created or all queue slots are occupied. Also release the queue mutex on
the full-table path and avoid caching a failed queue creation.
Assisted-by: OpenAI Codex
Signed-off-by: yushuailong <yyyusl@qq.com>
Store the thread PID only after kthread_create succeeds. This prevents a
negative error value from making subsequent attachment attempts fail with
EINVAL after a transient thread creation failure.
Assisted-by: OpenAI Codex
Signed-off-by: yushuailong <yyyusl@qq.com>
up_switch_context() and up_exit() released the critical section and then
kept using the outgoing task's stack: a call/ret through
nxsched_switch_context() and the call into x86_64_fullcontextrestore().
Once the lock is released the outgoing task can be woken and run by
another CPU on that same stack, so those accesses race with it.
Release the critical section as the last step and enter
x86_64_fullcontextrestore() with a jmp so nothing is read from or
written to the outgoing stack after the release.
Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
x86_64_fullcontextrestore() built the iretq frame by pushing onto the
current stack. When called from up_switch_context()/up_exit() that is
the outgoing task's stack, and the outgoing task may already be running
on another CPU, whose pushes clobber the frame before iretq consumes it,
causing a #GP/#PF panic under SMP load.
REG_RIP..REG_SS are contiguous and match the iretq frame layout, so
point RSP at the register save area and iretq from there without
touching the stack at all.
Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
The initial IDLE RSP was placed inside the IDLE register save area that
up_initial_state() later carves out of the stack top, so the idle thread
ran on its own saved context and corrupted it, causing a #GP/#PF panic
under interrupt load. Compute the save area position exactly and start
RSP below it.
Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
The -16 in g_idle_topstack put the derived CPU0 idle stack base at
_ebss - 16, and tls_init_info() writes the TLS info there, corrupting
the last 16 bytes of .bss. Start the stack at _ebss like other
architectures.
Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
Add commonly required POSIX/BSD interfaces that portable command-line
utilities expect but that were missing from the C library:
- include/paths.h: _PATH_DEFPATH and the other standard default paths.
- include/sys/ttydefaults.h: BSD default control-character and terminal
flag definitions.
- include/termios.h: define the IUTF8 input flag.
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
The memory report builds this pull request merged into master, together
with the nuttx-apps default branch, and nothing else, so a companion or
predecessor pull request it declares is absent. For a breaking change the
target then fails to build and no report is produced at all, even though
the Build workflow already tests the declared sources through Depends-On.
Apply the declared dependencies before building, reusing the parser and
the fetch/cherry-pick sequence that build.yml uses, and mapping each
repository to its checkout exactly as build.yml does. A stacked nuttx
dependency is no more optional than an apps one: a pull request that uses
an API its predecessor introduces does not build without it.
As build.yml does, read the description through the API rather than
trusting the event payload, so that a manual re-run after editing a
Depends-On line applies the current declaration instead of the one the run
was created with. Unlike build.yml, a failed read stops the job rather
than falling back to the payload: build.yml resolves this once and hands
every target the same tree, while this job runs per target, so a fallback
could leave targets on different declarations while their results are
filed under one SHA.
A declared dependency that cannot be applied fails the job, and so does a
missing parser, a parser crash, or a status this step does not recognise:
each of those means the declaration was never evaluated, and continuing
would measure a combination nobody asked for. build.yml fails Fetch-Source
on the same conditions, and no other step in this job carries
continue-on-error, so falling back silently would be inconsistent with
both. A declaration that parses to nothing valid only warns, again
matching build.yml.
Forward the parser's warnings too. --print-state prints only the state, so
an entry the parser drops -- an unsupported repository, say -- would
otherwise leave no trace here at all, although build.yml annotates it, and
the source set named below would be silently incomplete.
The report is filed under the pull request head SHA rather than the SHA of
the tree that was built, so the measurement cannot be reproduced from that
SHA alone and cannot be split per dependency. That limits provenance, not
the measurement: a combined result is what the declaration asks for, and a
regression that only appears in combination is still a regression. Name
the whole source set in the step summary so the reader knows which heads
went into the number.
Note in the parser that the --print-state output is a parsed contract; the
edit gate that used to be its only caller is gone.
Update the CI documentation to match. Its Pull Request Dependencies
section attributes dependency application to build.yml's Fetch-Source
job alone, so after this change it would read as if the memory report
measured the normal source selection. Cross-reference the two sections
rather than restating the rules, which stay shared.
Signed-off-by: zhangning21 <zhangning21@xiaomi.com>
Fix a potential deadlock in the DMA driver. DMA completion callbacks
may immediately submit another transfer, for example:
imx9_dmaterminate()
-> imx9_dma_txcallback()
-> imx9_dma_txavailable()
-> uart_xmitchars_dma()
-> imx9_dma_send()
-> imx9_dmach_stop()
-> imx9_dmaterminate()
Resulting dmaterminate to take the same spinlock again. Fix this by moving
the spin_unlock_irqrestore_nopreempt before calling the callback. It is not
necessary to keep dma channel locked during the callback; the channel is
already free at this point.
This doesn't directly affect arch/arm/imx9 (the cortex-m version) because
it is not SMP (the spinlock is reduced to blocking irqs), but it is worth
fixing at the same to keep drivers in sync.
Signed-off-by: Jukka Laitinen <jukka.laitinen@tii.ae>
sensor_poll() arms a per-subscriber watchdog for fetch()-only sensors
with a requested interval. The watchdog handler sensor_fetch_expired()
dereferences the subscriber and re-arms itself unless user->fds is NULL.
sensor_poll() teardown clears user->fds and cancels the watchdog, but
sensor_close() removed the subscriber from the user list and freed it
without doing either. A close() racing an armed timer therefore lets
the handler run after the subscriber is freed, causing a timer-context
use-after-free and re-arm of a freed watchdog.
Mirror the poll teardown in sensor_close(): clear user->fds and cancel
user->wdog under upper->lock before notifying other users and freeing
the subscriber.
Fixes#20145.
Signed-off-by: arnavsharma990 <2006arnavsharma@gmail.com>
Document the Python format and lint tools enforced by checkpatch.sh and CI,
and show how to run the existing Python auto-format path.
Assisted-by: ChatGPT:gpt-5.6-sol
Signed-off-by: Taha Zarif <tahazarif380@gmail.com>
atexit_call_exitfuncs() cached its loop bound on entry
(for (idx = aehead->nfuncs - 1; idx >= 0; idx--)), while
atexit_register() appends new entries at funcs[nfuncs] and bumps nfuncs.
Any function registered by an exit handler via atexit() / on_exit() /
__cxa_atexit() lands above the cached bound and is never invoked, even
though the registration returns OK.
This contradicts the exit(3) documentation that NuttX mirrors verbatim
in its own exit() docstring (libs/libc/stdlib/lib_exit.c):
It is possible for one of these functions to use atexit(3) or
on_exit(3) to register an additional function to be executed
during exit processing; the new registration is added to the
front of the list of functions that remain to be called.
The same restructure closes a second defect: atexit_call_exitfuncs()
read and cleared the task-group-shared ta_exit list without holding
ta_lock, while atexit_register() takes it ("The following must be
atomic"). Entries are now claimed under the lock and the handler is
invoked with the lock released, so a handler re-entering
atexit_register() cannot deadlock (also safe with the non-recursive
nxmutex used here).
Evidence: exit(3) man page, DESCRIPTION -
https://man7.org/linux/man-pages/man3/exit.3.html
NuttX mirrors this passage verbatim in its own exit() docstring --
5a209a853e/libs/libc/stdlib/lib_exit.c (L65-L70)
Before:
```
A handler that registers another function during exit processing
gets a success return from atexit(), but the new function is never
invoked - it lands above the loop bound cached on entry.
```
After:
```
A registration made during exit processing runs before the older
remaining handlers (order A -> B -> C below), matching the exit(3)
guarantee, and the list is consumed under ta_lock.
```
Testing:
Simulated (sim:nsh, CONFIG_LIBC_MAX_EXITFUNS=8).
Build and run:
```
cmake -B build -DBOARD_CONFIG=sim:nsh -GNinja
cmake -S . -B build # after setting CONFIG_LIBC_MAX_EXITFUNS=8
# in build/.config (sim:nsh default is 1)
cmake --build build -j$(nproc)
(echo hello; echo poweroff) | ./build/nuttx
```
"hello" runs the test at the NSH prompt; poweroff terminates the sim.
The test was carried by apps/examples/hello/hello_main.c (scratch only,
not part of this commit); its diff:
```
--- a/examples/hello/hello_main.c
+++ b/examples/hello/hello_main.c
@@ -24,6 +24,7 @@
#include <nuttx/config.h>
#include <stdio.h>
+#include <stdlib.h>
/****************************************************************************
* Public Functions
@@ -33,8 +34,29 @@
* hello_main
****************************************************************************/
+static void handler_b(void)
+{
+ printf("ATEXIT-TEST: handler B called (registered during exit)\n");
+}
+
+static void handler_a(void)
+{
+ int ret;
+
+ printf("ATEXIT-TEST: handler A called\n");
+ ret = atexit(handler_b);
+ printf("ATEXIT-TEST: atexit(handler_b) inside A returned %d\n", ret);
+}
+
+static void handler_c(void)
+{
+ printf("ATEXIT-TEST: handler C called\n");
+}
+
int main(int argc, FAR char *argv[])
{
printf("Hello, World!!\n");
+ atexit(handler_c); /* older entry, must run LAST */
+ atexit(handler_a); /* registers handler_b during exit */
return 0;
}
```
Before the fix:
```
Hello, World!!
ATEXIT-TEST: handler A called
ATEXIT-TEST: atexit(handler_b) inside A returned 0
ATEXIT-TEST: handler C called
```
(handler B is never invoked although its registration returned 0)
After the fix:
```
Hello, World!!
ATEXIT-TEST: handler A called
ATEXIT-TEST: atexit(handler_b) inside A returned 0
ATEXIT-TEST: handler B called (registered during exit)
ATEXIT-TEST: handler C called
```
Assisted-by: Claude Code (GLM-5.3) <claude@anthropic.com>
Signed-off-by: Junbo Zheng <zhengjunbo1@xiaomi.com>
strlcpy() was given sizeof(tcb->name), i.e. CONFIG_TASK_NAME_SIZE + 1,
but the documented caller contract is a buffer of CONFIG_TASK_NAME_SIZE
bytes (include/sys/prctl.h). When a task name is exactly
CONFIG_TASK_NAME_SIZE chars (the normal result of nxtask_setup_name()
truncation), the terminating NUL lands one byte past the caller buffer.
Pass CONFIG_TASK_NAME_SIZE to strlcpy() so the copy is truncated
in-bounds, and drop the stale forced-NUL line left over from the strncpy
era (it ran after the overflow had already happened).
Before:
```
guard byte placed right after a CONFIG_TASK_NAME_SIZE caller buffer
reads 0x00 (expected 0xAA) after the call: strlcpy writes its
terminating NUL one byte past the buffer when the task name is exactly
CONFIG_TASK_NAME_SIZE chars.
```
After:
```
strlcpy(name, tcb->name, CONFIG_TASK_NAME_SIZE) writes at most
CONFIG_TASK_NAME_SIZE bytes; the caller buffer stays intact.
```
Testing:
Simulated (sim:nsh, CONFIG_TASK_NAME_SIZE=31).
Build and run:
```
cmake -B build -DBOARD_CONFIG=sim:nsh -GNinja
cmake --build build -j$(nproc)
echo hello | ./build/nuttx
```
then run "hello" at the NSH prompt.
The test was carried by apps/examples/hello/hello_main.c (scratch only,
not part of this commit); its diff:
```
--- a/examples/hello/hello_main.c
+++ b/examples/hello/hello_main.c
@@ -24,6 +24,8 @@
#include <nuttx/config.h>
#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
/****************************************************************************
* Public Functions
@@ -35,6 +37,55 @@
int main(int argc, FAR char *argv[])
{
+ /* Longest-legal task name: exactly CONFIG_TASK_NAME_SIZE chars, the
+ * normal result of nxtask_setup_name() truncation.
+ */
+
+ static const char longname[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+ /* Caller buffer per the documented prctl(PR_GET_NAME) contract, with a
+ * guard byte immediately after it to detect the 1-byte overflow.
+ */
+
+ struct
+ {
+ char buf[CONFIG_TASK_NAME_SIZE];
+ volatile unsigned char guard;
+ } s;
+
+ _Static_assert(sizeof(longname) - 1 > CONFIG_TASK_NAME_SIZE,
+ "test name must exceed CONFIG_TASK_NAME_SIZE");
+
printf("Hello, World!!\n");
+ printf("prctl test: CONFIG_TASK_NAME_SIZE=%d\n", CONFIG_TASK_NAME_SIZE);
+
+ s.guard = 0xaa;
+ s.buf[0] = '\0';
+
+ if (prctl(PR_SET_NAME, (unsigned long)longname) != 0)
+ {
+ printf("prctl test: PR_SET_NAME failed\n");
+ return 1;
+ }
+
+ if (prctl(PR_GET_NAME, (unsigned long)s.buf) != 0)
+ {
+ printf("prctl test: PR_GET_NAME failed\n");
+ return 1;
+ }
+
+ printf("prctl test: guard=0x%02x (expected 0xaa), name len=%zu, "
+ "last char=0x%02x\n",
s.guard, strlen(s.buf), (unsigned char)s.buf[strlen(s.buf)]);
+
+ if (s.guard != 0xaa)
+ {
+ printf("prctl test: FAIL - terminating NUL written 1 byte past "
+ "the caller buffer\n");
+ return 1;
+ }
+
+ printf("prctl test: PASS - caller buffer intact\n");
return 0;
}
```
Before the fix:
```
prctl test: guard=0x00 (expected 0xaa), name len=30, last char=0x00
prctl test: FAIL - terminating NUL written 1 byte past the caller buffer
```
After the fix:
```
prctl test: guard=0xaa (expected 0xaa), name len=30, last char=0x00
prctl test: PASS - caller buffer intact
```
Assisted-by: Claude Code (GLM-5.3) <claude@anthropic.com>
Signed-off-by: Junbo Zheng <zhengjunbo1@xiaomi.com>
The compilation of stm32_mpuinit.c is guarded by CMakeLists.txt and
Make.defs, so this is unneccessary.
Signed-off-by: Darryl Ring <darryl@bluerobotics.com>
If CONFIG_ARM_MPU and CONFIG_STM32_ICACHE are set, this will configure an
MPU region marking the OTP flash as non-cacheable. This prevents hard faults
when accessing the 4K OTP region from software.
Signed-off-by: Darryl Ring <darryl@bluerobotics.ca>
This adds MPU initialization code based on the STM32U5. Unlike the
STM32U5 code, though, this allows the MPU to be used outside of
PROTECTED build mode.
PROTECTED build mode is still not yet supported.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Darryl Ring <darryl@bluerobotics.com>
Add a Peripheral Support section to the board page listing the GPIO and
MCU_MCSPI0 drivers, and replace the "UART console only" warning on both
the chip and board pages -- it no longer describes the port. The
replacement states what actually constrains the port: NuttX runs on the
R5F under RemoteProc and depends on the bootloader or Linux Device
Manager having powered and clocked the peripherals, because there is no
TISCI client yet.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Ulaş Sertan Kemeç <sertan.usk@gmail.com>