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
6 changes: 6 additions & 0 deletions .ci/boot-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,10 @@ if grep -q '^CONFIG_VIRTIO_NET_VMNET=y$' .config; then
RET=$((${RET} + $?))
fi

# Virtio-snd tests
if grep -q '^CONFIG_VIRTIO_SND=y$' .config; then
(. "${SCRIPT_DIR}/sound.sh")
RET=$((${RET} + $?))
fi

exit ${RET}
6 changes: 3 additions & 3 deletions .ci/brew-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ set -e -u -o pipefail

LLVM_VERSION=$(cat "${SCRIPT_DIR}/llvm-version")

brew install make dtc expect sdl2 bc e2fsprogs p7zip "llvm@${LLVM_VERSION}" dcfldd
brew install make dtc expect sdl2 bc e2fsprogs p7zip "llvm@${LLVM_VERSION}" dcfldd pkgconf portaudio

# SDL_mixer only gates the audio tests, so a bottle outage should not take the
# whole job down with it.
Expand All @@ -24,12 +24,12 @@ brew install sdl2_mixer \
# Relink every formula the later build steps invoke by name. Failures are
# tolerated (a keg-only formula refuses to link, and is reached through "brew
# --prefix" instead), so the verification below is what actually decides.
for formula in make dtc expect sdl2 bc p7zip dcfldd; do
for formula in make dtc expect sdl2 bc p7zip dcfldd pkgconf portaudio; do
brew link --overwrite "${formula}" > /dev/null 2>&1 || true
done

MISSING=
for tool in dtc expect bc; do
for tool in dtc expect bc pkg-config; do
command -v "${tool}" > /dev/null || MISSING="${MISSING} ${tool}"
done
if [ -n "${MISSING}" ]; then
Expand Down
153 changes: 153 additions & 0 deletions .ci/sound.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#!/usr/bin/env bash

# Get the directory of this script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/common.sh"

check_platform

# Overwrite the variable to use initrd rootfs when explicitly requested.
SPECIFY_ROOTFS=${1:-}
if [[ -n "${SPECIFY_ROOTFS}" && "${SPECIFY_ROOTFS}" != "initrd" ]]; then
print_error "Unsupported rootfs for virtio-snd test: ${SPECIFY_ROOTFS}"
exit 2
fi
if [[ "${SPECIFY_ROOTFS}" == "initrd" ]]; then
OPTS_BASE_ROOTFS_EXT4="${OPTS_BASE}"
fi

# Ensure a failed/timeout test does not leave rv32emu running.
register_cleanup cleanup_emulator

RET=0

MESSAGES=(
"${COLOR_G}OK!"
"${COLOR_R}Fail to boot"
"${COLOR_R}Fail to login"
"${COLOR_R}Fail to bind virtio-snd driver"
"${COLOR_R}Fail to enumerate ALSA card"
"${COLOR_R}Fail to enumerate ALSA PCM device"
"${COLOR_R}Fail to play PCM with speaker-test"
)

# GitHub Actions runners do not provide a physical audio output device.
# Use the ALSA null PCM as the default output so PortAudio can exercise
# the virtio-snd playback path in headless CI.
if [[ "${CI:-}" == "true" && "$(uname -s)" == "Linux" ]]; then
cat > "${HOME}/.asoundrc" << 'EOF'
pcm.!default {
type null
}
EOF
fi

RUN_LINUX="build/rv32emu ${OPTS_BASE_ROOTFS_EXT4} -x vsnd"

printf "${COLOR_Y}===== Test option: ${OPTS_BASE_ROOTFS_EXT4} -x vsnd =====${COLOR_N}\n"

ASSERT expect <<- DONE
set timeout ${TIMEOUT}

spawn ${RUN_LINUX}

expect {
"buildroot login:" {
send "root\r"
}
timeout {
exit 1
}
}

expect {
"# " {}
timeout {
exit 2
}
}

send "readlink /sys/bus/virtio/devices/virtio0/driver\r"
expect {
"virtio_snd" {}
timeout {
exit 3
}
}

expect "# "
send "cat /proc/asound/cards\r"
expect {
"VirtIO SoundCard" {}
timeout {
exit 4
}
}

expect "# "
send "aplay -l\r"
expect {
"VirtIO SoundCard" {}
timeout {
exit 5
}
}

# Exercise the PCM playback path with a bounded single loop. Use an
# explicitly sized buffer to tolerate scheduling jitter on shared CI runners.
expect "# "
set timeout 60
send "speaker-test -D hw:0,0 -c 1 -r 48000 -F S16_LE -t sine -b 1200000 -p 300000 -P 4 -l 1; echo SPEAKER_TEST_RC:\\\$?\r"

expect {
"Playback open error:" {
exit 6
}
"Setting of hwparams failed:" {
exit 6
}
"Setting of swparams failed:" {
exit 6
}
"Transfer failed:" {
exit 6
}
"0 - Mono" {}
timeout {
exit 6
}
}

# One completed loop is sufficient to verify end-to-end PCM progress.
expect {
"Time per period =" {}
"Transfer failed:" {
exit 6
}
timeout {
exit 6
}
}

expect {
"SPEAKER_TEST_RC:0" {}
-re {SPEAKER_TEST_RC:([1-9][0-9]*)} {
exit 6
}
timeout {
exit 6
}
}

expect "# "
send "\x01"
send "x"
DONE

ret=$?
RET=$((${RET} + ${ret}))
cleanup

printf "\nVirtio-snd Test: [ ${MESSAGES[$ret]}${COLOR_N} ]\n"

exit ${RET}
4 changes: 2 additions & 2 deletions .github/actions/setup-build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ runs:
shell: bash
run: |
.ci/apt-install.sh curl wget lsb-release software-properties-common \
gnupg libsdl2-dev libsdl2-mixer-dev device-tree-compiler expect \
bc p7zip-full e2fsprogs
gnupg libsdl2-dev libsdl2-mixer-dev portaudio19-dev pkg-config \
device-tree-compiler expect bc p7zip-full e2fsprogs

# No Homebrew cache on purpose. The cache can only carry the Cellar, not
# the opt/ and bin/ symlinks, and "brew install" on a formula it already
Expand Down
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ deps :=
# Feature Flags (Kconfig -> RV32_FEATURE_*)
$(call set-features, ELF_LOADER MOP_FUSION BLOCK_CHAINING LOG_COLOR)
$(call set-features, SYSTEM GOLDFISH_RTC ARCH_TEST)
$(call set-features, VIRTIO_NET VIRTIO_NET_TAP VIRTIO_NET_USER VIRTIO_NET_VMNET)
$(call set-features, VIRTIO_NET VIRTIO_NET_TAP VIRTIO_NET_USER VIRTIO_NET_VMNET VIRTIO_SND)
$(call set-features, EXT_M EXT_A EXT_F EXT_C EXT_V RV32E)
$(call set-features, Zicsr Zifencei Zba Zbb Zbc Zbs)
$(call set-features, SDL SDL_MIXER GDBSTUB JIT LINK_ZLIB ROOTFS_EXT4)
Expand Down Expand Up @@ -219,7 +219,7 @@ ifeq ($(CONFIG_JIT),y)
$(error JIT only supports x86_64 and ARM64 platforms.)
endif
endif
$(OUT)/jit.o: src/jit.c src/rv32_jit.c $(CONFIG_HEADER)
$(OUT)/jit.o: src/jit.c src/rv32_jit.c $(CONFIG_HEADER) $(EFFECTIVE_CONFIG_STAMP)
$(VECHO) " CC\t$@\n"
$(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF $@.d $<
# T2C optimization level from Kconfig (0-3, default 3)
Expand All @@ -240,6 +240,9 @@ include mk/http.mk
# VirtIO networking
include mk/virtio-net.mk

# VirtIO sound
include mk/virtio-snd.mk

# External Dependencies & System Emulation
include mk/external.mk
include mk/artifact.mk
Expand All @@ -265,7 +268,7 @@ deps += $(OBJS:%.o=%.o.d)
EFFECTIVE_CONFIG_VARS := \
CONFIG_BUILD_WASM CONFIG_SYSTEM CONFIG_GOLDFISH_RTC CONFIG_ELF_LOADER \
CONFIG_VIRTIO_NET CONFIG_VIRTIO_NET_TAP CONFIG_VIRTIO_NET_USER \
CONFIG_VIRTIO_NET_VMNET \
CONFIG_VIRTIO_NET_VMNET CONFIG_VIRTIO_SND CONFIG_HAVE_PORTAUDIO \
CONFIG_EXT_M CONFIG_EXT_A CONFIG_EXT_F CONFIG_EXT_C CONFIG_EXT_V CONFIG_RV32E \
CONFIG_Zicsr CONFIG_Zifencei CONFIG_Zba CONFIG_Zbb CONFIG_Zbc CONFIG_Zbs \
CONFIG_MOP_FUSION CONFIG_BLOCK_CHAINING CONFIG_LOG_COLOR CONFIG_ARCH_TEST \
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ button that switches directly to the other.
| Prebuilt binaries | [docs/prebuilt.md](docs/prebuilt.md) |
| Base image preparation | [docs/base-image.md](docs/base-image.md) |
| Networking guidance | [docs/networking.md](docs/networking.md) |
| Sound guidance | [docs/sound.md](docs/sound.md) |

## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
Expand Down
22 changes: 22 additions & 0 deletions configs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ config HAVE_RISCV_TOOLCHAIN
bool
default $(python,assert run(sys.executable, 'tools/detect-env.py', '--have-riscv-toolchain'))

config HAVE_PORTAUDIO
bool
default n if CC_IS_EMCC
default $(python,assert run('pkg-config', '--exists', 'portaudio-2.0'))

# Host platform detection used by platform-specific system devices.
config HOST_LINUX
def_bool $(python,assert sys.platform.startswith('linux'))
Expand Down Expand Up @@ -310,6 +315,23 @@ config ELF_LOADER
Enable this for running 'make check' tests in system mode.
Disable this for booting Linux kernel images.

config VIRTIO_SND
bool "Enable VirtIO sound device"
default y
depends on SYSTEM && !ELF_LOADER && !CC_IS_EMCC
depends on HAVE_PORTAUDIO
help
Enable VirtIO sound device emulation for Linux system mode.

PCM playback is provided through PortAudio. The device can be
instantiated at runtime with the '-x vsnd' option.

Disable this option to exclude virtio-snd and its PortAudio
dependency from the rv32emu executable.

comment "PortAudio not found - VirtIO sound support is unavailable"
depends on SYSTEM && !ELF_LOADER && !CC_IS_EMCC && !HAVE_PORTAUDIO

config VIRTIO_NET
bool "Enable VirtIO network device"
default y
Expand Down
1 change: 1 addition & 0 deletions configs/system_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ CONFIG_VIRTIO_NET_TAP=y
CONFIG_VIRTIO_NET_USER=y
CONFIG_VIRTIO_NET_VMNET=y
CONFIG_ROOTFS_EXT4=y
CONFIG_VIRTIO_SND=y
2 changes: 2 additions & 0 deletions configs/system_interpreter_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ CONFIG_VIRTIO_NET=y
CONFIG_VIRTIO_NET_TAP=y
CONFIG_VIRTIO_NET_USER=y
CONFIG_VIRTIO_NET_VMNET=y
CONFIG_VIRTIO_SND=y

26 changes: 20 additions & 6 deletions docs/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,28 @@ where `<backend>` is one of the supported backend names for the current host.

### Linux TAP mode

TAP mode requires root privileges or `CAP_NET_ADMIN`.
TAP mode requires network administration privileges, which can be provided either by running rv32emu with `sudo` or by granting the executable `CAP_NET_ADMIN`.

Start rv32emu:
When TAP networking is used together with virtio-snd, prefer `CAP_NET_ADMIN` instead of running the whole emulator with `sudo`. PortAudio normally connects to the audio session of the logged-in desktop user, such as PipeWire or PulseAudio. Running rv32emu as root may prevent PortAudio from accessing that user session.

Grant the required capability to the rv32emu executable:

```shell
sudo setcap cap_net_admin+ep build/rv32emu
getcap build/rv32emu

build/rv32emu \
-k build/linux-image/Image \
-x vblk:build/linux-image/rootfs.ext4,rootfs \
-x vnet:tap \
-x vsnd
```
If you just want to use virtio-net only (without virtio-snd), you can start rv32emu with `sudo -E`:

```shell
sudo -E build/rv32emu \
-k build/linux-image/Image \
-i build/linux-image/rootfs.cpio \
-x vblk:build/linux-image/rootfs.ext4,rootfs \
-x vnet:tap
```

Expand Down Expand Up @@ -100,7 +114,7 @@ Start rv32emu:
```shell
build/rv32emu \
-k build/linux-image/Image \
-i build/linux-image/rootfs.cpio \
-x vblk:build/linux-image/rootfs.ext4,rootfs \
-x vnet:user
```

Expand All @@ -125,7 +139,7 @@ mode through:
```shell
sudo -E build/rv32emu \
-k build/linux-image/Image \
-i build/linux-image/rootfs.cpio \
-x vblk:build/linux-image/rootfs.ext4,rootfs \
-x vnet:vmnet
```

Expand Down Expand Up @@ -167,7 +181,7 @@ On macOS, use the user-mode SLIRP backend:
```shell
build/rv32emu \
-k build/linux-image/Image \
-i build/linux-image/rootfs.cpio \
-x vblk:build/linux-image/rootfs.ext4,rootfs \
-x vnet:user
```

Expand Down
Loading
Loading