diff --git a/.ci/run-qemu-tests.sh b/.ci/run-qemu-tests.sh new file mode 100755 index 0000000..1aea7d9 --- /dev/null +++ b/.ci/run-qemu-tests.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e + +# Check if at least one app is provided +if [ $# -eq 0 ]; then + echo "Usage: $0 [app2] [app3] ..." + echo "Example: $0 hello echo cpubench" + exit 1 +fi + +APPS="$@" +TIMEOUT=10 +TOOLCHAIN_TYPE=${TOOLCHAIN_TYPE:-gnu} + +echo "[+] Will run apps: $APPS" +echo "[+] Using toolchain: $TOOLCHAIN_TYPE" +echo "" + +# Loop through each app +for app in $APPS; do + echo "=== Running $app ($TOOLCHAIN_TYPE) ===" + + # Build the app + echo "[+] Building $app with $TOOLCHAIN_TYPE toolchain..." + make clean >/dev/null 2>&1 + if ! make "$app" TOOLCHAIN_TYPE="$TOOLCHAIN_TYPE" >/dev/null 2>&1; then + echo "[!] Failed to build $app with $TOOLCHAIN_TYPE" + echo "" + continue + fi + + # Run in QEMU with timeout + echo "[+] Running $app in QEMU (timeout: ${TIMEOUT}s)..." + set +e + timeout ${TIMEOUT}s qemu-system-riscv32 -nographic -machine virt -bios none -kernel build/image.elf + exit_code=$? + set -e + + # Print result + if [ $exit_code -eq 124 ]; then + echo "[!] $app timed out" + elif [ $exit_code -eq 0 ]; then + echo "[✓] $app completed successfully" + else + echo "[!] $app failed with exit code $exit_code" + fi + echo "" +done + +echo "[+] All apps tested with $TOOLCHAIN_TYPE toolchain" diff --git a/.ci/setup-toolchain.sh b/.ci/setup-toolchain.sh new file mode 100755 index 0000000..8c665d4 --- /dev/null +++ b/.ci/setup-toolchain.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -e + +# Default to GNU if no toolchain specified +TOOLCHAIN_TYPE=${1:-gnu} + +TOOLCHAIN_REPO=https://github.com/riscv-collab/riscv-gnu-toolchain +TOOLCHAIN_VERSION=2025.05.30 +TOOLCHAIN_OS=ubuntu-24.04 + +setup_gnu_toolchain() { + echo "[+] Setting up GNU RISC-V toolchain..." + + local URL="${TOOLCHAIN_REPO}/releases/download/${TOOLCHAIN_VERSION}/riscv32-elf-${TOOLCHAIN_OS}-gcc-nightly-${TOOLCHAIN_VERSION}-nightly.tar.xz" + + echo "[+] Downloading RISC-V GNU toolchain..." + wget -q "$URL" + tar -xf "$(basename "$URL")" + + echo "[+] Exporting GNU toolchain path..." + echo "$PWD/riscv/bin" >> "$GITHUB_PATH" + + # Set cross-compile prefix for GNU + echo "CROSS_COMPILE=riscv32-unknown-elf-" >> "$GITHUB_ENV" + echo "TOOLCHAIN_TYPE=gnu" >> "$GITHUB_ENV" +} + +setup_llvm_toolchain() { + echo "[+] Setting up LLVM RISC-V toolchain..." + + # upstream URL for LLVM toolchainzz2 + local URL="${TOOLCHAIN_REPO}/releases/download/${TOOLCHAIN_VERSION}/riscv32-elf-${TOOLCHAIN_OS}-llvm-nightly-${TOOLCHAIN_VERSION}-nightly.tar.xz" + + echo "[+] Downloading RISC-V LLVM toolchain..." + wget -q "$URL" + tar -xf "$(basename "$URL")" + + echo "[+] Exporting LLVM toolchain path..." + echo "$PWD/riscv/bin" >> "$GITHUB_PATH" + + # Set cross-compile prefix for LLVM + echo "CROSS_COMPILE=riscv32-unknown-elf-" >> "$GITHUB_ENV" + echo "TOOLCHAIN_TYPE=llvm" >> "$GITHUB_ENV" +} + +case "$TOOLCHAIN_TYPE" in + "gnu") + setup_gnu_toolchain + ;; + "llvm") + setup_llvm_toolchain + ;; + *) + echo "Error: Unknown toolchain type '$TOOLCHAIN_TYPE'" + echo "Usage: $0 [gnu|llvm]" + exit 1 + ;; +esac + +echo "[+] Toolchain setup complete: $TOOLCHAIN_TYPE" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..01a9e44 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,85 @@ +name: Linmo CI + +on: + push: + pull_request: + +jobs: + matrix-tests: + runs-on: ubuntu-24.04 + name: Test on ${{ matrix.toolchain }} toolchain + + strategy: + fail-fast: false + matrix: + toolchain: [gnu, llvm] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install base dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential qemu-system-riscv32 wget + + - name: Setup ${{ matrix.toolchain }} toolchain + run: .ci/setup-toolchain.sh ${{ matrix.toolchain }} + + - name: Verify toolchain installation + run: | + if [ "${{ matrix.toolchain }}" = "gnu" ]; then + riscv32-unknown-elf-gcc --version + else + # LLVM toolchain fallback: try system llvm-objdump + riscv32-unknown-elf-clang --version || clang --version + riscv32-unknown-elf-llvm-objdump --version || llvm-objdump --version + fi + qemu-system-riscv32 --version + + - name: Build Kernel + run: | + make clean + make + env: + TOOLCHAIN_TYPE: ${{ matrix.toolchain }} + + - name: Run Basic Apps + id: test + run: | + output=$(.ci/run-qemu-tests.sh cpubench ) + echo "TEST_OUTPUT<> $GITHUB_OUTPUT + echo "$output" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + env: + TOOLCHAIN_TYPE: ${{ matrix.toolchain }} + + - name: Comment PR with results + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const output = `${{ steps.test.outputs.TEST_OUTPUT }}`; + const toolchain = `${{ matrix.toolchain }}`.toUpperCase(); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## Linmo QEMU App Test Result (${toolchain} Toolchain)\n\n\`\`\`\n${output}\n\`\`\`\n\n_This is an automated report from CI._` + }); + + # Optional: Create a summary job that depends on all matrix jobs + test-summary: + runs-on: ubuntu-24.04 + needs: matrix-tests + if: always() + + steps: + - name: Check test results + run: | + if [ "${{ needs.matrix-tests.result }}" = "success" ]; then + echo "✅ All toolchain tests passed!" + else + echo "❌ Some toolchain tests failed" + exit 1 + fi diff --git a/arch/riscv/build.mk b/arch/riscv/build.mk index 2b00dca..8019a04 100644 --- a/arch/riscv/build.mk +++ b/arch/riscv/build.mk @@ -17,27 +17,54 @@ DEFINES := -DF_CPU=$(F_CLK) \ -DF_TIMER=$(F_TICK) \ -include config.h -ASFLAGS = -march=rv32imzicsr -mabi=ilp32 +CROSS_COMPILE ?= riscv32-unknown-elf- +CC = $(CROSS_COMPILE)gcc +CC_IS_CLANG := $(shell $(CC) --version 2>/dev/null | grep -qi clang && echo 1) + +# Architecture flags +ARCH_FLAGS = -march=rv32imzicsr -mabi=ilp32 + +# Common compiler flags CFLAGS += -Wall -Wextra -Wshadow -Wno-unused-parameter -Werror CFLAGS += -O2 -std=gnu99 -CFLAGS += -march=rv32imzicsr -mabi=ilp32 +CFLAGS += $(ARCH_FLAGS) CFLAGS += -mstrict-align -ffreestanding -nostdlib -fomit-frame-pointer CFLAGS += $(INC_DIRS) $(DEFINES) -fdata-sections -ffunction-sections -ARFLAGS = r -# Linker flags -LDFLAGS = -melf32lriscv --gc-sections -LDSCRIPT = $(ARCH_DIR)/riscv32-qemu.ld +ifeq ($(CC_IS_CLANG),1) +ifeq ($(TOOLCHAIN_TYPE),llvm) + CC = $(CROSS_COMPILE)clang + AS = $(CROSS_COMPILE)clang + LD = $(CROSS_COMPILE)ld.lld + DUMP = $(CROSS_COMPILE)llvm-objdump -M no-aliases + READ = $(CROSS_COMPILE)llvm-readelf + OBJ = $(CROSS_COMPILE)llvm-objcopy + SIZE = $(CROSS_COMPILE)llvm-size + AR = $(CROSS_COMPILE)ar + + CFLAGS += --target=riscv32-unknown-elf + CFLAGS += -Wno-unused-command-line-argument + ASFLAGS = --target=riscv32-unknown-elf $(ARCH_FLAGS) + LDFLAGS = -m elf32lriscv --gc-sections +else + CC = $(CC_DEFAULT) + CC = $(CROSS_COMPILE)gcc + AS = $(CROSS_COMPILE)as + LD = $(CROSS_COMPILE)ld + DUMP = $(CROSS_COMPILE)objdump -Mno-aliases + READ = $(CROSS_COMPILE)readelf + OBJ = $(CROSS_COMPILE)objcopy + SIZE = $(CROSS_COMPILE)size + AR = $(CROSS_COMPILE)ar -CROSS_COMPILE ?= riscv-none-elf- -CC = $(CROSS_COMPILE)gcc -AS = $(CROSS_COMPILE)as -LD = $(CROSS_COMPILE)ld -DUMP = $(CROSS_COMPILE)objdump -Mno-aliases -READ = $(CROSS_COMPILE)readelf -OBJ = $(CROSS_COMPILE)objcopy -SIZE = $(CROSS_COMPILE)size -AR = $(CROSS_COMPILE)ar + ASFLAGS = $(ARCH_FLAGS) + LDFLAGS = -melf32lriscv --gc-sections +endif + +AR = $(CROSS_COMPILE)ar + +ARFLAGS = r +LDSCRIPT = $(ARCH_DIR)/riscv32-qemu.ld HAL_OBJS := boot.o hal.o muldiv.o HAL_OBJS := $(addprefix $(BUILD_KERNEL_DIR)/,$(HAL_OBJS)) diff --git a/kernel/pipe.c b/kernel/pipe.c index 169c6f9..fd7c5af 100644 --- a/kernel/pipe.c +++ b/kernel/pipe.c @@ -9,6 +9,8 @@ #define PIPE_MIN_SIZE 4 #define PIPE_MAX_SIZE 32768 +#define __maybe_unused __attribute__((__unused__)) + /* Enhanced validation with comprehensive integrity checks */ static inline bool pipe_is_valid(const pipe_t *p) { @@ -34,7 +36,7 @@ static inline uint16_t pipe_free_space_internal(const pipe_t *p) return (p->mask + 1) - p->used; } -static inline char pipe_get_byte(pipe_t *p) +static inline __attribute__((__unused__)) char pipe_get_byte(pipe_t *p) { char val = p->buf[p->head]; p->head = (p->head + 1) & p->mask; @@ -42,7 +44,7 @@ static inline char pipe_get_byte(pipe_t *p) return val; } -static inline void pipe_put_byte(pipe_t *p, char c) +static inline __attribute__((__unused__)) void pipe_put_byte(pipe_t *p, char c) { p->buf[p->tail] = c; p->tail = (p->tail + 1) & p->mask; diff --git a/lib/malloc.c b/lib/malloc.c index ac439ab..ef708fe 100644 --- a/lib/malloc.c +++ b/lib/malloc.c @@ -109,7 +109,6 @@ void free(void *ptr) static void selective_coalesce(void) { memblock_t *p = first_free; - uint32_t coalesced = 0; while (p && p->next) { /* Merge only when blocks are FREE *and* adjacent in memory */ @@ -117,7 +116,6 @@ static void selective_coalesce(void) if (!IS_USED(p) && !IS_USED(p->next) && pend == (uint8_t *) p->next) { p->size = GET_SIZE(p) + sizeof(memblock_t) + GET_SIZE(p->next); p->next = p->next->next; - coalesced++; free_blocks_count--; } else { p = p->next;