Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion ports/zephyr-cp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,21 @@ sim:
clean-sim:
rm -rf $(wildcard build-native_*)

test: build-native_native_sim/zephyr-cp/zephyr/zephyr.exe
# Every board the test suite uses: native sim (non-asan + asan) and the bsim
# boards (tests/bsim/conftest.py parametrizes over both). Zephyr samples for
# bsim tests are built on demand by the zephyr_sample fixture.
TEST_BOARDS := native_native_sim native_native_sim_asan native_nrf5340bsim native_nrf54lm20bsim

# Delegate to a sub-make with BOARD set so the per-board build rule (and its
# shield args + bsim prep) applies. The targets are phony: the west builds are
# incremental, so this just keeps the test binaries fresh.
define test_board_rule
.PHONY: test-build-$(1)
test-build-$(1):
+$$(MAKE) BOARD=$(1) BUILD=build-$(1) build-$(1)/zephyr-cp/zephyr/zephyr.elf
endef
$(foreach board,$(TEST_BOARDS),$(eval $(call test_board_rule,$(board))))

test: $(TEST_BOARDS:%=test-build-%)
pytest cptools/tests
pytest tests/ -v
1 change: 1 addition & 0 deletions ports/zephyr-cp/boards/board_aliases.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ cp_board_alias(renesas_ek_ra6m5 ek_ra6m5)
cp_board_alias(renesas_ek_ra8d1 ek_ra8d1)
cp_board_alias(renesas_da14695_dk_usb da14695_dk_usb)
cp_board_alias(native_native_sim native_sim/native)
cp_board_alias(native_native_sim_asan native_sim/native)
cp_board_alias(native_nrf5340bsim nrf5340bsim/nrf5340/cpuapp)
cp_board_alias(native_nrf54lm20bsim nrf54lm20bsim/nrf54lm20a/cpuapp)
cp_board_alias(nordic_nrf54l15dk nrf54l15dk/nrf54l15/cpuapp)
Expand Down
131 changes: 131 additions & 0 deletions ports/zephyr-cp/boards/native/native_sim_asan/autogen_board_info.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info.
name = "zephyr Native simulator - native_sim"

[modules]
__future__ = true
_bleio = false
_eve = false
_pew = false
_pixelmap = false
_stage = false
adafruit_bus_device = true
adafruit_pixelbuf = false
aesio = true
alarm = false
analogbufio = false
analogio = false
atexit = false
audiobusio = true # Zephyr board has audiobusio
audiocore = true # Zephyr board has audiobusio
audiodelays = true # Zephyr board has audiobusio
audiofilewriter = false
audiofilters = true # Zephyr board has audiobusio
audiofreeverb = true # Zephyr board has audiobusio
audioi2sin = false
audioio = false
audiomixer = true # Zephyr board has audiobusio
audiomp3 = true # Zephyr board has audiobusio
audiopwmio = false
audiospeed = false
aurora_epaper = false
bitbangio = false
bitmapfilter = true # Zephyr board has busio
bitmaptools = true # Zephyr board has busio
bitops = false
board = false
busdisplay = true # Zephyr board has busio
busio = true # Zephyr board has busio
camera = false
canio = false
codeop = false
countio = false
digitalio = true
displayio = true # Zephyr board has displayio
dotclockframebuffer = false
dualbank = false
emmcio = false
epaperdisplay = true # Zephyr board has busio
floppyio = false
fontio = true # Zephyr board has busio
fourwire = true # Zephyr board has busio
framebufferio = true # Zephyr board has busio
frequencyio = false
getpass = true
gifio = true # Zephyr board has busio
gnss = false
hashlib = true # Zephyr networking enabled
hostnetwork = true # Zephyr board has hostnetwork
i2cdisplaybus = true # Zephyr board has busio
i2cioexpander = false
i2ctarget = false
imagecapture = false
ipaddress = true # Zephyr networking enabled
is31fl3741 = false
jpegio = true # Zephyr board has busio
keypad = false
keypad_demux = false
locale = false
lvfontio = true # Zephyr board has busio
math = true
max3421e = false
mcp4822 = false
mdns = false
memorymap = false
memorymonitor = false
microcontroller = true
mipidsi = false
msgpack = true
neopixel_write = false
nvm = true # Zephyr board has nvm
onewireio = false
os = true
paralleldisplaybus = false
picogame = false
ps2io = false
pulseio = false
pwmio = false
qrio = false
qspibus = false
rainbowio = true
random = true
rclcpy = false
rgbmatrix = false
rotaryio = true # Zephyr board has rotaryio
rtc = false
sdcardio = true # Zephyr board has busio
sdioio = false
sharpdisplay = true # Zephyr board has busio
socketpool = true # Zephyr networking enabled
spitarget = false
ssl = false
storage = true
struct = true
supervisor = true
synthio = true # Zephyr board has audiobusio
terminalio = true # Zephyr board has busio
tilepalettemapper = true # Zephyr board has busio
time = true
touchio = false
traceback = true
uheap = false
usb = false
usb_audio = false
usb_cdc = false
usb_hid = false
usb_host = false
usb_midi = false
usb_video = false
ustack = false
vectorio = true # Zephyr board has busio
warnings = true
watchdog = false
wifi = false
zephyr_display = true # Zephyr board has zephyr_display
zephyr_kernel = false
zlib = true
# extmod modules shared with MicroPython
asyncio = true
binascii = true
json = true
re = true
select = true
51 changes: 51 additions & 0 deletions ports/zephyr-cp/boards/native/native_sim_asan/board.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Build with AddressSanitizer to catch memory errors in tests.
CONFIG_ASAN=y

# No Bluetooth hardware on native_sim
CONFIG_BT=n

CONFIG_EMUL=y
CONFIG_GPIO=y
CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME=n

# So we can test safe mode
CONFIG_NATIVE_SIM_REBOOT=y

CONFIG_TRACING=y
CONFIG_TRACING_PERFETTO=y
CONFIG_TRACING_SYNC=y
CONFIG_TRACING_BACKEND_POSIX=y
CONFIG_TRACING_GPIO=y

# I2C emulation for testing
CONFIG_I2C_EMUL=y

# Display emulation for display/terminal golden tests.
CONFIG_DISPLAY=y
CONFIG_SDL_DISPLAY=y
# Don't require hardware acceleration so the software renderer can be used.
CONFIG_SDL_DISPLAY_USE_HARDWARE_ACCELERATOR=n

# EEPROM emulation for testing
CONFIG_EEPROM=y
CONFIG_EEPROM_AT24=y
CONFIG_EEPROM_AT2X_EMUL=y

# I2S SDL emulation for audio testing
CONFIG_I2S_SDL=y

CONFIG_NETWORKING=y
CONFIG_NET_IPV4=y
CONFIG_NET_TCP=y
CONFIG_NET_SOCKETS=y
CONFIG_ETH_NATIVE_TAP=n
CONFIG_NET_DRIVERS=y
CONFIG_NET_SOCKETS_OFFLOAD=y
CONFIG_NET_NATIVE_OFFLOADED_SOCKETS=y
CONFIG_HEAP_MEM_POOL_SIZE=1024

CONFIG_NET_LOG=y

CONFIG_MBEDTLS=y
CONFIG_PSA_WANT_ALG_SHA_1=y
CONFIG_PSA_WANT_ALG_SHA_256=y
8 changes: 8 additions & 0 deletions ports/zephyr-cp/boards/native/native_sim_asan/board.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Device tree overlay for CircuitPython on native_sim with AddressSanitizer.
* Identical hardware description to the non-asan board, so just reuse it.
*/

#include "../native_sim/board.overlay"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CIRCUITPY_BUILD_EXTENSIONS = ["exe"]
35 changes: 34 additions & 1 deletion ports/zephyr-cp/supervisor/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#if defined(CONFIG_ARCH_POSIX)
#include <limits.h>
#include <stdio.h>
#include <fcntl.h>

#include "cmdline.h"
Expand Down Expand Up @@ -280,7 +281,39 @@ uint32_t *port_stack_get_limit(void) {
uint32_t *port_stack_get_top(void) {
_thread_stack_info_t stack_info = k_current_get()->stack_info;

return (uint32_t *)(stack_info.start + stack_info.size - stack_info.delta);
uint32_t *top = (uint32_t *)(stack_info.start + stack_info.size - stack_info.delta);
#if defined(CONFIG_ARCH_POSIX)
// On hosted builds the thread stack is a pthread stack. pthread_getattr_np(),
// which the POSIX arch uses to fix up stack_info, can report a size larger
// than the real mapping (ASan intercepts it and inflates the size). The GC
// scans up to the returned top, so clamp it to the end of the mapping that
// contains the current stack pointer.
//
// Only ever clamp *downwards*: /proc/self/maps merges adjacent anonymous
// mappings with the same flags, so the line containing our stack pointer
// routinely covers several thread stacks at once (e.g. 0xf63c2000-0xf73c4000
// for two 8MB stacks). Taking its end as the top made the GC scan megabytes
// past this thread's stack, into the neighbouring thread's live stack -- and
// segfault whenever the range had an unmapped hole (a guard page, or a stack
// that has since been freed).
volatile uint32_t stack_probe;
uintptr_t sp = (uintptr_t)&stack_probe;
FILE *maps = fopen("/proc/self/maps", "r");
if (maps != NULL) {
char line[256];
unsigned long low, high;
while (fgets(line, sizeof(line), maps) != NULL) {
if (sscanf(line, "%lx-%lx", &low, &high) == 2 && low <= sp && sp < high) {
if (high < (uintptr_t)top) {
top = (uint32_t *)high;
}
break;
}
}
fclose(maps);
}
#endif
return top;
}

uint64_t port_get_raw_ticks(uint8_t *subticks) {
Expand Down
17 changes: 12 additions & 5 deletions ports/zephyr-cp/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,21 @@ def log_uart_trace_output(trace_file: Path) -> None:
)


@pytest.fixture
# Native_sim boards each test runs against: the non-asan default and the
# asan-enabled build, so memory errors fail tests.
NATIVE_BOARDS = ["native_native_sim", "native_native_sim_asan"]


@pytest.fixture(params=NATIVE_BOARDS)
def board(request):
"""Parametrized over both native_sim builds (non-asan and asan).

The bsim conftest overrides this fixture with its own bsim boards.
"""
board = request.node.get_closest_marker("circuitpython_board")
if board is not None:
board = board.args[0]
else:
board = "native_native_sim"
return board
return board.args[0]
return request.param


@pytest.fixture
Expand Down
4 changes: 2 additions & 2 deletions ports/zephyr-cp/tests/test_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@


@pytest.mark.circuitpy_drive(None)
def test_blank_flash_hello_world(circuitpython):
def test_blank_flash_hello_world(board, circuitpython):
"""Test that an erased flash shows code.py output header."""
circuitpython.wait_until_done()

output = circuitpython.serial.all_output
assert "Board ID:native_native_sim" in output
assert f"Board ID:{board}" in output
assert "UID:" in output
assert "code.py output:" in output
assert "Hello World" in output
Expand Down
14 changes: 14 additions & 0 deletions ports/zephyr-cp/zephyr_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

extern int circuitpython_main(void);

#if defined(__SANITIZE_ADDRESS__) || (defined(__has_feature) && __has_feature(address_sanitizer))
// ASAN's stack-use-after-return detection (on by default in recent runtimes)
// moves C locals into "fake stack" frames on the heap. The GC scans the real
// machine stack for live object pointers, so with fake stacks enabled it misses
// pointers held in C frames and collects still-referenced objects, corrupting
// the heap. GCC's instrumentation consults this global at every instrumented
// function entry, so clearing it from a constructor disables only that check;
// all other ASAN checks stay enabled.
extern char __asan_option_detect_stack_use_after_return;
__attribute__((constructor)) static void cp_disable_asan_fake_stack(void) {
__asan_option_detect_stack_use_after_return = 0;
}
#endif

int main(void) {
// Use a unique name for CP main so that the linker needs to look in libcircuitpython.a
return circuitpython_main();
Expand Down
Loading
Loading