Skip to content

Commit 3fa8d76

Browse files
committed
Add LLVM toolchain support to RISC-V build system
This commit introduces initial support for compiling the RISC-V target using the LLVM toolchain. Add CI workflow to build and test with QEMU Introduce GitHub Actions-based CI that builds the kernel using the GNU RISC-V toolchain and runs selected apps (e.g., cpubench) in QEMU. Results are extracted and posted to PRs. Toolchain setup, build, and QEMU test logic are modularized into scripts in .ci/. Features: - Support multiple apps per run - CI comments results on PR automatically - Timeout prevents hangs Limitations: - Currently GNU-only (LLVM WIP) - cpubench doesn't exit cleanly due to preemptive mode Fixes: #2 Supersedes: #11 Refactor riscv build.mk toolchain selection logic Add CI workflow for building and testing Improve CI workflow with QEMU, cpubench, timeout I notice that the cpu bench will make qemu into Scheduler mode: Preemptive so the qemu will not stop, add some trick to pass the test(not good) Refactor CI workflow for improved toolchain usage Comment out hello build and run step in CI workflow Update RISC-V toolchain and enable hello build step Add -bios none to QEMU commands in CI workflow Remove hello build and run step from CI workflow Add timeout to QEMU command in CI workflow Add completion message to cpubench CI step Allow QEMU step to pass without stopping CI job ci: refactor QEMU test workflow and extract scripts The workflow now extracts cpubench results from QEMU output and automatically comments the result on pull requests. Improve CI workflow with QEMU, cpubench, timeout I notice that the cpu bench will make qemu into Scheduler mode: Preemptive so the qemu will not stop, add some trick to pass the test(not good) Refactor CI workflow for improved toolchain usage Comment out hello build and run step in CI workflow Update RISC-V toolchain and enable hello build step Add -bios none to QEMU commands in CI workflow Remove hello build and run step from CI workflow Add timeout to QEMU command in CI workflow Add completion message to cpubench CI step Allow QEMU step to pass without stopping CI job Fix merge conflict markers in CI workflow file Use Ubuntu 24.04 in CI workflow Remove merge conflict markers from error.c Remove merge conflict markers from error.h Remove redundant libc.h include from libc.c Delete libc.c Add CI scripts and refactor workflow - Move toolchain setup, build, QEMU run, and result extraction to dedicated scripts in .ci/ - Update ci.yml to use these scripts for improved maintainability Make CI scripts executable Add flexible test sequencing and CI test suites - Replace run-qemu.sh with run-test-sequence.sh for configurable, robust test execution (supports timeouts, app lists, output dirs, continue-on-failure, and verbosity) - Add run-qemu-tests.sh for simple sequential app testing - Add test suite definitions: basic.txt, performance.txt, comprehensive.txt - Overhaul extract-result.sh to aggregate, summarize, and format results for GitHub Actions and artifacts - Update CI workflow to run multiple test suites (basic, performance, comprehensive, custom), upload artifacts, and generate a consolidated summary - Remove obsolete run-qemu.sh script Simplify CI test runner and result extraction Comment PR with benchmark results in CI workflow Run all basic apps in CI and comment results on PRs Remove extract-result.sh and update CI to inline test output Update QEMU tests to run cpubench and test64 only Inline build steps in CI workflow and remove build.sh
1 parent ceee53c commit 3fa8d76

File tree

6 files changed

+155
-19
lines changed

6 files changed

+155
-19
lines changed

.ci/run-qemu-tests.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/bin/bash
2+
set -e
3+
export CROSS_COMPILE=${CROSS_COMPILE:-riscv32-unknown-elf-}
4+
5+
# Check if at least one app is provided
6+
if [ $# -eq 0 ]; then
7+
echo "Usage: $0 <app1> [app2] [app3] ..."
8+
echo "Example: $0 hello echo cpubench"
9+
exit 1
10+
fi
11+
12+
APPS="$@"
13+
TIMEOUT=10
14+
15+
echo "[+] Will run apps: $APPS"
16+
echo ""
17+
18+
# Loop through each app
19+
for app in $APPS; do
20+
echo "=== Running $app ==="
21+
22+
# Build the app
23+
echo "[+] Building $app..."
24+
make clean >/dev/null 2>&1
25+
if ! make "$app" >/dev/null 2>&1; then
26+
echo "[!] Failed to build $app"
27+
echo ""
28+
continue
29+
fi
30+
31+
# Run in QEMU with timeout
32+
echo "[+] Running $app in QEMU (timeout: ${TIMEOUT}s)..."
33+
set +e
34+
timeout ${TIMEOUT}s qemu-system-riscv32 -nographic -machine virt -bios none -kernel build/image.elf
35+
exit_code=$?
36+
set -e
37+
38+
# Print result
39+
if [ $exit_code -eq 124 ]; then
40+
echo "[!] $app timed out"
41+
elif [ $exit_code -eq 0 ]; then
42+
echo "[✓] $app completed successfully"
43+
else
44+
echo "[!] $app failed with exit code $exit_code"
45+
fi
46+
echo ""
47+
done
48+
49+
echo "[+] All apps tested"

.ci/setup-toolchain.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
set -e
3+
4+
URL="https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.06.13/riscv32-elf-ubuntu-22.04-gcc-nightly-2025.06.13-nightly.tar.xz"
5+
6+
echo "[+] Downloading RISC-V GNU toolchain..."
7+
wget "$URL"
8+
tar -xf "$(basename "$URL")"
9+
10+
echo "[+] Exporting toolchain path..."
11+
echo "$PWD/riscv/bin" >> "$GITHUB_PATH"

.github/workflows/ci.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Linmo CI
2+
3+
on:
4+
push:
5+
branches: [main, ci]
6+
pull_request:
7+
branches: [main, ci]
8+
9+
jobs:
10+
basic-tests:
11+
runs-on: ubuntu-24.04
12+
name: Basic Tests
13+
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v4
17+
18+
- name: Install dependencies
19+
run: |
20+
sudo apt-get update
21+
sudo apt-get install -y build-essential qemu-system-riscv32 wget
22+
23+
- name: Setup RISC-V Toolchain
24+
run: .ci/setup-toolchain.sh
25+
26+
- name: Verify toolchain installation
27+
run: |
28+
riscv32-unknown-elf-gcc --version
29+
qemu-system-riscv32 --version
30+
env:
31+
CROSS_COMPILE: riscv32-unknown-elf-
32+
33+
- name: Build Kernel
34+
run: |
35+
make clean
36+
make
37+
env:
38+
CROSS_COMPILE: riscv32-unknown-elf-
39+
40+
- name: Run Basic Apps
41+
id: test
42+
run: |
43+
output=$(.ci/run-qemu-tests.sh cpubench test64)
44+
echo "TEST_OUTPUT<<EOF" >> $GITHUB_OUTPUT
45+
echo "$output" >> $GITHUB_OUTPUT
46+
echo "EOF" >> $GITHUB_OUTPUT
47+
48+
- name: Comment PR with results
49+
if: github.event_name == 'pull_request'
50+
uses: actions/github-script@v7
51+
with:
52+
script: |
53+
const output = `${{ steps.test.outputs.TEST_OUTPUT }}`;
54+
github.rest.issues.createComment({
55+
issue_number: context.issue.number,
56+
owner: context.repo.owner,
57+
repo: context.repo.repo,
58+
body: `## Linmo QEMU App Test Result\n\n\`\`\`\n${output}\n\`\`\`\n\n_This is an automated report from CI._`
59+
});

arch/riscv/build.mk

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,46 @@ DEFINES := -DF_CPU=$(F_CLK) \
1717
-DF_TIMER=$(F_TICK) \
1818
-include config.h
1919

20-
ASFLAGS = -march=rv32imzicsr -mabi=ilp32
20+
# Architecture flags
21+
ARCH_FLAGS = -march=rv32imzicsr -mabi=ilp32
22+
23+
# Common compiler flags
2124
CFLAGS += -Wall -Wextra -Wshadow -Wno-unused-parameter -Werror
2225
CFLAGS += -O2 -std=gnu99
23-
CFLAGS += -march=rv32imzicsr -mabi=ilp32
26+
CFLAGS += $(ARCH_FLAGS)
2427
CFLAGS += -mstrict-align -ffreestanding -nostdlib -fomit-frame-pointer
2528
CFLAGS += $(INC_DIRS) $(DEFINES) -fdata-sections -ffunction-sections
26-
ARFLAGS = r
2729

28-
# Linker flags
29-
LDFLAGS = -melf32lriscv --gc-sections
30-
LDSCRIPT = $(ARCH_DIR)/riscv32-qemu.ld
30+
ifeq ($(CC_IS_CLANG),1)
31+
CC = $(CROSS_COMPILE)clang
32+
AS = $(CROSS_COMPILE)clang
33+
LD = $(CROSS_COMPILE)ld.lld
34+
DUMP = $(CROSS_COMPILE)llvm-objdump -M no-aliases
35+
READ = $(CROSS_COMPILE)llvm-readelf
36+
OBJ = $(CROSS_COMPILE)llvm-objcopy
37+
SIZE = $(CROSS_COMPILE)llvm-size
38+
39+
CFLAGS += --target=riscv32-unknown-elf
40+
CFLAGS += -Wno-unused-command-line-argument
41+
ASFLAGS = --target=riscv32-unknown-elf $(ARCH_FLAGS)
42+
LDFLAGS = -m elf32lriscv --gc-sections
43+
else
44+
CC = $(CC_DEFAULT)
45+
AS = $(CROSS_COMPILE)as
46+
LD = $(CROSS_COMPILE)ld
47+
DUMP = $(CROSS_COMPILE)objdump -Mno-aliases
48+
READ = $(CROSS_COMPILE)readelf
49+
OBJ = $(CROSS_COMPILE)objcopy
50+
SIZE = $(CROSS_COMPILE)size
3151

32-
CROSS_COMPILE ?= riscv-none-elf-
33-
CC = $(CROSS_COMPILE)gcc
34-
AS = $(CROSS_COMPILE)as
35-
LD = $(CROSS_COMPILE)ld
36-
DUMP = $(CROSS_COMPILE)objdump -Mno-aliases
37-
READ = $(CROSS_COMPILE)readelf
38-
OBJ = $(CROSS_COMPILE)objcopy
39-
SIZE = $(CROSS_COMPILE)size
40-
AR = $(CROSS_COMPILE)ar
52+
ASFLAGS = $(ARCH_FLAGS)
53+
LDFLAGS = -melf32lriscv --gc-sections
54+
endif
55+
56+
AR = $(CROSS_COMPILE)ar
57+
58+
ARFLAGS = r
59+
LDSCRIPT = $(ARCH_DIR)/riscv32-qemu.ld
4160

4261
HAL_OBJS := boot.o hal.o muldiv.o
4362
HAL_OBJS := $(addprefix $(BUILD_KERNEL_DIR)/,$(HAL_OBJS))

kernel/pipe.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ static inline uint16_t pipe_free_space_internal(const pipe_t *p)
3434
return (p->mask + 1) - p->used;
3535
}
3636

37-
static inline char pipe_get_byte(pipe_t *p)
37+
static inline __attribute__((__unused__)) char pipe_get_byte(pipe_t *p)
3838
{
3939
char val = p->buf[p->head];
4040
p->head = (p->head + 1) & p->mask;
4141
p->used--;
4242
return val;
4343
}
4444

45-
static inline void pipe_put_byte(pipe_t *p, char c)
45+
static inline __attribute__((__unused__)) void pipe_put_byte(pipe_t *p, char c)
4646
{
4747
p->buf[p->tail] = c;
4848
p->tail = (p->tail + 1) & p->mask;

lib/malloc.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,13 @@ void free(void *ptr)
109109
static void selective_coalesce(void)
110110
{
111111
memblock_t *p = first_free;
112-
uint32_t coalesced = 0;
113112

114113
while (p && p->next) {
115114
/* Merge only when blocks are FREE *and* adjacent in memory */
116115
uint8_t *pend = (uint8_t *) p + sizeof(memblock_t) + GET_SIZE(p);
117116
if (!IS_USED(p) && !IS_USED(p->next) && pend == (uint8_t *) p->next) {
118117
p->size = GET_SIZE(p) + sizeof(memblock_t) + GET_SIZE(p->next);
119118
p->next = p->next->next;
120-
coalesced++;
121119
free_blocks_count--;
122120
} else {
123121
p = p->next;

0 commit comments

Comments
 (0)