Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .ci/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ set -euo pipefail
MACHINE_TYPE="$(uname -m)"
OS_TYPE="$(uname -s)"

# Enable SDL headless mode explicitly.
export SDL_VIDEODRIVER=offscreen

# Cleanup function - kills all semu processes
cleanup() {
sleep 1
Expand Down
73 changes: 73 additions & 0 deletions .ci/test-vinput.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env bash

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/common.sh"

# Override timeout for macOS - emulation is significantly slower
case "${OS_TYPE}" in
Darwin)
TIMEOUT=1200
;;
esac

cleanup
trap cleanup EXIT

# Feature toggles are passed through environment variables, which do not
# participate in make's normal dependency tracking. Force a rebuild here so
# one-feature-at-a-time test runs never reuse a stale semu binary.
make -B semu minimal.dtb

# NOTE: We want to capture the expect exit code and map
# it to our MESSAGES array for meaningful error output.
# Temporarily disable errexit for the expect call.
set +e
expect <<'DONE'
set timeout $env(TIMEOUT)
spawn make check

# Boot and login
expect "buildroot login:" { send "root\r" } timeout { exit 1 }
expect "# " { send "uname -a\r" } timeout { exit 2 }
expect "riscv32 GNU/Linux" {}

# ---------------- virtio-input ----------------
# Require actual event* nodes, not just /dev/input directory existence.
# Print a concrete status marker that is not present in the echoed command text.
expect "# " { send "if ls /dev/input/event* >/dev/null 2>&1; then status=OK; else status=BAD; fi; printf \"__EVT_%s__\\n\" \"\$status\"\r" }
expect {
-exact "__EVT_OK__" {}
-exact "__EVT_BAD__" { exit 3 }
timeout { exit 3 }
}

expect "# " { send "cat /proc/bus/input/devices | head -20\r" }
# Emit a shell-expanded status marker so expect cannot match the echoed command.
expect "# " { send "if grep -qi virtio /proc/bus/input/devices; then status=OK; else status=BAD; fi; printf \"__VPROC_%s__\\n\" \"\$status\"\r" }
expect {
-exact "__VPROC_OK__" {}
-exact "__VPROC_BAD__" { exit 3 }
timeout { exit 3 }
}
DONE

ret="$?"
set -e # Re-enable errexit after capturing expect's return code

MESSAGES=(
"PASS: headless virtio-input checks"
"FAIL: boot/login prompt not found"
"FAIL: shell prompt not found"
"FAIL: virtio-input basic checks failed (/dev/input/event* or /proc/bus/input/devices)"
"FAIL: virtio-input event stream did not produce bytes (needs host->virtio-input injection path)"
)

if [[ "${ret}" -eq 0 ]]; then
print_success "${MESSAGES[0]}"
exit 0
fi

print_error "${MESSAGES[${ret}]:-FAIL: unknown error (exit code ${ret})}"
exit "${ret}"
5 changes: 3 additions & 2 deletions .github/actions/setup-semu/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ runs:
device-tree-compiler \
expect \
libasound2-dev \
libudev-dev
libudev-dev \
libsdl2-dev

- name: Install dependencies (macOS)
if: runner.os == 'macOS'
Expand All @@ -23,4 +24,4 @@ runs:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_ANALYTICS: 1
run: |
brew install make dtc expect e2fsprogs
brew install make dtc expect e2fsprogs sdl2
8 changes: 8 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ jobs:
run: .ci/test-sound.sh
shell: bash
timeout-minutes: 5
- name: virtio-input test
run: .ci/test-vinput.sh
shell: bash
timeout-minutes: 5

semu-macOS:
runs-on: macos-latest
Expand Down Expand Up @@ -126,6 +130,10 @@ jobs:
run: .ci/test-sound.sh
shell: bash
timeout-minutes: 20
- name: virtio-input test
run: .ci/test-vinput.sh
shell: bash
timeout-minutes: 20

coding_style:
runs-on: ubuntu-24.04
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ rootfs.cpio

# intermediate
riscv-harts.dtsi
.smp_stamp
Comment thread
Mes0903 marked this conversation as resolved.
rootfs_full.cpio

# Build directories
buildroot/
linux/
rootfs/
25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,31 @@ LDFLAGS += -lm
# after git submodule.
.DEFAULT_GOAL := all

# SDL2
ENABLE_SDL ?= 1
ifeq (, $(shell which sdl2-config))
$(warning No sdl2-config in $$PATH. Check SDL2 installation in advance)
override ENABLE_SDL := 0
endif
ifeq ($(ENABLE_SDL),1)
CFLAGS += $(shell sdl2-config --cflags)
LDFLAGS += $(shell sdl2-config --libs)
else
# Disable virtio-input if SDL is not set
override ENABLE_VIRTIOINPUT := 0
endif

# virtio-input
ENABLE_VIRTIOINPUT ?= 1
ENABLE_INPUT_DEBUG ?= 0
CFLAGS += -DSEMU_INPUT_DEBUG=$(ENABLE_INPUT_DEBUG)
$(call set-feature, VIRTIOINPUT)
ifeq ($(call has, VIRTIOINPUT), 1)
OBJS_EXTRA += virtio-input-event.o
OBJS_EXTRA += virtio-input.o
OBJS_EXTRA += window-sw.o
endif

BIN = semu
all: $(BIN) minimal.dtb

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ A minimalist RISC-V system emulator capable of running Linux the kernel and corr
- UART: 8250/16550
- PLIC (platform-level interrupt controller): 32 interrupts, no priority
- Standard SBI, with the timer extension
- Three types of I/O support using VirtIO standard:
- Four types of I/O support using VirtIO standard:
- virtio-blk acquires disk image from the host.
- virtio-net is mapped as TAP interface.
- virtio-snd uses [PortAudio](https://github.com/PortAudio/portaudio) for sound playback on the host with one limitations:
Expand All @@ -18,6 +18,8 @@ A minimalist RISC-V system emulator capable of running Linux the kernel and corr
the program cannot write the PCM frames into guest OS ALSA stack.
- For instance, the following buffer/period size settings on `aplay` has been tested
with broken and stutter effects yet complete with no any errors: `aplay --buffer-size=32768 --period-size=4096 /usr/share/sounds/alsa/Front_Center.wav`.
- virtio-input exposes SDL-backed keyboard and mouse devices to the guest.
- You can exit the SDL window by pressing Ctrl+A+G

## Prerequisites

Expand Down
4 changes: 2 additions & 2 deletions configs/linux.config
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ CONFIG_INPUT=y
#
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_JOYDEV is not set
# CONFIG_INPUT_EVDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_EVBUG is not set

#
Expand Down Expand Up @@ -1053,7 +1053,7 @@ CONFIG_VIRTIO_ANCHOR=y
CONFIG_VIRTIO=y
CONFIG_VIRTIO_MENU=y
# CONFIG_VIRTIO_BALLOON is not set
# CONFIG_VIRTIO_INPUT is not set
CONFIG_VIRTIO_INPUT=y
CONFIG_VIRTIO_MMIO=y
# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set
# CONFIG_VDPA is not set
Expand Down
83 changes: 83 additions & 0 deletions device.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
#define DTB_SIZE (1 * 1024 * 1024)
#define INITRD_SIZE (8 * 1024 * 1024)

#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 768

void ram_read(hart_t *core,
uint32_t *mem,
const uint32_t addr,
Expand Down Expand Up @@ -229,6 +232,67 @@ void virtio_rng_write(hart_t *vm,
void virtio_rng_init(void);
#endif /* SEMU_HAS(VIRTIORNG) */

/* VirtIO Input */

#if SEMU_HAS(VIRTIOINPUT)

#define IRQ_VINPUT_KEYBOARD 7
#define IRQ_VINPUT_KEYBOARD_BIT (1 << IRQ_VINPUT_KEYBOARD)

#define IRQ_VINPUT_MOUSE 8
#define IRQ_VINPUT_MOUSE_BIT (1 << IRQ_VINPUT_MOUSE)

typedef struct {
uint32_t QueueNum;
uint32_t QueueDesc;
uint32_t QueueAvail;
uint32_t QueueUsed;
uint16_t last_avail;
bool ready;
} virtio_input_queue_t;

typedef struct {
/* feature negotiation */
uint32_t DeviceFeaturesSel;
uint32_t DriverFeatures;
uint32_t DriverFeaturesSel;
/* queue config */
uint32_t QueueSel;
virtio_input_queue_t queues[2];
/* status */
uint32_t Status;
uint32_t InterruptStatus;
/* supplied by environment */
uint32_t *ram;
/* implementation-specific */
void *priv;
} virtio_input_state_t;

void virtio_input_read(hart_t *vm,
virtio_input_state_t *vinput,
uint32_t addr,
uint8_t width,
uint32_t *value);

void virtio_input_write(hart_t *vm,
virtio_input_state_t *vinput,
uint32_t addr,
uint8_t width,
uint32_t value);

void virtio_input_init(virtio_input_state_t *vinput);

/* Drain translated host window events and update guest-visible virtio-input
* device state. Must be called from the emulator thread.
*/
void virtio_input_drain_host_events(void);

/* Returns true if the device has a pending interrupt. Safe to call from
* the emulator thread without holding any lock internally.
*/
bool virtio_input_irq_pending(virtio_input_state_t *vinput);
#endif /* SEMU_HAS(VIRTIOINPUT) */

/* ACLINT MTIMER */
typedef struct {
/* A MTIMER device has two separate base addresses: one for the MTIME
Expand Down Expand Up @@ -433,6 +497,7 @@ bool virtio_fs_init(virtio_fs_state_t *vfs, char *mtag, char *dir);

/* memory mapping */
typedef struct {
int exit_code;
bool debug;
bool stopped;
uint32_t *ram;
Expand All @@ -459,6 +524,24 @@ typedef struct {
#if SEMU_HAS(VIRTIOFS)
virtio_fs_state_t vfs;
#endif
#if SEMU_HAS(VIRTIOINPUT)
virtio_input_state_t vkeyboard;
virtio_input_state_t vmouse;
/* Use self-pipe trick to unblock the emulator loop when the
* window backend has queued work, such as input events or
* window shutdown. When all harts are idle, semu_run() calls
* poll(-1) and blocks indefinitely waiting for timer or UART
* events. The window-event thread has no way to wake that
* blocked poll() other than writing to a file descriptor it is
* watching.
*
* wake_fd[0] (read end) is added to pfds[] so poll() monitors it.
* wake_fd[1] (write end) is handed to the window backend, which
* writes one byte when backend work arrives to make wake_fd[0]
* readable and return poll() immediately.
*/
int wake_fd[2];
#endif

uint32_t peripheral_update_ctr;

Expand Down
5 changes: 5 additions & 0 deletions feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,10 @@
#define SEMU_FEATURE_VIRTIOFS 1
#endif

/* virtio-input */
#ifndef SEMU_FEATURE_VIRTIOINPUT
#define SEMU_FEATURE_VIRTIOINPUT 1
#endif

/* Feature test macro */
#define SEMU_HAS(x) SEMU_FEATURE_##x
Loading