diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 50a8e9b3..408cf837 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -284,6 +284,13 @@ jobs: env: CC: ${{ steps.install_cc.outputs.cc }} run: | + . .ci/common.sh + export LATEST_RELEASE=$(download_with_headers "https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases" \ + "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + | grep '"tag_name"' \ + | grep "sail" \ + | head -n 1 \ + | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') .ci/riscv-tests.sh if: ${{ always() }} @@ -341,7 +348,8 @@ jobs: - uses: actions/checkout@v4 - name: install-dependencies run: | - brew install make dtc expect sdl2 sdl2_mixer bc e2fsprogs p7zip llvm@18 dcfldd + brew install make dtc expect sdl2 bc e2fsprogs p7zip llvm@18 dcfldd + brew install sdl2_mixer || echo "Warning: sdl2_mixer installation failed, continuing without SDL_MIXER support" .ci/riscv-toolchain-install.sh echo "${{ github.workspace }}/toolchain/bin" >> $GITHUB_PATH echo "$(brew --prefix llvm@18)/bin" >> $GITHUB_PATH @@ -489,6 +497,13 @@ jobs: env: CC: ${{ steps.install_cc.outputs.cc }} run: | + . .ci/common.sh + export LATEST_RELEASE=$(download_with_headers "https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases" \ + "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + | grep '"tag_name"' \ + | grep "sail" \ + | head -n 1 \ + | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') python3 -m venv venv . venv/bin/activate .ci/riscv-tests.sh @@ -525,8 +540,12 @@ jobs: sudo apt-get install -q=2 clang-18 clang-tools-18 shell: bash - name: run scan-build without JIT + env: + LATEST_RELEASE: dummy run: make distclean && scan-build-18 -v -o ~/scan-build --status-bugs --use-cc=clang-18 --force-analyze-debug-code --show-description -analyzer-config stable-report-filename=true -enable-checker valist,nullability make ENABLE_EXT_F=0 ENABLE_SDL=0 ENABLE_JIT=0 - name: run scan-build with JIT + env: + LATEST_RELEASE: dummy run: | make ENABLE_JIT=1 distclean && scan-build-18 -v -o ~/scan-build --status-bugs --use-cc=clang-18 --force-analyze-debug-code --show-description -analyzer-config stable-report-filename=true -enable-checker valist,nullability make ENABLE_EXT_F=0 ENABLE_SDL=0 ENABLE_JIT=1 diff --git a/Makefile b/Makefile index b123215a..8454d807 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,11 @@ include mk/common.mk include mk/toolchain.mk +# Verify GNU make version (3.80+ required for order-only prerequisites) +ifeq ($(filter 3.80 3.81 3.82 3.83 3.84 4.% 5.%,$(MAKE_VERSION)),) +$(error GNU make 3.80 or higher is required. Current version: $(MAKE_VERSION)) +endif + OUT ?= build BIN := $(OUT)/rv32emu @@ -46,6 +51,7 @@ endif # Device Tree(initrd, memory range) # src/io.c(memory init) # src/riscv.c(system emulation layout init) +# Note: These memory settings are for SYSTEM mode only (when ELF_LOADER=0) ifeq ($(call has, SYSTEM), 1) ifeq ($(call has, ELF_LOADER), 0) MiB = 1024*1024 @@ -66,6 +72,7 @@ CFLAGS_dt += -DMEM_START=0x$(MEM_START) \ -DINITRD_END=0x$(shell echo "obase=16; ibase=16; \ $(REAL_MEM_SIZE) - $(call compute_size, $(DTB_SIZE)) - 1" | bc) +# Memory size for SYSTEM mode (may be overridden by FULL4G if ENABLE_ELF_LOADER=1) CFLAGS += -DMEM_SIZE=0x$(REAL_MEM_SIZE) -DDTB_SIZE=0x$(REAL_DTB_SIZE) -DINITRD_SIZE=0x$(REAL_INITRD_SIZE) endif endif @@ -173,6 +180,7 @@ ENABLE_FULL4G ?= 0 # Experimental SDL oriented system calls ENABLE_SDL ?= 1 +ENABLE_SDL_MIXER ?= 1 ifneq ("$(CC_IS_EMCC)", "1") # note that emcc generates port SDL headers/library, so it does not requires system SDL headers/library ifeq ($(call has, SDL), 1) ifeq (, $(shell which sdl2-config)) @@ -180,20 +188,31 @@ $(warning No sdl2-config in $$PATH. Check SDL2 installation in advance) override ENABLE_SDL := 0 endif ifeq (1, $(shell pkg-config --exists SDL2_mixer; echo $$?)) -$(warning No SDL2_mixer lib installed. Check SDL2_mixer installation in advance) -override ENABLE_SDL := 0 +$(warning No SDL2_mixer lib installed. SDL2_mixer support will be disabled) +override ENABLE_SDL_MIXER := 0 endif endif +else +# Disable SDL_MIXER for emscripten builds to avoid SDL2_mixer port compilation issues +# The emscripten-ports/SDL2_mixer was archived in Jan 2024 with unfixable warnings +override ENABLE_SDL_MIXER := 0 +endif $(call set-feature, SDL) +$(call set-feature, SDL_MIXER) ifeq ($(call has, SDL), 1) OBJS_EXT += syscall_sdl.o +ifneq ("$(CC_IS_EMCC)", "1") $(OUT)/syscall_sdl.o: CFLAGS += $(shell sdl2-config --cflags) +endif # 4 GiB of memory is required to run video games. ENABLE_FULL4G := 1 +ifneq ("$(CC_IS_EMCC)", "1") LDFLAGS += $(shell sdl2-config --libs) -pthread +ifeq ($(call has, SDL_MIXER), 1) LDFLAGS += $(shell pkg-config --libs SDL2_mixer) endif endif +endif # If SYSTEM is enabled and ELF_LOADER is not, then skip FULL4G bacause guestOS # has dedicated memory mapping range. @@ -206,7 +225,22 @@ endif # Full access to a 4 GiB address space, necessitating more memory mapping # during emulator initialization. $(call set-feature, FULL4G) + +# Configuration validation and conflict detection +ifeq ($(call has, SDL), 1) +ifeq ($(call has, SYSTEM), 1) +ifeq ($(call has, ELF_LOADER), 0) +ifeq ($(call has, FULL4G), 0) + $(warning SDL requires FULL4G=1 but SYSTEM forces FULL4G=0. Set ENABLE_ELF_LOADER=1 or disable SDL/SYSTEM) +endif +endif +endif +endif + ifeq ($(call has, FULL4G), 1) +# Note: If both SYSTEM and FULL4G are enabled with ELF_LOADER=1, +# this MEM_SIZE definition will override the SYSTEM mode definition. +# This is intentional for ELF loader use cases. CFLAGS += -DMEM_SIZE=0xFFFFFFFFULL # 2^{32} - 1 endif @@ -259,6 +293,8 @@ ifeq ($(call has, JIT), 1) else $(error No llvm-config-18 installed. Check llvm-config-18 installation in advance, or use "ENABLE_T2C=0" to disable tier-2 LLVM compiler) endif + else + $(warning T2C (tier-2 compiler) is disabled. Using tier-1 JIT only.) endif ifneq ($(processor),$(filter $(processor),x86_64 aarch64 arm64)) $(error JIT mode only supports for x64 and arm64 target currently.) @@ -292,7 +328,7 @@ include mk/artifact.mk include mk/system.mk include mk/wasm.mk -all: config $(BUILD_DTB) $(BUILD_DTB2C) $(BIN) +all: $(BUILD_DTB) $(BUILD_DTB2C) $(BIN) OBJS := \ map.o \ @@ -327,19 +363,31 @@ ifeq ($(call has, GDBSTUB), 1) $(OBJS): $(GDBSTUB_LIB) endif -$(OUT)/%.o: src/%.c $(deps_emcc) - $(Q)mkdir -p $(shell dirname $@) +$(OUT)/%.o: src/%.c $(CONFIG_FILE) $(deps_emcc) | $(OUT) + $(Q)mkdir -p $(dir $@) $(VECHO) " CC\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) $(CFLAGS_emcc) -c -MMD -MF $@.d $< -$(BIN): $(OBJS) $(DEV_OBJS) +$(OUT): + $(Q)mkdir -p $@ + +$(BIN): $(OBJS) $(DEV_OBJS) | $(OUT) $(VECHO) " LD\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS_emcc) $^ $(LDFLAGS) +$(CONFIG_FILE): FORCE + $(Q)mkdir -p $(OUT) + $(Q)echo "$(CFLAGS)" | xargs -n1 | sort | sed -n 's/^RV32_FEATURE/ENABLE/p' > $@.tmp + $(Q)if ! cmp -s $@ $@.tmp 2>/dev/null; then \ + mv $@.tmp $@; \ + $(PRINTF) "Configuration updated. Check $(OUT)/.config for configured items.\n"; \ + else \ + $(RM) $@.tmp; \ + fi + +.PHONY: FORCE config +FORCE: config: $(CONFIG_FILE) -$(CONFIG_FILE): - $(Q)echo "$(CFLAGS)" | xargs -n1 | sort | sed -n 's/^RV32_FEATURE/ENABLE/p' > $@ - $(VECHO) "Check the file $(OUT)/.config for configured items.\n" # Tools include mk/tools.mk @@ -434,4 +482,7 @@ distclean: clean $(Q)-$(RM) -r $(SOFTFLOAT_DUMMY_PLAT) $(OUT)/softfloat $(Q)$(call notice, [OK]) +.PHONY: all config tool check check-hello misalign misalign-in-blk-emu mmu-test +.PHONY: gdbstub-test doom quake clean distclean artifact + -include $(deps) diff --git a/mk/external.mk b/mk/external.mk index 16f9fa44..b4d7f68e 100644 --- a/mk/external.mk +++ b/mk/external.mk @@ -4,10 +4,10 @@ COMPRESSED_IS_DIR := EXTRACTOR := VERIFIER := -# temporarily files to store correct SHA value and computed SHA value +# Temporary files to store correct SHA value and computed SHA value # respectively for verification of directory source -$(eval SHA_FILE1 := $(shell mktemp)) -$(eval SHA_FILE2 := $(shell mktemp)) +SHA_FILE1 := $(shell mktemp) +SHA_FILE2 := $(shell mktemp) # $(1): compressed source define prologue @@ -17,7 +17,7 @@ endef # $(1), $(2), $(3): files to be deleted define epilogue - $(eval _ := $(shell $(RM) $(1) $(2) $(3))) + $(RM) $(1) $(2) $(3) endef # $(1): compressed source URL diff --git a/mk/riscv-arch-test.mk b/mk/riscv-arch-test.mk index 5ca0941d..941e5009 100644 --- a/mk/riscv-arch-test.mk +++ b/mk/riscv-arch-test.mk @@ -1,8 +1,8 @@ riscof-check: $(Q)if [ "$(shell pip show riscof 2>&1 | head -n 1 | cut -d' ' -f1)" = "WARNING:" ]; then \ - $(PRINTF) "Run 'pip3 install -r requirements.txt to install dependencies.\n"; \ - exit 1; \ - fi; + $(PRINTF) "Run 'pip3 install -r requirements.txt' to install dependencies.\n"; \ + exit 1; \ + fi ARCH_TEST_DIR ?= tests/riscv-arch-test ARCH_TEST_SUITE ?= $(ARCH_TEST_DIR)/riscv-test-suite @@ -27,3 +27,5 @@ endif --config=$(RISCV_TARGET)/config.ini \ --suite=$(ARCH_TEST_SUITE) \ --env=$(ARCH_TEST_DIR)/riscv-test-suite/env + +.PHONY: riscof-check arch-test diff --git a/mk/softfloat.mk b/mk/softfloat.mk index acc9a9e2..cb82a5e0 100644 --- a/mk/softfloat.mk +++ b/mk/softfloat.mk @@ -334,11 +334,13 @@ $(SOFTFLOAT_DUMMY_PLAT): $(Q)touch $@ $(SOFTFLOAT_FILES): $(SOFTFLOAT_SENTINEL) -$(OUT)/softfloat/%.o: $(SOFTFLOAT_DIR)/%.c $(SOFTFLOAT_SENTINEL) $(SOFTFLOAT_DUMMY_PLAT) - $(Q)mkdir -p $(shell dirname $@) +$(OUT)/softfloat/%.o: $(SOFTFLOAT_DIR)/%.c $(CONFIG_FILE) $(SOFTFLOAT_SENTINEL) $(SOFTFLOAT_DUMMY_PLAT) | $(OUT)/softfloat $(OUT)/softfloat/RISCV $(VECHO) " CC\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) $(CFLAGS_softfloat) -c -MMD -MF $@.d $< +$(OUT)/softfloat $(OUT)/softfloat/RISCV: + $(Q)mkdir -p $@ + SOFTFLOAT_LIB := $(OUT)/softfloat/softfloat.a $(SOFTFLOAT_LIB): $(SOFTFLOAT_OBJS) $(VECHO) " AR\t$@\n" diff --git a/mk/system.mk b/mk/system.mk index 14d73591..6f63fb44 100644 --- a/mk/system.mk +++ b/mk/system.mk @@ -29,10 +29,13 @@ $(BUILD_DTB2C): $(BIN_TO_C) $(BUILD_DTB) $(VECHO) " BIN2C\t$@\n" $(Q)$(BIN_TO_C) $(BUILD_DTB) > $@ -$(DEV_OUT)/%.o: $(DEV_SRC)/%.c $(deps_emcc) - $(Q)mkdir -p $(DEV_OUT) +$(DEV_OUT)/%.o: $(DEV_SRC)/%.c $(CONFIG_FILE) $(deps_emcc) | $(DEV_OUT) $(VECHO) " CC\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) $(CFLAGS_emcc) -c -MMD -MF $@.d $< + +$(DEV_OUT): + $(Q)mkdir -p $@ + DEV_OBJS := $(patsubst $(DEV_SRC)/%.c, $(DEV_OUT)/%.o, $(wildcard $(DEV_SRC)/*.c)) deps := $(DEV_OBJS:%.o=%.o.d) @@ -46,4 +49,6 @@ system_deps += artifact $(BUILD_DTB) $(BUILD_DTB2C) $(BIN) system: $(system_deps) $(system_action) +.PHONY: system + endif diff --git a/mk/tests.mk b/mk/tests.mk index 29816e3c..cf89f82a 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -85,11 +85,13 @@ $(CACHE_TEST_TARGET): $(CACHE_TEST_OBJS) $(VECHO) " CC\t$@\n" $(Q)$(CC) $^ -o $@ $(LDFLAGS) -$(CACHE_TEST_OUTDIR)/%.o: $(CACHE_TEST_SRCDIR)/%.c +$(CACHE_TEST_OUTDIR)/%.o: $(CACHE_TEST_SRCDIR)/%.c $(CONFIG_FILE) | $(CACHE_TEST_OUTDIR) $(CACHE_TEST_OUTDIR)/lfu $(VECHO) " CC\t$@\n" - $(Q)mkdir -p $(dir $@)/lfu $(Q)$(CC) -o $@ $(CFLAGS) -I./src -c -MMD -MF $@.d $< +$(CACHE_TEST_OUTDIR) $(CACHE_TEST_OUTDIR)/lfu: + $(Q)mkdir -p $@ + $(MAP_TEST_OUT): $(MAP_TEST_TARGET) $(Q)touch $@ @@ -97,11 +99,13 @@ $(MAP_TEST_TARGET): $(MAP_TEST_OBJS) $(VECHO) " CC\t$@\n" $(Q)$(CC) $^ -o $@ $(LDFLAGS) -$(MAP_TEST_OUTDIR)/%.o: $(MAP_TEST_SRCDIR)/%.c +$(MAP_TEST_OUTDIR)/%.o: $(MAP_TEST_SRCDIR)/%.c $(CONFIG_FILE) | $(MAP_TEST_OUTDIR) $(VECHO) " CC\t$@\n" - $(Q)mkdir -p $(dir $@) $(Q)$(CC) -o $@ $(CFLAGS) -I./src -c -MMD -MF $@.d $< +$(MAP_TEST_OUTDIR): + $(Q)mkdir -p $@ + $(PATH_TEST_OUT): $(PATH_TEST_TARGET) $(Q)touch $@ @@ -109,7 +113,11 @@ $(PATH_TEST_TARGET): $(PATH_TEST_OBJS) $(VECHO) " CC\t$@\n" $(Q)$(CC) $^ -o $@ $(LDFLAGS) -$(PATH_TEST_OUTDIR)/%.o: $(PATH_TEST_SRCDIR)/%.c +$(PATH_TEST_OUTDIR)/%.o: $(PATH_TEST_SRCDIR)/%.c $(CONFIG_FILE) | $(PATH_TEST_OUTDIR) $(VECHO) " CC\t$@\n" - $(Q)mkdir -p $(dir $@) $(Q)$(CC) -o $@ $(CFLAGS) -I./src -c -MMD -MF $@.d $< + +$(PATH_TEST_OUTDIR): + $(Q)mkdir -p $@ + +.PHONY: tests run-test-cache run-test-map run-test-path diff --git a/mk/tools.mk b/mk/tools.mk index 0d6a2157..fbef691f 100644 --- a/mk/tools.mk +++ b/mk/tools.mk @@ -23,7 +23,7 @@ HIST_OBJS := \ HIST_OBJS := $(addprefix $(OUT)/, $(HIST_OBJS)) deps += $(HIST_OBJS:%.o=%.o.d) -$(OUT)/%.o: tools/%.c +$(OUT)/%.o: tools/%.c | $(OUT) $(VECHO) " CC\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) -Wno-missing-field-initializers -Isrc -c -MMD -MF $@.d $< @@ -39,3 +39,5 @@ LINUX_IMAGE_SRC = $(BUILDROOT_DATA) $(LINUX_DATA) build-linux-image: $(LINUX_IMAGE_SRC) $(Q)./tools/build-linux-image.sh $(Q)$(PRINTF) "Build done.\n" + +.PHONY: build-linux-image diff --git a/mk/wasm.mk b/mk/wasm.mk index 21c0d2d6..78b818cd 100644 --- a/mk/wasm.mk +++ b/mk/wasm.mk @@ -18,7 +18,10 @@ CFLAGS += -mtail-call # Build emscripten-port SDL ifeq ($(call has, SDL), 1) -CFLAGS_emcc += -sUSE_SDL=2 -sSDL2_MIXER_FORMATS=wav,mid -sUSE_SDL_MIXER=2 +# Disable STRICT mode to avoid -Werror in SDL2_mixer port compilation. +# The emscripten-ports/SDL2_mixer was archived in Jan 2024 and has warnings +# in music_modplug.c that become fatal errors under STRICT mode. +CFLAGS_emcc += -sSTRICT=0 -sUSE_SDL=2 -sSDL2_MIXER_FORMATS=wav,mid -sUSE_SDL_MIXER=2 OBJS_EXT += syscall_sdl.o LDFLAGS += -pthread endif @@ -159,4 +162,7 @@ start-web: $(start_web_deps) $(foreach T, $(STATIC_WEB_FILES), $(call cp-web-file, $(T))) $(Q)mv $(DEMO_DIR)/*.html $(DEMO_DIR)/index.html $(Q)python3 -m http.server --bind $(DEMO_IP) $(DEMO_PORT) --directory $(DEMO_DIR) + +.PHONY: check-demo-dir-exist start-web + endif diff --git a/src/syscall_sdl.c b/src/syscall_sdl.c index ae95b37f..32a33972 100644 --- a/src/syscall_sdl.c +++ b/src/syscall_sdl.c @@ -15,7 +15,9 @@ #include #include +#if RV32_HAS(SDL_MIXER) #include +#endif #include "riscv.h" #include "riscv_private.h" @@ -94,11 +96,13 @@ typedef struct sound { /* SDL-mixer-related and music-related variables */ static pthread_t music_thread; static uint8_t *music_midi_data; +#if RV32_HAS(SDL_MIXER) static Mix_Music *mid; /* SDL-mixer-related and sfx-related variables */ static pthread_t sfx_thread; static Mix_Chunk *sfx_chunk; +#endif static uint8_t *sfx_samples; static uint32_t nr_sfx_samples; static int chan; @@ -718,6 +722,7 @@ uint8_t *mus2midi(uint8_t *data, int *length) return midi_data; } +#if RV32_HAS(SDL_MIXER) static void *sfx_handler(void *arg) { sound_t *sfx = (sound_t *) arg; @@ -836,8 +841,10 @@ static void play_sfx(riscv_t *rv) .size = sfx_data_size, .volume = volume, }; +#if RV32_HAS(SDL_MIXER) pthread_create(&sfx_thread, NULL, sfx_handler, &sfx); sfx_thread_init = true; +#endif /* FIXME: In web browser runtime, web workers in thread pool do not reap * after sfx_handler return, thus we have to join them. sfx_handler does not * contain infinite loop,so do not worry to be stalled by it */ @@ -893,8 +900,10 @@ static void play_music(riscv_t *rv) .looping = looping, .volume = volume, }; +#if RV32_HAS(SDL_MIXER) pthread_create(&music_thread, NULL, music_handler, &music); music_thread_init = true; +#endif /* FIXME: In web browser runtime, web workers in thread pool do not reap * after music_handler return, thus we have to join them. music_handler does * not contain infinite loop,so do not worry to be stalled by it */ @@ -916,6 +925,7 @@ static void set_music_volume(riscv_t *rv) /* multiplied by 8 because volume's max is 15 */ Mix_VolumeMusic(volume * 8); } +#endif /* RV32_HAS(SDL_MIXER) */ static void init_audio(void) { @@ -933,6 +943,7 @@ static void init_audio(void) exit(EXIT_FAILURE); } +#if RV32_HAS(SDL_MIXER) /* Initialize SDL2 Mixer */ if (Mix_Init(MIX_INIT_MID) != MIX_INIT_MID) { rv_log_fatal("Mix_Init failed: %s", Mix_GetError()); @@ -943,6 +954,7 @@ static void init_audio(void) Mix_Quit(); exit(EXIT_FAILURE); } +#endif audio_init = true; } @@ -956,6 +968,7 @@ static void shutdown_audio() * on a valid pthread_t identifier. */ +#if RV32_HAS(SDL_MIXER) if (music_thread_init) { stop_music(); pthread_join(music_thread, NULL); @@ -974,6 +987,7 @@ static void shutdown_audio() Mix_CloseAudio(); Mix_Quit(); +#endif audio_init = sfx_thread_init = music_thread_init = false; } @@ -1020,16 +1034,24 @@ void syscall_control_audio(riscv_t *rv) switch (request) { case PLAY_MUSIC: +#if RV32_HAS(SDL_MIXER) play_music(rv); +#endif break; case PLAY_SFX: +#if RV32_HAS(SDL_MIXER) play_sfx(rv); +#endif break; case SET_MUSIC_VOLUME: +#if RV32_HAS(SDL_MIXER) set_music_volume(rv); +#endif break; case STOP_MUSIC: +#if RV32_HAS(SDL_MIXER) stop_music(); +#endif break; default: rv_log_error("Unknown sound control request: %d", request);