Set i_owner and i_group from the caller's effective credentials in
inode_reserve(), covering IPC objects, FIFOs, and pseudo-files.
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
Implement POSIX real/effective/saved credential getters and paired
setters in the task group layer, with libc stubs and syscalls.
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
The USB device controller drivers invoke CLASS_DISCONNECT() on every
USB bus reset, and a bus reset is the first step of normal host
enumeration. Every other class driver (cdcacm, usbmsc, rndis)
re-asserts DEV_CONNECT() at the end of its disconnect() handler so
that the device remains attached; cdcecm and cdcncm did not, so on
controllers that soft-disconnect around bus reset (e.g. rp2040, which
drops the pull-up in its bus-reset handler) a standalone CDC-ECM or
CDC-NCM device is left soft-disconnected by the first bus reset and
never enumerates on the host.
Mirror the cdcacm behavior and perform the soft connect in the
disconnect() methods, unless part of a composite device (composite.c
already re-connects in its own disconnect handler).
Fixes the standalone CDC-ECM case of issue #15880.
Validated on raspberrypi-pico (RP2040): with this change a standalone
CONFIG_NET_CDCECM device that previously never appeared on the host
enumerates via cdc_ether and pings with 0% loss. cdcncm has the
identical defect and receives the identical fix.
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Co-authored-by: Xiang Xiao <xiaoxiang781216@gmail.com>
Assisted-by: Claude (Anthropic Claude Code)
rp2040_update_buffer_control()/rp23xx_update_buffer_control() re-arm an
endpoint buffer by setting the AVAILABLE bit in the buffer-control word,
which lives in DPSRAM. When a buffer is re-armed from the completion
path (rp2040_usbintr_buffstat/rp23xx_usbintr_buffstat), that runs after
the handler has cleared the endpoint's bit in BUFF_STATUS, which lives in
the USB controller register block -- a different peripheral region.
The bus fabric may reorder those two stores. If the controller observes
the AVAILABLE re-arm before the BUFF_STATUS clear lands, it can transmit
the next IN packet and latch its completion in BUFF_STATUS before the
clear arrives; the late clear then wipes that just-set completion bit.
The lost completion edge stops all further buffer interrupts for the
endpoint, so the class driver's write-complete callback never runs and
TX wedges permanently.
This is most visible on RP2350 (Cortex-M33) under dense/bursty IN traffic
such as CDC-NCM with TCP write buffers. Add a UP_DMB() at the top of the
AVAILABLE re-arm so the preceding BUFF_STATUS clear is ordered ahead of
it. (Non-SMP builds reduce spin_lock_irqsave to a barrier-free
up_irq_save, so nothing else orders these two stores.)
Assisted-by: Claude (Anthropic Claude Code)
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Adds both xipfs configurations to the board configuration list, and notes
that xipfs-nxflat needs the NXFLAT tools (mknxflat and ldnxflat), which a
standard toolchain installation does not provide.
Impact: documentation only.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
The xipfs configuration enabled CONFIG_NXFLAT and the nxflatxip example,
whose build runs mknxflat to generate the module thunk. The NXFLAT tools
are not installed in the CI container, so every CI build of this
configuration failed with "mknxflat: command not found".
* xipfs keeps the file system, the xipfs command and the test suite. It
builds with a plain toolchain, so CI still covers the file system code.
* xipfs-nxflat adds CONFIG_NXFLAT and CONFIG_EXAMPLES_NXFLATXIP and is
excluded from the CI build list, the same treatment the other NXFLAT
configurations already get (eagle100:nxflat, lm3s6965-ek:qemu-nxflat,
olimex-lpc1766stk:thttpd-nxflat).
Impact: CI and board configurations only. No source or runtime change.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Pages for the three applications that come with xipfs: the command that
compacts a volume and prints its block map, the test suite and what each of
its sections covers, and the demo that downloads an NXFLAT module into a
volume and runs two instances of it in place.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
A configuration for the Pimoroni Pico Plus 2 that mounts xipfs on the flash
MTD and builds everything that exercises it: the test suite with fault
injection, the xipfs command, and the NXFLAT execute-in-place demo.
Building an NXFLAT module needs mknxflat and ldnxflat, so name them in the
rp23xx board Make.defs files, which already carry the rest of the NXFLAT
flags but not these.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Describe the write-once usage model, the strict in-place mmap and what
MAP_XIP_STRICT is for, extent pinning, manual defragmentation and how to read
its result, the power-loss ordering, the on-media layout, and the
limitations.
The NXFLAT page said ROMFS was the only file system able to serve the XIP
mappings its loader needs. That is now one of two, so point at both, and at
what the writable one adds: a module can arrive at run time instead of being
baked into a host-built image.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Give the QEMU boards the same directly addressable media the sim
configuration has: rammtd answers BIOC_XIPBASE with the base of its buffer,
so xipfs layered on it hands out real pointers and the in-place mmap path
can be exercised on an ARM target with no flash present. Registered as
/dev/rammtd and mounted at /mnt/xipfs when xipfs is configured.
Both mps2-an500 (Cortex-M7, armv7e-m) and mps2-an521 (Cortex-M33, armv8-m)
get it, which is what makes the filesystem testable on two different core
generations without either one needing flash.
Each board also gets a xipfs configuration that runs the test suite, so the
bringup above is exercised rather than only compiled. Both run the suite to
completion under QEMU 10.1, 90 checks apiece, the power loss sweeps included.
The an521 configuration carries CONFIG_CMSDK_UART0_RX_IRQ=48 and _TX_IRQ=49
rather than the reversed pair the an521 nsh configuration uses. That is the
SSE-200 order, receive first, and the one consistent both with the overflow
interrupt at 63 that nsh already has and with mps2-an500, which puts RX at 16
and TX at 17. With the pair reversed the TX interrupt reaches
uart_cmsdk_rx_interrupt, which acknowledges only UART_INTSTATUS_RX, so the
board live-locks on its first console write. Correcting nsh is left to a
separate change.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
rammtd answers BIOC_XIPBASE with the base of its RAM buffer, so it is a
usable stand-in for memory mapped NOR: extents are directly addressable and
the in-place mmap path can be exercised end to end without any flash. Mount
xipfs on it when it is the configured file system, and add a configuration
that runs the xipfs test suite, fault injection included.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
The flash MTD device answers BIOC_XIPBASE, which is what xipfs needs to
serve mappings straight out of the memory mapped QSPI flash. Mount it at
/mnt/xipfs when both are configured, formatting on first boot, so a board
comes up with somewhere to download and run a module from.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
A file system that answers statfs with a magic nothing maps to shows up as
"Unrecognized" in df. Give xipfs its constant alongside the others in
sys/statfs.h and the case in fs_gettype that turns it into a name.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
ROMFS is the usual way to carry executables on a NOMMU target with memory
mapped NOR flash: it can hand out a real flash pointer from mmap(), so the
NXFLAT loader maps a module's text in place instead of copying it into RAM.
But a ROMFS image is built on the host and is read only, so a module cannot
be downloaded onto the board at run time.
xipfs is a writable file system with the same in-place property. Each file
is stored as one physically contiguous, erase-block aligned extent, so an
mmap() of it resolves to flash_base + extent_offset and a loader can execute
the file where it already lies. This needs the underlying MTD driver to
answer BIOC_XIPBASE; on the RP2350 rp23xx_flash_mtd.c does.
Files are write once. A file is created, its size is declared, it is
written sequentially, closed, and is thereafter immutable until it is
deleted. That is the whole life cycle of a downloaded module, and it is
what licenses the design: the exact extent is reserved at create time, so
no file ever grows, moves, or fragments internally. Random writes, appends
and truncation of a written file are not supported and are refused.
The only source of fragmentation is therefore free space holes left by
deletes. Allocation fails with -ENOSPC when no single contiguous run is
large enough, and never defragments on its own; the caller decides whether
to compact and retry, through XIPFSIOC_DEFRAG. Defragmentation is manual,
best effort and interruptible: it is a loop of atomic single-extent
relocations, each one copy, commit, erase, so every stop point -- a time
budget, a pinned extent, an erase error -- leaves a consistent layout that
is simply less compact. It reports the largest contiguous run it achieved,
which is what tells the caller whether the retry will fit.
Metadata is committed power safely. Two metadata block sets are used in
ping-pong, each generation carrying a sequence number and a CRC, and every
state change is ordered as write the new data, flip the metadata reference,
then erase what the old one referenced. Mount scans both sets and selects
the last fully valid generation, so a torn write costs the interrupted
operation and nothing else.
A mapping takes a pin on the extent, and the pin lives on the extent rather
than on the file descriptor, so three running instances of one module hold
three pins and the extent becomes movable only when the last one goes.
Defragmentation skips pinned extents, which is what stops it relocating
code that is executing. The pin is released by munmap() or by the task
teardown walk, so a task that dies without unmapping does not leak it.
Directories are records in that same generation, carrying their own identity
and the identity of the directory holding them; the root is implicit and owns
identity zero. They are deliberately NOT objects in the data region, which
is what keeps the commit story in one piece: mkdir and rmdir add or remove a
record and commit one generation, exactly as create and unlink do, so there
is never a multi-object update to journal or an orphan to collect at mount.
An empty directory therefore exists, survives a remount, and costs one entry
out of the volume's fixed supply and no flash blocks at all.
A name is one path component; depth comes from the parent, so XIPFS_NAME_MAX
bounds a component, which is what statfs reports it as. Mount rebuilds the
tree and checks that it is one: identities unique, names unique within a
directory, every parent a live directory, and following parents reaching the
root -- a cycle on the medium would otherwise hang a path walk rather than
merely answering wrongly. '.' and '..' are refused as components, since an
entry stored under either could never be reached again.
The commands that act on the volume rather than on one file --
XIPFSIOC_DEFRAG and XIPFSIOC_LISTPINNED -- are reached through the ioctldir
method, on a descriptor for the mountpoint directory. They are accepted on
a descriptor for a file inside the volume too, but that route holds the file
open for the duration and an open extent cannot be relocated, so a pass
asked for that way is obstructed by the act of asking.
mmap() falls back to the generic RAM copy for ordinary readers when the
media cannot be addressed directly. A module loader must not silently get
a RAM copy, so MAP_XIP_STRICT is added: with it the mapping either resolves
in place or fails with -ENXIO, which the caller can turn into defragment
and retry.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
syslog_write_foreach() compares an unsigned count against a signed
accumulator:
size_t nwritten = 0;
ssize_t nwritten_max = -EIO;
...
if (nwritten > nwritten_max)
{
nwritten_max = nwritten;
}
return nwritten_max;
The usual arithmetic conversions promote nwritten_max to size_t, so -EIO
becomes 4294967291 on a 32-bit target, and the comparison is never true.
nwritten_max keeps its initial value and the function returns -EIO no
matter how many bytes actually went out. Observed under gdb on a running
target: nwritten == 64, nwritten_max == -5, (nwritten > nwritten_max) == 0.
Most callers discard the result -- syslog() itself returns void -- so this
is normally invisible. It becomes fatal when /dev/console is backed by
syslog_console_write(), because then stdio acts on it.
lib_fflush_unlocked() sees a negative return, sets __FS_FLAG_ERROR and
returns early, before resetting fs_bufpos. The bytes have already been
emitted, but the buffer is never cleared, so every subsequent stdio call
re-flushes the same CONFIG_STDIO_BUFFER_SIZE bytes. The console fills
with one repeated fragment and the system makes no further progress.
Reaching that state needs CONFIG_CONSOLE_SYSLOG=y together with no driver
claiming /dev/console ahead of syslog_console_init(). Three in-tree
defconfigs qualify: x86/qemu-i486:ostest, renesas/skp16c26:ostest and
x86_64/qemu-intel64:earlyfb. The other 56 CONSOLE_SYSLOG configurations
have a serial console that registers /dev/console first, which is why this
has gone unnoticed.
Introduced by 1685e8ff7b ("syslog: avoid an infinite loop if one channel
fails"), which changed nwritten_max from size_t to ssize_t = -EIO so that
an all-channels-failed case could be reported. Give nwritten the same type
so the comparison is signed, which preserves that intent: nwritten_max
stays -EIO only when no channel wrote anything. nwritten is never negative,
so the remaining comparisons against buflen are unaffected.
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
During the Toybox port to NuttX, Claude noticed that changes in the
menuconfig weren't taking affect. This issue exists for a long time on
NuttX, in fact BayLibre's presentation from 2017 make jokes about our
building system not been reliable:
https://www.youtube.com/watch?v=XUJK2htXxKw&t=320s
Stale archive members from $(AR)'s additive-only behavior can linger
after Kconfig toggles change which files provide a symbol, causing dead
weight or "multiple definition" link errors on incremental builds.
Fixed by splitting ARCHIVE into two macros: ARCHIVE keeps the original
additive behavior for apps/libapps.a, which many independent
subdirectories contribute to across a build, while the new
ARCHIVE_REBUILD deletes then archives for the far more common case
of a single Makefile building its own self-contained $(OBJS)
- all 39 such call sites now use it.
Assisted-By: Claude Sonnet 5
Signed-off-by: Alan C. Assis <acassis@gmail.com>
The reference manual (section 3.2.1) states the user has to
read GLERR and INTERR registers to clear their bits and release
ERR pin after the startup sequence. Error bits are set to 1 after
the startup if external power supply is used.
Not clearing the bits leads to subsequent read call errors if VBB
errors are checked.
Signed-off-by: Michal Lenc <michallenc@seznam.cz>
tcp_start_monitor() is called from accept() (net/inet/inet_sockif.c) for
each newly accepted connection. When the peer had already closed the
connection before accept() ran, the monitor takes an early-return path so
that any read-ahead data buffered on the connection can still be drained;
it returns OK in that case. accept() (net/socket/accept.c) then marks the
new socket _SF_CONNECTED unconditionally.
If the peer aborts the connection with an RST immediately after the
three-way handshake completes (for example any close with SO_LINGER
{1, 0}), the connection is moved to TCP_CLOSED with no buffered data, yet
accept() still hands back a socket that reports _SS_ISCONNECTED. A
subsequent blocking send() on that socket passes the connected check,
registers a send callback and waits on its semaphore forever: the only
TCP_ABORT event was delivered before the callback existed, and no further
ACK, POLL or disconnect event is generated for a closed connection, so the
waiter is never woken.
Any server that writes before reading can hit this; the telnet daemon
(netutils/telnetd) is one example, where the accepted session task blocks
in send() and never completes.
Only return OK from the already-closed path when there is actually
read-ahead data to drain. Otherwise the connection is dead, so fall
through to the -ENOTCONN return: accept() then fails cleanly instead of
handing back a socket wedged on a connection that will never make progress.
The graceful-close-with-pending-data case (the reason the OK path exists)
is preserved by the conn->readahead check.
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude (Anthropic Claude Code)
mkallsyms.py prints "Please execute the following command to install
dependencies: pip install pyelftools cxxfilt" and then calls
os._exit(errno.EINVAL). os._exit() terminates without flushing stdio, so
when stdout is a pipe -- which it always is under make -- the message sits in
the buffer and is discarded. What the developer sees is:
make[1]: *** [Makefile:65: nuttx] Error 22
with no indication of the cause anywhere in the build output. Error 22 is
just errno.EINVAL leaking out as an exit status. The same applies to
usage(), which exits ENOENT the same way.
Use sys.exit() in both places, which raises SystemExit and lets the
interpreter flush on the way out. Nothing else changes: the exit statuses
are the same, and os is no longer referenced, so the import goes too.
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
In a protected build arm_svcall.c treats the caller's CONTROL as part of
the saved system call state: it stores it in xcp.syscall[].ctrlreturn on
entry and restores it from there on SYS_syscall_return. All three
Cortex-M profiles do this -- armv6-m, armv7-m and armv8-m each define the
field in arch/arm/include/<arch>/irq.h and use it symmetrically.
arm_fork_direct() copied sysreturn and excreturn to the child but not
ctrlreturn. The child's TCB comes from kmm_zalloc(), so the field was
zero, and CONTROL == 0 is nPRIV clear: the child returned to user space
privileged while its parent returned unprivileged. The child ran out its
life with the MPU restrictions its parent is under silently lifted, which
is the isolation BUILD_PROTECTED exists to provide.
Nothing faults, and that is why this survived. CONTROL == 0 also selects
MSP, which sounds like it should crash immediately, but NuttX already
runs Cortex-M threads on MSP -- the parent's saved value is 0x1, nPRIV
set and SPSEL clear -- so the two differ only in the privilege bit and
there is no stack change to trip over. Privileged code then passes every
test unprivileged code passes, so ostest cannot see it either.
Measured on an RP2350 (Cortex-M33) in BUILD_PROTECTED, breaking at the
nxtask_start_fork() call in arm_fork_direct() during task_fork_test:
parent ctrlreturn 0x00000001, child ctrlreturn 0x00000000. With this
change both read 0x00000001.
BUILD_FLAT is unaffected: without CONFIG_LIB_SYSCALL, nsyscalls is 0 and
the whole block is skipped. armv7-a and armv7-r are unaffected too; they
carry cpsr instead, and that is already copied.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Two device-controller defects that break standard host error recovery,
found while root-causing why macOS never mounts a NuttX mass-storage
function (full analysis and host traces in apache/nuttx#19435):
1. epsubmit() aborted (-EBUSY) any IN request submitted while the
endpoint was halted. The mass storage class halts bulk-IN for a
failed device-to-host command (BOT-legal) and then submits the CSW;
the CSW was dropped on the floor, so after the host's Clear-Halt the
endpoint NAKed forever and the host timed out (macOS: 30 s, then a
Bulk-Only-reset/device-reset spiral until it disables the port).
This is exactly the race documented for years in the usbmsc_scsi.c
header (David Hewson's analysis); the USBMSC_STALL_RACEWAR sleep
workaround only survives hosts that clear the halt within 100 ms --
Linux does, macOS does not, which is why Linux testing never saw it.
Implement the stall-queueing contract instead: requests submitted
while an endpoint is halted are queued without arming the hardware
(arming rewrites the buffer control word and would silently clear
the STALL bit); a halt terminates any in-flight IN transfer (its
hardware buffer is disarmed by the STALL write and would never
complete); clearing the halt resets the data toggle and re-arms the
head of the queue; stale buffer completions latched for transfers
aborted by a halt are ignored. RP2040/RP23XX now select
ARCH_USBDEV_STALLQUEUE, which also retires the RACEWAR's two 100 ms
sleeps per failed command.
2. The USB_REQ_GETSTATUS handler in ep0setup() validated the request
but never queued the two-byte response, for all three recipients
(device/interface/endpoint), so EP0 NAKed the host's data stage
forever and every GET STATUS timed out. macOS issues GetPipeStatus
= GET STATUS(endpoint) on a halted pipe before running Bulk-Only
reset recovery and hit this on every probe; lsusb -v's device-status
query hangs on it as well. Send the response: endpoint recipient
reports the halt bit, device recipient reports self-powered,
interface reports zeros; the status stage is armed by handle_zlp()
exactly as for class-dispatched IN transfers.
Validated on RP2350 silicon (Raspberry Pi Pico 2 W, composite
CDC-ACM + CDC-NCM + USBMSC): pre-fix, a raw-usbfs replay of macOS's
sequence and timing reproduced both defects deterministically (CSW read
ETIMEDOUT after a delayed clear-halt; all GET STATUS variants
ETIMEDOUT). Post-fix: the CSW survives the halt and is delivered after
Clear-Halt with correct tag/status/residue across a post-stall delay
sweep of 0-1000 ms; all GET STATUS variants answer immediately with
correct halt reporting; no regressions in the exact-length SCSI suite,
Bulk-Only reset, FAT mount and full reads, CDC-NCM/ACM, or warm
reboots; and macOS now mounts the volume (together with the companion
usbmsc fixes). The rp2040 driver shares the code and receives the
identical fix (build-tested).
Assisted-by: Claude (Anthropic Claude Code)
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
cdcncm_send() defers each transmit with MSEC2TICK(CDCNCM_DGRAM_COMBINE_PERIOD)
(1 ms). MSEC2TICK() rounds up to the system tick, so at the default 100 Hz tick
the "1 ms" coalescing window becomes a full 10 ms tick (10-20 ms with phase),
adding that latency to every single-datagram reply (ICMP echo, TCP ACK, one-MSS
HTTP segment) and dominating the CDC-NCM round-trip time.
The window only coalesces datagrams appended within the same synchronous TX
burst (already queued before the worker runs), so an inter-burst delay adds
latency without batching benefit in the common case. Fire the transmit worker
immediately (delay 0); within-burst coalescing is preserved.
On RP2350 (Pico 2 W) USB-NIC at 100 Hz tick: ping RTT 21.7 -> 2.8 ms, a 257 KB
HTTP download 5.79 -> 0.92 s (44.5 -> 279 KB/s).
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude (Anthropic Claude Code)
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
arch/x86 set CROSSDEV only under Cygwin, so everywhere else the build used
the bare tool names and got the host compiler. On Linux that is a native
gcc which can produce i486 ELF objects, which is what the board README
assumes. On macOS it is Apple clang, and on Apple Silicon that cannot
target i386 in any form -- so there is no configuration in which the default
works and a cross toolchain is not optional.
Default it to i686-elf-, which Homebrew packages and which accepts the
-march=i486 -mtune=i486 already in ARCHCPUFLAGS. arch/x86_64 has had
exactly this stanza for its own toolchain all along; this is the same shape.
The Cygwin assignment becomes ?= to match, so that a CROSSDEV passed in from
the environment or the command line is honoured rather than overridden.
Note for anyone tempted by the toolchain they already have: a 64-bit x86
compiler with -m32 is not a substitute unless it was built with multilib.
Homebrew's x86_64-elf-gcc compiles 32-bit objects perfectly happily and has
no 32-bit libgcc to link them against, so the entire build succeeds and only
the final link fails, on __udivdi3, __divdi3, __moddi3 and __udivmoddi4.
`x86_64-elf-gcc -print-multi-lib' prints just `.;', which is the toolchain
saying so up front.
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two related defects corrupt CDC-NCM transmit once TCP write buffers make TX
bursty (a single txavail poll drains many queued segments back-to-back through
cdcncm_send):
1. Buffer-reuse race. cdcncm coalesces datagrams into the single pre-allocated
wrreq->buf that the USB controller transmits directly from, but cdcncm_send
formatted a new NTB batch into it (cdcncm_transmit_format) without first
waiting for the previous transfer to complete -- the wrreq_idle wait happened
only later, in cdcncm_transmit_work. A new batch started while the previous
NTB was still in flight overwrote the in-flight buffer, so the host dropped
the corrupted NTB and TX could wedge (wrreq_idle never reposted).
Fix: acquire wrreq_idle in cdcncm_send when starting a new batch
(dgramcount == 0), before formatting; drop the now-redundant wait in
cdcncm_transmit_work (a second wait on the init-to-1 semaphore would deadlock).
2. Concurrent transmit_work. cdcncm_send runs under the recursive netdev_lock and
calls cdcncm_transmit_work() synchronously in the buffer-full branch, while a
scheduled delaywork instance runs cdcncm_transmit_work() on ETHWORK -- two
different threads. Two EP_SUBMITs of the one wrreq corrupt the IN request
queue and leave the IN buffer prepared-but-unarmed (controller idle,
wrreq_idle never reposted).
Fix: wrap cdcncm_transmit_work in netdev_lock (the synchronous caller already
holds this recursive nxrmutex; a delaywork instance blocks until the drain
releases it), and add an empty-batch guard (dgramcount == 0 -> return) so a
delaywork that runs after a synchronous flush emptied the batch does not seal
an empty NTB and double-submit the in-flight wrreq.
Validated on RP2350 (Pico 2 W) with CONFIG_NET_TCP_WRITE_BUFFERS=y as part of the
complete fix set: 144 dense/concurrent HTTP downloads, zero wedges, ~486 KB/s
(previously transmit hung within a few requests). On RP2350 full stability under
maximal TX density additionally requires a memory barrier between the BUFF_STATUS
clear and the AVAILABLE re-arm in the Cortex-M33 USB device driver (a separate
change); these cdcncm defects are real and the fixes correct independent of it.
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude (Anthropic Claude Code)
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
The `to->preemp_start = current` assignment when `to->lockcount > 0`
was executed twice under CONFIG_SCHED_CRITMONITOR_MAXTIME_PREEMPTION,
once in the main preemption block and again after the csection block.
Remove the redundant duplicate.
Signed-off-by: yushuailong <yyyusl@qq.com>
Registering /dev/rtc0 from up_rtc_initialize() creates the pseudofs inode before clock_inittime() synchronizes CLOCK_REALTIME. Its timestamp is consequently zero and omitted by ls -l.
Keep lower-half setup in early RTC initialization, but defer character-device registration to up_initialize(), which runs after clock initialization.
Fixes#19504
Signed-off-by: hanzhijian <hanzhijian@zepp.com>
There was another small issue on sixlowpan_input.c code, it was
processing protosize and 8-bit instead of 16-bit.
It was working because the max tcp->tcpoffset was 0xf0, so
protosize = ((uint16_t)tcp->tcpoffset >> 4) << 2;
Will be protosize = 15 * 4 = 60 and will fit inside 8-bit.
Signed-off-by: Alan C. Assis <acassis@gmail.com>
This commit checks if the incoming 6LoWPAN frame header len + the
IPv6_HDRLEN will fit inside the b->io_len.
Signed-off-by: Alan C. Assis <acassis@gmail.com>
Adds support for the PIO4IOE IO Expander, more specifically the PIO4IOE5V6408 version.
Assisted-by: Cursor IDE agents
Signed-off-by: Filipe Cavalcanti <filipe.cavalcanti@espressif.com>
sim_head.c is linked separately from libarch, so it was omitted from the
source and dependency lists. Track its source independently so
configuration and header changes rebuild sim_head.o while preserving its
link behavior.
Assisted-by: Codex:gpt-5.6 Sol
Signed-off-by: hanzhijian <hanzhijian@zepp.com>
The rp23xx common board sources reference rp23xx_st7735.c from both
Make.defs and CMakeLists.txt under CONFIG_LCD_ST7735, but the file was
never added. Enabling CONFIG_LCD_ST7735 on any rp23xx board therefore
fails the build on the missing source.
Add the file, modelled on the existing RP2040 sibling
boards/arm/rp2040/common/src/rp2040_st7735.c and retargeted to
rp23xx/SPI1. It implements board_lcd_initialize(), board_lcd_getdev()
and board_lcd_uninitialize(): bring up SPI1, claim the D/C (shared with
the unused SPI1 RX pad, per the rp23xx common convention), RST and BL
pads as GPIO, pulse the panel reset and bind the ST7735 driver.
Validated on silicon on a Waveshare RP2350-LCD-0.96 (RP2350A + ST7735S
160x80 IPS): the panel powers up and displays correctly, painted at boot
with no console interaction. Build-tested raspberrypi-pico-2:nsh with
CONFIG_LCD_ST7735=y (compiles and links).
Assisted-by: Claude (Anthropic Claude Code)
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
A task does not necessarily own an address environment. tcb->addrenv_own is
set only by addrenv_attach(), which is reached only from addrenv_allocate();
a kernel thread never allocates one, and in a protected build nothing does --
there is a single address space for the whole system and the architecture's
up_addrenv_*() are stubs. addrenv_own is then NULL for every task, always.
That a task may have no address environment is already an expected state.
addrenv_switch() returns OK when tcb->addrenv_curr is NULL and addrenv_drop()
returns early, and every caller of addrenv_select() checks addrenv_own != NULL
before calling in: nxsched_get_stateinfo(), nxtask_argvstr(), proc_groupenv()
and the arm, arm64, risc-v and tricore up_check_tcbstack().
addrenv_take() and addrenv_give() are the only two that dereference
unconditionally. addrenv_join() calls addrenv_take(ptcb->addrenv_own) without
a check, so pthread_create() faults on &((struct addrenv_s *)NULL)->refs
whenever the calling task has no address environment. With
CONFIG_DEBUG_ASSERTIONS off the same access silently corrupts low memory
instead.
Handle NULL in both, the way the rest of the file already does.
addrenv_give() returns a non-zero count for the NULL case so that callers
never conclude an absent address environment has become unreferenced and
should be destroyed.
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
intel64_timer_start_absolute() computed "expected - current_us" unsigned. A
watchdog started with a delay of zero asks for a deadline that is already
current, the subtraction wraps to nearly 2^64, and the comparator is set so
far ahead that the timer never fires. The CONFIG_INTEL64_HPET_MIN_DELAY
clamp in intel64_oneshot_start() cannot help: the wrapped value is enormous,
not small.
Ask for the shortest delay the hardware can take instead of wrapping, and let
that existing minimum-delay logic pick it.
It presents as ostest hanging in wdog_test with no output and no fault.
apps/testing/ostest/wdog.c:281 calls wdtest_once(&test_wdog, param, 0), and
NSEC2TICK() takes the next few delays (1ns, 10ns, ...) to zero ticks as well;
wdtest_once() then spins forever in its "wait until the callback is triggered
exactly once" loop.
Impact: runtime, ARCH_INTEL64_HPET_ALARM only.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
copy_kernel_mappings() copies exactly one PDPT entry, the 1GB linear window
that maps physical 0-1GB at 4GB-5GB. The boot identity mapping of the low
4GB, which lives in PDPT entries 0-3 of g_pdpt_low (intel64_head.S:756, one
page directory per 1GB) and is where every MMIO register is reached, is not
carried over.
A kernel thread never gets an address environment of its own and
addrenv_switch() leaves the last one in place for it, so as soon as any
process exists, kernel code touching MMIO faults. The HPET at 0xfed00000
finds it immediately -- CR2=fed000f0, in intel64_hpet_getreg() under
clock_systime_ticks() on the lpwork thread -- and any MMIO driver would.
Inherit the four boot PDPT entries. They point at the boot page directories
rather than at copies, so anything intel64_map_region() adds later is
inherited too, and they carry no X86_PAGE_USER, so user code still cannot
reach them.
Impact: runtime, CONFIG_ARCH_ADDRENV builds only. User-space access is
unchanged; the entries added are supervisor-only.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
mm_pginitialize() documents heap_start as "the physical address of the start
of memory region", and every x86_64 consumer of mm_pgalloc() agrees:
create_spgtables(), x86_64_get_pgtable() and up_addrenv_create() all put the
result through x86_64_pgvaddr() before touching it. x86_64_pgvaddr() in turn
range-checks against CONFIG_ARCH_PGPOOL_PBASE (arch/x86_64/src/common/
pgalloc.h:67). arm64's equivalent passes CONFIG_ARCH_PGPOOL_PBASE.
up_allocate_pgheap() passed CONFIG_ARCH_PGPOOL_VBASE instead, and in the
other branch X86_64_PGPOOL_BASE + X86_64_LOAD_OFFSET, which is the same
mistake spelled out. Every page handed out was therefore a virtual address
that fell outside the pool's physical window, x86_64_pgvaddr() returned 0,
and the first x86_64_pgwipe() memset NULL.
It presents as a page fault in memset() under create_spgtables() the first
time a process address environment is created, which is loading the init
program. qemu-intel64:knsh_romfs sets PGPOOL_PBASE=0x00c000000 and
PGPOOL_VBASE=0x10c000000, so the value passed was off by the 4GB load
offset.
Impact: runtime, CONFIG_ARCH_ADDRENV builds only (CONFIG_MM_PGALLOC).
Assisted-by: Claude:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
794c325947 ("arch/x64:Syscall support is enabled by default", 2025-05-27)
changed nine guards from CONFIG_LIB_SYSCALL to CONFIG_ARCH_HAVE_SYSCALL but
never added the Kconfig symbol. Only ARCH_HAVE_SYSCALL_HOOKS exists in tree;
ARCH_HAVE_SYSCALL itself is defined nowhere, so it is always unset and since
that commit x86_64 has had no x86_64_syscall_entry(), no x86_64_syscall(), no
IA32_LSTAR/IA32_STAR programming and no syscall stub layer in any
configuration:
arch/x86_64/src/common/Make.defs:37 x86_64_syscall.c not compiled
arch/x86_64/src/common/CMakeLists.txt:41 likewise
arch/x86_64/include/irq.h:86
arch/x86_64/src/intel64/intel64_cpu.c:247, :386
arch/x86_64/src/intel64/intel64_head.S:83, :351, :538
arch/x86_64/src/intel64/intel64_saveusercontext.S:108
qemu-intel64:knsh_romfs and qemu-intel64:knsh_romfs_pci are the two
CONFIG_BUILD_KERNEL configurations in tree, and neither can have worked in
that time. They still link -- nothing references the missing pieces, so
libstubs.a is simply never pulled in -- and then die the first time user code
executes SYSCALL.
Define the symbol with the condition the code had before that commit, which
is LIB_SYSCALL: every protected and every kernel build needs the interface,
and a flat build is left exactly as it is today.
Impact: restores the system call interface for CONFIG_BUILD_KERNEL and
CONFIG_BUILD_PROTECTED on x86_64. CONFIG_BUILD_FLAT is unaffected --
qemu-intel64:nsh still builds with ARCH_HAVE_SYSCALL unset.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
g_x86_64_timer_freq is assigned only under ARCH_INTEL64_TSC_DEADLINE or
ARCH_INTEL64_TSC, and is read only by the two intel64_tsc_*.c files those
options build. With the HPET as the system clock it stays 0, which is
correct and harmless -- but x86_64_timer_calibrate_freq() panics on 0
unconditionally, so the board dies during x86_64_lowsetup().
Require a frequency only where something needs one, which is
ARCH_INTEL64_HAVE_TSC.
The failure mode is worth recording, because it gives nothing to work from:
the PANIC() happens before x86_64_earlyserialinit(), and the panic handler
itself then triple-faults, because _assert() reads up_interrupt_context() --
a %gs-relative load -- and the GS base is not programmed until
x86_64_cpu_priv_set(). The console stays completely empty and the machine
resets.
Impact: runtime, ARCH_INTEL64_HPET_ALARM only. Configurations with a TSC
are unchanged -- the PANIC() is still compiled for them.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
ARCH_INTEL64_HPET_ALARM is one of the three members of the "System Timer
Source" choice, but selecting it does not build. Taking qemu-intel64:nsh on
master and moving the choice off ARCH_INTEL64_TSC_DEADLINE onto it:
intel64/intel64_hpet_alarm.c:41:24: error:
'CONFIG_ARCH_INTEL64_HPET_ALARM_CHAN' undeclared
ARCH_INTEL64_HPET_ALARM_CHAN lives inside "if INTEL64_HPET" and nothing
selects INTEL64_HPET. Enabling that by hand moves the failure to link time,
because intel64_oneshot_lower.c is built only when INTEL64_ONESHOT is set:
undefined reference to `oneshot_initialize'
Enabling INTEL64_ONESHOT as well finally reaches the real problem.
intel64_oneshot_lower.c implements the counter flavour of struct
oneshot_operations_s, which exists only with ONESHOT_COUNT:
intel64_oneshot_lower.c: error: 'const struct oneshot_operations_s' has no
member named 'start_absolute'
intel64_oneshot_lower.c: error: implicit declaration of function
'oneshot_count_init'
intel64_oneshot_lower.c: error: initialization of
'int (*)(struct oneshot_lowerhalf_s *, const struct timespec *)' from
incompatible pointer type ... (four more of these)
ONESHOT, ONESHOT_COUNT and ONESHOT_FAST_DIVISION were selected by
ARCH_INTEL64_TSC_DEADLINE and by nothing else, so the other members of the
same choice could never be built.
Select the four from ARCH_INTEL64_HPET_ALARM as well. INTEL64_ONESHOT
selects INTEL64_HPET in turn, which is what brings
ARCH_INTEL64_HPET_ALARM_CHAN into existence, so one added select closes all
three stages.
Impact: build only, and only for a configuration that could not be built
before. No existing defconfig selects ARCH_INTEL64_HPET_ALARM.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Update the NXDoom configuration to make use of the new PWM audio
support. Now RTTL music is played over the audio jack.
Signed-off-by: Matteo Golin <matteo.golin@gmail.com>
Fix build error where PWM0 pinout selection checks would still be
compiled even when PWM0 was not enabled.
Signed-off-by: Matteo Golin <matteo.golin@gmail.com>
The TRNG driver waited for EHR_VALID and copied the block out without
checking the CryptoCell health-test result, so a sample that failed the
on-chip von-Neumann, CRNGT or autocorrelation test was served as entropy.
Check RNG_ISR before consuming a block: on a health-test error, discard
the block and re-arm the source; only a block that passed is returned.
Add a small startup self-test that draws a few conditioned blocks and, if
the source is stuck (identical blocks), latches /dev/random and /dev/urandom
to fail with -EIO rather than emit predictable output that would silently
weaken every derived key.
Builds on the driver added in 52e84e0c0a.
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude (Anthropic Claude Code)
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
nxstyle checks one file per invocation. nxstyle_sweep.sh runs it over
the directories given, or the whole repository, and collects what it
reports.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code