Skip to content

Commit a6ecb0c

Browse files
committed
Bring up Linux kernel
The 8250 UART and PLIC peripherals, defined in src/devices/uart.[ch] and src/devices/plic.[ch], have been integrated. Modifications to src/system.c enable MMIO read and write operations for these peripherals. The minimal device tree source file is also integrated in src/devices/minimal.dts. Default termios control flags are set to ICANON | ECHO | ISIG, allowing all keyboard input to be captured by the guestOS. The CLI parser has been updated to support custom Linux Kernel Images, rootfs, and Device Tree Blobs. The ELF_LOADER has been introduced to support test suites involving single ELF files that use the rv32emu built-in syscall. It helps to choose the right executable loader and requires differentiation from guestOS syscalls. The csrrw instruction is now branchable since it may overwrite the SATP CSR during guestOS process scheduling. last_pc and is_branch_taken are now updated only when not in a trapped state, otherwise might lost the original state of them. A system make target has been added to simplify running system emulation. Misc changes: - Rename 'ARRAYS_SIZE' to 'ARRAY_SIZE' for better readability - Move JIT related variables to proper conditional build flag
1 parent 5ffb157 commit a6ecb0c

21 files changed

+1431
-324
lines changed

Makefile

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,16 @@ CONFIG_FILE := $(OUT)/.config
99

1010
CFLAGS = -std=gnu99 -O2 -Wall -Wextra
1111
CFLAGS += -Wno-unused-label
12-
CFLAGS += -include src/common.h
12+
CFLAGS += -include src/common.h -Isrc/
1313

14+
# In the system test suite, the executable is an ELF file (e.g., MMU).
15+
# However, the Linux kernel emulation includes the Image, DT, and
16+
# root filesystem (rootfs). Therefore, the test suite needs this
17+
# flag to load the ELF and differentiate it from the kernel emulation.
18+
ENABLE_ELF_LOADER ?= 0
19+
$(call set-feature, ELF_LOADER)
20+
21+
# Enable system emulation
1422
ENABLE_SYSTEM ?= 0
1523
$(call set-feature, SYSTEM)
1624

@@ -45,10 +53,6 @@ CFLAGS += $(CFLAGS_NO_CET)
4553

4654
OBJS_EXT :=
4755

48-
ifeq ($(call has, SYSTEM), 1)
49-
OBJS_EXT += system.o
50-
endif
51-
5256
# Integer Multiplication and Division instructions
5357
ENABLE_EXT_M ?= 1
5458
$(call set-feature, EXT_M)
@@ -71,7 +75,14 @@ src/softfloat/build/Linux-RISCV-GCC/Makefile:
7175
SOFTFLOAT_LIB := $(SOFTFLOAT_OUT)/softfloat.a
7276
$(SOFTFLOAT_LIB): src/softfloat/build/Linux-RISCV-GCC/Makefile
7377
$(MAKE) -C $(dir $<) BUILD_DIR=$(SOFTFLOAT_OUT) CC=$(CC) AR=$(AR)
74-
$(OUT)/decode.o $(OUT)/riscv.o: $(SOFTFLOAT_LIB)
78+
ifeq ($(call has, SYSTEM), 1)
79+
DEV_OUT := $(OUT)/devices
80+
endif
81+
OBJS_NEED_SOFTFLOAT := $(OUT)/decode.o \
82+
$(OUT)/riscv.o \
83+
$(DEV_OUT)/uart.o \
84+
$(DEV_OUT)/plic.o
85+
$(OBJS_NEED_SOFTFLOAT): $(SOFTFLOAT_LIB)
7586
LDFLAGS += $(SOFTFLOAT_LIB)
7687
LDFLAGS += -lm
7788
endif
@@ -204,8 +215,9 @@ $(OUT)/emulate.o: CFLAGS += -foptimize-sibling-calls -fomit-frame-pointer -fno-s
204215
include mk/external.mk
205216
include mk/artifact.mk
206217
include mk/wasm.mk
218+
include mk/system.mk
207219

208-
all: config $(BIN)
220+
all: config $(BUILD_DTB) $(BIN)
209221

210222
OBJS := \
211223
map.o \
@@ -222,7 +234,7 @@ OBJS := \
222234
main.o
223235

224236
OBJS := $(addprefix $(OUT)/, $(OBJS))
225-
deps := $(OBJS:%.o=%.o.d)
237+
deps += $(OBJS:%.o=%.o.d) # mk/system.mk includes prior this line, so declare deps at there
226238

227239
ifeq ($(call has, EXT_F), 1)
228240
$(OBJS): $(SOFTFLOAT_LIB)
@@ -236,7 +248,7 @@ $(OUT)/%.o: src/%.c $(deps_emcc)
236248
$(VECHO) " CC\t$@\n"
237249
$(Q)$(CC) -o $@ $(CFLAGS) $(CFLAGS_emcc) -c -MMD -MF $@.d $<
238250

239-
$(BIN): $(OBJS)
251+
$(BIN): $(OBJS) $(DEV_OBJS)
240252
$(VECHO) " LD\t$@\n"
241253
$(Q)$(CC) -o $@ $(CFLAGS_emcc) $^ $(LDFLAGS)
242254

@@ -333,9 +345,10 @@ endif
333345
endif
334346

335347
clean:
336-
$(RM) $(BIN) $(OBJS) $(HIST_BIN) $(HIST_OBJS) $(deps) $(WEB_FILES) $(CACHE_OUT) src/rv32_jit.c
348+
$(RM) $(BIN) $(OBJS) $(DEV_OBJS) $(BUILD_DTB) $(HIST_BIN) $(HIST_OBJS) $(deps) $(WEB_FILES) $(CACHE_OUT) src/rv32_jit.c
337349
distclean: clean
338-
-$(RM) $(DOOM_DATA) $(QUAKE_DATA)
350+
-$(RM) $(DOOM_DATA) $(QUAKE_DATA) $(BUILDROOT_DATA) $(LINUX_DATA)
351+
$(RM) -r $(OUT)/linux-image
339352
$(RM) -r $(TIMIDITY_DATA)
340353
$(RM) -r $(OUT)/id1
341354
$(RM) -r $(DEMO_DIR)

mk/artifact.mk

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,14 @@ TEST_BENCHES += \
3434
SCIMARK2_URL := https://math.nist.gov/scimark2/scimark2_1c.zip
3535
SCIMARK2_SHA1 := de278c5b8cef84ab6dda41855052c7bfef919e36
3636

37-
SHELL_HACK := $(shell mkdir -p $(BIN_DIR)/linux-x86-softfp $(BIN_DIR)/riscv32)
37+
SHELL_HACK := $(shell mkdir -p $(BIN_DIR)/linux-x86-softfp $(BIN_DIR)/riscv32 $(BIN_DIR)/linux-image)
3838

3939
ifeq ($(call has, PREBUILT), 1)
40-
LATEST_RELEASE := $(shell wget -q https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases/latest -O- | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/')
40+
ifeq ($(call has, SYSTEM), 1)
41+
LATEST_RELEASE := $(shell wget -q https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases -O- | grep '"tag_name"' | grep "Linux-Image" | head -n 1 | sed -E 's/.*"tag_name": "([^"]+)".*/\1/')
42+
else
43+
LATEST_RELEASE := $(shell wget -q https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases -O- | grep '"tag_name"' | grep "ELF" | head -n 1 | sed -E 's/.*"tag_name": "([^"]+)".*/\1/')
44+
endif
4145
else
4246
# Since rv32emu only supports the dynamic binary translation of integer instruction in tiered compilation currently,
4347
# we disable the hardware floating-point and the related SIMD operation of x86.
@@ -53,24 +57,40 @@ endif
5357
artifact: fetch-checksum ieeelib scimark2
5458
ifeq ($(call has, PREBUILT), 1)
5559
$(Q)$(PRINTF) "Checking SHA-1 of prebuilt binaries ... "
60+
$(Q)$(eval RES := 0)
61+
62+
ifeq ($(call has, SYSTEM), 1)
63+
$(Q)$(eval PREBUILT_LINUX_IMAGE_FILENAME := $(shell cat $(BIN_DIR)/sha1sum-linux-image | awk '{ print $$2 };'))
5664

65+
$(Q)$(eval $(foreach FILE,$(PREBUILT_LINUX_IMAGE_FILENAME), \
66+
$(call verify,$(shell grep -w $(FILE) $(BIN_DIR)/sha1sum-linux-image | awk '{ print $$1 };'),$(BIN_DIR)/linux-image/$(FILE),RES) \
67+
))
68+
69+
$(Q)$(eval RV32EMU_PREBUILT_TARBALL := rv32emu-linux-image-prebuilt.tar.gz)
70+
else
5771
$(Q)$(eval PREBUILT_X86_FILENAME := $(shell cat $(BIN_DIR)/sha1sum-linux-x86-softfp | awk '{ print $$2 };'))
5872
$(Q)$(eval PREBUILT_RV32_FILENAME := $(shell cat $(BIN_DIR)/sha1sum-riscv32 | awk '{ print $$2 };'))
5973

60-
$(Q)$(eval RES := 0)
6174
$(Q)$(eval $(foreach FILE,$(PREBUILT_X86_FILENAME), \
6275
$(call verify,$(shell grep -w $(FILE) $(BIN_DIR)/sha1sum-linux-x86-softfp | awk '{ print $$1 };'),$(BIN_DIR)/linux-x86-softfp/$(FILE),RES) \
6376
))
6477
$(Q)$(eval $(foreach FILE,$(PREBUILT_RV32_FILENAME), \
6578
$(call verify,$(shell grep -w $(FILE) $(BIN_DIR)/sha1sum-riscv32 | awk '{ print $$1 };'),$(BIN_DIR)/riscv32/$(FILE),RES) \
6679
))
6780

81+
$(Q)$(eval RV32EMU_PREBUILT_TARBALL := rv32emu-prebuilt.tar.gz)
82+
endif
83+
6884
$(Q)if [ "$(RES)" = "1" ]; then \
6985
$(PRINTF) "\n$(YELLOW)SHA-1 verification fails! Re-fetching prebuilt binaries from \"rv32emu-prebuilt\" ...\n$(NO_COLOR)"; \
70-
wget -q --show-progress https://github.com/sysprog21/rv32emu-prebuilt/releases/download/$(LATEST_RELEASE)/rv32emu-prebuilt.tar.gz -O- | tar -C build --strip-components=1 -xz; \
86+
wget -q --show-progress https://github.com/sysprog21/rv32emu-prebuilt/releases/download/$(LATEST_RELEASE)/$(RV32EMU_PREBUILT_TARBALL) -O- | tar -C build --strip-components=1 -xz; \
7187
else \
7288
$(call notice, [OK]); \
7389
fi
90+
else
91+
ifeq ($(call has, SYSTEM), 1)
92+
$(Q)(cd $(BIN_DIR) && $(SHA1SUM) Image >> sha1sum-linux-image)
93+
$(Q)(cd $(BIN_DIR) && $(SHA1SUM) rootfs.cpio >> sha1sum-linux-image)
7494
else
7595
git submodule update --init $(addprefix ./tests/,$(foreach tb,$(TEST_SUITES),$(tb)))
7696
$(Q)for tb in $(TEST_SUITES); do \
@@ -103,17 +123,24 @@ else
103123
$(Q)(cd $(BIN_DIR)/linux-x86-softfp; for fd in *; do $(SHA1SUM) "$$fd"; done) >> $(BIN_DIR)/sha1sum-linux-x86-softfp
104124
$(Q)(cd $(BIN_DIR)/riscv32; for fd in *; do $(SHA1SUM) "$$fd"; done) >> $(BIN_DIR)/sha1sum-riscv32
105125
endif
126+
endif
106127

107128
fetch-checksum:
108129
ifeq ($(call has, PREBUILT), 1)
109130
$(Q)$(PRINTF) "Fetching SHA-1 of prebuilt binaries ... "
131+
ifeq ($(call has, SYSTEM), 1)
132+
$(Q)wget -q -O $(BIN_DIR)/sha1sum-linux-image https://github.com/sysprog21/rv32emu-prebuilt/releases/download/$(LATEST_RELEASE)/sha1sum-linux-image
133+
$(Q)$(call notice, [OK])
134+
else
110135
$(Q)wget -q -O $(BIN_DIR)/sha1sum-linux-x86-softfp https://github.com/sysprog21/rv32emu-prebuilt/releases/download/$(LATEST_RELEASE)/sha1sum-linux-x86-softfp
111136
$(Q)wget -q -O $(BIN_DIR)/sha1sum-riscv32 https://github.com/sysprog21/rv32emu-prebuilt/releases/download/$(LATEST_RELEASE)/sha1sum-riscv32
112137
$(Q)$(call notice, [OK])
113138
endif
139+
endif
114140

115141
scimark2:
116142
ifeq ($(call has, PREBUILT), 0)
143+
ifeq ($(call has, SYSTEM), 0)
117144
$(Q)$(call prologue,"scimark2")
118145
$(Q)$(call download,$(SCIMARK2_URL))
119146
$(Q)$(call verify,$(SCIMARK2_SHA1),$(notdir $(SCIMARK2_URL)))
@@ -126,6 +153,7 @@ ifeq ($(call has, PREBUILT), 0)
126153
$(Q)$(MAKE) -C ./tests/scimark2 CC=$(CROSS_COMPILE)gcc CFLAGS="-march=rv32imf -mabi=ilp32 -O2"
127154
$(Q)cp ./tests/scimark2/scimark2 $(BIN_DIR)/riscv32/scimark2
128155
endif
156+
endif
129157

130158
ieeelib:
131159
ifeq ($(call has, PREBUILT), 0)

mk/system.mk

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Peripherals for system emulation
2+
ifeq ($(call has, SYSTEM), 1)
3+
4+
DEV_SRC := src/devices
5+
6+
DTC ?= dtc
7+
$(OUT)/minimal.dtb: $(DEV_SRC)/minimal.dts
8+
$(VECHO) " DTC\t$@\n"
9+
$(Q)$(DTC) $^ -o $@
10+
BUILD_DTB := $(OUT)/minimal.dtb
11+
12+
$(DEV_OUT)/%.o: $(DEV_SRC)/%.c $(deps_emcc)
13+
$(Q)mkdir -p $(DEV_OUT)
14+
$(VECHO) " CC\t$@\n"
15+
$(Q)$(CC) -o $@ $(CFLAGS) $(CFLAGS_emcc) -c -MMD -MF $@.d $<
16+
DEV_OBJS := $(patsubst $(DEV_SRC)/%.c, $(DEV_OUT)/%.o, $(wildcard $(DEV_SRC)/*.c))
17+
deps := $(DEV_OBJS:%.o=%.o.d)
18+
19+
OBJS_EXT += system.o
20+
21+
# system target execution by using default dependencies
22+
LINUX_IMAGE_DIR := linux-image
23+
system_action := ($(BIN) -k $(OUT)/$(LINUX_IMAGE_DIR)/Image -i $(OUT)/$(LINUX_IMAGE_DIR)/rootfs.cpio -b $(OUT)/minimal.dtb)
24+
system_deps += artifact $(BUILD_DTB) $(BIN)
25+
system: $(system_deps)
26+
$(system_action)
27+
28+
endif

src/common.h

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
#pragma once
77

8+
#include <assert.h>
9+
#include <stdint.h>
10+
811
#include "feature.h"
912

1013
#if defined(__GNUC__) || defined(__clang__)
@@ -23,10 +26,64 @@
2326
#endif
2427
#endif
2528

26-
#define ARRAYS_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
29+
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
2730

2831
#define MASK(n) (~((~0U << (n))))
2932

33+
#if defined(_MSC_VER)
34+
#include <intrin.h>
35+
static inline int rv_clz(uint32_t v)
36+
{
37+
/* 0 is considered as undefined behavior */
38+
assert(v);
39+
40+
uint32_t leading_zero = 0;
41+
_BitScanReverse(&leading_zero, v);
42+
return 31 - leading_zero;
43+
}
44+
#elif defined(__GNUC__) || defined(__clang__)
45+
static inline int rv_clz(uint32_t v)
46+
{
47+
/* https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */
48+
/* 0 is considered as undefined behavior */
49+
assert(v);
50+
51+
return __builtin_clz(v);
52+
}
53+
#else /* generic implementation */
54+
static inline int rv_clz(uint32_t v)
55+
{
56+
/* 0 is considered as undefined behavior */
57+
assert(v);
58+
59+
/* http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn */
60+
static const uint8_t mul_debruijn[] = {
61+
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
62+
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31,
63+
};
64+
65+
v |= v >> 1;
66+
v |= v >> 2;
67+
v |= v >> 4;
68+
v |= v >> 8;
69+
v |= v >> 16;
70+
71+
return mul_debruijn[(uint32_t) (v * 0x07C4ACDDU) >> 27];
72+
}
73+
#endif
74+
75+
/*
76+
* Integer log base 2
77+
*
78+
* The input x must not be zero.
79+
* Otherwise, the result is undefined on some platform.
80+
*
81+
*/
82+
static inline uint8_t ilog2(uint32_t x)
83+
{
84+
return 31 - rv_clz(x);
85+
}
86+
3087
/* Alignment macro */
3188
#if defined(__GNUC__) || defined(__clang__)
3289
#define __ALIGNED(x) __attribute__((aligned(x)))

src/decode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ enum op_field {
9090
) \
9191
/* RV32 Zicsr Standard Extension */ \
9292
IIF(RV32_HAS(Zicsr))( \
93-
_(csrrw, 0, 4, 0, ENC(rs1, rd)) \
93+
_(csrrw, 1, 4, 0, ENC(rs1, rd)) \
9494
_(csrrs, 0, 4, 0, ENC(rs1, rd)) \
9595
_(csrrc, 0, 4, 0, ENC(rs1, rd)) \
9696
_(csrrwi, 0, 4, 0, ENC(rs1, rd)) \

src/devices/minimal.dts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/dts-v1/;
2+
3+
/ {
4+
#address-cells = <1>;
5+
#size-cells = <1>;
6+
model = "rv32emu";
7+
8+
aliases {
9+
serial0 = "/soc@F0000000/serial@4000000";
10+
};
11+
12+
chosen {
13+
bootargs = "earlycon console=ttyS0";
14+
stdout-path = "serial0";
15+
linux,initrd-start = <0x1f700000>; /* @403 MiB (503 * 1024 * 1024) */
16+
linux,initrd-end = <0x1fefffff>; /* @511 MiB (511 * 1024 * 1024 - 1) */
17+
};
18+
19+
cpus {
20+
#address-cells = <1>;
21+
#size-cells = <0>;
22+
timebase-frequency = <65000000>;
23+
cpu0: cpu@0 {
24+
device_type = "cpu";
25+
compatible = "riscv";
26+
reg = <0>;
27+
riscv,isa = "rv32ima";
28+
mmu-type = "riscv,sv32";
29+
cpu0_intc: interrupt-controller {
30+
#interrupt-cells = <1>;
31+
#address-cells = <0>;
32+
interrupt-controller;
33+
compatible = "riscv,cpu-intc";
34+
};
35+
};
36+
};
37+
38+
sram: memory@0 {
39+
device_type = "memory";
40+
reg = <0x00000000 0x20000000>;
41+
reg-names = "sram0";
42+
};
43+
44+
soc: soc@F0000000 {
45+
#address-cells = <1>;
46+
#size-cells = <1>;
47+
compatible = "simple-bus";
48+
ranges = <0x0 0xF0000000 0x10000000>;
49+
interrupt-parent = <&plic0>;
50+
51+
plic0: interrupt-controller@0 {
52+
#interrupt-cells = <1>;
53+
#address-cells = <0>;
54+
compatible = "sifive,plic-1.0.0";
55+
reg = <0x0000000 0x4000000>;
56+
interrupt-controller;
57+
interrupts-extended = <&cpu0_intc 9>;
58+
riscv,ndev = <31>;
59+
};
60+
61+
serial@4000000 {
62+
compatible = "ns16550";
63+
reg = <0x4000000 0x100000>;
64+
interrupts = <1>;
65+
no-loopback-test;
66+
clock-frequency = <5000000>; /* the baudrate divisor is ignored */
67+
};
68+
};
69+
};

0 commit comments

Comments
 (0)