Skip to content

Commit 52d21c0

Browse files
Implement virtio filesystem in semu
Map MMIO region at 0xF48____ for virtio-fs device in semu. Introduce special-case logic for char tag in virtio_fs config handling in semu. Add inode_map hash table for mapping inodes to file paths. Implement FUSE core operations: - INIT - GETATTR - OPENDIR - READDIRPLUS - LOOKUP - FORGET - RELEASEDIR - OPEN - READ - RELEASE - FLUSH - DESTROY to make semu supports commands:`cd`, `cat`, `ls`. Introduced uthash.h to implement a hash table for mapping inode numbers to file paths. This improves lookup performance and simplifies path management in the virtio-fs implementation. Set default mount tag to "myfs" and mount directory to "./shared". When `make check`, if MOUNT_DIRECTORY is not exist, then create a new one. If -s parameter is empty, virtio-fs is unused. Add the explanation about virtio-fs in `Usage` of README.md. Add the introduction of how to mount and unmount in semu.
1 parent 32c5bbf commit 52d21c0

File tree

10 files changed

+2486
-16
lines changed

10 files changed

+2486
-16
lines changed

Makefile

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ ifeq ($(call has, VIRTIORNG), 1)
4141
OBJS_EXTRA += virtio-rng.o
4242
endif
4343

44+
# virtio-fs
45+
ENABLE_VIRTIOFS ?= 1
46+
$(call set-feature, VIRTIOFS)
47+
MOUNT_DIRECTORY ?= ./shared
48+
ifeq ($(call has, VIRTIOFS), 1)
49+
OBJS_EXTRA += virtio-fs.o
50+
OPTS += -s $(MOUNT_DIRECTORY)
51+
endif
52+
4453
NETDEV ?= tap
4554
# virtio-net
4655
ENABLE_VIRTIONET ?= 1
@@ -197,7 +206,14 @@ ext4.img:
197206
$(Q)dd if=/dev/zero of=$@ bs=4k count=600
198207
$(Q)$(MKFS_EXT4) -F $@
199208

200-
check: $(BIN) minimal.dtb $(KERNEL_DATA) $(INITRD_DATA) $(DISKIMG_FILE)
209+
.PHONY: $(DIRECTORY)
210+
$(MOUNT_DIRECTORY):
211+
@if [ ! -d $@ ]; then \
212+
echo "Creating mount directory: $@"; \
213+
mkdir -p $@; \
214+
fi
215+
216+
check: $(BIN) minimal.dtb $(KERNEL_DATA) $(INITRD_DATA) $(DISKIMG_FILE) $(MOUNT_DIRECTORY)
201217
@$(call notice, Ready to launch Linux kernel. Please be patient.)
202218
$(Q)./$(BIN) -k $(KERNEL_DATA) -c $(SMP) -b minimal.dtb -i $(INITRD_DATA) -n $(NETDEV) $(OPTS)
203219

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,33 @@ You can exit the emulator using: \<Ctrl-a x\>. (press Ctrl+A, leave it, afterwar
7777
## Usage
7878

7979
```shell
80-
./semu -k linux-image [-b dtb-file] [-i initrd-image] [-d disk-image]
80+
./semu -k linux-image [-b dtb-file] [-i initrd-image] [-d disk-image] [-s mount-directory]
8181
```
8282

8383
* `linux-image` is the path to the Linux kernel `Image`.
8484
* `dtb-file` is optional, as it specifies the user-specified device tree blob.
8585
* `initrd-image` is optional, as it specifies the user-specified initial RAM disk image.
8686
* `disk-image` is optional, as it specifies the path of a disk image in ext4 file system for the virtio-blk device.
87+
* `mount-directory` is optional, as it specifies the path of a directory you want to mount in host.
88+
89+
## Mount and unmount a directory in semu
90+
91+
To mount the directory in semu:
92+
93+
```shell
94+
$ mount -t virtiofs myfs [mount-directory]
95+
```
96+
97+
* `mount-directory` is the path of a directory you want to mount in semu.
98+
99+
To unmount the directory in semu:
100+
101+
```shell
102+
$ umount [mount-directory]
103+
```
104+
105+
* `mount-directory` is the path of a directory you want to unmount in semu.
106+
87107

88108
## Build Linux kernel image and root file system
89109

device.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#if SEMU_HAS(VIRTIONET)
44
#include "netdev.h"
55
#endif
6+
#if SEMU_HAS(VIRTIOFS)
7+
#include "uthash.h"
8+
#endif
69
#include "riscv.h"
710
#include "virtio.h"
811

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

363+
/* VirtIO-File-System */
364+
365+
#if SEMU_HAS(VIRTIOFS)
366+
#define IRQ_VFS 6
367+
#define IRQ_VFS_BIT (1 << IRQ_VFS)
368+
369+
typedef struct {
370+
uint64_t ino;
371+
char path[4096];
372+
UT_hash_handle hh;
373+
} inode_map_entry;
374+
375+
typedef struct {
376+
uint32_t QueueNum;
377+
uint32_t QueueDesc;
378+
uint32_t QueueAvail;
379+
uint32_t QueueUsed;
380+
uint16_t last_avail;
381+
bool ready;
382+
} virtio_fs_queue_t;
383+
384+
typedef struct {
385+
/* feature negotiation */
386+
uint32_t DeviceFeaturesSel;
387+
uint32_t DriverFeatures;
388+
uint32_t DriverFeaturesSel;
389+
390+
/* queue config */
391+
uint32_t QueueSel;
392+
virtio_fs_queue_t queues[3];
393+
394+
/* status */
395+
uint32_t Status;
396+
uint32_t InterruptStatus;
397+
398+
/* guest memory base */
399+
uint32_t *ram;
400+
401+
char *mount_tag; // guest sees this tag
402+
char shared_dir[4096];
403+
404+
inode_map_entry *inode_map;
405+
406+
/* optional implementation-specific */
407+
void *priv;
408+
} virtio_fs_state_t;
409+
410+
/* MMIO read/write */
411+
void virtio_fs_read(hart_t *core,
412+
virtio_fs_state_t *vfs,
413+
uint32_t addr,
414+
uint8_t width,
415+
uint32_t *value);
416+
417+
void virtio_fs_write(hart_t *core,
418+
virtio_fs_state_t *vfs,
419+
uint32_t addr,
420+
uint8_t width,
421+
uint32_t value);
422+
423+
bool virtio_fs_init(virtio_fs_state_t *vfs, char *mtag, char *dir);
424+
425+
#endif /* SEMU_HAS(VIRTIOFS) */
426+
360427
/* memory mapping */
361428
typedef struct {
362429
bool debug;
@@ -382,6 +449,9 @@ typedef struct {
382449
#if SEMU_HAS(VIRTIOSND)
383450
virtio_snd_state_t vsnd;
384451
#endif
452+
#if SEMU_HAS(VIRTIOFS)
453+
virtio_fs_state_t vfs;
454+
#endif
385455

386456
uint32_t peripheral_update_ctr;
387457

feature.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,10 @@
1717
#define SEMU_FEATURE_VIRTIOSND 1
1818
#endif
1919

20+
/* virtio-fs */
21+
#ifndef SEMU_FEATURE_VIRTIOFS
22+
#define SEMU_FEATURE_VIRTIOFS 1
23+
#endif
24+
2025
/* Feature test macro */
2126
#define SEMU_HAS(x) SEMU_FEATURE_##x

fuse.h

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#include "riscv.h"
2+
#include "virtio.h"
3+
4+
#define FUSE_REC_ALIGN(x) \
5+
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
6+
#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
7+
8+
struct fuse_in_header {
9+
uint32_t len;
10+
uint32_t opcode;
11+
uint64_t unique;
12+
uint64_t nodeid;
13+
uint32_t uid;
14+
uint32_t gid;
15+
uint32_t pid;
16+
uint32_t padding;
17+
};
18+
19+
struct fuse_out_header {
20+
uint32_t len;
21+
int32_t error;
22+
uint64_t unique;
23+
};
24+
25+
struct vfs_req_header {
26+
struct fuse_in_header in;
27+
};
28+
29+
struct vfs_resp_header {
30+
struct fuse_out_header out;
31+
};
32+
33+
struct fuse_init_in {
34+
/* FUSE major version supported by the guest (typically 7) */
35+
uint32_t major;
36+
/* FUSE minor version supported by the guest (e.g., 31, 26) */
37+
uint32_t minor;
38+
uint32_t max_readahead; /* Maximum readahead size supported by the guest */
39+
uint32_t flags; /* Flags requested by the guest */
40+
};
41+
42+
struct fuse_init_out {
43+
uint32_t major; /* FUSE major version supported by the device */
44+
uint32_t minor; /* FUSE minor version supported by the device */
45+
uint32_t max_readahead; /* Maximum readahead size accepted by the device */
46+
/* Flags supported by the device (negotiated with the guest) */
47+
uint32_t flags;
48+
uint16_t max_background; /* Maximum number of background requests */
49+
uint16_t congestion_threshold;
50+
uint32_t max_write; /* Maximum write size the device can handle */
51+
uint32_t time_gran; /* Time granularity (in nanoseconds) */
52+
uint32_t unused[11]; /* Reserved */
53+
};
54+
55+
struct fuse_getattr_in {
56+
/* bitmask for valid fields (e.g. FUSE_GETATTR_FH) */
57+
uint32_t getattr_flags;
58+
uint32_t padding; /* unused, reserved for alignment */
59+
uint64_t fh; /* optional: file handle (used when getattr_flags has */
60+
};
61+
62+
struct fuse_attr {
63+
uint64_t ino; /* inode number */
64+
uint64_t size; /* file size in bytes */
65+
uint64_t blocks; /* number of 512B blocks allocated */
66+
uint64_t atime; /* last access time (UNIX time) */
67+
uint64_t mtime; /* last modification time */
68+
uint64_t ctime; /* last status change time */
69+
uint32_t atimensec; /* nanoseconds part */
70+
uint32_t mtimensec;
71+
uint32_t ctimensec;
72+
uint32_t mode; /* file mode (e.g. S_IFDIR | 0755) */
73+
uint32_t nlink; /* number of hard links */
74+
uint32_t uid; /* owner uid */
75+
uint32_t gid; /* owner gid */
76+
uint32_t rdev; /* device ID (if special file) */
77+
uint32_t blksize; /* block size */
78+
uint32_t flags; /* reserved */
79+
};
80+
81+
struct fuse_attr_out {
82+
uint64_t attr_valid; /* seconds the attributes are valid */
83+
uint32_t attr_valid_nsec; /* nanoseconds part of attr_valid */
84+
uint32_t dummy; /* padding for alignment */
85+
struct fuse_attr attr; /* actual attributes */
86+
};
87+
88+
struct fuse_open_in {
89+
uint32_t flags;
90+
uint32_t open_flags;
91+
};
92+
93+
struct fuse_open_out {
94+
uint64_t fh;
95+
uint32_t open_flags;
96+
int32_t backing_id;
97+
};
98+
99+
struct fuse_read_in {
100+
uint64_t fh;
101+
uint64_t offset;
102+
uint32_t size;
103+
uint32_t read_flags;
104+
uint64_t lock_owner;
105+
uint32_t flags;
106+
uint32_t padding;
107+
};
108+
109+
struct fuse_entry_out {
110+
uint64_t nodeid; /* inode number */
111+
uint64_t generation; /* inode generation */
112+
uint64_t entry_valid; /* cache timeout (sec) */
113+
uint64_t attr_valid; /* attr cache timeout (sec) */
114+
uint32_t entry_valid_nsec; /* cache timeout (nsec) */
115+
uint32_t attr_valid_nsec; /* attr cache timeout (nsec) */
116+
struct fuse_attr attr; /* file attributes */
117+
};
118+
119+
struct fuse_dirent {
120+
uint64_t ino; /* inode number */
121+
uint64_t off; /* offset to next entry */
122+
uint32_t namelen; /* length of the entry name */
123+
uint32_t type; /* file type (DT_REG, DT_DIR, etc.) */
124+
char name[]; /* name (not null-terminated) */
125+
};
126+
127+
struct fuse_direntplus {
128+
struct fuse_entry_out entry_out;
129+
struct fuse_dirent dirent;
130+
};
131+
132+
struct fuse_lookup_in {
133+
uint64_t parent; /* inode of parent dir */
134+
};
135+
136+
struct fuse_forget_in {
137+
uint64_t nlookup;
138+
};
139+
140+
struct fuse_create_in {
141+
uint32_t flags;
142+
uint32_t mode;
143+
uint32_t umask;
144+
uint32_t open_flags;
145+
};
146+
147+
struct fuse_release_in {
148+
uint64_t fh;
149+
uint32_t flags;
150+
uint32_t release_flags;
151+
uint64_t lock_owner;
152+
};
153+
154+
struct fuse_flush_in {
155+
uint64_t fh;
156+
uint32_t unused;
157+
uint32_t padding;
158+
uint64_t lock_owner;
159+
};

0 commit comments

Comments
 (0)