Skip to content

Support virtio-fs #88

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 2 commits into
base: master
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
18 changes: 17 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ ifeq ($(call has, VIRTIORNG), 1)
OBJS_EXTRA += virtio-rng.o
endif

# virtio-fs
ENABLE_VIRTIOFS ?= 1
$(call set-feature, VIRTIOFS)
SHARED_DIRECTORY ?= ./shared
ifeq ($(call has, VIRTIOFS), 1)
OBJS_EXTRA += virtio-fs.o
OPTS += -s $(SHARED_DIRECTORY)
endif

NETDEV ?= tap
# virtio-net
ENABLE_VIRTIONET ?= 1
Expand Down Expand Up @@ -197,7 +206,14 @@ ext4.img:
$(Q)dd if=/dev/zero of=$@ bs=4k count=600
$(Q)$(MKFS_EXT4) -F $@

check: $(BIN) minimal.dtb $(KERNEL_DATA) $(INITRD_DATA) $(DISKIMG_FILE)
.PHONY: $(DIRECTORY)
$(SHARED_DIRECTORY):
@if [ ! -d $@ ]; then \
echo "Creating mount directory: $@"; \
mkdir -p $@; \
fi

check: $(BIN) minimal.dtb $(KERNEL_DATA) $(INITRD_DATA) $(DISKIMG_FILE) $(SHARED_DIRECTORY)
@$(call notice, Ready to launch Linux kernel. Please be patient.)
$(Q)./$(BIN) -k $(KERNEL_DATA) -c $(SMP) -b minimal.dtb -i $(INITRD_DATA) -n $(NETDEV) $(OPTS)

Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,33 @@ You can exit the emulator using: \<Ctrl-a x\>. (press Ctrl+A, leave it, afterwar
## Usage

```shell
./semu -k linux-image [-b dtb-file] [-i initrd-image] [-d disk-image]
./semu -k linux-image [-b dtb-file] [-i initrd-image] [-d disk-image] [-s shared-directory]
```

* `linux-image` is the path to the Linux kernel `Image`.
* `dtb-file` is optional, as it specifies the user-specified device tree blob.
* `initrd-image` is optional, as it specifies the user-specified initial RAM disk image.
* `disk-image` is optional, as it specifies the path of a disk image in ext4 file system for the virtio-blk device.
* `shared-directory` is optional, as it specifies the path of a directory you want to mount in host.

## Mount and unmount a directory in semu

To mount the directory in semu:

```shell
$ mount -t virtiofs myfs [shared-directory]
```

* `shared-directory` is the path of a directory you want to mount in semu.

To unmount the directory in semu:

```shell
$ umount [shared-directory]
```

* `shared-directory` is the path of a directory you want to unmount in semu.


## Build Linux kernel image and root file system

Expand Down
4 changes: 3 additions & 1 deletion configs/linux.config
Original file line number Diff line number Diff line change
Expand Up @@ -1257,7 +1257,9 @@ CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
CONFIG_AUTOFS4_FS=y
CONFIG_AUTOFS_FS=y
# CONFIG_FUSE_FS is not set
CONFIG_CUSE=y
CONFIG_FUSE_FS=y
CONFIG_VIRTIO_FS=y
# CONFIG_OVERLAY_FS is not set

#
Expand Down
70 changes: 70 additions & 0 deletions device.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#if SEMU_HAS(VIRTIONET)
#include "netdev.h"
#endif
#if SEMU_HAS(VIRTIOFS)
#include "uthash.h"
#endif
#include "riscv.h"
#include "virtio.h"

Expand Down Expand Up @@ -357,6 +360,70 @@ void virtio_snd_write(hart_t *core,
bool virtio_snd_init(virtio_snd_state_t *vsnd);
#endif /* SEMU_HAS(VIRTIOSND) */

/* VirtIO-File-System */

#if SEMU_HAS(VIRTIOFS)
#define IRQ_VFS 6
#define IRQ_VFS_BIT (1 << IRQ_VFS)

typedef struct {
uint64_t ino;
char *path;
UT_hash_handle hh;
} inode_map_entry;

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

typedef struct {
/* feature negotiation */
uint32_t DeviceFeaturesSel;
uint32_t DriverFeatures;
uint32_t DriverFeaturesSel;

/* queue config */
uint32_t QueueSel;
virtio_fs_queue_t queues[3];

/* status */
uint32_t Status;
uint32_t InterruptStatus;

/* guest memory base */
uint32_t *ram;

char *mount_tag; /* guest sees this tag */
char *shared_dir;

inode_map_entry *inode_map;

/* optional implementation-specific */
void *priv;
} virtio_fs_state_t;

/* MMIO read/write */
void virtio_fs_read(hart_t *core,
virtio_fs_state_t *vfs,
uint32_t addr,
uint8_t width,
uint32_t *value);

void virtio_fs_write(hart_t *core,
virtio_fs_state_t *vfs,
uint32_t addr,
uint8_t width,
uint32_t value);

bool virtio_fs_init(virtio_fs_state_t *vfs, char *mtag, char *dir);

#endif /* SEMU_HAS(VIRTIOFS) */

/* memory mapping */
typedef struct {
bool debug;
Expand All @@ -382,6 +449,9 @@ typedef struct {
#if SEMU_HAS(VIRTIOSND)
virtio_snd_state_t vsnd;
#endif
#if SEMU_HAS(VIRTIOFS)
virtio_fs_state_t vfs;
#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 @@ -17,5 +17,10 @@
#define SEMU_FEATURE_VIRTIOSND 1
#endif

/* virtio-fs */
#ifndef SEMU_FEATURE_VIRTIOFS
#define SEMU_FEATURE_VIRTIOFS 1
#endif

/* Feature test macro */
#define SEMU_HAS(x) SEMU_FEATURE_##x
159 changes: 159 additions & 0 deletions fuse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#include "riscv.h"
#include "virtio.h"

#define FUSE_REC_ALIGN(x) \
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)

struct fuse_in_header {
uint32_t len;
uint32_t opcode;
uint64_t unique;
uint64_t nodeid;
uint32_t uid;
uint32_t gid;
uint32_t pid;
uint32_t padding;
};

struct fuse_out_header {
uint32_t len;
int32_t error;
uint64_t unique;
};

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove redundant line.

struct vfs_req_header {
struct fuse_in_header in;
};

struct vfs_resp_header {
struct fuse_out_header out;
};

struct fuse_init_in {
/* FUSE major version supported by the guest (typically 7) */
uint32_t major;
/* FUSE minor version supported by the guest (e.g., 31, 26) */
uint32_t minor;
uint32_t max_readahead; /* Maximum readahead size supported by the guest */
uint32_t flags; /* Flags requested by the guest */
};

struct fuse_init_out {
uint32_t major; /* FUSE major version supported by the device */
uint32_t minor; /* FUSE minor version supported by the device */
uint32_t max_readahead; /* Maximum readahead size accepted by the device */
/* Flags supported by the device (negotiated with the guest) */
uint32_t flags;
uint16_t max_background; /* Maximum number of background requests */
uint16_t congestion_threshold;
uint32_t max_write; /* Maximum write size the device can handle */
uint32_t time_gran; /* Time granularity (in nanoseconds) */
uint32_t unused[11]; /* Reserved */
};

struct fuse_getattr_in {
/* bitmask for valid fields (e.g. FUSE_GETATTR_FH) */
uint32_t getattr_flags;
uint32_t padding; /* unused, reserved for alignment */
uint64_t fh; /* optional: file handle (used when getattr_flags has */
};

struct fuse_attr {
uint64_t ino; /* inode number */
uint64_t size; /* file size in bytes */
uint64_t blocks; /* number of 512B blocks allocated */
uint64_t atime; /* last access time (UNIX time) */
uint64_t mtime; /* last modification time */
uint64_t ctime; /* last status change time */
uint32_t atimensec; /* nanoseconds part */
uint32_t mtimensec;
uint32_t ctimensec;
uint32_t mode; /* file mode (e.g. S_IFDIR | 0755) */
uint32_t nlink; /* number of hard links */
uint32_t uid; /* owner uid */
uint32_t gid; /* owner gid */
uint32_t rdev; /* device ID (if special file) */
uint32_t blksize; /* block size */
uint32_t flags; /* reserved */
};

struct fuse_attr_out {
uint64_t attr_valid; /* seconds the attributes are valid */
uint32_t attr_valid_nsec; /* nanoseconds part of attr_valid */
uint32_t dummy; /* padding for alignment */
struct fuse_attr attr; /* actual attributes */
};

struct fuse_open_in {
uint32_t flags;
uint32_t open_flags;
};

struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
int32_t backing_id;
};

struct fuse_read_in {
uint64_t fh;
uint64_t offset;
uint32_t size;
uint32_t read_flags;
uint64_t lock_owner;
uint32_t flags;
uint32_t padding;
};

struct fuse_entry_out {
uint64_t nodeid; /* inode number */
uint64_t generation; /* inode generation */
uint64_t entry_valid; /* cache timeout (sec) */
uint64_t attr_valid; /* attr cache timeout (sec) */
uint32_t entry_valid_nsec; /* cache timeout (nsec) */
uint32_t attr_valid_nsec; /* attr cache timeout (nsec) */
struct fuse_attr attr; /* file attributes */
};

struct fuse_dirent {
uint64_t ino; /* inode number */
uint64_t off; /* offset to next entry */
uint32_t namelen; /* length of the entry name */
uint32_t type; /* file type (DT_REG, DT_DIR, etc.) */
char name[]; /* name (not null-terminated) */
};

struct fuse_direntplus {
struct fuse_entry_out entry_out;
struct fuse_dirent dirent;
};

struct fuse_lookup_in {
uint64_t parent; /* inode of parent dir */
};

struct fuse_forget_in {
uint64_t nlookup;
};

struct fuse_create_in {
uint32_t flags;
uint32_t mode;
uint32_t umask;
uint32_t open_flags;
};

struct fuse_release_in {
uint64_t fh;
uint32_t flags;
uint32_t release_flags;
uint64_t lock_owner;
};

struct fuse_flush_in {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap with PACKED() for all structures that require proper memory alignment.

Check the following code as example:

semu/common.h

Line 31 in 5cb78df

#define PACKED(name) name __attribute__((packed))

semu/virtio.h

Line 60 in 5cb78df

PACKED(struct virtq_desc {

uint64_t fh;
uint32_t unused;
uint32_t padding;
uint64_t lock_owner;
};
Loading