Skip to content

virtio: add virtiofs #86768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions doc/LICENSING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ Python Devicetree library test files

* Various yaml files under ``scripts/dts/python-devicetree/tests``

FUSE Interface Definition Header File
--------------------------------------

* *Licensing:* `BSD-2-clause`_
* *Impact:* This header is used in Zephyr build only if :kconfig:option:`CONFIG_FUSE_CLIENT` is enabled.
* *Files*:

* :zephyr_file:`subsys/fs/fuse_client/fuse_abi.h`

.. _Apache 2.0 License:
https://github.com/zephyrproject-rtos/zephyr/blob/main/LICENSE

Expand All @@ -120,6 +129,9 @@ Python Devicetree library test files
.. _BSD-3-clause:
https://opensource.org/license/bsd-3-clause

.. _BSD-2-clause:
https://opensource.org/license/bsd-2-clause

.. _Coccinelle:
https://coccinelle.gitlabpages.inria.fr/website/

Expand Down
25 changes: 25 additions & 0 deletions doc/hardware/virtualization/virtio.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,31 @@ virtqueue has to be acquired using :c:func:`virtio_get_virtqueue`. To send data
will be invoked once the device returns the given descriptor chain. After that, the virtqueue has to be notified using
:c:func:`virtio_notify_virtqueue` from the Virtio API.

Guest-side Virtio drivers
*************************
Currently Zephyr provides drivers for Virtio over PCI and Virtio over MMIO and drivers for two devices using virtio - virtiofs, used
to access the filesystem of the host and virtio-entropy, used as an entropy source.

Virtiofs
=========
This driver provides support for `virtiofs <https://virtio-fs.gitlab.io/>`_ - a filesystem allowing a virtual machine guest to access
a directory on the host. It uses FUSE messages to communicate between the host and the guest in order to perform filesystem operations such as
opening and reading files. Every time the guest wants to perform some filesystem operation it places in the virtqueue a descriptor chain
starting with the device readable part, containing the FUSE input header and input data, and ending it with the device writeable part, with place
for the FUSE output header and output data.

Virtio-entropy
==============
This driver allows using virtio-entropy as an entropy source in Zephyr. The operation of this device is simple - the driver places a
buffer in the virtqueue and receives it back, filled with random data.

Virtio samples
**************
A sample showcasing the use of a driver relying on Virtio is provided in :zephyr:code-sample:`virtiofs`. If you wish
to check code interfacing directly with the Virtio driver, you can check the virtiofs driver, especially :c:func:`virtiofs_init`
for initialization and :c:func:`virtiofs_send_receive` with the :c:func:`virtiofs_recv_cb` for data transfer to/from
the Virtio device.

API Reference
*************

Expand Down
3 changes: 3 additions & 0 deletions include/zephyr/fs/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ enum {
/** Identifier for in-tree Ext2 file system. */
FS_EXT2,

/** Identifier for in-tree Virtiofs file system. */
FS_VIRTIOFS,

/** Base identifier for external file systems. */
FS_TYPE_EXTERNAL_BASE,
};
Expand Down
4 changes: 4 additions & 0 deletions include/zephyr/fs/fs_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ extern "C" {
#define MAX_FILE_NAME 255
#endif

#if !defined(MAX_FILE_NAME) && defined(CONFIG_FILE_SYSTEM_VIRTIOFS)
#define MAX_FILE_NAME 255
#endif

#if !defined(MAX_FILE_NAME) /* filesystem selection */
/* Use standard 8.3 when no filesystem is explicitly selected */
#define MAX_FILE_NAME 12
Expand Down
23 changes: 23 additions & 0 deletions include/zephyr/fs/virtiofs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2025 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_FS_VIRTIOFS_H_
#define ZEPHYR_INCLUDE_FS_VIRTIOFS_H_
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

struct virtiofs_fs_data {
uint32_t max_write;
};

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_FS_VIRTIOFS_H_ */
8 changes: 8 additions & 0 deletions samples/subsys/fs/virtiofs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(virtiofs)

target_sources(app PRIVATE src/main.c)
73 changes: 73 additions & 0 deletions samples/subsys/fs/virtiofs/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
.. zephyr:code-sample:: virtiofs
:name: virtiofs filesystem
:relevant-api: file_system_api

Use file system API over virtiofs.

Overview
********

This sample app demonstrates the use of Zephyr's :ref:`file system API
<file_system_api>` over `virtiofs <https://virtio-fs.gitlab.io/>`_ by reading, creating and listing files and directories.
In the case of virtiofs the mounted filesystem is a directory on the host.

Requirements
************
This sample requires `virtiofsd <https://gitlab.com/virtio-fs/virtiofsd>`_ to run.

Building
********
.. zephyr-app-commands::
:zephyr-app: samples/subsys/fs/virtiofs
:board: qemu_x86_64
:goals: build
:compact:


Running
*******
Before launching QEMU ``virtiofsd`` has to be running. QEMU's arguments are embedded using :code:`CONFIG_QEMU_EXTRA_FLAGS` and socket path is set to :code:`/tmp/vhostqemu`, so ``virtiofsd`` has to be launched using

.. code-block::

virtiofsd --socket-path=/tmp/vhostqemu -o source=shared_dir_path

where :code:`shared_dir_path` is a directory that will be mounted on Zephyr side.
Then you can launch QEMU using:

.. code-block::

west build -t run

This sample will list the files and directories in the mounted filesystem and print the contents of the file :code:`file` in the mounted directory.
This sample will also create some files and directories.
You can create the sample directory using :code:`prepare_sample_directory.sh`.

Example output:

.. code-block::

*** Booting Zephyr OS build v4.1.0-rc1-28-gc6816316fc50 ***
/virtiofs directory tree:
- dir2 (type=dir)
- b (type=file, size=3)
- a (type=file, size=2)
- c (type=file, size=4)
- dir (type=dir)
- some_file (type=file, size=0)
- nested_dir (type=dir)
- some_other_file (type=file, size=0)
- file (type=file, size=27)

/virtiofs/file content:
this is a file on the host


After running the sample you can check the created files:

.. code-block:: console

shared_dir_path$ cat file_created_by_zephyr
hello world
shared_dir_path$ cat second_file_created_by_zephyr
lorem ipsum
13 changes: 13 additions & 0 deletions samples/subsys/fs/virtiofs/boards/qemu_x86.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
&pcie0 {
virtio_pci: virtio_pci {
compatible = "virtio,pci";

vendor-id = <0x1af4>;
device-id = <0x105a>;

interrupts = <0xb 0x0 0x0>;
interrupt-parent = <&intc>;

status = "okay";
};
};
13 changes: 13 additions & 0 deletions samples/subsys/fs/virtiofs/boards/qemu_x86_64.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
&pcie0 {
virtio_pci: virtio_pci {
compatible = "virtio,pci";

vendor-id = <0x1af4>;
device-id = <0x105a>;

interrupts = <0xb 0x0 0x0>;
interrupt-parent = <&intc>;

status = "okay";
};
};
11 changes: 11 additions & 0 deletions samples/subsys/fs/virtiofs/prepare_sample_directory.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright (c) 2025 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: Apache-2.0

mkdir -p dir/nested_dir
touch dir/some_file
touch dir/nested_dir/some_other_file
mkdir dir2
echo "a" > dir2/a
echo "bb" > dir2/b
echo "ccc" > dir2/c
echo "this is a file on the host" > file
7 changes: 7 additions & 0 deletions samples/subsys/fs/virtiofs/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CONFIG_VIRTIO=y
CONFIG_PCIE=y
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_VIRTIOFS=y
CONFIG_HEAP_MEM_POOL_SIZE=100000
CONFIG_MAIN_STACK_SIZE=16384
CONFIG_QEMU_EXTRA_FLAGS="-chardev socket,id=char0,path=/tmp/vhostqemu -device vhost-user-fs-pci,queue-size=1024,chardev=char0,tag=myfs -m 32M -object memory-backend-memfd,id=mem,size=32M,share=on -numa node,memdev=mem"
10 changes: 10 additions & 0 deletions samples/subsys/fs/virtiofs/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
sample:
name: virtio filesystem sample
common:
tags:
- filesystem
- virtio
tests:
sample.filesystem.virtiofs:
build_only: true
filter: CONFIG_DT_HAS_VIRTIO_PCI_ENABLED
133 changes: 133 additions & 0 deletions samples/subsys/fs/virtiofs/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2025 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdio.h>
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/fs/fs.h>
#include <zephyr/fs/virtiofs.h>
#include <zephyr/kernel.h>

#define VIRTIO_DEV DEVICE_DT_GET(DT_NODELABEL(virtio_pci))

#define MOUNT_POINT "/virtiofs"

struct virtiofs_fs_data fs_data;

static struct fs_mount_t mp = {
.type = FS_VIRTIOFS,
.fs_data = &fs_data,
.flags = 0,
.storage_dev = (void *)VIRTIO_DEV,
.mnt_point = MOUNT_POINT,
};

void dirtree(const char *path, int indent)
{
struct fs_dir_t dir;

fs_dir_t_init(&dir);
if (fs_opendir(&dir, path) == 0) {
while (1) {
struct fs_dirent entry;

if (fs_readdir(&dir, &entry) == 0) {
if (entry.name[0] == '\0') {
break;
}
if (entry.type == FS_DIR_ENTRY_DIR) {
printf("%*s- %s (type=dir)\n", indent * 2, "", entry.name);
char *subdir_path = k_malloc(
strlen(path) + strlen(entry.name) + 2
);

if (subdir_path == NULL) {
printf("failed to allocate subdir path\n");
continue;
}
strcpy(subdir_path, path);
strcat(subdir_path, "/");
strcat(subdir_path, entry.name);
dirtree(subdir_path, indent + 1);
k_free(subdir_path);
} else {
printf(
"%*s- %s (type=file, size=%zu)\n",
indent * 2, "", entry.name, entry.size
);
}
} else {
printf("failed to readdir %s\n", path);
break;
}
};

fs_closedir(&dir);
} else {
printf("failed to opendir %s\n", path);
}
}

void create_file(const char *path, const char *content)
{
struct fs_file_t file;

fs_file_t_init(&file);
if (fs_open(&file, path, FS_O_CREATE | FS_O_WRITE) == 0) {
fs_write(&file, content, strlen(content) + 1);
} else {
printf("failed to create %s\n", path);
}
fs_close(&file);
}

void print_file(const char *path)
{
struct fs_file_t file;

fs_file_t_init(&file);
if (fs_open(&file, path, FS_O_READ) == 0) {
char buf[256] = "\0";
int read_c = fs_read(&file, buf, sizeof(buf));

if (read_c >= 0) {
buf[read_c] = 0;

printf(
"%s content:\n"
"%s\n",
path, buf
);
} else {
printf("failed to read from %s\n", path);
}

fs_close(&file);
} else {
printf("failed to open %s\n", path);
}
}

int main(void)
{
if (fs_mount(&mp) == 0) {
printf("%s directory tree:\n", MOUNT_POINT);
dirtree(MOUNT_POINT, 0);
printf("\n");

print_file(MOUNT_POINT"/file");

create_file("/virtiofs/file_created_by_zephyr", "hello world\n");

create_file("/virtiofs/second_file_created_by_zephyr", "lorem ipsum\n");

fs_mkdir("/virtiofs/dir_created_by_zephyr");
} else {
printf("failed to mount %s\n", MOUNT_POINT);
}

return 0;
}
4 changes: 4 additions & 0 deletions subsys/fs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ if(CONFIG_FILE_SYSTEM_LIB_LINK)
endif()

add_subdirectory_ifdef(CONFIG_FILE_SYSTEM_EXT2 ext2)
add_subdirectory_ifdef(CONFIG_FUSE_CLIENT fuse_client)
add_subdirectory_ifdef(CONFIG_FILE_SYSTEM_VIRTIOFS virtiofs)

zephyr_library_link_libraries(FS)

target_link_libraries_ifdef(CONFIG_FAT_FILESYSTEM_ELM FS INTERFACE ELMFAT)
target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS FS INTERFACE LITTLEFS)
target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_EXT2 FS INTERFACE EXT2)
target_link_libraries_ifdef(CONFIG_FUSE_CLIENT FS INTERFACE FUSE_CLIENT)
target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_VIRTIOFS FS INTERFACE VIRTIOFS)
endif()

add_subdirectory_ifdef(CONFIG_FCB ./fcb)
Expand Down
Loading
Loading