Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ab7bcdb
examples/virtio-socket: checkpoint port over from virtio example and …
dreamliner787-9 Mar 30, 2025
1f80fb4
virtio/socket: add virtio-socket device implementation
dreamliner787-9 Apr 1, 2025
2a42530
examples/virtio-socket: add test programs and checkpoint everything w…
dreamliner787-9 Apr 1, 2025
b5d3634
examples/virtio-socket: add missing virtio socket device to dts
dreamliner787-9 Apr 1, 2025
f98ed06
virtio/socket: clean ups + docs
dreamliner787-9 Apr 1, 2025
4f3fe6d
examples/virtio-socket: add odroidc4 support and make sender sleep lo…
dreamliner787-9 Apr 1, 2025
114dd45
virtio/socket: integrate tooling
dreamliner787-9 Apr 4, 2025
d29f72c
examples/virtio-socket: fix dts node name and improve logging of exam…
dreamliner787-9 Apr 4, 2025
77ffd07
examples/virtio-socket: update docs
dreamliner787-9 Apr 4, 2025
2d34827
examples/virtio: download correct kernel
dreamliner787-9 Apr 4, 2025
c192a99
examples/virtio-socket: improve debug prints
dreamliner787-9 Apr 4, 2025
e7f359c
vmm.mk: make indentation consistent
dreamliner787-9 Apr 4, 2025
bdc6b65
examples/virtio: converge with examples/virtio-socket
dreamliner787-9 Apr 4, 2025
c85b778
examples/virtio: fix licences
dreamliner787-9 Apr 4, 2025
dd6c196
virtio/socket: style + trailing spaces
dreamliner787-9 Apr 4, 2025
5d24ad7
examples/virtio: style + trailing whitespaces
dreamliner787-9 Apr 4, 2025
6d549ea
examples/virtio: trailing whitespaces
dreamliner787-9 Apr 4, 2025
0ed5653
examples/virtio: fix readme trailing whitespace
dreamliner787-9 May 6, 2025
4155c85
virtio/socket: fix a spec non-compliance issue where a reset packet m…
dreamliner787-9 May 6, 2025
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: 3 additions & 3 deletions examples/virtio/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2024, UNSW
# Copyright 2025, UNSW
#
# SPDX-License-Identifier: BSD-2-Clause
#
Expand Down Expand Up @@ -33,7 +33,7 @@ else ifeq ($(strip $(MICROKIT_BOARD)), qemu_virt_aarch64)
export GIC_DT_OVERLAY := gic_v2_overlay.dts
QEMU := qemu-system-aarch64

export BLK_NUM_PART = 1
export BLK_NUM_PART = 2
export BLK_SIZE = 512
# 16MiB of disk space
export BLK_MEM ?= 16777216
Expand All @@ -48,7 +48,7 @@ export PARTITION_ARG := --partition $(PARTITION)
endif

# All platforms use the same Linux and initrd images.
export LINUX := a3f4bf9e2eb24fa8fc0d3d8cd02e4d8097062e8b-linux
export LINUX := 372b2c56f55f2d461e4570d79332298e0f7ecce4-linux
export INITRD := b6a276df6a0e39f76bc8950e975daa2888ad83df-rootfs.cpio.gz

export BUILD_DIR:=$(abspath $(BUILD_DIR))
Expand Down
113 changes: 94 additions & 19 deletions examples/virtio/README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
<!--
Copyright 2024, UNSW
Copyright 2025, UNSW
SPDX-License-Identifier: CC-BY-SA-4.0
-->

# Using multiple virtIO devices with a Linux guest
# Using multiple virtIO devices with Linux guests

This example shows off the virtIO support that libvmm provides using the
[seL4 Device Driver Framework (sDDF)](https://github.com/au-ts/sddf) to talk to
the actual hardware.
This example shows off the multiple guests and virtIO support that
libvmm provides using the
[seL4 Device Driver Framework (sDDF)](https://github.com/au-ts/sddf)
to talk to the actual hardware.

This example makes use of the following virtIO devices emulated by libvmm:
* console
* block
* network
* socket (also known as 'vsock' and does not interact with any real hardware)

All of the virtIO devices are emulated with their corresponding native drivers
from sDDF.
Note that while we demo all virtIO devices together in one unified example system.
THeir implementation are independant of each others and thus can be deployed
independantly.

The example currently works on the following platforms:
All of the virtIO devices (except socket) are emulated with their corresponding
native drivers from sDDF.

The example currently works on the following platforms:
* QEMU virt AArch64
* Avnet MaaXBoard

Expand All @@ -28,7 +33,7 @@ Unlike the other examples, this one uses a metaprogram (`meta.py`) with
the [sdfgen](https://github.com/au-ts/microkit_sdf_gen) tooling to generate the
System Description File (SDF) and other necessary artefacts. Previously,
SDFs were written manually, along with C headers for sDDF-specific configurations,
but this approach was tedious and error-prone. Wit this tooling, we can describe
but this approach was tedious and error-prone. With this tooling, we can describe
the system at a higher level, automating the generation of system-specific data.

## Dependencies
Expand All @@ -39,7 +44,7 @@ dependencies are needed:
* sdfgen (for generating the System Description File with a metaprogram).

### Linux

<!-- TODO bump sdfgen ver once vsock PR in sdfgen is merged -->
On apt based Linux distributions run the following commands:
```sh
sudo apt-get install dosfstools
Expand Down Expand Up @@ -104,14 +109,18 @@ system running the whole system.

### virtIO console

This example makes use of the virtIO console device so that the guest has access
This example makes use of the virtIO console device so that guests has access
to the serial device on the platform. The virtIO console support in libvmm talks to
the sDDF serial sub-system which contains a driver for input/output to the physical
serial device.

Since only 1 guest can read from the console at any given time, by default console
reading is granted to the first guest (guest #0). You can switch the console input
by pressing `Ctrl` + `\`, then the guest's index (`0` or `1` in this example).

### virtIO block

The guest also doubles as a client in the block system that talks virtIO to a native
The guests also doubles as clients in the block system that talks virtIO to a native
block device. The requests from the guest are multiplexed through the additional block
virtualiser component.

Expand Down Expand Up @@ -139,17 +148,17 @@ starting block number that is a multiple of sDDF block's transfer size of 4096 b
divided by the disk's logical size. Partitions that do not follow this restriction
are unsupported.

By default on QEMU virt AArch64, we mount the first partition of the disk image,
on Avnet MaaXBoard we mount the third partition of the SD Card. You can change the partition mounted
by passing `PARTITION=n` when executing the Makefile.
By default on QEMU virt AArch64, we mount the first and second partitions of the disk image to
guest #0 and #1 respectively. On Avnet MaaXBoard we mount the second and third partitions
of the SD Card. You can change the partition mounted by passing `PARTITION=n` when executing the Makefile.

### virtIO net

In addition to virtIO console and block, the guest can also talk with the native
In addition to virtIO console and block, the guests can also talk with the native
sDDF network driver via virtIO for in-guest networking. Packets in and out of
the guest are multiplexed through the network virtualiser components.
the guests are multiplexed through the network virtualiser components.

When the guest boots up, you must bring up the network device. First check the
When the guests boots up, you must bring up the network device. First check the
name of the network device, it should be called `eth0`:
```
# ip link show
Expand All @@ -172,7 +181,7 @@ To obtain an IP address, initiate DHCP with:
udhcpc
```

Now the guest network, you can try to ping Google DNS with:
Now the guest can talk on the network, you can try to ping Google DNS with:
```
# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
Expand All @@ -189,6 +198,72 @@ round-trip min/avg/max = 5.361/9.776/18.560 ms

The guest has a DNS resolver so you can also ping a URL.

### virtIO socket
The virtIO socket device allow inter-guests communication without Ethernet
or IP protocols. In this example, the two guests act as a sender as receiver
with corresponding user-level programs packaged into the ramdisk. Guest #0
and #1 are allocated CID 3 and 4 respectively.

```
--------- VM --------- --------- VM ---------
Userland: Userland:
vsock_recv vsock_send
^ |
| v
Kernel: Kernel:
virtio MMIO vsock driver virtio MMIO vsock driver
^ |
| v
--------- VMM -------- --------- VMM --------
virtio vsock device /-- virtio vsock device
^ /
| /
rx buffer <----------------/ rx buffer
```

In sender VM, `vsock_send` will send 32k worth of data through virtIO socket
to `vsock_recv` in the receiver VM. Then, the receiver VM will verify that the
data are all correct and both programs will quit.

Each virtIO socket device have a small receive buffer that the peer can write to
to send data. The buffer size is advertised to both the sender and receiver via the
`buf_alloc` field of the packet header for the guest driver to split up packets
as necessary.

Here is a demo of this process happening. Red (-) is guest #0 while
green (+) is guest #1:
```diff
+ Welcome to Buildroot
+ buildroot login:
- Welcome to Buildroot
- buildroot login: root
- login[262]: root login on 'console'
- # ./vsock_recv 3
- VSOCK RECV|INFO: starting
- VSOCK RECV|INFO: creating socket to wait on CID 3
VIRT_RX|LOG: switching to client 1

+ Welcome to Buildroot
+ buildroot login: root
+ login[260]: root login on 'console'
+ # ./vsock_send 3
+ VSOCK SEND|INFO: starting
+ VSOCK SEND|INFO: creating socket to send on CID 3
- VSOCK RECV|INFO: peer connected
+ VSOCK SEND|INFO: connected, preparing payload
+ VSOCK SEND|INFO: now sending 32768 bytes!
- VSOCK RECV|INFO: Accumulatively received 4050 bytes
- VSOCK RECV|INFO: Accumulatively received 8100 bytes
- VSOCK RECV|INFO: Accumulatively received 12150 bytes
- VSOCK RECV|INFO: Accumulatively received 16200 bytes
- VSOCK RECV|INFO: Accumulatively received 20250 bytes
- VSOCK RECV|INFO: Accumulatively received 24300 bytes
- VSOCK RECV|INFO: Accumulatively received 28350 bytes
- VSOCK RECV|INFO: Accumulatively received 32400 bytes
- VSOCK RECV|INFO: Total bytes received 32768, verifying data...
- VSOCK RECV|INFO: All is well in the universe
```

### QEMU set up

When running on QEMU, read and writes go to an emulated ramdisk instead of to your
Expand Down
18 changes: 9 additions & 9 deletions examples/virtio/client_vm/buildroot_config
Original file line number Diff line number Diff line change
Expand Up @@ -1080,7 +1080,7 @@ BR2_PACKAGE_QT6_ARCH_SUPPORTS=y
# BR2_PACKAGE_BRLTTY is not set

#
# cc-tool needs a toolchain w/ C++, threads, wchar, gcc >= 4.9
# cc-tool needs a toolchain w/ C++, threads, wchar, gcc >= 4.9
#
# BR2_PACKAGE_CDRKIT is not set
BR2_PACKAGE_CPUBURN_ARM_ARCH_SUPPORTS=y
Expand Down Expand Up @@ -3270,35 +3270,35 @@ BR2_PACKAGE_MONGREL2_LIBC_SUPPORTS=y
#

#
# Please note:
# Please note:
#

#
# - Buildroot does *not* generate binary packages,
# - Buildroot does *not* generate binary packages,
#

#
# - Buildroot does *not* install any package database.
# - Buildroot does *not* install any package database.
#

#
# *
# *
#

#
# It is up to you to provide those by yourself if you
# It is up to you to provide those by yourself if you
#

#
# want to use any of those package managers.
# want to use any of those package managers.
#

#
# *
# *
#

#
# See the manual:
# See the manual:
#

#
Expand Down
6 changes: 6 additions & 0 deletions examples/virtio/client_vm/linux.dts
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,10 @@
reg = <0x00 0x160000 0x00 0x200>;
interrupts = <0x00 44 0x04>;
};

virtio-socket@170000 {
compatible = "virtio,mmio";
reg = <0x00 0x170000 0x00 0x200>;
interrupts = <0x00 45 0x04>;
};
};
7 changes: 6 additions & 1 deletion examples/virtio/client_vm/linux_config
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,11 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
CONFIG_DNS_RESOLVER=y
# CONFIG_BATMAN_ADV is not set
# CONFIG_OPENVSWITCH is not set
# CONFIG_VSOCKETS is not set
CONFIG_VSOCKETS=y
CONFIG_VSOCKETS_DIAG=y
CONFIG_VSOCKETS_LOOPBACK=y
CONFIG_VIRTIO_VSOCKETS=y
CONFIG_VIRTIO_VSOCKETS_COMMON=y
# CONFIG_NETLINK_DIAG is not set
# CONFIG_MPLS is not set
# CONFIG_NET_NSH is not set
Expand Down Expand Up @@ -2299,6 +2303,7 @@ CONFIG_VIRTIO_MMIO=y
# CONFIG_VDPA is not set
CONFIG_VHOST_MENU=y
# CONFIG_VHOST_NET is not set
# CONFIG_VHOST_VSOCK is not set
# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set

#
Expand Down
77 changes: 77 additions & 0 deletions examples/virtio/client_vm_userlevel/vsock_recv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2025, UNSW
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/vm_sockets.h>
#include <unistd.h>

#define NUMS_TO_RECV (16384)
/* 32k buffers of 2-bytes unsigneds. */
uint16_t nums[NUMS_TO_RECV];

_Static_assert(NUMS_TO_RECV <= UINT16_MAX);

int main(int argc, char *argv[])
{
printf("VSOCK RECV|INFO: starting\n");

if (argc != 2) {
printf("./vsock_recv.elf <CID>\n");
return 1;
}
uint32_t cid = atoi(argv[1]);
printf("VSOCK RECV|INFO: creating socket to wait on CID %d\n", cid);

int s = socket(AF_VSOCK, SOCK_STREAM, 0);

struct sockaddr_vm addr;
memset(&addr, 0, sizeof(struct sockaddr_vm));
addr.svm_family = AF_VSOCK;
addr.svm_port = 9999;
addr.svm_cid = cid;

int r = bind(s, &addr, sizeof(struct sockaddr_vm));
if (r) {
printf("VSOCK RECV|ERROR: bind failed with '%s'\n", strerror(errno));
return 1;
}

r = listen(s, 0);
if (r) {
printf("VSOCK RECV|ERROR: listen failed with '%s'\n", strerror(errno));
return 1;
}

struct sockaddr_vm peer_addr;
socklen_t peer_addr_size = sizeof(struct sockaddr_vm);
int peer_fd = accept(s, &peer_addr, &peer_addr_size);

printf("VSOCK RECV|INFO: peer connected\n");

size_t bytes_recved = 0;
char *buf = (char *) nums;
while ((bytes_recved += recv(peer_fd, &buf[bytes_recved], sizeof(nums), 0)) != sizeof(nums)) {
printf("VSOCK RECV|INFO: Accumulatively received %lu bytes\n", bytes_recved);
}

printf("VSOCK RECV|INFO: Total bytes received %lu, verifying data...\n", sizeof(nums));

for (uint16_t i = 0; i < NUMS_TO_RECV; i++) {
if (nums[i] != i) {
printf("VSOCK RECV|ERROR: at sequence %u, but got %u!\n", i, nums[i]);
return 1;
}
}

printf("VSOCK RECV|INFO: All is well in the universe\n");

return 0;
}
Loading
Loading