The CS, MOSI and MISO pins can be set to -1 in Kconfig when the board
does not use them, and esp32s3_spi_init() skips a pin when it is
negative. The pins are stored as uint8_t in esp32s3_spi_config_s, so
-1 becomes 255, every "pin >= 0" check is always true, and 255 is
passed to esp_gpiowrite() and esp_configgpio(), which trips their
DEBUGASSERT.
Store these three pins as int8_t so -1 is kept and the existing checks
work.
Fixesapache/nuttx#20186
Assisted-by: Grok Bot
Signed-off-by: Zhaoqi Xu <lzy00419@outlook.com>
The filter expression for CONFIG_STM32_IWDG and CONFIG_STM32_RTC_LSICLOCK
returns "y y" when both are enabled. Comparing that result with "y"
omits stm32_lsi.c, leaving calls to stm32_rcc_enablelsi() unresolved.
Test for a nonempty result instead. Include the helper once when either
or both consumers are enabled, and omit it when neither is enabled.
This changes source selection only, without changing clock or watchdog
policy.
Assisted-by: Codex:GPT-6
Signed-off-by: jsanchez-2g <jsanchez@2g-eng.com>
Use per-instance aligned buffers for memory that SDMMC IDMA cannot
access directly or that does not meet cache alignment requirements.
Copy writes before IDMA and copy successful bounced reads in the
waiting thread. Report the configured capacity through maxrequest.
Keep a positive IDMA RAM allow-list and runtime setup validation.
Invalidate only the actual DMA destination, prioritize errors over
DATAEND, and stop data access before releasing buffer ownership.
Reset an active data path on abort while restoring host bus settings;
clear cancellation state and handle immediate/watchdog-start errors.
This commit contains the STM32H7 driver and its Kconfig changes only.
The preceding commit supplies the common SDIO/MMCSD functionality.
Assisted-by: Codex:GPT-6
Signed-off-by: msli-dev <747640013@qq.com>
Every ARCH_CORTEX_A5x config selects ARCH_ARMV8A, and both
Toolchain.defs and cmake/Toolchain.cmake test CONFIG_ARCH_ARMV8A at
the head of the chain, so the -mcpu=cortex-a53/a55/a57/a72 branches
were unreachable and all Cortex-A targets built with plain
-march=armv8-a, losing per-core scheduling/tuning.
Test the specific cores first so -mcpu wins for them; the generic
ARCH_ARMV8A branch still covers cores without a dedicated entry and
keeps the armv8.5-a/MTE handling. Also add the missing cortex-a55
branch to the cmake toolchain for parity with the makefile.
Signed-off-by: rikaken2004 <244897142+rikaken2004@users.noreply.github.com>
A channel number of zero marks an unused slot, as pwm.rst documents.
pwm_start() handed every slot to pwm_update_duty(), which refuses
channel 0, so a caller that fills only some slots, like PX4's tone
alarm, got EINVAL.
Signed-off-by: Royyan Zahir <royzah@gmail.com>
Re-initialize the bus even in the case the bus cannot be re-covered. The
client still has a reference to the bus, and may try to access it again.
If the bus is not initialized or the root clock is off, the access may
cause a crash.
Signed-off-by: Jukka Laitinen <jukka.laitinen@tii.ae>
The change notification code assumed the EC/EF layout: the IRQ of an
I/O port was PIC32MZ_IRQ_PORTA plus the port index, and initialization
reset the change notification registers of every port index. The
PIC32MZ-W1 only implements PORTA, PORTB, PORTC and PORTK, its change
notification vectors are PIC32MZ_IRQ_CNA/CNB/CNC/CNK, and the addresses
of the missing ports D-J belong to other peripherals (I2C1 is at the
PORTE address).
Map the port index to its IRQ through a table on the W1, skip the
unimplemented ports during initialization, and pass the port index to
the common handler as its argument instead of deriving it from the IRQ
number. Hide the PORTD-PORTJ interrupt options on the W1.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Fix the indentation of two lines that nxstyle reports as bad
alignment. No functional change.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
- PIC32MZ-W1 only has the RMII interface and no DEVCFG3, so hide the
PIC32MZ_FMIIEN and PIC32MZ_FETHIO options and define
CONFIG_PIC32MZ_FMIIEN as 0 (RMII), matching DEVCFG1.FMIIEN. The
default of 1 (MII) made the driver skip the RMII reset and speed
setup, so the MAC ran its RMII logic at 10 Mbps.
- Enable ETH_CLK_OUT (EWPLLCON.ETHCLKOUTEN), the 50 MHz RMII reference
clock for the MAC and the PHY, only when the Ethernet MAC is enabled.
- Add PIC32MZ_W1_ETH_EXTREFCLK for boards that clock the PHY and the
MAC from an external 50 MHz oscillator; ETH_CLK_OUT is then left
disabled.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
CONFIG_ARCH_PERF_EVENTS is on by default for ARMv7-M, but imxrt never
called up_perf_init(), so perf_gettime() read zero and anything timed
with it, rpmsg_ping for one, reported 0 ns. Initialise the counter with
BOARD_CPU_FREQUENCY after the MPU is set up, as samv7 and stm32h7 do.
Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Lourens Naude <lourens@bearmetal.eu>
P1.0 powers the switched core off and keeps the XIP cache and SRAM.
Every peripheral loses its registers, so the chip comes back through
the bootrom and the ordinary boot, not from the WFI. The idle governor
never selects it: an application asks for it with the new
BOARDIOC_RP23XX_SUSPEND boardctl() command (arch/chip/pm.h, handled by
the common rp23xx board_ioctl()).
rp23xx_pm_suspend():
- saves the NVIC, writes a marker to POWMAN SCRATCH0, and arms the
wake: the RP23XX_PM_WAKEUP_GPIO pin in a POWMAN power-up detector,
and the always-on timer alarm for a timed wake. An armed RTC alarm
that comes first powers the chip up itself, so an application can
set the wake with RTC_SET_ALARM. Otherwise the RTC alarm is saved
with the new rp23xx_rtc_savealarm() and given back after the wake
with rp23xx_rtc_restorealarm().
- cleans the XIP cache, with the RP2350-E11 workaround, because the
resume discards it and PSRAM can hold task stacks.
- requests P1.0 and waits in WFI.
On the next boot, __start asks rp23xx_pm_resume_pending() (the marker
and CHIP_RESET.HAD_SWCORE_PD) before .bss and .data are touched. For a
resume it moves to its own stack, because the idle thread still runs on
the idle stack, sets up the hardware with the cold boot code (now
rp23xx_hwinit()), and longjmps back to the suspended thread. The UARTs
are set up from the driver state in RAM, the PSRAM format is applied
again without detection (the part is still in quad mode), the dormant-
wake GPIOs are armed again (the IO bank lost them, and the next dormant
period could then never end), and the time of day is taken from the
always-on timer.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Map the NuttX PM states onto the RP2350 low-power modes:
- PM_STANDBY is the RP2350 SLEEP state: a WFI with the clocks of the
blocks that have no driver in the configuration gated (SLEEP_EN0/1).
- PM_SLEEP is the DORMANT state: clk_sys moves to the crystal
oscillator, the PLLs and then the oscillator stop, and the clock tree
is restored after the wake, as pico-extras does. Only the GPIO
dormant-wake detector can wake the chip, so PM_SLEEP gives the
standby state when no wake GPIO is configured. The wake GPIO also
raises a one-shot interrupt, so that the core leaves its WFI and
restores the PLLs at once. The time of day is taken back from the
always-on timer after the wake.
The dormant state stops the UARTs. A PM_STANDBY wakelock
(pm_staytimeout()) is held for RP23XX_PM_WAKE_HOLD_MS after a dormant
wake and after each character a UART receives. The serial driver
refuses PM_SLEEP in its prepare callback while a UART transmits. A
dormant chip does not answer SWD, so the pm configurations use
PM_GOVERNOR_EXPLICIT_RELAX to stay out of it for a time after boot.
With RP23XX_PM_QUIESCE_PADS, arm_pminitialize() also isolates the
unused pads and holds the blocks with no driver in reset. A floating
bank 0 input settles near 2.2V (erratum RP2350-E9) and its input
buffer then draws a static current in every state.
Also select ARCH_HAVE_PM, and fix the LED PM callbacks of three boards,
which used BOARD_LED where the boards define BOARD_LED1. They did not
build with CONFIG_PM.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Add the blank line after the declarations in up_putc() and in
rp23xx_led_pminitialize() of three boards. Whitespace only.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
The QEMU virt machine has a PL031 RTC at 0x09010000 (SPI 2), which QEMU
sets from the host clock. qemu-armv8a did not register it, so the
system time started at CONFIG_START_YEAR, and files on a host share
(v9fs, hostfs) had times years in the future.
With CONFIG_RTC_PL031, up_rtc_initialize() now registers the PL031 as
the RTC, so the system time starts at the host's time.
The new option QEMU_RTC_PL031_SYNC makes the boot wait (up to a second)
for the RTC second to change. The PL031 counts whole seconds, so
without the wait the time starts up to a second behind the host.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
arch_setjmp.S stores d8-d15 only under CONFIG_ARCH_FPU, but it did not
include nuttx/config.h. So setjmp() never saved these registers and
longjmp() never restored them. They are callee-saved (AAPCS64), so a
function that kept a value in one of them could see it change after a
longjmp().
Include nuttx/config.h. The assembly then stores d8-d15, eight bytes
each, at offsets 112 to 176. struct setjmp_buf_s declared them as
eight 4-byte floats, 32 bytes short, so declare them as uint64_t too.
jmp_buf is now 176 bytes with the FPU, and its layout matches the
assembly.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
pgalloc() grows a process heap for sbrk(). It extended the address
environment of the running task (addrenv_own). While exec() sets up a
new process, the caller selects the new address environment and
allocates the new process's stack from its heap. When that stack does
not fit in the initial heap, the heap must grow, but the running task is
the caller. For the kernel thread that starts init this was an
assertion; for a user task it would have grown the caller's heap.
Use the selected address environment (addrenv_curr). For a normal sbrk()
it is the same as addrenv_own.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
This allows using >25MHz bus speeds by adding a configration option
to set SDIO_CAPS_SD_HS_MODE capability.
Signed-off-by: Jukka Laitinen <jukka.laitinen@tii.ae>
- Add the missing clock gating macros for imxrt118x
- Change sw_cd_gpio from int32_t to gpio_pinset_t (64-bits on 118x)
- Invalidate cache again after the data has been received. Cache might
be refilled by a prefetch or, in theory, by cpu read touching the same
cache line - even though the latter should not happen.
- Map any DMA accesses to/from DTCM to the shadow address window, which
gives the uSDHC DMA access to the DTCM.
Signed-off-by: Jukka Laitinen <jukka.laitinen@tii.ae>
With CONFIG_NET_PROMISCUOUS, also enable the multicast and "not me"
unicast receive filters (ETHRXFC MCEN and NOTMEEN), so that the MAC
accepts every frame on the link, as the option's help text describes.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
CPP is also used to preprocess init.rc into the /etc ROMFS. Without -P
it emits GNU linemarkers, which nxinit's parser rejects.
Assisted-by: OpenCode:claude-sonnet-5
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
A JRCR reset only flushes and halts the job ring; a second reset, once
JRINT reports the halt done, completes it, as Linux's caam_reset_hw_jr()
does. With the single write the ring stayed halted, so the RNG
instantiate job never completed and every boot logged "job ring did not
answer" before the settle loop's second ring init finished the reset.
Signed-off-by: Royyan Zahir <royzah@gmail.com>
Add the PIC32MZ-W1 family, used in Microchip's WFI32E01 Wi-Fi modules.
W1 shares the MIPS32 M-Class core and much of the peripheral IP with
PIC32MZ EC/EF, but its SFR map, IRQ vectors, PPS registers and
configuration words are laid out differently, so these are new files
selected by CONFIG_ARCH_CHIP_PIC32MZW1:
- hardware/pic32mzw1_*.h, irq_pic32mzw1.h: memory map, PPS, IRQ vectors
and configuration words, from the PIC32MZ-W_DFP (Apache-2.0).
- pic32mz_wfi32_pwrclk.c: W1 does not set up its PLLs from the
configuration words. Software starts the 40 MHz crystal oscillator,
runs SYSCLK at 200 MHz from the system PLL, starts the Ethernet/Wi-Fi
PLL and switches the regulator from MLDO to buck mode using the
factory trim values (on B0 silicon). Facts not found in the
data sheet or DFP are marked [EX] in comments; they come from
Microchip's WFI32 Ethernet/Wi-Fi bridge example firmware.
- pic32mz_head.S, pic32mz_config.h: emit the W1 configuration words
(DEVCFG0/1/2/4, FBCFG0, FCPN0, FSIGN0).
- W1 differences in shared code: PREFEN only accepts 0/1, PB6DIV clocks
the CPU and is left alone, UART1/2, SPI1/2 and I2C2 are clocked from
PBCLK3 and the timers from PBCLK1 (data sheet Table 11-1), I2C1 and
I2C2 are not contiguous in the SFR map, UART1 and SPI1 can use their
dedicated (non-PPS) pins.
New Kconfig options: PIC32MZ_W1_PMU_MLDO (keep the regulator in its
power-on MLDO mode), PIC32MZ_W1_FLASH_WAITSTATES (default 5, the value
used by Microchip's example at 200 MHz) and PIC32MZ_W1_BOOTTRACE (polled
early boot trace on UART1, a bring-up aid).
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
mmu_write_ttbr0() writes TTBR0_EL1 and then invalidates the TLB. A write
to TTBR0_EL1 takes effect only at the next context synchronization event,
so until the ISB at the end of the invalidation, a table walk can still
use the old table. The instruction fetches of the invalidation sequence
itself do such walks. An entry that they cache after the TLBI completes
stays valid: walk cache entries are not tagged with the table base, and
the kernel and every process use ASID 0.
A kernel build then translates a user address of the new process through
a level 0 entry of the old table, and gets a level 1 translation fault.
Under QEMU with HVF on Apple silicon this happens on every boot of
qemu-armv8a:knsh: up_addrenv_va_to_pa() fails for the first user buffer,
and virtio gets a descriptor with address 0. TCG has no walk caches, so
it does not show the problem.
Add an ISB after the write, as the Arm ARM sequence for a TTBR change
without an ASID change requires: write, ISB, TLBI, DSB, ISB.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
The completion handler did:
dmach->callback(...);
dmach->callback = NULL; /* after the call */
so a callback that registers itself again -- which is the only way for a
driver to keep a channel running continuously -- has that registration
wiped the moment it returns. The channel transfers one more block and
then goes deaf, with no error raised anywhere and nothing in the
registers to say why. A PWM audio driver hit this and worked around it
by restarting from a thread instead, which put a millisecond of silence
into every buffer boundary.
Lift the callback and its argument out first and clear them before the
call. One-shot behaviour is unchanged for every existing user, since
none of them re-register from inside the call; the difference is only
that one which does now survives.
Tested on a Pimoroni Pico Plus 2 W (pimoroni-pico-plus-2-w:nsh) with a
local test: a memory-to-memory transfer whose callback starts the next
one, 100 times. Master gives 1 completion of 100; this change gives 100.
The existing users (SPI, I2S, CYW43439, WS2812) do not start a transfer
from inside the callback, so they do not change.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
nxstyle reports "Missing blank line after declarations" in
rp23xx_dmachannel(). Add the blank line, because CI checks every file
that a change touches.
No functional change. The change is whitespace only.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Select STM32_HAVE_IP_USART_M33_V3 and drop the family serial, USART
header, and low-level console sources. Provide the USART clock and RCC
gate definitions for all thirteen ports in stm32_rcc_m33.h and the RX
DMA request numbers in the family DMA signal map. Include the family
DMA header from stm32.h so the common driver reaches the DMA API.
Extend the common Cortex-M33 v3 USART support with the STM32H5
features: USART6, UART7-9, USART10-11, and UART12 ports, the LPUART
prescaler for low baud rates, RX DMA through the family-provided
request number, wakeup-from-stop CR3 definitions, and per-port
descriptors gated by the family peripheral options.
Enable the USART FIFO for every Cortex-M33 family. The low-level
console uses the per-port kernel clock and RCC gate, which corrects
the UART9 and UART12 enable register. The RX DMA callback delivers
data only while reception is enabled.
The non-BSD break ioctl now uses the send-break request register
instead of a CR1 bit this USART IP does not have. Correct the UART12
gates and the RXDMA and console undef lists in the USART driver
header. Raise STM32_NUSART to six on the H56x and H57x parts so UART12
fits the device table, add the missing UART9 buffers, write LPUART CR1
only on the USART path, and build device names with snprintf so minors
above nine are valid.
Keep the termios flow control fields writable so TCSETS compiles with
input flow control, let up_putc emit a bare newline since syslog adds
the carriage return, and make the unconfigure-on-close options
available to every Cortex-M33 family.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
Select STM32_HAVE_IP_GPIO_M33_V1 and STM32_HAVE_IP_EXTI_M33_V1 and
drop the family GPIO and EXTI sources and headers in favor of the
common Cortex-M33 v1 implementation. Add the RM0481 line count for the
H56x and H57x parts to the common EXTI line inventory.
Add GPIO_PORTI to the common Cortex-M33 pin encoding for the parts
with a ninth GPIO port.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
Enable STM32_COMMON_M33 for STM32H5 and drop the family reset, NVIC,
SysTick, and idle sources in favor of the common Cortex-M33 v1
implementation. The common heap allocator replaces the generic ARM one
through a STM32_PRIMARY_SRAM_SIZE spanning the contiguous SRAM banks
and skips the SRAM2 region when the primary heap already covers it.
Rename the family RCC header to stm32_rcc_m33.h for the common RCC
dispatch.
Add the SRAM3 parity initialization block and the CONFIG_STM32_ICACHE
enable call to the common reset handler, taken from the STM32H5 start
logic. Without CONFIG_STM32_ICACHE the reset handler disables an
ICACHE left enabled by a bootloader on STM32_HAVE_ICACHE families.
Move the flat-build MPU initialization to stm32_mpuinit_m33_v1.c in
common/stm32, built for every Cortex-M33 family with ARM_MPU, and
declare stm32_mpuinitialize for ARM_MPU as well as protected builds.
Declare stm32_board_initialize in the common start header.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
An unprivileged task that touched memory it does not own took the whole
system down. The MMU refused the access, as it should, and then
x86_64_fault_panic_isr() panicked -- so a contained application bug
became a system-wide outage. x86_64 had no user-fault recovery at all,
while arm64, RISC-V and esp32s3 each have one, and ISR13 and ISR14 here
went straight to a handler whose own comment says "Don't even brother
to recover, just dump the regs and PANIC."
The decision has to be made in the handler. The user-task check in
_assert() looks like it already covers this, but a fault arrives as an
exception, so up_interrupt_context() is already true by the time it is
reached and the panic branch is taken no matter who faulted.
The CPL in the saved CS is the whole test, and on this architecture it
is exact: everything that runs on behalf of a user task inside the
kernel -- a system call body, an interrupt handler, a kernel thread --
runs in ring 0, so a fault there is correctly refused recovery. That is
what RISC-V reads out of STATUS_PPP and arm64 out of SPSR_MODE_EL0T.
x86_64 cannot use TCB_FLAG_SYSCALL the way those two do: it has never
set it, and making it do so is a separate change with its own hazards
(see the note in x86_64_syscall()). The task type is checked as well,
as RISC-V does, so that a frame that cannot be trusted -- an early-boot
fault, before any user task exists -- cannot talk its way in with a
stale selector.
Recovery is the same shape as the other architectures -- RIP to _exit,
first argument SIGSEGV, TCB_FLAG_FORCED_CANCEL raised -- plus the three
x86_64 specifics:
* CS and SS move to the kernel selectors together with RIP. The frame
being rewritten is the one x86_64_fullcontextrestore() is about to
iretq from, and iretq takes the target privilege level from the CS
it pops; in long mode it pops SS with it even when the level does
not change.
* RSP moves to the top of the task's own kernel stack. _exit() must
not run on the user stack the fault came from, because the address
environment that stack belongs to is torn down while _exit() is
still running. The kernel stack is free -- the fault was taken in
user mode, so no system call of this task is in flight. The -8
reproduces the offset a call would have left, which is what the SysV
ABI states its 16-byte rule against and what up_initial_state() sets
up for the same reason.
* RFLAGS is reset to the value up_initial_state() gives a new thread
rather than carried over. The faulting task's flags are its own to
set, and DF in particular must be clear on entry to any C function.
ISR6 is routed through the same handler. An invalid opcode is
attributable to the instruction that raised it, so a user task running
garbage should die on its own rather than take the system with it --
the same conclusion esp32s3 reached for EXCCAUSE_ILLEGAL. ISR8 keeps
panicking unconditionally: a double fault says an exception could not be
delivered at all, and there is nothing left to trust.
The message gives the fault address (CR2) only for a page fault, because
the other exceptions do not set CR2.
X86_GDT_PL_MASK and X86_GDT_RPL_USER now live in intel64/arch.h beside
the selectors they mask; x86_64_fork.c had a private copy of the latter.
Verified on qemu-intel64:knsh_romfs under QEMU TCG with
apps/examples/sandbox. The probe targets kernel .text at _stext
(0x100909000). This config sets CONFIG_RAM_START to 0x0, a Kconfig
default, so the address is given on the command line. The "x" probes
call the address; that mode was added to the sandbox locally for the
test.
sandbox r 0x100909000 -> Exception 14, error code 5, 2816
sandbox w 0x100909000 -> Exception 14, error code 7, 2816
sandbox x 0x100909000 -> Exception 14 at RIP=100909000, 2816
sandbox r 0x8000000000000000 -> Exception 13 (non-canonical), 2816
sandbox x 0x80000000d -> Exception 6 at RIP=80000000d, 2816
All five probes run in one boot and report CONTAINED. The offender
dies with SIGSEGV, the parent survives, a canary thread keeps running,
and the memory and descriptors of the offender come back. The shell
answers after the last probe. Without this change each probe panics
the system.
ostest exits with status 0 on knsh_romfs, fork and vfork included. It
also exits with status 0 on the flat qemu-intel64:nsh with
CONFIG_SCHED_THREAD_LOCAL disabled. With it enabled, ostest faults in
sched_thread_local_test() with and without this change.
Each vector is reached deliberately. A non-canonical address is a #GP
rather than a #PF, which the read and call probes never produce on their
own. The #UD needs no corrupt binary either: the crt0 stub this
architecture links into every user ELF already ends in a ud2 at
_stext+0xd, and with CONFIG_ARCH_TEXT_VBASE at 0x800000000 the call
probe can simply be aimed at it, executing an invalid opcode in ring 3
out of the process's own text.
The negative direction was checked too, on the flat build, where the
same non-canonical read is taken at CPL 0: it reaches
x86_64_fault_panic_isr() exactly as before and panics with a full
register dump reporting CPL 0.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Two problems in the same path make a contained user fault look like a
kernel failure.
arm64_el1_undef() dumps the words around ELR. For an exception taken
from EL0, ELR is a user address, and the words around it can be in a
page that is not mapped. Then the memcpy faults inside the fatal
handler. That nested exception trips the DEBUGASSERT in
arm64_fatal_handler(), and the fault that the user took is not reported.
The ESR that tells what happened is lost. Skip the dump when the
exception came from EL0. At EL1 the address is kernel code that was
just fetched, so keep the dump there.
arm64_fatal_handler() then reports the fault that it recovers from as
"PANIC: Unhandled user exception", followed by a full register dump.
But there is no panic: it sets TCB_FLAG_FORCED_CANCEL, changes ELR to
_exit(SIGSEGV), and the system continues without the offending task.
Print "Segmentation fault in <process> (PID n: <thread>)" instead, the
same message as risc-v, and keep the register dump for the
PANIC_WITH_REGS() path, which is fatal.
Tested on QEMU qemu-armv8a:knsh with examples/sandbox and ostest. A
user read or write of kernel memory (0x40000000) now prints:
arm64_exception_handler: ESR_ELn: 0x9200000e
arm64_fatal_handler: Segmentation fault in sandbox (PID 10: sandbox)
arm64_fatal_handler: Reason: DABT (lower EL) - Data Abort from a ...
sandbox: the offender exited with status 2816
Before this change it printed "PANIC: Unhandled user exception" and a
register dump for the same recovered fault. An undefined instruction
at EL0 now prints "Undefined instruction at <ELR>" without the dump,
then the same segmentation fault message. The shell survives in all
cases, and ostest exits with status 0 before and after this change.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
nxstyle reports "Bad right brace alignment" for the closing brace of the
switch in arm64_el1_exception_handler(). Indent it like the switch,
because CI checks every file that a change touches.
No functional change. The change is whitespace only.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
In CONFIG_BUILD_PROTECTED, a user task that touches memory it does not
own must be terminated on its own. The rest of the system must keep
running. riscv_fault_handler() already does this: it checks for a fault
taken from U-mode, sets TCB_FLAG_FORCED_CANCEL and changes the exception
return to _exit(SIGSEGV) in privileged mode. But the whole block was
inside #ifdef CONFIG_ARCH_KERNEL_STACK. Configurations that do not
select that symbol, such as rv-virt:pnsh and rv-virt:pnsh64, fell
through to PANIC_WITH_REGS(). A contained user-space bug stopped the
whole system.
Only the last line of the block needs a kernel stack:
running_regs()[REG_SP] = tcb->xcp.ktopstk;
because xcp.ktopstk exists only with one. Narrow the guard to that
assignment, so the rest compiles in all configurations. arm64 already
does the same in arm64_fatal_handler().
It is correct to leave REG_SP unchanged. In riscv_exception_common.S
the switch to the kernel stack at exception entry is also inside
#ifdef CONFIG_ARCH_KERNEL_STACK. Without a kernel stack, the exception
frame goes on the user stack and REG_SP holds the user SP.
dispatch_syscall() already runs on that stack, so the kernel runs all
system calls of this task there, exit() included. Running _exit on it
after a fault is the same case and adds no new exposure. The stack also
stays mapped until the scheduler switches away, because a build without
a kernel stack cannot select CONFIG_ARCH_ADDRENV
(riscv_exception_common.S has an #error for that combination).
No behaviour change in other builds. The recovery runs only when the
saved STATUS_PPP is clear, that is, when the fault came from U-mode. In
CONFIG_BUILD_FLAT, tasks run at kernel privilege (M-mode, or S-mode on
the nsbi configurations), so STATUS_PPP is set and the panic path stays
the same. CONFIG_BUILD_KERNEL configurations select ARCH_KERNEL_STACK
through ARCH_ADDRENV, so their code does not change.
Tested on QEMU with examples/sandbox and ostest. On rv-virt:pnsh and
rv-virt:pnsh64 a forbidden read or write of kernel memory now kills
only the offending task, with status 2816 (SIGSEGV). The shell and an
unrelated thread keep running. Before this change the same access
caused a PANIC. ostest exits with status 0 on both, before and after
this change. rv-virt:knsh still builds, and its riscv_exception.o
differs only in the __LINE__ value of the PANIC call.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Expose the RTL8730E I2C controllers through the shared Ameba I2C driver
(arch/arm/src/common/ameba/ameba_i2c.c) by adding the chip-specific
glue, build wiring and a board bus table. The change is gated by
CONFIG_AMEBA_I2C (default disabled).
Chip glue (ameba_i2c_chip.h) supplies the three controller register
bases, the APB function/clock masks -- amebasmart encodes these in
bit 25/26/27 with the group selector bit30=0, unlike the (bit30 |
bit10/11) layout of the KM4-based parts -- and the pad-mux code.
amebasmart has a single generic PINMUX_FUNCTION_I2C shared by every I2C
pad instead of per-signal SCL/SDA crossbar codes, and its
I2C_InitTypeDef omits the DMA request-level fields, so
AMEBA_I2C_HAS_DMA_FIELDS stays undefined. The whole I2C fwlib lives in
ram_common/ameba_i2c.c rather than lib_rom.a, so it is compiled into
libameba_fwlib.a when I2C is enabled.
The shared driver gains an optional per-controller IP-clock hook,
AMEBA_I2C_IPCLK_FN(bus). amebasmart needs it because its
I2C_StructInit() fills in a 10 MHz placeholder rather than the real
reference clock (the other Ameba parts fill in a correct XTAL_ClkGet()
/ PLL_GetHBUSClk() / HPERI_ClkGet()), which makes I2C_SetSpeed()
miscompute the SCL counts. The hook resolves the rate at run time: the
two HS controllers sit on HS_AHB, i.e. NP_PLL divided by
REG_LSYS_CKD_GRP0.CKD_HBUS, so it is a PLL/board setting and not a
constant -- an EVB measured CKD_HBUS=8 (div9, 88.9 MHz) while the SDK's
own I2CCLK_TABLE claims a flat 100 MHz and the register reset value
would imply 80 MHz. The LP-domain I2C0 keeps its fixed 20 MHz. Chips
that leave the macro undefined use the fwlib default and are
unaffected; regression-tested on pke8721daf:i2c.
All three controllers are registered: /dev/i2c0 on PA9/PA10, /dev/i2c1
on PA3/PA4 and /dev/i2c2 on PB10/PB11. Pad choice is constrained on
this chip -- a pad reaches exactly one controller (SDA fixed per
controller, SCL the next pad up), and the pads inside the analogue
audio ranges (PA20-PA29, PA30-PB2, PB3-PB6) are driven by the codec and
leave the bus stuck idle. Both rules are noted in the table's
comment.
Also add a weak DiagVprintf stub: the I2C fwlib logs through RTK_LOGx()
-> rtk_log_write(), and unlike DiagPrintf that symbol is not in the AP
ROM symbol table. It is weak so the strong definition in
rtl8730e_wifi_stubs.c still wins when WiFi is enabled; both route to
vprintf.
Hardware-verified on an RTL8730E EVB against a second board running an
I2C slave: register read, write, write/read round-trip, multi-byte dump
and a full address scan, on all three buses at 100 kHz.
Signed-off-by: dechao_gong <dechao_gong@realsil.com.cn>
Assisted-by: Claude <noreply@anthropic.com>
OpenAMP libmetal calls up_addrenv_pa_to_va() and up_addrenv_va_to_pa(),
and every virtio driver uses libmetal. With CONFIG_DEV_SIMPLE_ADDRENV,
drivers/misc/addrenv.c provides both. Without it, arm64 provides only
up_addrenv_va_to_pa(), in arm64_physpgaddr.c. So an arm64 build with
an MMU and any virtio driver does not link, flat or kernel:
undefined reference to `up_addrenv_pa_to_va'
The flat qemu-armv8a virtio configurations set CONFIG_DEV_SIMPLE_ADDRENV.
That does not fit a kernel build: its table gives the same address back
for a user address of the process.
Add up_addrenv_pa_to_va() next to up_addrenv_va_to_pa(). It translates
the page pool and the kernel RAM with arm64_pgvaddr(), and any other
address to itself. up_addrenv_va_to_pa() must then give back the same
physical address, otherwise the function returns NULL, as
include/nuttx/arch.h specifies.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
With CONFIG_ARCH_HIPRI_INTERRUPT enabled, arm_doirq() dispatches PendSV
for deferred processing. The STM32H7 debug handler unconditionally panics
on that valid interrupt, preventing normal operation with debug enabled.
Guard the panic with CONFIG_ARCH_HIPRI_INTERRUPT, allowing the common
interrupt path to finish signal delivery and context switching. Preserve
the diagnostic when high-priority interrupts are disabled.
Assisted-by: Codex:GPT-6
Signed-off-by: jsanchez-2g <jsanchez@2g-eng.com>
GPIO_EDGE_RISING was (12 << GPIO_CN_SHIFT), which sets bits 10 and 11
(GPIO_PULLDOWN | GPIO_EDGE_DETECT) instead of the edge type bit 12 that
its comment describes. A pin configured for rising edges therefore also
got the pull-down, and a falling-edge pin with GPIO_PULLDOWN was taken
as a rising-edge pin. Use bit 12.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
The RX byte count and the EMAC1MAXF limit both include the 4-byte FCS,
but the driver did not account for it:
- RXBUFSZ was programmed with CONFIG_NET_ETH_PKTSIZE, rounded down to
16 bytes (1504 for 1514), so longer frames were split into fragments
and dropped.
- EMAC1MAXF was set to CONFIG_NET_ETH_PKTSIZE, so the MAC rejected
frames longer than CONFIG_NET_ETH_PKTSIZE - 4.
- d_len included the FCS.
Make room for the FCS in the buffers, program RXBUFSZ with the aligned
buffer size and EMAC1MAXF with CONFIG_NET_ETH_PKTSIZE + 4, and remove
the FCS from d_len. The largest ping that got an answer was 1458
bytes; it is now 1472, the full 1500-byte MTU.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
VIRT_ADDR() converted the DMA buffer addresses to KSEG1, while the
buffers come from g_buffers, which is linked in KSEG0 when the data
memory is cached. Buffers then ended up in the free list under both
aliases. Use the segment g_buffers is linked in instead.
A buffer handed to an RX descriptor may still have dirty D-Cache lines,
at least the free list link written into it. If such a line is evicted
while the DMA writes the frame, it overwrites part of the frame.
Discard the buffer from the D-Cache before giving it to the DMA.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
After starting an MII management command, the driver executed 16 NOPs
before waiting for the busy flag to clear. The flag is set a few clock
cycles after the command, and when the code runs from the I-Cache the
NOPs end before that: the wait returned at once and phyread() returned
the previous read data. With the L1 cache enabled the PHY was not found
(ID1 read as 0x3000) and the interface never came up.
Poll until the busy flag is set, bounded in case the command has
already completed, before waiting for it to clear. A management frame
lasts 64 MDC cycles, so the flag cannot be missed.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
pic32mz_bufferinit() appended every buffer to pd_freebuffers without
emptying the list first. On the first ifup the list is empty (the
driver structure was cleared), but on later ones it still holds the
buffers that were free at ifdown. Appending them again truncates the
list and loses buffers, depending on which ones were free. With too few
buffers left the driver could no longer transmit or replace RX buffers,
so after ifdown/ifup the interface stayed up without answering (not
even ARP) until the next ifdown/ifup.
Reproduced with ifdown/ifup from NSH while pinging the board every
10 ms: 3 of 10 cycles left the interface dead before the fix, none
after it. This also happens on cable reconnection with
CONFIG_NETINIT_MONITOR, which takes the interface down and up.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
The driver had no d_ioctl, so CONFIG_NETDEV_PHY_IOCTL had no effect.
Implement SIOCGMIIPHY, SIOCGMIIREG and SIOCSMIIREG and, with
CONFIG_ARCH_PHY_INTERRUPT, SIOCMIINOTIFY. SIOCMIINOTIFY subscribes
through phy_notify_subscribe() (the board provides arch_phy_irq()) and
enables the PHY link down and auto-negotiation complete interrupts.
This is what CONFIG_NETINIT_MONITOR needs.
The PHY interrupt is implemented for the LAN8720 and LAN8740; add their
interrupt source/mask register bits to mii.h.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
After a successful auto-negotiation, pic32mz_phyinit() called
pic32mz_phymode() with the negotiated speed and duplex. That function
clears MII_MCR_ANENABLE, so the PHY stayed in a forced mode. The link
keeps working until the cable is removed, but on reconnection the PHY
no longer negotiates and, against an auto-negotiating partner, the link
stays down (seen with a LAN8720A: MCR 0x2100, MSR without link status).
Only force the mode when CONFIG_PIC32MZ_PHY_AUTONEG is not selected.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
- Close the TX descriptor ring on the last TX descriptor. It used
CONFIG_PIC32MZ_ETH_NRXDESC, so with more RX than TX descriptors the
DMA ran past the TX ring and stopped transmitting after two packets.
- Decrement ETHSTAT.BUFCNT (ETHCON1.BUFCDEC) for each received
descriptor that is processed.
- Drop a received packet instead of asserting when no buffer is free to
replace the one in the RX descriptor.
- Program EMAC1SA0-2 with the MAC address assigned to the device, if
any. The driver only read these registers, which are preloaded with a
factory address on PIC32MZ EC/EF but reset to zero on PIC32MZ-W1.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Fix the indentation of a wd_cancel() call and add braces to an empty
while loop, so that the file passes checkpatch.sh. No functional change.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Duplicate an address environment into freshly allocated pages mapped at the
same virtual addresses, which is what POSIX fork() is built on. It lives in
arm64_addrenv_mmu.c: an MPU address environment is a set of protection
regions over one physical address space, not a mapping that can be duplicated
at the same virtual addresses. So ARCH_ARM64 selects ARCH_HAVE_FORK only
in a kernel build with ARCH_ADDRENV. The condition repeats the
ARCH_ADDRENV dependency, because a select bypasses depends on.
arm64_fork_stack() then lets the child run at the parent's stack addresses. A
pointer to a stack local taken before fork() must name the same object in the
child that it named in the parent, so the child adopts the parent's stack
geometry rather than being given a relocated copy; the parent's stack is
already in the duplicate, at the parent's address, with its contents. With a
zero offset arm64_fork_reloc() is then the identity, so the register context
needs no further special casing.
Verified on qemu-armv8a:knsh under qemu-system-aarch64: ostest's fork_test
reports "Parent and child had independent memory", and vfork_test passes.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
jz4780_decodeirq() saves the interrupted context into
g_running_tasks[this_cpu()]->xcp.regs on entry, but nothing updates
g_running_tasks[] after a context switch: every interrupt saves the
context into the Idle task's TCB, and once a task exits (up_exit() sets
the entry to NULL) no context is saved at all and the next context
switch restores stale registers.
Set g_running_tasks[this_cpu()] to this_task() before returning, as
pic32mz_decodeirq() does. This is the same bug that crashed the
PIC32MZ-W1 when the netinit thread exited.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
pic32mx_decodeirq() saves the interrupted context into
g_running_tasks[this_cpu()]->xcp.regs on entry, but nothing updates
g_running_tasks[] after a context switch: every interrupt saves the
context into the Idle task's TCB, and once a task exits (up_exit() sets
the entry to NULL) no context is saved at all and the next context
switch restores stale registers.
Set g_running_tasks[this_cpu()] to this_task() before returning, as
pic32mz_decodeirq() does. This is the same bug that crashed the
PIC32MZ-W1 when the netinit thread exited.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
pic32mz_decodeirq() saves the interrupted context to the TCB in
g_running_tasks[], but never updated g_running_tasks[] after a context
switch. It kept pointing at the Idle task from nx_start(), so every
interrupt overwrote the Idle task's saved registers, and after up_exit()
set it to NULL no context was saved at all. The next context switch
then restored stale registers; on PIC32MZ-W1 the system crashed as soon
as the netinit thread exited.
Set g_running_tasks[] to this_task() before returning, as the other
architectures do.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>