virtio: add virtio framework in NuttX

1. virtio devics/drivers match and probe/remote mechanism;
2. virtio mmio transport layer based on OpenAmp (Compatible with both
   virtio mmio version 1 and 2);
3. virtio-serial driver based on new virtio framework;
4. virtio-rng driver based on new virtio framework;
5. virtio-net driver based on new virtio framework
   (IOB Offload implementation);
6. virtio-blk driver based on new virtio framework;
7. Remove the old virtio mmio framework, the old framework only
   support mmio transport layer, and the new framwork support
   more transport layer and this commit has implemented all the
   old virtio drivers;
8. Refresh the the qemu-arm64 and qemu-riscv virtio related
   configs, and update its README.txt;

New virtio-net driver has better performance
Compared with previous virtio-mmio-net:
|                        | master/-c | master/-s | this/-c | this/-s |
| :--------------------: | :-------: | :-------: | :-----: | :-----: |
| qemu-armv8a:netnsh     |  539Mbps  |  524Mbps  | 906Mbps | 715Mbps |
| qemu-armv8a:netnsh_smp |  401Mbps  |  437Mbps  | 583Mbps | 505Mbps |
| rv-virt:netnsh         |  487Mbps  |  512Mbps  | 760Mbps | 634Mbps |
| rv-virt:netnsh_smp     |  387Mbps  |  455Mbps  | 447Mbps | 502Mbps |
| rv-virt:netnsh64       |  602Mbps  |  595Mbps  | 881Mbps | 769Mbps |
| rv-virt:netnsh64_smp   |  414Mbps  |  515Mbps  | 491Mbps | 525Mbps |
| rv-virt:knetnsh64      |  515Mbps  |  457Mbps  | 606Mbps | 540Mbps |
| rv-virt:knetnsh64_smp  |  308Mbps  |  389Mbps  | 415Mbps | 474Mbps |
Note: Both CONFIG_IOB_NBUFFERS=64, using iperf command, all in Mbits/sec
      Tested in QEMU 7.2.2

Signed-off-by: wangbowen6 <wangbowen6@xiaomi.com>
Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
wangbowen6 2023-03-22 11:49:43 +08:00 committed by Xiang Xiao
parent d010744582
commit 9aa57b6c53
34 changed files with 3646 additions and 2243 deletions

View file

@ -26,7 +26,3 @@ CHIP_CSRCS = qemu_boot.c qemu_serial.c
ifeq ($(CONFIG_ARCH_EARLY_PRINT),y)
CHIP_ASRCS = qemu_lowputc.S
endif
ifeq ($(CONFIG_DRIVERS_VIRTIO_NET),y)
CHIP_CSRCS += qemu_virtio.c
endif

View file

@ -36,7 +36,3 @@ endif
ifeq ($(CONFIG_MM_PGALLOC),y)
CHIP_CSRCS += qemu_rv_pgalloc.c
endif
ifeq ($(CONFIG_DRIVERS_VIRTIO_NET),y)
CHIP_CSRCS += qemu_rv_virtio.c
endif

View file

@ -51,7 +51,7 @@ Getting Started
-net none -chardev stdio,id=con,mux=on -serial chardev:con \
-mon chardev=con,mode=readline -kernel ./nuttx
3.1.1 Single Core with virtio network and block driver (GICv3)
3.1.1 Single Core with virtio network, block, rng, serial driver (GICv3)
Configuring NuttX and compile:
$ ./tools/configure.sh -l qemu-armv8a:netnsh
$ make
@ -61,9 +61,14 @@ Getting Started
-machine virt,virtualization=on,gic-version=3 \
-chardev stdio,id=con,mux=on -serial chardev:con \
-global virtio-mmio.force-legacy=false \
-drive file=./mydisk-1gb.img,if=none,format=raw,id=hd -device virtio-blk-device,drive=hd \
-device virtio-serial-device,bus=virtio-mmio-bus.0 \
-chardev socket,telnet=on,host=127.0.0.1,port=3450,server=on,wait=off,id=foo \
-device virtconsole,chardev=foo \
-device virtio-rng-device,bus=virtio-mmio-bus.1 \
-netdev user,id=u1,hostfwd=tcp:127.0.0.1:10023-10.0.2.15:23,hostfwd=tcp:127.0.0.1:15001-10.0.2.15:5001 \
-device virtio-net-device,netdev=u1,bus=virtio-mmio-bus.0 \
-device virtio-net-device,netdev=u1,bus=virtio-mmio-bus.2 \
-drive file=./mydisk-1gb.img,if=none,format=raw,id=hd \
-device virtio-blk-device,bus=virtio-mmio-bus.3,drive=hd \
-mon chardev=con,mode=readline -kernel ./nuttx
3.2 SMP (GICv3)

View file

@ -5,6 +5,7 @@
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_NDEBUG is not set
CONFIG_ALLOW_BSD_COMPONENTS=y
CONFIG_ARCH="arm64"
CONFIG_ARCH_ARM64=y
@ -22,11 +23,10 @@ CONFIG_DEFAULT_TASK_STACKSIZE=8192
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x0a000000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=48
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=32
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x200
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_FAT_LCNAMES=y
CONFIG_FAT_LFN=y
@ -38,7 +38,10 @@ CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_IDLETHREAD_STACKSIZE=8192
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_ALIGNMENT=16
CONFIG_IOB_BUFSIZE=1534
CONFIG_IOB_NBUFFERS=64
CONFIG_IOB_THROTTLE=8
CONFIG_NET=y
CONFIG_NETDB_DNSCLIENT=y
CONFIG_NETDB_DNSCLIENT_ENTRIES=4
@ -55,7 +58,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=32
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=32768
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -65,6 +70,7 @@ CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_OPENAMP=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_PTHREAD_STACK_MIN=8192
CONFIG_RAM_SIZE=134217728

View file

@ -24,11 +24,10 @@ CONFIG_DEFAULT_TASK_STACKSIZE=8192
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x0a000000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=48
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=32
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x200
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_FAT_LCNAMES=y
CONFIG_FAT_LFN=y
@ -40,7 +39,10 @@ CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_IDLETHREAD_STACKSIZE=8192
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_ALIGNMENT=16
CONFIG_IOB_BUFSIZE=1534
CONFIG_IOB_NBUFFERS=64
CONFIG_IOB_THROTTLE=8
CONFIG_NET=y
CONFIG_NETDB_DNSCLIENT=y
CONFIG_NETDB_DNSCLIENT_ENTRIES=4
@ -57,7 +59,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=32
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=32768
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -67,6 +71,7 @@ CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_OPENAMP=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_PTHREAD_STACK_MIN=8192
CONFIG_RAM_SIZE=134217728

View file

@ -22,11 +22,10 @@ CONFIG_DEFAULT_TASK_STACKSIZE=8192
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x0a000000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=48
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=32
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x200
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_FAT_LCNAMES=y
CONFIG_FAT_LFN=y
@ -38,8 +37,9 @@ CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_IDLETHREAD_STACKSIZE=8192
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_BUFSIZE=1534
CONFIG_IOB_NBUFFERS=64
CONFIG_IOB_THROTTLE=8
CONFIG_NET=y
CONFIG_NETDB_DNSCLIENT=y
CONFIG_NETDB_DNSCLIENT_ENTRIES=4
@ -56,7 +56,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=32
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=32768
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -66,6 +68,7 @@ CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_OPENAMP=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_PTHREAD_STACK_MIN=8192
CONFIG_RAM_SIZE=134217728

View file

@ -24,11 +24,10 @@ CONFIG_DEFAULT_TASK_STACKSIZE=8192
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x0a000000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=48
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=32
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x200
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_FAT_LFN=y
CONFIG_FS_FAT=y
@ -39,8 +38,9 @@ CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_IDLETHREAD_STACKSIZE=8192
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_BUFSIZE=1534
CONFIG_IOB_NBUFFERS=64
CONFIG_IOB_THROTTLE=8
CONFIG_NET=y
CONFIG_NETDB_DNSCLIENT=y
CONFIG_NETDB_DNSCLIENT_ENTRIES=4
@ -57,7 +57,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=32
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=32768
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -67,6 +69,7 @@ CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_OPENAMP=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_PTHREAD_STACK_MIN=8192
CONFIG_RAM_SIZE=134217728

View file

@ -28,9 +28,42 @@
#include <syslog.h>
#include <nuttx/fs/fs.h>
#include <nuttx/virtio/virtio-mmio.h>
#include "qemu-armv8a.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define QEMU_VIRTIO_MMIO_BASE 0x0a000000
#define QEMU_VIRTIO_MMIO_REGSIZE 0x200
#define QEMU_VIRTIO_MMIO_IRQ 48
#define QEMU_VIRTIO_MMIO_NUM 4
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: qemu_virtio_register_mmio_devices
****************************************************************************/
#ifdef CONFIG_DRIVERS_VIRTIO_MMIO
static void qemu_virtio_register_mmio_devices(void)
{
uintptr_t virtio = (uintptr_t)QEMU_VIRTIO_MMIO_BASE;
size_t size = QEMU_VIRTIO_MMIO_REGSIZE;
int irq = QEMU_VIRTIO_MMIO_IRQ;
int i;
for (i = 0; i < QEMU_VIRTIO_MMIO_NUM; i++)
{
virtio_register_mmio_device((FAR void *)(virtio + size * i), irq + i);
}
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -57,6 +90,10 @@ int qemu_bringup(void)
}
#endif
#ifdef CONFIG_DRIVERS_VIRTIO_MMIO
qemu_virtio_register_mmio_devices();
#endif
UNUSED(ret);
return OK;
}

View file

@ -57,25 +57,35 @@
...
nsh>
4. Run the virtio network and block driver with qemu
4. Run the virtio network, block, serial and rng driver with qemu
$ dd if=/dev/zero of=./mydisk-1gb.img bs=1M count=1024
$ qemu-system-riscv32 -semihosting -M virt,aclint=on -cpu rv32 -smp 8 \
-global virtio-mmio.force-legacy=false \
-drive file=./mydisk-1gb.img,if=none,format=raw,id=hd -device virtio-blk-device,drive=hd \
-device virtio-serial-device,bus=virtio-mmio-bus.0 \
-chardev socket,telnet=on,host=127.0.0.1,port=3450,server=on,wait=off,id=foo \
-device virtconsole,chardev=foo \
-device virtio-rng-device,bus=virtio-mmio-bus.1 \
-netdev user,id=u1,hostfwd=tcp:127.0.0.1:10023-10.0.2.15:23,hostfwd=tcp:127.0.0.1:15001-10.0.2.15:5001 \
-device virtio-net-device,netdev=u1,bus=virtio-mmio-bus.0 \
-bios none -kernel nuttx -nographic
-device virtio-net-device,netdev=u1,bus=virtio-mmio-bus.2 \
-drive file=./mydisk-1gb.img,if=none,format=raw,id=hd \
-device virtio-blk-device,bus=virtio-mmio-bus.3,drive=hd \
-bios none -kernel ./nuttx/nuttx -nographic
or
$ qemu-system-riscv64 -semihosting -M virt,aclint=on -cpu rv64 -smp 8 \
-global virtio-mmio.force-legacy=false \
-drive file=./mydisk-1gb.img,if=none,format=raw,id=hd -device virtio-blk-device,drive=hd \
-device virtio-serial-device,bus=virtio-mmio-bus.0 \
-chardev socket,telnet=on,host=127.0.0.1,port=3450,server=on,wait=off,id=foo \
-device virtconsole,chardev=foo \
-device virtio-rng-device,bus=virtio-mmio-bus.1 \
-netdev user,id=u1,hostfwd=tcp:127.0.0.1:10023-10.0.2.15:23,hostfwd=tcp:127.0.0.1:15001-10.0.2.15:5001 \
-device virtio-net-device,netdev=u1,bus=virtio-mmio-bus.0 \
-bios none -kernel nuttx -nographic
-device virtio-net-device,netdev=u1,bus=virtio-mmio-bus.2 \
-drive file=./mydisk-1gb.img,if=none,format=raw,id=hd \
-device virtio-blk-device,bus=virtio-mmio-bus.3,drive=hd \
-bios none -kernel ./nuttx/nuttx -nographic
5. TODO

View file

@ -50,11 +50,10 @@ CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x10001000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=26
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=8
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x1000
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_ELF=y
CONFIG_EXAMPLES_HELLO=m
CONFIG_FAT_LCNAMES=y
@ -73,7 +72,8 @@ CONFIG_INIT_MOUNT_SOURCE=""
CONFIG_INIT_MOUNT_TARGET="/system"
CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_BUFSIZE=1534
CONFIG_IOB_THROTTLE=2
CONFIG_LIBC_ENVPATH=y
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIBC_PERROR_STDOUT=y
@ -97,7 +97,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=32
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=16384
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -108,6 +110,7 @@ CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_FILE_APPS=y
CONFIG_NSH_READLINE=y
CONFIG_OPENAMP=y
CONFIG_PATH_INITIAL="/system/bin"
CONFIG_RAM_SIZE=1048576
CONFIG_RAM_START=0x80100000

View file

@ -50,11 +50,10 @@ CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x10001000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=26
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=8
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x1000
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_ELF=y
CONFIG_EXAMPLES_HELLO=m
CONFIG_FAT_LCNAMES=y
@ -73,12 +72,15 @@ CONFIG_INIT_MOUNT_SOURCE=""
CONFIG_INIT_MOUNT_TARGET="/system"
CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_BUFSIZE=1534
CONFIG_IOB_NBUFFERS=64
CONFIG_IOB_THROTTLE=8
CONFIG_LIBC_ENVPATH=y
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIBC_PERROR_STDOUT=y
CONFIG_LIBC_STRERROR=y
CONFIG_MEMCPY_64BIT=y
CONFIG_MEMCPY_VIK=y
CONFIG_MEMSET_64BIT=y
CONFIG_MEMSET_OPTSPEED=y
CONFIG_MM_PGALLOC=y
@ -98,7 +100,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=32
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=32768
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -109,6 +113,7 @@ CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_FILE_APPS=y
CONFIG_NSH_READLINE=y
CONFIG_OPENAMP=y
CONFIG_PATH_INITIAL="/system/bin"
CONFIG_RAM_SIZE=1048576
CONFIG_RAM_START=0x80100000

View file

@ -35,11 +35,10 @@ CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x10001000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=28
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=8
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x1000
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_ELF=y
CONFIG_EXAMPLES_HELLO=m
CONFIG_FAT_LCNAMES=y
@ -52,7 +51,8 @@ CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_BUFSIZE=1530
CONFIG_IOB_THROTTLE=2
CONFIG_LIBC_ENVPATH=y
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIBC_PERROR_STDOUT=y
@ -73,7 +73,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=28
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=16384
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -87,6 +89,7 @@ CONFIG_NSH_READLINE=y
CONFIG_NSH_SYMTAB=y
CONFIG_NSH_SYMTAB_ARRAYNAME="g_symtab"
CONFIG_NSH_SYMTAB_COUNTNAME="g_nsymbols"
CONFIG_OPENAMP=y
CONFIG_PATH_INITIAL="/system/bin"
CONFIG_RAM_SIZE=33554432
CONFIG_RAM_START=0x80000000

View file

@ -35,11 +35,10 @@ CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x10001000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=28
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=8
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x1000
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_ELF=y
CONFIG_EXAMPLES_HELLO=m
CONFIG_FAT_LCNAMES=y
@ -52,7 +51,8 @@ CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_BUFSIZE=1534
CONFIG_IOB_THROTTLE=2
CONFIG_LIBC_ENVPATH=y
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIBC_PERROR_STDOUT=y
@ -73,7 +73,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=32
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=16384
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -87,6 +89,7 @@ CONFIG_NSH_READLINE=y
CONFIG_NSH_SYMTAB=y
CONFIG_NSH_SYMTAB_ARRAYNAME="g_symtab"
CONFIG_NSH_SYMTAB_COUNTNAME="g_nsymbols"
CONFIG_OPENAMP=y
CONFIG_PATH_INITIAL="/system/bin"
CONFIG_RAM_SIZE=33554432
CONFIG_RAM_START=0x80000000

View file

@ -35,11 +35,10 @@ CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x10001000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=28
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=8
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x1000
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_ELF=y
CONFIG_EXAMPLES_HELLO=m
CONFIG_FAT_LCNAMES=y
@ -52,8 +51,9 @@ CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_BUFSIZE=1534
CONFIG_IOB_NBUFFERS=64
CONFIG_IOB_THROTTLE=8
CONFIG_LIBC_ENVPATH=y
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIBC_PERROR_STDOUT=y
@ -77,7 +77,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=32
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=32768
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -91,6 +93,7 @@ CONFIG_NSH_READLINE=y
CONFIG_NSH_SYMTAB=y
CONFIG_NSH_SYMTAB_ARRAYNAME="g_symtab"
CONFIG_NSH_SYMTAB_COUNTNAME="g_nsymbols"
CONFIG_OPENAMP=y
CONFIG_PATH_INITIAL="/system/bin"
CONFIG_RAM_SIZE=33554432
CONFIG_RAM_START=0x80000000

View file

@ -35,11 +35,10 @@ CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_ZERO=y
CONFIG_DRIVERS_VIRTIO=y
CONFIG_DRIVERS_VIRTIO_BLK=y
CONFIG_DRIVERS_VIRTIO_MMIO_BASE=0x10001000
CONFIG_DRIVERS_VIRTIO_MMIO_IRQ=28
CONFIG_DRIVERS_VIRTIO_MMIO_NUM=8
CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE=0x1000
CONFIG_DRIVERS_VIRTIO_MMIO=y
CONFIG_DRIVERS_VIRTIO_NET=y
CONFIG_DRIVERS_VIRTIO_RNG=y
CONFIG_DRIVERS_VIRTIO_SERIAL=y
CONFIG_ELF=y
CONFIG_EXAMPLES_HELLO=m
CONFIG_FAT_LCNAMES=y
@ -52,8 +51,9 @@ CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_IOB_BUFSIZE=1514
CONFIG_IOB_BUFSIZE=1530
CONFIG_IOB_NBUFFERS=64
CONFIG_IOB_THROTTLE=8
CONFIG_LIBC_ENVPATH=y
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIBC_PERROR_STDOUT=y
@ -77,7 +77,9 @@ CONFIG_NET_BROADCAST=y
CONFIG_NET_ETH_PKTSIZE=1514
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LL_GUARDSIZE=28
CONFIG_NET_MAX_LISTENPORTS=8
CONFIG_NET_RECV_BUFSIZE=32768
CONFIG_NET_STATISTICS=y
CONFIG_NET_TCP=y
CONFIG_NET_TCPBACKLOG=y
@ -91,6 +93,7 @@ CONFIG_NSH_READLINE=y
CONFIG_NSH_SYMTAB=y
CONFIG_NSH_SYMTAB_ARRAYNAME="g_symtab"
CONFIG_NSH_SYMTAB_COUNTNAME="g_nsymbols"
CONFIG_OPENAMP=y
CONFIG_PATH_INITIAL="/system/bin"
CONFIG_RAM_SIZE=33554432
CONFIG_RAM_START=0x80000000

View file

@ -30,9 +30,46 @@
#include <errno.h>
#include <nuttx/board.h>
#include <nuttx/virtio/virtio-mmio.h>
#include <sys/mount.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define QEMU_VIRTIO_MMIO_BASE 0x10001000
#define QEMU_VIRTIO_MMIO_REGSIZE 0x1000
#ifdef CONFIG_ARCH_USE_S_MODE
# define QEMU_VIRTIO_MMIO_IRQ 26
#else
# define QEMU_VIRTIO_MMIO_IRQ 28
#endif
#define QEMU_VIRTIO_MMIO_NUM 4
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: qemu_virtio_register_mmio_devices
****************************************************************************/
#ifdef CONFIG_DRIVERS_VIRTIO_MMIO
static void qemu_virtio_register_mmio_devices(void)
{
uintptr_t virtio = (uintptr_t)QEMU_VIRTIO_MMIO_BASE;
size_t size = QEMU_VIRTIO_MMIO_REGSIZE;
int irq = QEMU_VIRTIO_MMIO_IRQ;
int i;
for (i = 0; i < QEMU_VIRTIO_MMIO_NUM; i++)
{
virtio_register_mmio_device((FAR void *)(virtio + size * i), irq + i);
}
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -75,6 +112,10 @@ int board_app_initialize(uintptr_t arg)
#endif
#ifdef CONFIG_DRIVERS_VIRTIO_MMIO
qemu_virtio_register_mmio_devices();
#endif
return OK;
#endif
}

View file

@ -44,6 +44,7 @@
#include <nuttx/syslog/syslog.h>
#include <nuttx/syslog/syslog_console.h>
#include <nuttx/usrsock/usrsock_rpmsg.h>
#include <nuttx/virtio/virtio.h>
/****************************************************************************
* Public Functions
@ -203,4 +204,8 @@ void drivers_initialize(void)
#ifdef CONFIG_MTD_LOOP
mtd_loop_register();
#endif
#ifdef CONFIG_DRIVERS_VIRTIO
virtio_register_drivers();
#endif
}

View file

@ -0,0 +1,47 @@
# ##############################################################################
# drivers/virtio/CMakeLists.txt
#
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
# license agreements. See the NOTICE file distributed with this work for
# additional information regarding copyright ownership. The ASF licenses this
# file to you under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# ##############################################################################
set(SRCS)
if(CONFIG_DRIVERS_VIRTIO)
list(APPEND SRCS virtio.c)
endif()
if(CONFIG_DRIVERS_VIRTIO_MMIO)
list(APPEND SRCS virtio-mmio.c)
endif()
if(CONFIG_DRIVERS_VIRTIO_BLK)
list(APPEND SRCS virtio-blk.c)
endif()
if(CONFIG_DRIVERS_VIRTIO_NET)
list(APPEND SRCS virtio-net.c)
endif()
if(CONFIG_DRIVERS_VIRTIO_RNG)
list(APPEND SRCS virtio-rng.c)
endif()
if(CONFIG_DRIVERS_VIRTIO_SERIAL)
list(APPEND SRCS virtio-serial.c)
endif()
target_sources(drivers PRIVATE ${SRCS})
target_include_directories(drivers PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

View file

@ -5,50 +5,59 @@
menuconfig DRIVERS_VIRTIO
bool "Virtio Device Support"
depends on !OPENAMP
depends on OPENAMP
default n
if DRIVERS_VIRTIO
menuconfig DRIVERS_VIRTIO_MMIO_NUM
int "The number of virtio mmio devices"
default 1
config DRIVERS_VIRTIO_MMIO
bool "Virtio MMIO Device Support"
default n
if DRIVERS_VIRTIO_MMIO_NUM != 0
menuconfig DRIVERS_VIRTIO_MMIO_BASE
hex "Virtio-mmio base address"
menuconfig DRIVERS_VIRTIO_MMIO_REGSIZE
hex "Virtio-mmio register size"
config DRIVERS_VIRTIO_MMIO_QUEUE_LEN
int "Virtio MMIO Virtio Queue Length"
default 0
range 0 1024
depends on DRIVERS_VIRTIO_MMIO
---help---
If this value equals to 0, use the max queue length get from
mmio register.
menuconfig DRIVERS_VIRTIO_MMIO_IRQ
int "Virtio-mmio irq number"
endif
menuconfig DRIVERS_VIRTIO_NET
bool "Virtio network support"
default n
depends on DRIVERS_VIRTIO_MMIO_NUM > 0
select ARCH_HAVE_NETDEV_STATISTICS
if DRIVERS_VIRTIO_NET
config DRIVERS_VIRTIO_NET_QUEUE_LEN
int "Queue length"
default 16
endif
menuconfig DRIVERS_VIRTIO_BLK
config DRIVERS_VIRTIO_BLK
bool "Virtio block support"
depends on !DISABLE_MOUNTPOINT
default n
depends on DRIVERS_VIRTIO_MMIO_NUM > 0
endif
if DRIVERS_VIRTIO_BLK
config DRIVERS_VIRTIO_BLK_QUEUE_LEN
int "Queue length"
default 16
config DRIVERS_VIRTIO_NET
bool "Virtio network support"
depends on NETDEVICES
default n
select ARCH_HAVE_NETDEV_STATISTICS
select NETDEV_LATEINIT
config DRIVERS_VIRTIO_NET_BUFNUM
int "Virtio network driver buffer number"
default 0
depends on DRIVERS_VIRTIO_NET
---help---
The buffer number in each virtqueue. (We have 2 virtqueues.)
If this value equals to 0, use CONFIG_IOB_NBUFFERS / 4 for each.
Normally we get just a little improvement for >8 buffers, and very little for >32.
config DRIVERS_VIRTIO_RNG
bool "Virtio rng support"
default n
select ARCH_HAVE_RNG
config DRIVERS_VIRTIO_SERIAL
bool "Virtio serial support"
depends on SERIAL
default n
select SERIAL_RXDMA
select SERIAL_TXDMA
if DRIVERS_VIRTIO_SERIAL
config DRIVERS_VIRTIO_SERIAL_BUFSIZE
int "Virtio serial driver buffer size"
default 256
endif
endif # DRIVERS_VIRTIO

View file

@ -21,15 +21,27 @@
# Include virtio support
ifeq ($(CONFIG_DRIVERS_VIRTIO),y)
CSRCS += virtio.c
endif
ifeq ($(CONFIG_DRIVERS_VIRTIO_MMIO),y)
CSRCS += virtio-mmio.c
endif
ifeq ($(CONFIG_DRIVERS_VIRTIO_NET),y)
CSRCS += virtio-mmio-net.c
ifeq ($(CONFIG_DRIVERS_VIRTIO_BLK),y)
CSRCS += virtio-blk.c
endif
ifeq ($(CONFIG_DRIVERS_VIRTIO_BLK),y)
CSRCS += virtio-mmio-blk.c
ifeq ($(CONFIG_DRIVERS_VIRTIO_NET),y)
CSRCS += virtio-net.c
endif
ifeq ($(CONFIG_DRIVERS_VIRTIO_RNG),y)
CSRCS += virtio-rng.c
endif
ifeq ($(CONFIG_DRIVERS_VIRTIO_SERIAL),y)
CSRCS += virtio-serial.c
endif
# Include build support

586
drivers/virtio/virtio-blk.c Normal file
View file

@ -0,0 +1,586 @@
/****************************************************************************
* drivers/virtio/virtio-blk.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <debug.h>
#include <errno.h>
#include <stdio.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <nuttx/virtio/virtio.h>
#include "virtio-blk.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define VIRTIO_BLK_REQ_HEADER_SIZE sizeof(struct virtio_blk_req_s)
#define VIRTIO_BLK_RESP_HEADER_SIZE sizeof(struct virtio_blk_resp_s)
/* Block request type */
#define VIRTIO_BLK_T_IN 0 /* READ */
#define VIRTIO_BLK_T_OUT 1 /* WRITE */
#define VIRTIO_BLK_T_FLUSH 4 /* FLUSH */
/* Block request return status */
#define VIRTIO_BLK_S_OK 0
#define VIRTIO_BLK_S_IOERR 1
#define VIRTIO_BLK_S_UNSUPP 2
/* Block device sector size */
#define VIRTIO_BLK_SECTOR_SIZE 512
/****************************************************************************
* Private Types
****************************************************************************/
/* Block request out header */
begin_packed_struct struct virtio_blk_req_s
{
uint32_t type;
uint32_t reserved;
uint64_t sector;
} end_packed_struct;
/* Block request in header */
begin_packed_struct struct virtio_blk_resp_s
{
uint8_t status;
} end_packed_struct;
begin_packed_struct struct virtio_blk_config_s
{
uint64_t capacity;
uint32_t size_max;
uint32_t seg_max;
uint16_t cylinders; /* block geometry */
uint8_t heads; /* block geometry */
uint8_t sectors; /* block geometry */
uint32_t blk_size;
uint8_t physical_block_exp;
uint8_t alignment_offset;
uint16_t min_io_size;
uint32_t opt_io_size;
uint8_t writeback;
uint8_t unused0;
uint16_t num_queues;
uint32_t max_discard_sectors;
uint32_t max_discard_seg;
uint32_t discard_sector_alignment;
uint32_t max_write_zeroes_sectors;
uint32_t max_write_zeroes_seg;
uint8_t write_zeroes_may_unmap;
uint8_t unused1[3];
uint32_t max_secure_erase_sectors;
uint32_t max_secure_erase_seg;
uint32_t secure_erase_sector_alignment;
} end_packed_struct;
struct virtio_blk_priv_s
{
FAR struct virtio_device *vdev; /* Virtio deivce */
FAR struct virtio_blk_req_s *req; /* Virtio block out header */
FAR struct virtio_blk_resp_s *resp; /* Virtio block in header */
mutex_t lock; /* Lock */
uint64_t nsectors; /* Sectore numbers */
char name[NAME_MAX]; /* Device name */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* BLK block_operations functions and they helper function */
static ssize_t virtio_blk_rdwr(FAR struct virtio_blk_priv_s *priv,
FAR void *buffer, blkcnt_t startsector,
unsigned int nsectors, bool write);
static int virtio_blk_open(FAR struct inode *inode);
static int virtio_blk_close(FAR struct inode *inode);
static ssize_t virtio_blk_read(FAR struct inode *inode,
FAR unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors);
static ssize_t virtio_blk_write(FAR struct inode *inode,
FAR const unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors);
static int virtio_blk_geometry(FAR struct inode *inode,
FAR struct geometry *geometry);
static int virtio_blk_ioctl(FAR struct inode *inode, int cmd,
unsigned long arg);
static int virtio_blk_flush(FAR struct virtio_blk_priv_s *priv);
/* Other functions */
static int virtio_blk_init(FAR struct virtio_blk_priv_s *priv,
FAR struct virtio_device *vdev);
static void virtio_blk_uninit(FAR struct virtio_blk_priv_s *priv);
static void virtio_blk_done(FAR struct virtqueue *vq);
static int virtio_blk_probe(FAR struct virtio_device *vdev);
static void virtio_blk_remove(FAR struct virtio_device *vdev);
/****************************************************************************
* Private Data
****************************************************************************/
static struct virtio_driver g_virtio_blk_driver =
{
LIST_INITIAL_VALUE(g_virtio_blk_driver.node), /* node */
VIRTIO_ID_BLOCK, /* device id */
virtio_blk_probe, /* probe */
virtio_blk_remove, /* remove */
};
static const struct block_operations g_virtio_blk_bops =
{
virtio_blk_open, /* open */
virtio_blk_close, /* close */
virtio_blk_read, /* read */
virtio_blk_write, /* write */
virtio_blk_geometry, /* geometry */
virtio_blk_ioctl /* ioctl */
};
static int g_virtio_blk_idx = 0;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: virtio_blk_rdwr
*
* Description:
* Common function for read and write
*
****************************************************************************/
static ssize_t virtio_blk_rdwr(FAR struct virtio_blk_priv_s *priv,
FAR void *buffer, blkcnt_t startsector,
unsigned int nsectors, bool write)
{
FAR struct virtio_device *vdev = priv->vdev;
FAR struct virtqueue *vq = vdev->vrings_info[0].vq;
FAR struct virtqueue_buf vb[3];
sem_t respsem;
ssize_t ret;
int readnum;
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
nxsem_init(&respsem, 0, 0);
/* Build the block request */
priv->req->type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
priv->req->reserved = 0;
priv->req->sector = startsector;
priv->resp->status = VIRTIO_BLK_S_IOERR;
/* Fill the virtqueue buffer:
* Buffer 0: the block out header;
* Buffer 1: the read/write buffer;
* Buffer 2: the block in header, return the status.
*/
vb[0].buf = priv->req;
vb[0].len = VIRTIO_BLK_REQ_HEADER_SIZE;
vb[1].buf = buffer;
vb[1].len = nsectors * VIRTIO_BLK_SECTOR_SIZE;
vb[2].buf = priv->resp;
vb[2].len = VIRTIO_BLK_RESP_HEADER_SIZE;
readnum = write ? 2 : 1;
ret = virtqueue_add_buffer(vq, vb, readnum, 3 - readnum, &respsem);
if (ret < 0)
{
vrterr("virtqueue_add_buffer failed, ret=%zd\n", ret);
goto err;
}
virtqueue_kick(vq);
/* Wait for the request completion */
nxsem_wait_uninterruptible(&respsem);
if (priv->resp->status != VIRTIO_BLK_S_OK)
{
vrterr("%s Error\n", write ? "Write" : "Read");
ret = -EIO;
}
err:
nxmutex_unlock(&priv->lock);
return ret >= 0 ? nsectors : ret;
}
/****************************************************************************
* Name: virtio_blk_open
*
* Description: Open the block device
*
****************************************************************************/
static int virtio_blk_open(FAR struct inode *inode)
{
DEBUGASSERT(inode && inode->i_private);
return OK;
}
/****************************************************************************
* Name: virtio_blk_close
*
* Description: close the block device
*
****************************************************************************/
static int virtio_blk_close(FAR struct inode *inode)
{
DEBUGASSERT(inode && inode->i_private);
return OK;
}
/****************************************************************************
* Name: virtio_blk_read
*
* Description:
* Read the specified number of sectors from the read-ahead buffer or from
* the physical device.
*
****************************************************************************/
static ssize_t virtio_blk_read(FAR struct inode *inode,
FAR unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors)
{
FAR struct virtio_blk_priv_s *priv;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct virtio_blk_priv_s *)inode->i_private;
return virtio_blk_rdwr(priv, buffer, startsector, nsectors, false);
}
/****************************************************************************
* Name: virtio_blk_write
*
* Description:
* Write the specified number of sectors to the write buffer or to the
* physical device.
*
****************************************************************************/
static ssize_t virtio_blk_write(FAR struct inode *inode,
FAR const unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors)
{
FAR struct virtio_blk_priv_s *priv;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct virtio_blk_priv_s *)inode->i_private;
return virtio_blk_rdwr(priv, (FAR void *)buffer, startsector, nsectors,
true);
}
/****************************************************************************
* Name: virtio_blk_geometry
*
* Description: Return device geometry
*
****************************************************************************/
static int virtio_blk_geometry(FAR struct inode *inode,
FAR struct geometry *geometry)
{
FAR struct virtio_blk_priv_s *priv;
int ret = -EINVAL;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct virtio_blk_priv_s *)inode->i_private;
if (geometry)
{
geometry->geo_available = true;
geometry->geo_mediachanged = false;
geometry->geo_writeenabled = true;
geometry->geo_nsectors = priv->nsectors;
geometry->geo_sectorsize = VIRTIO_BLK_SECTOR_SIZE;
ret = OK;
}
return ret;
}
/****************************************************************************
* Name: virtio_blk_ioctl
****************************************************************************/
static int virtio_blk_flush(FAR struct virtio_blk_priv_s *priv)
{
FAR struct virtio_device *vdev = priv->vdev;
FAR struct virtqueue *vq = vdev->vrings_info[0].vq;
FAR struct virtqueue_buf vb[2];
sem_t respsem;
int ret;
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
nxsem_init(&respsem, 0, 0);
/* Build the block request */
priv->req->type = VIRTIO_BLK_T_FLUSH;
priv->req->reserved = 0;
priv->req->sector = 0;
priv->resp->status = VIRTIO_BLK_S_IOERR;
vb[0].buf = priv->req;
vb[0].len = VIRTIO_BLK_REQ_HEADER_SIZE;
vb[1].buf = priv->resp;
vb[1].len = VIRTIO_BLK_RESP_HEADER_SIZE;
ret = virtqueue_add_buffer(vq, vb, 1, 1, &respsem);
if (ret < 0)
{
goto err;
}
virtqueue_kick(vq);
/* Wait for the request completion */
nxsem_wait_uninterruptible(&respsem);
if (priv->resp->status != VIRTIO_BLK_S_OK)
{
vrterr("Flush Error\n");
ret = -EIO;
}
err:
nxmutex_unlock(&priv->lock);
return ret;
}
/****************************************************************************
* Name: virtio_blk_ioctl
****************************************************************************/
static int virtio_blk_ioctl(FAR struct inode *inode, int cmd,
unsigned long arg)
{
FAR struct virtio_blk_priv_s *priv;
int ret = -ENOTTY;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct virtio_blk_priv_s *)inode->i_private;
switch (cmd)
{
case BIOC_FLUSH:
ret = virtio_blk_flush(priv);
break;
}
return ret;
}
/****************************************************************************
* Name: virtio_blk_done
****************************************************************************/
static void virtio_blk_done(FAR struct virtqueue *vq)
{
FAR sem_t *respsem;
respsem = virtqueue_get_buffer(vq, NULL, NULL);
if (respsem != NULL)
{
nxsem_post(respsem);
}
}
/****************************************************************************
* Name: virtio_blk_init
****************************************************************************/
static int virtio_blk_init(FAR struct virtio_blk_priv_s *priv,
FAR struct virtio_device *vdev)
{
FAR const char *vqname[1];
vq_callback callback[1];
int ret;
priv->vdev = vdev;
vdev->priv = priv;
nxmutex_init(&priv->lock);
/* Alloc the request and in header from tansport layer */
priv->req = virtio_alloc_buf(vdev, sizeof(*priv->req), 16);
if (priv->req == NULL)
{
ret = -ENOMEM;
goto err_with_lock;
}
priv->resp = virtio_alloc_buf(vdev, sizeof(*priv->resp), 16);
if (priv->resp == NULL)
{
ret = -ENOMEM;
goto err_with_req;
}
/* Initialize the virtio device */
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER);
virtio_set_features(vdev, 0);
virtio_set_status(vdev, VIRTIO_CONFIG_FEATURES_OK);
vqname[0] = "virtio_blk_vq";
callback[0] = virtio_blk_done;
ret = virtio_create_virtqueues(vdev, 0, 1, vqname, callback);
if (ret < 0)
{
vrterr("virtio_device_create_virtqueue failed, ret=%d\n", ret);
goto err_with_resp;
}
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
virtqueue_enable_cb(vdev->vrings_info[0].vq);
return OK;
err_with_resp:
virtio_free_buf(vdev, priv->resp);
err_with_req:
virtio_free_buf(vdev, priv->req);
err_with_lock:
nxmutex_destroy(&priv->lock);
return ret;
}
/****************************************************************************
* Name: virtio_blk_uninit
****************************************************************************/
static void virtio_blk_uninit(FAR struct virtio_blk_priv_s *priv)
{
FAR struct virtio_device *vdev = priv->vdev;
virtio_reset_device(vdev);
virtio_delete_virtqueues(vdev);
virtio_free_buf(vdev, priv->resp);
virtio_free_buf(vdev, priv->req);
nxmutex_destroy(&priv->lock);
}
/****************************************************************************
* Name: virtio_blk_probe
****************************************************************************/
static int virtio_blk_probe(FAR struct virtio_device *vdev)
{
FAR struct virtio_blk_priv_s *priv;
int ret;
/* Alloc the virtio block driver private data */
priv = kmm_zalloc(sizeof(*priv));
if (priv == NULL)
{
vrterr("No enough memory\n");
return -ENOMEM;
}
/* Init the virtio block driver */
ret = virtio_blk_init(priv, vdev);
if (ret < 0)
{
vrterr("virtio_blk_init failed, ret=%d\n", ret);
goto err_with_priv;
}
/* Read the block config and save the capacity to nsectors */
virtio_read_config_member(priv->vdev, struct virtio_blk_config_s, capacity,
&priv->nsectors);
vrtinfo("Virio blk capacity=%" PRIu64 " sectors\n", priv->nsectors);
/* Register block driver */
snprintf(priv->name, NAME_MAX, "/dev/virtblk%d", g_virtio_blk_idx);
ret = register_blockdriver(priv->name, &g_virtio_blk_bops, 0660, priv);
if (ret < 0)
{
vrterr("Register block driver failed, ret=%d\n", ret);
goto err_with_init;
}
g_virtio_blk_idx++;
return ret;
err_with_init:
virtio_blk_uninit(priv);
err_with_priv:
kmm_free(priv);
return ret;
}
/****************************************************************************
* Name: virtio_blk_remove
****************************************************************************/
static void virtio_blk_remove(FAR struct virtio_device *vdev)
{
FAR struct virtio_blk_priv_s *priv = vdev->priv;
unregister_driver(priv->name);
virtio_blk_uninit(priv);
kmm_free(priv);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: virtio_register_blk_driver
****************************************************************************/
int virtio_register_blk_driver(void)
{
return virtio_register_driver(&g_virtio_blk_driver);
}

View file

@ -1,5 +1,5 @@
/****************************************************************************
* drivers/virtio/virtio-mmio-blk.h
* drivers/virtio/virtio-blk.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@ -18,8 +18,8 @@
*
****************************************************************************/
#ifndef __DRIVERS_VIRTIO_VIRTIO_MMIO_BLK_H
#define __DRIVERS_VIRTIO_VIRTIO_MMIO_BLK_H
#ifndef __DRIVERS_VIRTIO_VIRTIO_BLK_H
#define __DRIVERS_VIRTIO_VIRTIO_BLK_H
/****************************************************************************
* Included Files
@ -49,15 +49,7 @@ extern "C"
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: virtio_mmio_blk_init
*
* Description:
* Called from virtio-mmio.c to initialize virtblk
*
****************************************************************************/
int virtio_mmio_blk_init(FAR struct virtio_mmio_regs *regs, uint32_t intid);
int virtio_register_blk_driver(void);
#undef EXTERN
#ifdef __cplusplus
@ -65,4 +57,5 @@ int virtio_mmio_blk_init(FAR struct virtio_mmio_regs *regs, uint32_t intid);
#endif
#endif /* CONFIG_DRIVERS_VIRTIO_BLK */
#endif /* __DRIVERS_VIRTIO_VIRTIO_MMIO_BLK_H */
#endif /* __DRIVERS_VIRTIO_VIRTIO_BLK_H */

View file

@ -1,524 +0,0 @@
/****************************************************************************
* drivers/virtio/virtio-mmio-blk.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/virtio/virtio-mmio.h>
#include "virtio-mmio-blk.h"
#ifdef CONFIG_DRIVERS_VIRTIO_BLK
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define VIRTIO_BLK_REQ_HEADER_SIZE 16
#define VIRTIO_BLK_REQ_FOOTER_SIZE 1
#define VIRTIO_BLK_IN 0 /* read */
#define VIRTIO_BLK_OUT 1 /* write */
#define VIRTIO_BLK_Q 0
#define VIRTIO_BLK_SECTOR_SIZE 512
/* VIRTIO_BLK_NINTERFACES determines the number of
* physical interfaces that will be supported.
*/
#ifndef VIRTIO_BLK_NINTERFACES
# define VIRTIO_BLK_NINTERFACES 1
#endif
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Block driver methods *****************************************************/
static int virtblk_open(FAR struct inode *inode);
static int virtblk_close(FAR struct inode *inode);
static ssize_t virtblk_read(FAR struct inode *inode,
FAR unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors);
static ssize_t virtblk_write(FAR struct inode *inode,
FAR const unsigned char *buffer,
blkcnt_t startsector,
unsigned int nsectors);
static int virtblk_geometry(FAR struct inode *inode,
FAR struct geometry *geometry);
static int virtblk_ioctl(FAR struct inode *inode, int cmd,
unsigned long arg);
static int virtblk_interrupt(int irq, FAR void *context, FAR void *arg);
/****************************************************************************
* Private Types
****************************************************************************/
struct virtio_blk_req
{
uint32_t type;
uint32_t reserved;
uint64_t sector;
uint8_t status;
uint8_t pad[3];
uint32_t descriptor;
};
struct virtblk_driver_s
{
int irq;
FAR struct virtio_mmio_regs *regs; /* virtio_mmio registers */
FAR struct virtqueue *txq; /* TX queue */
uint64_t nsectors; /* number of sectors on device */
sem_t req_sem; /* semaphore for virtio request */
};
struct virtio_blk_config
{
uint64_t capacity;
uint32_t size_max;
uint32_t seg_max;
uint32_t chs;
uint32_t blk_size;
uint64_t topology;
uint8_t writeback;
};
/* Driver state structure */
static struct virtblk_driver_s g_virtblk[VIRTIO_BLK_NINTERFACES];
static uint32_t g_virtblk_ninterfaces = 0;
/****************************************************************************
* Private Data
****************************************************************************/
static const struct block_operations g_virtblk_bops =
{
virtblk_open, /* open */
virtblk_close, /* close */
virtblk_read, /* read */
virtblk_write, /* write */
virtblk_geometry, /* geometry */
virtblk_ioctl /* ioctl */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: virtblk_rw_common
*
* Description:
* Common function for read and write
*
****************************************************************************/
static ssize_t virtblk_rw_common(FAR struct virtblk_driver_s *priv,
FAR void *buffer,
blkcnt_t startsector,
unsigned int nsectors,
bool write)
{
struct virtio_blk_req req[2];
uint16_t idx;
uint32_t d1;
uint32_t d2;
uint32_t d3;
memset(req, 0, sizeof(req));
idx = priv->txq->avail->idx;
if (write)
{
req[0].type = VIRTIO_BLK_OUT;
}
else
{
req[0].type = VIRTIO_BLK_IN;
}
req[0].sector = startsector;
/* Allocate descriptor for header */
d1 = virtq_alloc_desc(priv->txq, idx,
(FAR void *)&req[0]);
d2 = virtq_alloc_desc(priv->txq, idx + 1,
(FAR void *)buffer);
DEBUGASSERT(d1 != d2);
req[0].descriptor = d1;
d3 = virtq_alloc_desc(priv->txq, idx + 2,
(FAR void *)&req[1]);
DEBUGASSERT(d2 != d3);
/* Set up the descriptor for d1 (header) */
priv->txq->desc[d1].len = VIRTIO_BLK_REQ_HEADER_SIZE;
priv->txq->desc[d1].flags = VIRTQ_DESC_F_NEXT;
priv->txq->desc[d1].next = d2;
/* Set up the descriptor for d2 (sector buffer) */
priv->txq->desc[d2].len = VIRTIO_BLK_SECTOR_SIZE * nsectors;
priv->txq->desc[d2].flags = VIRTQ_DESC_F_NEXT;
if (!write)
{
/* Make the sector buffer writable for read operation */
priv->txq->desc[d2].flags |= VIRTQ_DESC_F_WRITE;
}
priv->txq->desc[d2].next = d3;
/* Set up the descriptor for d3 (status) */
priv->txq->desc[d3].len = 1;
priv->txq->desc[d3].flags = VIRTQ_DESC_F_WRITE;
priv->txq->desc[d3].next = 0;
/* Set the first descriptor to the avail->ring */
priv->txq->avail->ring[d1] = d1;
/* Increment the avail->idx for each three descriptors */
virtio_mb();
priv->txq->avail->idx += 1;
/* Send request */
virtio_putreg32(VIRTIO_BLK_Q, &priv->regs->queue_notify);
finfo("*** finish updating queue_notify\n");
/* Wait for the request completion */
nxsem_wait_uninterruptible(&priv->req_sem);
/* On success, return the number of blocks read */
return nsectors;
}
/****************************************************************************
* Name: virtblk_open
*
* Description: Open the block device
*
****************************************************************************/
static int virtblk_open(FAR struct inode *inode)
{
finfo("Entry\n");
DEBUGASSERT(inode && inode->i_private);
return OK;
}
/****************************************************************************
* Name: virtblk_close
*
* Description: close the block device
*
****************************************************************************/
static int virtblk_close(FAR struct inode *inode)
{
finfo("Entry\n");
DEBUGASSERT(inode && inode->i_private);
return OK;
}
/****************************************************************************
* Name: virtblk_read
*
* Description:
* Read the specified number of sectors from the read-ahead buffer or from
* the physical device.
*
****************************************************************************/
static ssize_t virtblk_read(FAR struct inode *inode,
FAR unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors)
{
FAR struct virtblk_driver_s *priv;
finfo("Entry: nsectors=%d \n", nsectors);
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct virtblk_driver_s *)inode->i_private;
return virtblk_rw_common(priv,
buffer,
startsector, nsectors, false);
}
/****************************************************************************
* Name: virtblk_write
*
* Description:
* Write the specified number of sectors to the write buffer or to the
* physical device.
*
****************************************************************************/
static ssize_t virtblk_write(FAR struct inode *inode,
FAR const unsigned char *buffer,
blkcnt_t startsector, unsigned int nsectors)
{
FAR struct virtblk_driver_s *priv;
finfo("Entry: nsectors=%d \n", nsectors);
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct virtblk_driver_s *)inode->i_private;
return virtblk_rw_common(priv,
(FAR void *)buffer,
startsector, nsectors, true);
}
/****************************************************************************
* Name: virtblk_geometry
*
* Description: Return device geometry
*
****************************************************************************/
static int virtblk_geometry(FAR struct inode *inode,
FAR struct geometry *geometry)
{
FAR struct virtblk_driver_s *priv;
int ret = -EINVAL;
finfo("Entry\n");
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct virtblk_driver_s *)inode->i_private;
if (geometry)
{
geometry->geo_available = true;
geometry->geo_mediachanged = false;
geometry->geo_writeenabled = true;
geometry->geo_nsectors = priv->nsectors;
geometry->geo_sectorsize = VIRTIO_BLK_SECTOR_SIZE;
ret = OK;
}
return ret;
}
/****************************************************************************
* Name: virtblk_ioctl
*
* Description: Return device geometry
*
****************************************************************************/
static int virtblk_ioctl(FAR struct inode *inode,
int cmd, unsigned long arg)
{
int ret = -ENOTTY;
finfo("Entry\n");
DEBUGASSERT(inode && inode->i_private);
return ret;
}
/****************************************************************************
* Name: virtblk_interrupt
*
* Description:
* Hardware interrupt handler
*
* Input Parameters:
* irq - Number of the IRQ that generated the interrupt
* context - Interrupt register state save info (architecture-specific)
*
* Returned Value:
* OK on success
*
* Assumptions:
* Runs in the context of a the Ethernet interrupt handler. Local
* interrupts are disabled by the interrupt logic.
*
****************************************************************************/
static int virtblk_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct virtblk_driver_s *priv = (FAR struct virtblk_driver_s *)arg;
uint32_t stat;
DEBUGASSERT(priv != NULL);
/* Get and clear interrupt status bits */
stat = virtio_getreg32(&priv->regs->interrupt_status);
virtio_putreg32(stat, &priv->regs->interrupt_ack);
finfo("+++ called (stat=0x%" PRIx32 ")\n", stat);
nxsem_post(&priv->req_sem);
return OK;
}
/****************************************************************************
* Name: virtblk_initialize
*
* Description:
* Initialize the virt-blk driver
*
* Input Parameters:
*
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
* Called early in initialization before multi-tasking is initiated.
*
****************************************************************************/
static int virtblk_initialize(FAR struct virtio_mmio_regs *regs, int irq)
{
FAR struct virtio_blk_config *blk_config;
FAR struct virtblk_driver_s *priv;
char devname[16];
int ret = -ENOMEM;
priv = &g_virtblk[g_virtblk_ninterfaces];
/* Initialize the driver structure */
memset(priv, 0, sizeof(struct virtblk_driver_s));
/* Check if a Ethernet chip is recognized at its I/O base */
/* Attach the IRQ to the driver */
priv->irq = irq;
if (irq_attach(priv->irq, virtblk_interrupt, priv))
{
/* We could not attach the ISR to the interrupt */
return -EAGAIN;
}
/* Setup virtio related */
priv->regs = regs;
priv->txq = virtq_create(CONFIG_DRIVERS_VIRTIO_BLK_QUEUE_LEN);
virtq_add_to_mmio_device(regs, priv->txq, VIRTIO_BLK_Q);
nxsem_init(&priv->req_sem, 0, 0);
/* Create a ramdisk device name */
snprintf(devname, 16, "/dev/virtblk%" PRId32, g_virtblk_ninterfaces);
blk_config = (struct virtio_blk_config *)regs->config;
finfo("capacity=%" PRId64 " (sectors) \n", blk_config->capacity);
/* Save the capacity to nsectors */
priv->nsectors = blk_config->capacity;
/* Register the device with the OS */
ret = register_blockdriver(devname, &g_virtblk_bops, 0, priv);
up_enable_irq(priv->irq);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: virtio_mmio_blk_init
*
* Description:
* Called from virtio-mmio.c to initialize virtblk
*
****************************************************************************/
int virtio_mmio_blk_init(FAR struct virtio_mmio_regs *regs, uint32_t irq)
{
int ret = OK;
/* TODO: feature negotiation */
/* Set STATUS_FEATURE_OK */
virtio_putreg32(virtio_getreg32(&regs->status) | VIRTIO_STATUS_FEATURES_OK,
&regs->status);
virtio_mb();
ret = virtblk_initialize(regs, irq);
if (OK != ret)
{
vrterr("error: virtblk_initialize() returned %d \n", ret);
return ret;
}
/* Set STATUS_FRIVER_OK */
virtio_putreg32(virtio_getreg32(&regs->status) | VIRTIO_STATUS_DRIVER_OK,
&regs->status);
virtio_mb();
g_virtblk_ninterfaces++;
return ret;
}
#endif /* CONFIG_DRIVERS_VIRTIO_BLK */

File diff suppressed because it is too large Load diff

View file

@ -24,142 +24,783 @@
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <debug.h>
#include <errno.h>
#include <stdint.h>
#include <sys/param.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/virtio/virtio.h>
#include <nuttx/virtio/virtio-mmio.h>
#ifdef CONFIG_DRIVERS_VIRTIO_NET
# include "virtio-mmio-net.h"
#endif
#ifdef CONFIG_DRIVERS_VIRTIO_BLK
# include "virtio-mmio-blk.h"
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define ptr_to_uint64(x) ((uint64_t)(uintptr_t)(x))
#define VIRITO_PAGE_SHIFT 12
#define VIRTIO_PAGE_SIZE (1 << VIRITO_PAGE_SHIFT)
#define VIRTIO_VRING_ALIGN VIRTIO_PAGE_SIZE
#define VIRTIO_MMIO_VERSION_1 1
/* Control registers */
/* Magic value ("virt" string) - Read Only */
#define VIRTIO_MMIO_MAGIC_VALUE 0x000
#define VIRTIO_MMIO_MAGIC_VALUE_STRING ('v' | ('i' << 8) | ('r' << 16) | ('t' << 24))
/* Virtio device version - Read Only */
#define VIRTIO_MMIO_VERSION 0x004
/* Virtio device ID - Read Only */
#define VIRTIO_MMIO_DEVICE_ID 0x008
/* Virtio vendor ID - Read Only */
#define VIRTIO_MMIO_VENDOR_ID 0x00c
/* Bitmask of the features supported by the device (host)
* (32 bits per set) - Read Only
*/
#define VIRTIO_MMIO_DEVICE_FEATURES 0x010
/* Device (host) features set selector - Write Only */
#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014
/* Bitmask of features activated by the driver (guest)
* (32 bits per set) - Write Only
*/
#define VIRTIO_MMIO_DRIVER_FEATURES 0x020
/* Activated features set selector - Write Only */
#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024
/* [VERSION 1 REGISTER] Guest page size */
#define VIRTIO_MMIO_PAGE_SIZE 0X028
/* Queue selector - Write Only */
#define VIRTIO_MMIO_QUEUE_SEL 0x030
/* Maximum size of the currently selected queue - Read Only */
#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034
/* Queue size for the currently selected queue - Write Only */
#define VIRTIO_MMIO_QUEUE_NUM 0x038
/* [VERSION 1 REGISTER] Used Ring alignment in the virtual queue */
#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c
/* [VERSION 1 REGISTER] Guest physical page number of the virtual queue
* Writing to this register notifies the device about location
*/
#define VIRTIO_MMIO_QUEUE_PFN 0x040
/* Ready bit for the currently selected queue - Read Write */
#define VIRTIO_MMIO_QUEUE_READY 0x044
/* Queue notifier - Write Only */
#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050
/* Interrupt status - Read Only */
#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060
/* Interrupt acknowledge - Write Only */
#define VIRTIO_MMIO_INTERRUPT_ACK 0x064
#define VIRTIO_MMIO_INTERRUPT_VRING (1 << 0)
#define VIRTIO_MMIO_INTERRUPT_CONFIG (1 << 1)
/* Device status register - Read Write */
#define VIRTIO_MMIO_STATUS 0x070
/* Selected queue's Descriptor Table address, 64 bits in two halves */
#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080
#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084
/* Selected queue's Available Ring address, 64 bits in two halves */
#define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090
#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094
/* Selected queue's Used Ring address, 64 bits in two halves */
#define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0
#define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4
/* Shared memory region id */
#define VIRTIO_MMIO_SHM_SEL 0x0ac
/* Shared memory region length, 64 bits in two halves */
#define VIRTIO_MMIO_SHM_LEN_LOW 0x0b0
#define VIRTIO_MMIO_SHM_LEN_HIGH 0x0b4
/* Shared memory region base address, 64 bits in two halves */
#define VIRTIO_MMIO_SHM_BASE_LOW 0x0b8
#define VIRTIO_MMIO_SHM_BASE_HIGH 0x0bc
/* Configuration atomicity value */
#define VIRTIO_MMIO_CONFIG_GENERATION 0x0fc
/* The config space is defined by each driver as
* the per-driver configuration space - Read Write
*/
#define VIRTIO_MMIO_CONFIG 0x100
/****************************************************************************
* Private Types
****************************************************************************/
struct virtio_mmio_device_s
{
struct virtio_device vdev; /* Virtio deivce */
struct metal_io_region shm_io; /* Share memory io region, virtqueue
* use this io.
*/
struct metal_io_region cfg_io; /* Config memory io region, used to
* read/write mmio register
*/
metal_phys_addr_t shm_phy; /* Share memory physical address */
metal_phys_addr_t cfg_phy; /* Config memory physical address */
int irq; /* The mmio interrupt number */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Helper functions */
static uint32_t virtio_mmio_get_queue_len(FAR struct metal_io_region *io,
int idx);
static int virtio_mmio_config_virtqueue(FAR struct metal_io_region *io,
FAR struct virtqueue *vq);
static int virtio_mmio_init_device(FAR struct virtio_mmio_device_s *vmdev,
FAR void *regs, int irq);
/* Virtio mmio dispatch functions */
static int
virtio_mmio_create_virtqueue(FAR struct virtio_mmio_device_s *vmdev,
unsigned int i, FAR const char *name,
vq_callback callback);
static int virtio_mmio_create_virtqueues(FAR struct virtio_device *vdev,
unsigned int flags,
unsigned int nvqs,
FAR const char *names[],
vq_callback callbacks[]);
static void virtio_mmio_delete_virtqueues(FAR struct virtio_device *vdev);
static void virtio_mmio_set_status(FAR struct virtio_device *vdev,
uint8_t status);
static uint8_t virtio_mmio_get_status(FAR struct virtio_device *vdev);
static void virtio_mmio_write_config(FAR struct virtio_device *vdev,
uint32_t offset, void *dst,
int length);
static void virtio_mmio_read_config(FAR struct virtio_device *vdev,
uint32_t offset, FAR void *dst,
int length);
static uint32_t virtio_mmio_get_features(FAR struct virtio_device *vdev);
static void virtio_mmio_set_features(FAR struct virtio_device *vdev,
uint32_t features);
static uint32_t virtio_mmio_negotiate_features(struct virtio_device *vdev,
uint32_t features);
static void virtio_mmio_reset_device(FAR struct virtio_device *vdev);
static void virtio_mmio_notify(FAR struct virtqueue *vq);
/* Interrupt */
static int virtio_mmio_interrupt(int irq, FAR void *context, FAR void *arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct virtio_dispatch g_virtio_mmio_dispatch =
{
virtio_mmio_create_virtqueues, /* create_virtqueues */
virtio_mmio_delete_virtqueues, /* delete_virtqueues */
virtio_mmio_get_status, /* get_status */
virtio_mmio_set_status, /* set_status */
virtio_mmio_get_features, /* get_features */
virtio_mmio_set_features, /* set_features */
virtio_mmio_negotiate_features, /* negotiate_features */
virtio_mmio_read_config, /* read_config */
virtio_mmio_write_config, /* write_config */
virtio_mmio_reset_device, /* reset_device */
virtio_mmio_notify, /* notify */
NULL, /* notify_wait */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: virtq_create
* Name: virtio_mmio_get_queue_len
****************************************************************************/
FAR struct virtqueue *virtq_create(uint32_t len)
static uint32_t virtio_mmio_get_queue_len(FAR struct metal_io_region *io,
int idx)
{
FAR struct virtqueue *virtq = (FAR struct virtqueue *)
kmm_zalloc(sizeof(struct virtqueue));
ASSERT(virtq);
uint32_t len;
virtq->len = len;
/* Select the queue we're interested in */
/* See: 2.6 Split Virtqueues */
metal_io_write32(io, VIRTIO_MMIO_QUEUE_SEL, idx);
len = metal_io_read32(io, VIRTIO_MMIO_QUEUE_NUM_MAX);
virtq->desc = (FAR struct virtqueue_desc *)
kmm_memalign(16, 16 * len);
ASSERT(virtq->desc);
if (CONFIG_DRIVERS_VIRTIO_MMIO_QUEUE_LEN != 0)
{
len = MIN(len, CONFIG_DRIVERS_VIRTIO_MMIO_QUEUE_LEN);
}
virtq->avail = (FAR struct virtqueue_avail *)
kmm_memalign(2, 6 + 2 * len);
ASSERT(virtq->avail);
virtq->used = (FAR struct virtqueue_used *)
kmm_memalign(4, 6 + 8 * len);
ASSERT(virtq->used);
virtq->desc_virt = (FAR void **)
kmm_memalign(16, sizeof(FAR void *) * len);
ASSERT(virtq->desc_virt);
vrtinfo("virtq=%p (len=%" PRId32 ")\n", virtq, len);
vrtinfo("virtq->desc=%p \n", virtq->desc);
vrtinfo("virtq->avail=%p \n", virtq->avail);
vrtinfo("virtq->used=%p \n", virtq->used);
virtq->avail->idx = 0;
virtq->used->idx = 0;
virtq->last_used_idx = 0;
return virtq;
return len;
}
/****************************************************************************
* Name: virtq_dev_init
* Name: virtio_mmio_config_virtqueue
****************************************************************************/
static int virtio_dev_init(uintptr_t virt, uint32_t irq)
static int virtio_mmio_config_virtqueue(FAR struct metal_io_region *io,
FAR struct virtqueue *vq)
{
FAR struct virtio_mmio_regs *regs = (FAR struct virtio_mmio_regs *)virt;
int ret = -ENODEV;
uint32_t val;
uint32_t version = vq->vq_dev->id.version;
uint64_t addr;
vrtinfo("examine virtio at 0x%" PRIxPTR "\n", virt);
/* Select the queue we're interested in */
val = virtio_getreg32(&regs->magic_value);
metal_io_write32(io, VIRTIO_MMIO_QUEUE_SEL, vq->vq_queue_index);
if (VIRTIO_MAGIC != val)
/* Queue shouldn't already be set up. */
if (metal_io_read32(io, version == VIRTIO_MMIO_VERSION_1 ?
VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY))
{
vrterr("error: virtio at 0x%" PRIxPTR
" had wrong magic value 0x%" PRIx32 "\n", virt, val);
vrterr("Virtio queue not ready\n");
return -ENOENT;
}
/* Activate the queue */
if (version == VIRTIO_MMIO_VERSION_1)
{
uint64_t pfn = (uintptr_t)vq->vq_ring.desc >> VIRITO_PAGE_SHIFT;
vrtinfo("Legacy, desc=%p, pfn=0x%" PRIx64 ", align=%d\n",
vq->vq_ring.desc, pfn, VIRTIO_PAGE_SIZE);
/* virtio-mmio v1 uses a 32bit QUEUE PFN. If we have something
* that doesn't fit in 32bit, fail the setup rather than
* pretending to be successful.
*/
if (pfn >> 32)
{
vrterr("Legacy virtio-mmio used RAM shoud not above 0x%llxGB\n",
0x1ull << (2 + VIRITO_PAGE_SHIFT));
}
metal_io_write32(io, VIRTIO_MMIO_QUEUE_NUM, vq->vq_nentries);
metal_io_write32(io, VIRTIO_MMIO_QUEUE_ALIGN, VIRTIO_PAGE_SIZE);
metal_io_write32(io, VIRTIO_MMIO_QUEUE_PFN, pfn);
}
else
{
metal_io_write32(io, VIRTIO_MMIO_QUEUE_NUM, vq->vq_nentries);
addr = (uint64_t)(uintptr_t)vq->vq_ring.desc;
metal_io_write32(io, VIRTIO_MMIO_QUEUE_DESC_LOW, addr);
metal_io_write32(io, VIRTIO_MMIO_QUEUE_DESC_HIGH, addr >> 32);
addr = (uint64_t)(uintptr_t)vq->vq_ring.avail;
metal_io_write32(io, VIRTIO_MMIO_QUEUE_AVAIL_LOW, addr);
metal_io_write32(io, VIRTIO_MMIO_QUEUE_AVAIL_HIGH, addr >> 32);
addr = (uint64_t)(uintptr_t)vq->vq_ring.used;
metal_io_write32(io, VIRTIO_MMIO_QUEUE_USED_LOW, addr);
metal_io_write32(io, VIRTIO_MMIO_QUEUE_USED_HIGH, addr >> 32);
metal_io_write32(io, VIRTIO_MMIO_QUEUE_READY, 1);
}
return OK;
}
/****************************************************************************
* Name: virtio_mmio_create_virtqueue
****************************************************************************/
static int
virtio_mmio_create_virtqueue(FAR struct virtio_mmio_device_s *vmdev,
unsigned int i, FAR const char *name,
vq_callback callback)
{
FAR struct virtio_device *vdev = &vmdev->vdev;
FAR struct virtio_vring_info *vrinfo;
FAR struct vring_alloc_info *vralloc;
FAR struct virtqueue *vq;
int vringsize;
int ret;
/* Alloc virtqueue and init the vring info and vring alloc info */
vrinfo = &vdev->vrings_info[i];
vralloc = &vrinfo->info;
vralloc->num_descs = virtio_mmio_get_queue_len(&vmdev->cfg_io, i);
vq = virtqueue_allocate(vralloc->num_descs);
if (vq == NULL)
{
vrterr("virtqueue_allocate failed\n");
return -ENOMEM;
}
/* Init the vring info and vring alloc info */
vrinfo->vq = vq;
vrinfo->io = &vmdev->shm_io;
vrinfo->notifyid = i;
vralloc->align = VIRTIO_VRING_ALIGN;
vringsize = vring_size(vralloc->num_descs, VIRTIO_VRING_ALIGN);
vralloc->vaddr = virtio_zalloc_buf(vdev, vringsize, VIRTIO_VRING_ALIGN);
if (vralloc->vaddr == NULL)
{
vrterr("vring alloc failed\n");
return -ENOMEM;
}
/* Initialize the virtio queue */
ret = virtqueue_create(vdev, i, name, vralloc, callback,
vdev->func->notify, vq);
if (ret < 0)
{
vrterr("virtqueue create error, ret=%d\n", ret);
return ret;
}
val = virtio_getreg32(&regs->version);
virtqueue_set_shmem_io(vq, &vmdev->shm_io);
if (VIRTIO_VERSION != val)
/* Set the mmio virtqueue register */
ret = virtio_mmio_config_virtqueue(&vmdev->cfg_io, vq);
if (ret < 0)
{
vrterr("error: virtio at 0x%" PRIxPTR
" had wrong version 0x%" PRIx32 "\n", virt, val);
vrterr("virtio_mmio_config_virtqueue failed, ret=%d\n", ret);
}
return ret;
}
/* Reset */
/****************************************************************************
* Name: virtio_mmio_create_virtqueues
****************************************************************************/
virtio_putreg32(0, &regs->status);
virtio_mb();
/* Set ack */
val = virtio_getreg32(&regs->status) | VIRTIO_STATUS_ACKNOWLEDGE;
virtio_putreg32(val, &regs->status);
virtio_mb();
/* Set driver */
val = virtio_getreg32(&regs->status) | VIRTIO_STATUS_DRIVER;
virtio_putreg32(val, &regs->status);
virtio_mb();
/* Check the device_id */
val = virtio_getreg32(&regs->device_id);
switch (val)
static int virtio_mmio_create_virtqueues(FAR struct virtio_device *vdev,
unsigned int flags,
unsigned int nvqs,
FAR const char *names[],
vq_callback callbacks[])
{
#ifdef CONFIG_DRIVERS_VIRTIO_NET
case VIRTIO_DEV_NET:
ret = virtio_mmio_net_init(regs, irq);
FAR struct virtio_mmio_device_s *vmdev =
(FAR struct virtio_mmio_device_s *)vdev;
unsigned int i;
int ret = OK;
/* Alloc vring info */
vdev->vrings_num = nvqs;
vdev->vrings_info = kmm_zalloc(sizeof(struct virtio_vring_info) * nvqs);
if (vdev->vrings_info == NULL)
{
vrterr("alloc vrings info failed\n");
return -ENOMEM;
}
/* Alloc and init the virtqueue */
for (i = 0; i < nvqs; i++)
{
ret = virtio_mmio_create_virtqueue(vmdev, i, names[i], callbacks[i]);
if (ret < 0)
{
goto err;
}
}
/* Finally, enable the interrupt */
up_enable_irq(vmdev->irq);
return ret;
err:
virtio_mmio_delete_virtqueues(vdev);
return ret;
}
/****************************************************************************
* Name: virtio_mmio_delete_virtqueues
****************************************************************************/
static void virtio_mmio_delete_virtqueues(FAR struct virtio_device *vdev)
{
FAR struct virtio_mmio_device_s *vmdev =
(FAR struct virtio_mmio_device_s *)vdev;
FAR struct virtio_vring_info *vrinfo;
unsigned int i;
/* Disable interrupt first */
up_disable_irq(vmdev->irq);
/* Free the memory */
if (vdev->vrings_info != NULL)
{
for (i = 0; i < vdev->vrings_num; i++)
{
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_QUEUE_SEL, i);
if (vdev->id.version == VIRTIO_MMIO_VERSION_1)
{
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_QUEUE_PFN, 0);
}
else
{
/* Virtio 1.2: To stop using the queue the driver MUST write
* zero (0x0) to this QueueReady and MUST read the value back
* to ensure synchronization.
*/
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_QUEUE_READY, 0);
if (metal_io_read32(&vmdev->cfg_io, VIRTIO_MMIO_QUEUE_READY))
{
vrtwarn("queue ready set zero failed\n");
}
}
/* Free the vring buffer and virtqueue */
vrinfo = &vdev->vrings_info[i];
if (vrinfo->info.vaddr != NULL)
{
virtio_free_buf(vdev, vrinfo->info.vaddr);
}
if (vrinfo->vq != NULL)
{
virtqueue_free(vrinfo->vq);
}
}
kmm_free(vdev->vrings_info);
}
}
/****************************************************************************
* Name: virtio_mmio_set_status
****************************************************************************/
static void virtio_mmio_set_status(FAR struct virtio_device *vdev,
uint8_t status)
{
FAR struct virtio_mmio_device_s *vmdev =
(FAR struct virtio_mmio_device_s *)vdev;
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_STATUS, status);
}
/****************************************************************************
* Name: virtio_mmio_get_status
****************************************************************************/
static uint8_t virtio_mmio_get_status(FAR struct virtio_device *vdev)
{
FAR struct virtio_mmio_device_s *vmdev =
(FAR struct virtio_mmio_device_s *)vdev;
return metal_io_read32(&vmdev->cfg_io, VIRTIO_MMIO_STATUS);
}
/****************************************************************************
* Name: virtio_mmio_write_config
****************************************************************************/
static void virtio_mmio_write_config(FAR struct virtio_device *vdev,
uint32_t offset, void *src, int length)
{
FAR struct virtio_mmio_device_s *vmdev =
(FAR struct virtio_mmio_device_s *)vdev;
uint32_t write_offset = VIRTIO_MMIO_CONFIG + offset;
uint32_t u32data;
uint16_t u16data;
uint8_t u8data;
if (vdev->id.version == VIRTIO_MMIO_VERSION_1 || length > 8)
{
FAR char *s = src;
int i;
for (i = 0; i < length; i++)
{
metal_io_write8(&vmdev->cfg_io, write_offset + i, s[i]);
}
return;
}
switch (length)
{
case 1:
memcpy(&u8data, src, sizeof(u8data));
metal_io_write8(&vmdev->cfg_io, write_offset, u8data);
break;
#endif
#ifdef CONFIG_DRIVERS_VIRTIO_BLK
case VIRTIO_DEV_BLK:
ret = virtio_mmio_blk_init(regs, irq);
case 2:
memcpy(&u16data, src, sizeof(u16data));
metal_io_write16(&vmdev->cfg_io, write_offset, u16data);
break;
case 4:
memcpy(&u32data, src, sizeof(u32data));
metal_io_write32(&vmdev->cfg_io, write_offset, u32data);
break;
case 8:
memcpy(&u32data, src, sizeof(u32data));
metal_io_write32(&vmdev->cfg_io, write_offset, u32data);
memcpy(&u32data, src + sizeof(u32data), sizeof(u32data));
metal_io_write32(&vmdev->cfg_io, write_offset + sizeof(u32data),
u32data);
break;
#endif
default:
vrtwarn("unsupported device_id 0x%" PRIx32 "\n", val);
DEBUGASSERT(0);
}
}
return ret;
/****************************************************************************
* Name: virtio_mmio_read_config
****************************************************************************/
static void virtio_mmio_read_config(FAR struct virtio_device *vdev,
uint32_t offset, FAR void *dst,
int length)
{
FAR struct virtio_mmio_device_s *vmdev =
(FAR struct virtio_mmio_device_s *)vdev;
uint32_t read_offset = VIRTIO_MMIO_CONFIG + offset;
uint32_t u32data;
uint16_t u16data;
uint8_t u8data;
if (vdev->id.version == VIRTIO_MMIO_VERSION_1 || length > 8)
{
FAR char *d = dst;
int i;
for (i = 0; i < length; i++)
{
d[i] = metal_io_read8(&vmdev->cfg_io, read_offset + i);
}
return;
}
switch (length)
{
case 1:
u8data = metal_io_read8(&vmdev->cfg_io, read_offset);
memcpy(dst, &u8data, sizeof(u8data));
break;
case 2:
u16data = metal_io_read16(&vmdev->cfg_io, read_offset);
memcpy(dst, &u16data, sizeof(u16data));
break;
case 4:
u32data = metal_io_read32(&vmdev->cfg_io, read_offset);
memcpy(dst, &u32data, sizeof(u32data));
break;
case 8:
u32data = metal_io_read32(&vmdev->cfg_io, read_offset);
memcpy(dst, &u32data, sizeof(u32data));
u32data = metal_io_read32(&vmdev->cfg_io,
read_offset + sizeof(u32data));
memcpy(dst + sizeof(u32data), &u32data, sizeof(u32data));
break;
default:
DEBUGASSERT(0);
}
}
/****************************************************************************
* Name: virtio_mmio_get_features
****************************************************************************/
static uint32_t virtio_mmio_get_features(FAR struct virtio_device *vdev)
{
FAR struct virtio_mmio_device_s *vmdev =
(FAR struct virtio_mmio_device_s *)vdev;
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0);
return metal_io_read32(&vmdev->cfg_io, VIRTIO_MMIO_DEVICE_FEATURES);
}
/****************************************************************************
* Name: virtio_mmio_set_features
****************************************************************************/
static void virtio_mmio_set_features(FAR struct virtio_device *vdev,
uint32_t features)
{
FAR struct virtio_mmio_device_s *vmdev =
(FAR struct virtio_mmio_device_s *)vdev;
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0);
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_DRIVER_FEATURES, features);
vdev->features = features;
}
/****************************************************************************
* Name: virtio_mmio_negotiate_features
****************************************************************************/
static uint32_t virtio_mmio_negotiate_features(struct virtio_device *vdev,
uint32_t features)
{
features = features & virtio_mmio_get_features(vdev);
virtio_mmio_set_features(vdev, features);
return features;
}
/****************************************************************************
* Name: virtio_mmio_reset_device
****************************************************************************/
static void virtio_mmio_reset_device(FAR struct virtio_device *vdev)
{
virtio_mmio_set_status(vdev, VIRTIO_CONFIG_STATUS_RESET);
}
/****************************************************************************
* Name: virtio_mmio_notify
****************************************************************************/
static void virtio_mmio_notify(FAR struct virtqueue *vq)
{
FAR struct virtio_mmio_device_s *vmdev =
(FAR struct virtio_mmio_device_s *)vq->vq_dev;
/* VIRTIO_F_NOTIFICATION_DATA is not supported for now */
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_QUEUE_NOTIFY,
vq->vq_queue_index);
}
/****************************************************************************
* Name: virtio_mmio_interrupt
****************************************************************************/
static int virtio_mmio_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct virtio_mmio_device_s *vmdev = arg;
FAR struct virtio_vring_info *vrings_info = vmdev->vdev.vrings_info;
FAR struct virtqueue *vq;
unsigned int i;
uint32_t isr;
isr = metal_io_read32(&vmdev->cfg_io, VIRTIO_MMIO_INTERRUPT_STATUS);
if (isr & VIRTIO_MMIO_INTERRUPT_VRING)
{
for (i = 0; i < vmdev->vdev.vrings_num; i++)
{
vq = vrings_info[i].vq;
if (vq->vq_used_cons_idx != vq->vq_ring.used->idx &&
vq->callback != NULL)
{
vq->callback(vq);
}
}
}
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_INTERRUPT_ACK, isr);
return OK;
}
/****************************************************************************
* Name: virtio_mmio_init_device
****************************************************************************/
static int virtio_mmio_init_device(FAR struct virtio_mmio_device_s *vmdev,
FAR void *regs, int irq)
{
FAR struct virtio_device *vdev = &vmdev->vdev;
uint32_t magic;
/* Save the irq */
vmdev->irq = irq;
/* Share memory io is used for the virtio buffer operations
* Config memory is used for the mmio register operations
*/
vmdev->shm_phy = (metal_phys_addr_t)0;
vmdev->cfg_phy = (metal_phys_addr_t)regs;
metal_io_init(&vmdev->shm_io, NULL, &vmdev->shm_phy,
SIZE_MAX, UINT_MAX, 0, NULL);
metal_io_init(&vmdev->cfg_io, regs, &vmdev->cfg_phy,
SIZE_MAX, UINT_MAX, 0, NULL);
/* Init the virtio device */
vdev->role = VIRTIO_DEV_DRIVER;
vdev->func = &g_virtio_mmio_dispatch;
magic = metal_io_read32(&vmdev->cfg_io, VIRTIO_MMIO_MAGIC_VALUE);
if (magic != VIRTIO_MMIO_MAGIC_VALUE_STRING)
{
vrterr("Bad magic value %" PRIu32 "\n", magic);
return -EINVAL;
}
vdev->id.version = metal_io_read32(&vmdev->cfg_io, VIRTIO_MMIO_VERSION);
vdev->id.device = metal_io_read32(&vmdev->cfg_io, VIRTIO_MMIO_DEVICE_ID);
if (vdev->id.device == 0)
{
vrterr("Device Id 0\n");
return -EINVAL;
}
vdev->id.vendor = metal_io_read32(&vmdev->cfg_io, VIRTIO_MMIO_VENDOR_ID);
/* Legacy mmio version, set the page size */
if (vdev->id.version == VIRTIO_MMIO_VERSION_1)
{
metal_io_write32(&vmdev->cfg_io, VIRTIO_MMIO_PAGE_SIZE,
VIRTIO_PAGE_SIZE);
}
vrtinfo("VIRTIO version: %"PRIu32" device: %"PRIu32" vendor: %"PRIx32"\n",
vdev->id.version, vdev->id.device, vdev->id.vendor);
/* Reset the virtio device and set ACK */
virtio_mmio_set_status(vdev, VIRTIO_CONFIG_STATUS_RESET);
virtio_mmio_set_status(vdev, VIRTIO_CONFIG_STATUS_ACK);
return OK;
}
/****************************************************************************
@ -167,69 +808,68 @@ static int virtio_dev_init(uintptr_t virt, uint32_t irq)
****************************************************************************/
/****************************************************************************
* Name: virtq_alloc_desc
* Name: virtio_register_mmio_device
*
* Description:
* Register virtio mmio device to the virtio bus
*
****************************************************************************/
uint32_t virtq_alloc_desc(FAR struct virtqueue *virtq,
uint32_t idx, FAR void *addr)
int virtio_register_mmio_device(FAR void *regs, int irq)
{
uint32_t id = idx % virtq->len;
struct metal_init_params params = METAL_INIT_DEFAULTS;
FAR struct virtio_mmio_device_s *vmdev;
static bool onceinit;
int ret;
vrtinfo("virtq=%p, idx=%" PRId32 "\n", virtq, idx);
DEBUGASSERT(regs != NULL);
virtq->desc[id].addr = (uintptr_t)addr;
virtq->desc_virt[id] = addr;
return id;
}
/****************************************************************************
* Name: virtq_add_to_mmio_device
****************************************************************************/
void virtq_add_to_mmio_device(FAR struct virtio_mmio_regs *regs,
FAR struct virtqueue *virtq,
uint32_t queue_sel)
if (onceinit == false)
{
vrtinfo("==== queue_sel=%" PRId32 ", virtq->len=%" PRId32 "\n",
queue_sel, virtq->len);
virtio_putreg32(queue_sel, &regs->queue_sel);
virtio_mb();
virtio_putreg32(virtq->len, &regs->queue_num);
virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->desc)),
&regs->queue_desc_low);
virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->desc) >> 32),
&regs->queue_desc_high);
virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->avail)),
&regs->queue_avail_low);
virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->avail) >> 32),
&regs->queue_avail_high);
virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->used)),
&regs->queue_used_low);
virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->used) >> 32),
&regs->queue_used_high);
virtio_mb();
virtio_putreg32(1, &regs->queue_ready);
}
/****************************************************************************
* Name: virtio_mmio_init
****************************************************************************/
void virtio_mmio_init(void)
onceinit = true;
ret = metal_init(&params);
if (ret < 0)
{
uintptr_t virtio = (uintptr_t)CONFIG_DRIVERS_VIRTIO_MMIO_BASE;
uint32_t irq = CONFIG_DRIVERS_VIRTIO_MMIO_IRQ;
uint32_t size = CONFIG_DRIVERS_VIRTIO_MMIO_REGSIZE;
uint32_t i;
for (i = 0; i < CONFIG_DRIVERS_VIRTIO_MMIO_NUM; i++)
{
virtio_dev_init(virtio + size * i, irq + i);
return ret;
}
}
vmdev = kmm_zalloc(sizeof(*vmdev));
if (vmdev == NULL)
{
vrterr("No enough memory\n");
return -ENOMEM;
}
ret = virtio_mmio_init_device(vmdev, regs, irq);
if (ret < 0)
{
vrterr("virtio_mmio_device_init failed, ret=%d\n", ret);
goto err;
}
/* Attach the intterupt before register the device driver */
ret = irq_attach(irq, virtio_mmio_interrupt, vmdev);
if (ret < 0)
{
vrterr("irq_attach failed, ret=%d\n", ret);
goto err;
}
/* Register the virtio device */
ret = virtio_register_device(&vmdev->vdev);
if (ret < 0)
{
vrterr("virt_device_register failed, ret=%d\n", ret);
irq_detach(irq);
goto err;
}
return ret;
err:
kmm_free(vmdev);
return ret;
}

552
drivers/virtio/virtio-net.c Normal file
View file

@ -0,0 +1,552 @@
/****************************************************************************
* drivers/virtio/virtio-net.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <debug.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <nuttx/compiler.h>
#include <nuttx/kmalloc.h>
#include <nuttx/net/netdev_lowerhalf.h>
#include <nuttx/virtio/virtio.h>
#include "virtio-net.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Virtio net header size and packet buffer size */
#define VIRTIO_NET_HDRSIZE (sizeof(struct virtio_net_hdr_s))
#define VIRTIO_NET_BUFSIZE (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE)
/* Virtio net virtqueue index and number */
#define VIRTIO_NET_RX 0
#define VIRTIO_NET_TX 1
#define VIRTIO_NET_NUM 2
/****************************************************************************
* Private Types
****************************************************************************/
/* Virtio net header, just define it here for further, now only use it
* to calculate the virto net header size, see marco VIRTIO_NET_HDRSIZE
*/
begin_packed_struct struct virtio_net_hdr_s
{
uint8_t flags;
uint8_t gso_type;
uint16_t hdr_len;
uint16_t gso_size;
uint16_t csum_start;
uint16_t csum_offset;
} end_packed_struct;
struct virtio_net_priv_s
{
/* This holds the information visible to the NuttX network */
struct netdev_lowerhalf_s lower; /* The netdev lowerhalf */
/* Virtio device information */
FAR struct virtio_device *vdev; /* Virtio device pointer */
int bufnum; /* TX and RX Buffer number */
};
/* Virtio Link Layer Header, follow shows the iob buffer layout:
*
* |<-- CONFIG_NET_LL_GUARDSIZE -->|
* +---------------+---------------+------------+------+ +-------------+
* | Virtio Header | ETH Header | data | free | --> | next netpkt |
* +---------------+---------------+------------+------+ +-------------+
* | |<--------- datalen -------->|
* ^base ^data
*
* CONFIG_NET_LL_GUARDSIZE = sizeof(struct virtio_net_llhdr_s) + ETH_HDR_SIZE
* = sizeof(uintptr) + 10 + 14
* = 32 (64-Bit)
* = 28 (32-Bit)
*/
struct virtio_net_llhdr_s
{
FAR netpkt_t *pkt; /* Netpaket pointer */
struct virtio_net_hdr_s vhdr; /* Virtio net header */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int virtio_net_ifup(FAR struct netdev_lowerhalf_s *dev);
static int virtio_net_ifdown(FAR struct netdev_lowerhalf_s *dev);
static int virtio_net_send(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt);
static netpkt_t *virtio_net_recv(FAR struct netdev_lowerhalf_s *dev);
#ifdef CONFIG_NET_MCASTGROUP
static int virtio_net_addmac(FAR struct netdev_lowerhalf_s *dev,
FAR const uint8_t *mac);
static int virtio_net_rmmac(FAR struct netdev_lowerhalf_s *dev,
FAR const uint8_t *mac);
#endif
#ifdef CONFIG_NETDEV_IOCTL
static int virtio_net_ioctl(FAR struct netdev_lowerhalf_s *dev);
#endif
static int virtio_net_probe(FAR struct virtio_device *vdev);
static void virtio_net_remove(FAR struct virtio_device *vdev);
/****************************************************************************
* Private Data
****************************************************************************/
static struct virtio_driver g_virtio_net_driver =
{
LIST_INITIAL_VALUE(g_virtio_net_driver.node), /* node */
VIRTIO_ID_NETWORK, /* device id */
virtio_net_probe, /* probe */
virtio_net_remove, /* remove */
};
static const struct netdev_ops_s g_virtio_net_ops =
{
virtio_net_ifup,
virtio_net_ifdown,
virtio_net_send,
virtio_net_recv,
#ifdef CONFIG_NET_MCASTGROUP
virtio_net_addmac,
virtio_net_rmmac,
#endif
#ifdef CONFIG_NETDEV_IOCTL
virtio_net_ioctl,
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: virtio_net_rxfill
****************************************************************************/
static void virtio_net_rxfill(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct virtio_net_priv_s *priv = (FAR struct virtio_net_priv_s *)dev;
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_NET_RX].vq;
FAR struct virtio_net_llhdr_s *hdr;
FAR struct virtqueue_buf vb;
FAR netpkt_t *pkt;
int i;
for (i = 0; i < priv->bufnum; i++)
{
/* IOB Offload, Alloc buffer from RX netpkt */
pkt = netpkt_alloc(dev, NETPKT_RX);
if (pkt == NULL)
{
vrtinfo("Has ran out of the RX buffer, i=%d\n", i);
break;
}
/* Alloc cookie and net header from transport layer */
hdr = (FAR struct virtio_net_llhdr_s *)netpkt_getbase(pkt);
memset(&hdr->vhdr, 0, sizeof(hdr->vhdr));
hdr->pkt = pkt;
/* Buffer 0, the virtio net header */
vb.buf = &hdr->vhdr;
vb.len = VIRTIO_NET_HDRSIZE + VIRTIO_NET_BUFSIZE;
vrtinfo("Fill rx, hdr=%p, buf=%p, buflen=%d\n", hdr, vb.buf, vb.len);
virtqueue_add_buffer(vq, &vb, 0, 1, hdr);
}
if (i > 0)
{
virtqueue_kick(vq);
}
}
/****************************************************************************
* Name: virtio_net_txfree
****************************************************************************/
static void virtio_net_txfree(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct virtio_net_priv_s *priv = (FAR struct virtio_net_priv_s *)dev;
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_NET_TX].vq;
FAR struct virtio_net_llhdr_s *hdr;
while (1)
{
/* Get buffer from tx virtqueue */
hdr = virtqueue_get_buffer(vq, NULL, NULL);
if (hdr == NULL)
{
break;
}
netpkt_free(dev, hdr->pkt, NETPKT_TX);
vrtinfo("Free, hdr: %p, pkt: %p\n", hdr, hdr->pkt);
}
}
/****************************************************************************
* Name: virtio_net_ifup
****************************************************************************/
static int virtio_net_ifup(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct virtio_net_priv_s *priv = (FAR struct virtio_net_priv_s *)dev;
#ifdef CONFIG_NET_IPv4
vrtinfo("Bringing up: %d.%d.%d.%d\n",
(int)dev->netdev.d_ipaddr & 0xff,
(int)(dev->netdev.d_ipaddr >> 8) & 0xff,
(int)(dev->netdev.d_ipaddr >> 16) & 0xff,
(int)dev->netdev.d_ipaddr >> 24);
#endif
#ifdef CONFIG_NET_IPv6
vrtinfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
dev->netdev.d_ipv6addr[0], dev->netdev.d_ipv6addr[1],
dev->netdev.d_ipv6addr[2], dev->netdev.d_ipv6addr[3],
dev->netdev.d_ipv6addr[4], dev->netdev.d_ipv6addr[5],
dev->netdev.d_ipv6addr[6], dev->netdev.d_ipv6addr[7]);
#endif
/* Prepare interrupt and packets for receiving */
virtqueue_enable_cb(priv->vdev->vrings_info[VIRTIO_NET_RX].vq);
virtio_net_rxfill(dev);
return netdev_lower_carrier_on(dev);
}
/****************************************************************************
* Name: virtio_net_ifdown
****************************************************************************/
static int virtio_net_ifdown(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct virtio_net_priv_s *priv = (FAR struct virtio_net_priv_s *)dev;
int i;
/* Disable the Ethernet interrupt */
for (i = 0; i < VIRTIO_NET_NUM; i++)
{
virtqueue_disable_cb(priv->vdev->vrings_info[i].vq);
}
return netdev_lower_carrier_off(dev);
}
/****************************************************************************
* Name: virtio_net_send
****************************************************************************/
static int virtio_net_send(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt)
{
FAR struct virtio_net_priv_s *priv = (FAR struct virtio_net_priv_s *)dev;
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_NET_TX].vq;
FAR struct virtio_net_llhdr_s *hdr;
struct virtqueue_buf vb;
size_t len;
/* Check the send length */
len = netpkt_getdatalen(dev, pkt);
if (len > VIRTIO_NET_BUFSIZE)
{
vrterr("net send buffer too large\n");
return -EINVAL;
}
hdr = (FAR struct virtio_net_llhdr_s *)netpkt_getbase(pkt);
hdr->pkt = pkt;
memset(&hdr->vhdr, 0, sizeof(hdr->vhdr));
/* Buffer 0 is the virtio net header */
vb.buf = &hdr->vhdr;
vb.len = VIRTIO_NET_HDRSIZE + len;
/* Add buffer to vq and notify the other side */
vrtinfo("Send, hdr=%p, buf=%p, buflen=%d\n", hdr, vb.buf, vb.len);
virtqueue_add_buffer(vq, &vb, 1, 0, hdr);
virtqueue_kick(vq);
/* Try return Netpkt TX buffer to upper-half. */
virtio_net_txfree(dev);
/* If we have no buffer left, enable TX done callback. */
if (netdev_lower_quota_load(dev, NETPKT_TX) <= 0)
{
virtqueue_enable_cb(vq);
}
return OK;
}
/****************************************************************************
* Name: virtio_net_recv
****************************************************************************/
static netpkt_t *virtio_net_recv(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct virtio_net_priv_s *priv = (FAR struct virtio_net_priv_s *)dev;
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_NET_RX].vq;
FAR struct virtio_net_llhdr_s *hdr;
uint32_t len;
/* Fill the free Netpkt RX buffer to the RX virtqueue */
virtio_net_rxfill(dev);
/* Get received buffer form RX virtqueue */
hdr = virtqueue_get_buffer(vq, &len, NULL);
if (hdr == NULL)
{
/* If we have no buffer left, enable RX callback. */
virtqueue_enable_cb(vq);
/* We do transmit after recv, now it's time to free TX buffer.
* Depends on upper-half order (Call TX after RX).
*
* TODO: Find a better way to free TX buffer.
*/
virtio_net_txfree(&priv->lower);
vrtinfo("get NULL buffer\n");
return NULL;
}
/* Set the received pkt length */
netpkt_setdatalen(dev, hdr->pkt, len - VIRTIO_NET_HDRSIZE);
vrtinfo("Recv, hdr=%p, pkt=%p, len=%" PRIu32 "\n", hdr, hdr->pkt, len);
return hdr->pkt;
}
#ifdef CONFIG_NET_MCASTGROUP
/****************************************************************************
* Name: virtio_net_addmac
****************************************************************************/
static int virtio_net_addmac(FAR struct netdev_lowerhalf_s *dev,
FAR const uint8_t *mac)
{
return -ENOSYS;
}
/****************************************************************************
* Name: virtio_net_rmmac
****************************************************************************/
static int virtio_net_rmmac(FAR struct netdev_lowerhalf_s *dev,
FAR const uint8_t *mac)
{
return -ENOSYS;
}
#endif
#ifdef CONFIG_NETDEV_IOCTL
/****************************************************************************
* Name: virtio_net_ioctl
****************************************************************************/
static int virtio_net_ioctl(FAR struct netdev_lowerhalf_s *dev)
{
return -ENOTTY;
}
#endif
/****************************************************************************
* Name: virtio_net_rxready
****************************************************************************/
static void virtio_net_rxready(FAR struct virtqueue *vq)
{
FAR struct virtio_net_priv_s *priv = vq->vq_dev->priv;
virtqueue_disable_cb(vq);
netdev_lower_rxready(&priv->lower);
}
/****************************************************************************
* Name: virtio_net_txdone
****************************************************************************/
static void virtio_net_txdone(FAR struct virtqueue *vq)
{
FAR struct virtio_net_priv_s *priv = vq->vq_dev->priv;
virtqueue_disable_cb(vq);
netdev_lower_txdone(&priv->lower);
}
/****************************************************************************
* Name: virtio_net_init
****************************************************************************/
static int virtio_net_init(FAR struct virtio_net_priv_s *priv,
FAR struct virtio_device *vdev)
{
FAR const char *vqnames[VIRTIO_NET_NUM];
vq_callback callbacks[VIRTIO_NET_NUM];
int ret;
priv->vdev = vdev;
vdev->priv = priv;
/* Initialize the virtio device */
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER);
virtio_set_features(vdev, 0);
virtio_set_status(vdev, VIRTIO_CONFIG_FEATURES_OK);
vqnames[VIRTIO_NET_RX] = "virtio_net_rx";
vqnames[VIRTIO_NET_TX] = "virtio_net_tx";
callbacks[VIRTIO_NET_RX] = virtio_net_rxready;
callbacks[VIRTIO_NET_TX] = virtio_net_txdone;
ret = virtio_create_virtqueues(vdev, 0, VIRTIO_NET_NUM, vqnames,
callbacks);
if (ret < 0)
{
vrterr("virtio_device_create_virtqueue failed, ret=%d\n", ret);
return ret;
}
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
#if CONFIG_DRIVERS_VIRTIO_NET_BUFNUM > 0
priv->bufnum = CONFIG_DRIVERS_VIRTIO_NET_BUFNUM;
#else
/* Calculate the virtio network buffer number:
* 1/4 for the TX netpkts, 1/4 for the RX netpkts.
*/
priv->bufnum = CONFIG_IOB_NBUFFERS / 4;
#endif
priv->bufnum = MIN(vdev->vrings_info[VIRTIO_NET_RX].info.num_descs,
priv->bufnum);
priv->bufnum = MIN(vdev->vrings_info[VIRTIO_NET_TX].info.num_descs,
priv->bufnum);
return OK;
}
/****************************************************************************
* Name: virtio_net_probe
****************************************************************************/
static int virtio_net_probe(FAR struct virtio_device *vdev)
{
FAR struct netdev_lowerhalf_s *netdev;
FAR struct virtio_net_priv_s *priv;
int ret;
priv = kmm_zalloc(sizeof(*priv));
if (priv == NULL)
{
vrterr("Virtio net driver priv alloc failed\n");
return -ENOMEM;
}
ret = virtio_net_init(priv, vdev);
if (ret < 0)
{
vrterr("virtio_net_init failed, ret=%d\n", ret);
goto err_with_priv;
}
/* Initialize the netdev lower half */
netdev = &priv->lower;
netdev->quota[NETPKT_RX] = priv->bufnum;
netdev->quota[NETPKT_TX] = priv->bufnum;
netdev->ops = &g_virtio_net_ops;
/* Register the net deivce */
ret = netdev_lower_register(netdev, NET_LL_ETHERNET);
if (ret < 0)
{
vrterr("netdev_lower_register failed, ret=%d\n", ret);
goto err_with_virtqueues;
}
return ret;
err_with_virtqueues:
virtio_reset_device(vdev);
virtio_delete_virtqueues(vdev);
err_with_priv:
kmm_free(priv);
return ret;
}
/****************************************************************************
* Name: virtio_net_remove
****************************************************************************/
static void virtio_net_remove(FAR struct virtio_device *vdev)
{
FAR struct virtio_net_priv_s *priv = vdev->priv;
netdev_lower_unregister(&priv->lower);
virtio_reset_device(vdev);
virtio_delete_virtqueues(vdev);
kmm_free(priv);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: virtio_register_net_driver
****************************************************************************/
int virtio_register_net_driver(void)
{
return virtio_register_driver(&g_virtio_net_driver);
}

View file

@ -1,5 +1,5 @@
/****************************************************************************
* drivers/virtio/virtio-mmio-net.h
* drivers/virtio/virtio-net.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@ -18,8 +18,8 @@
*
****************************************************************************/
#ifndef __DRIVERS_VIRTIO_VIRTIO_MMIO_NET_H
#define __DRIVERS_VIRTIO_VIRTIO_MMIO_NET_H
#ifndef __DRIVERS_VIRTIO_VIRTIO_NET_H
#define __DRIVERS_VIRTIO_VIRTIO_NET_H
/****************************************************************************
* Included Files
@ -49,15 +49,7 @@ extern "C"
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: virtio_mmio_net_init
*
* Description:
* Called from virtio-mmio.c to initialize virtnet
*
****************************************************************************/
int virtio_mmio_net_init(FAR struct virtio_mmio_regs *regs, uint32_t intid);
int virtio_register_net_driver(void);
#undef EXTERN
#ifdef __cplusplus
@ -65,4 +57,4 @@ int virtio_mmio_net_init(FAR struct virtio_mmio_regs *regs, uint32_t intid);
#endif
#endif /* CONFIG_DRIVERS_VIRTIO_NET */
#endif /* __DRIVERS_VIRTIO_VIRTIO_MMIO_NET_H */
#endif /* __DRIVERS_VIRTIO_VIRTIO_NET_H */

279
drivers/virtio/virtio-rng.c Normal file
View file

@ -0,0 +1,279 @@
/****************************************************************************
* drivers/virtio/virtio-rng.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/fs/fs.h>
#include <nuttx/semaphore.h>
#include <nuttx/virtio/virtio.h>
#include "virtio-rng.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
struct virtio_rng_cookie_s
{
sem_t sem;
size_t len;
};
struct virtio_rng_priv_s
{
FAR struct virtio_device *vdev;
char name[NAME_MAX];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* RNG file operation function */
static ssize_t virtio_rng_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
/* Virtio driver functions */
static int virtio_rng_probe(FAR struct virtio_device *vdev);
static void virtio_rng_remove(FAR struct virtio_device *vdev);
/****************************************************************************
* Private Data
****************************************************************************/
static struct virtio_driver g_virtio_rng_driver =
{
LIST_INITIAL_VALUE(g_virtio_rng_driver.node), /* node */
VIRTIO_ID_ENTROPY, /* device id */
virtio_rng_probe, /* probe */
virtio_rng_remove, /* remove */
};
static const struct file_operations g_virtio_rng_ops =
{
NULL, /* open */
NULL, /* close */
virtio_rng_read, /* read */
NULL, /* write */
NULL, /* seek */
NULL, /* ioctl */
NULL, /* mmap */
NULL, /* truncate */
NULL, /* poll */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
NULL, /* unlink */
#endif
};
static int g_virtio_rng_idx = 0;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: virtio_rng_done
****************************************************************************/
static void virtio_rng_done(FAR struct virtqueue *vq)
{
FAR struct virtio_rng_cookie_s *cookie;
uint32_t len;
/* Get the buffer, virtqueue_get_buffer() return the cookie added in
* virtio_rng_read().
*/
cookie = virtqueue_get_buffer(vq, &len, NULL);
if (cookie != NULL)
{
/* Assign the return length */
cookie->len = len;
/* Read completed, post the sem */
nxsem_post(&cookie->sem);
}
}
/****************************************************************************
* Name: virtio_rng_read
****************************************************************************/
static ssize_t virtio_rng_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct virtio_rng_priv_s *priv = filep->f_inode->i_private;
FAR struct virtqueue *vq = priv->vdev->vrings_info[0].vq;
struct virtio_rng_cookie_s cookie;
struct virtqueue_buf vb;
int ret;
/* Init the cookie */
cookie.len = 0;
nxsem_init(&cookie.sem, 0, 0);
/* Add the input buffer to the virtqueue, and the cookie as the virtqueue
* cookie. (virtqueue_get_buffer() will return cookie).
*/
vb.buf = buffer;
vb.len = buflen;
ret = virtqueue_add_buffer(vq, &vb, 0, 1, &cookie);
if (ret < 0)
{
return ret;
}
/* Notify the other side to process the added virtqueue buffer */
virtqueue_kick(vq);
/* Wait fot completion */
nxsem_wait_uninterruptible(&cookie.sem);
return cookie.len;
}
/****************************************************************************
* Name: virtio_rng_probe
****************************************************************************/
static int virtio_rng_probe(FAR struct virtio_device *vdev)
{
FAR struct virtio_rng_priv_s *priv;
FAR const char *vqnames[1];
vq_callback callback[1];
int ret;
priv = kmm_zalloc(sizeof(*priv));
if (priv == NULL)
{
vrterr("No enough memory\n");
return -ENOMEM;
}
priv->vdev = vdev;
vdev->priv = priv;
/* Call openamp api to intialize the virtio deivce */
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER);
virtio_set_features(vdev, 0);
virtio_set_status(vdev, VIRTIO_CONFIG_FEATURES_OK);
vqnames[0] = "virtio_rng_rx";
callback[0] = virtio_rng_done;
ret = virtio_create_virtqueues(vdev, 0, 1, vqnames, callback);
if (ret < 0)
{
vrterr("virtio_device_create_virtqueue failed, ret=%d\n", ret);
goto err_with_priv;
}
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
virtqueue_enable_cb(vdev->vrings_info[0].vq);
/* Register NuttX driver */
if (g_virtio_rng_idx == 0)
{
strlcpy(priv->name, "/dev/random", NAME_MAX);
}
else
{
snprintf(priv->name, NAME_MAX, "/dev/random%d", g_virtio_rng_idx);
}
ret = register_driver(priv->name, &g_virtio_rng_ops, 0444, priv);
if (ret < 0)
{
vrterr("Register NuttX driver failed, ret=%d\n", ret);
goto err_with_virtqueue;
}
g_virtio_rng_idx++;
return ret;
err_with_virtqueue:
virtio_reset_device(vdev);
virtio_delete_virtqueues(vdev);
err_with_priv:
kmm_free(priv);
return ret;
}
/****************************************************************************
* Name: virtio_rng_remove
****************************************************************************/
static void virtio_rng_remove(FAR struct virtio_device *vdev)
{
FAR struct virtio_rng_priv_s *priv = vdev->priv;
unregister_driver(priv->name);
virtio_reset_device(vdev);
virtio_delete_virtqueues(vdev);
kmm_free(priv);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: devrandom_register
*
* Description:
* Initialize the RNG hardware and register the /dev/random driver.
*
****************************************************************************/
#ifdef CONFIG_DEV_RANDOM
void weak_function devrandom_register(void)
{
/* Nothing, implement it here just avoid the compile error, the driver
* /dev/random will be registered in the virtio rng driver.
*/
}
#endif
/****************************************************************************
* Name: virtio_register_rng_driver
****************************************************************************/
int virtio_register_rng_driver(void)
{
return virtio_register_driver(&g_virtio_rng_driver);
}

View file

@ -1,5 +1,5 @@
/****************************************************************************
* arch/arm64/src/qemu/qemu_virtio.c
* drivers/virtio/virtio-rng.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@ -18,23 +18,44 @@
*
****************************************************************************/
#ifndef __DRIVERS_VIRTIO_VIRTIO_RNG_H
#define __DRIVERS_VIRTIO_VIRTIO_RNG_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <syslog.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/virtio/virtio-mmio.h>
#ifdef CONFIG_DRIVERS_VIRTIO_RNG
/****************************************************************************
* Public Functions
* Pre-processor Definitions
****************************************************************************/
void arm64_netinitialize(void)
/****************************************************************************
* Public Data
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
virtio_mmio_init();
#else
#define EXTERN extern
#endif
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
int virtio_register_rng_driver(void);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_DRIVERS_VIRTIO_RNG */
#endif /* __DRIVERS_VIRTIO_VIRTIO_RNG_H */

View file

@ -0,0 +1,597 @@
/****************************************************************************
* drivers/virtio/virtio-serial.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <debug.h>
#include <errno.h>
#include <stdio.h>
#include <nuttx/serial/serial.h>
#include <nuttx/virtio/virtio.h>
#include "virtio-serial.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define VIRTIO_SERIAL_RX 0
#define VIRTIO_SERIAL_TX 1
#define VIRTIO_SERIAL_NUM 2
/****************************************************************************
* Private Types
****************************************************************************/
struct virtio_serial_priv_s
{
/* Virtio device informations */
FAR struct virtio_device *vdev;
/* Nuttx uart device informations */
FAR struct uart_dev_s udev;
char name[NAME_MAX];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Uart operation functions */
static int virtio_serial_setup(FAR struct uart_dev_s *dev);
static void virtio_serial_shutdown(FAR struct uart_dev_s *dev);
static int virtio_serial_attach(FAR struct uart_dev_s *dev);
static void virtio_serial_detach(FAR struct uart_dev_s *dev);
static int virtio_serial_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static void virtio_serial_rxint(FAR struct uart_dev_s *dev, bool enable);
static void virtio_serial_send(FAR struct uart_dev_s *dev, int ch);
static void virtio_serial_txint(FAR struct uart_dev_s *dev, bool enable);
static bool virtio_serial_txready(FAR struct uart_dev_s *dev);
static bool virtio_serial_txempty(FAR struct uart_dev_s *dev);
static void virtio_serial_dmasend(FAR struct uart_dev_s *dev);
static void virtio_serial_dmatxavail(FAR struct uart_dev_s *dev);
static void virtio_serial_dmareceive(FAR struct uart_dev_s *dev);
static void virtio_serial_dmarxfree(FAR struct uart_dev_s *dev);
/* Other functions */
static void virtio_serial_rxready(FAR struct virtqueue *vq);
static void virtio_serial_txdone(FAR struct virtqueue *vq);
static int virtio_serial_probe(FAR struct virtio_device *vdev);
static void virtio_serial_remove(FAR struct virtio_device *vdev);
/****************************************************************************
* Private Data
****************************************************************************/
static struct virtio_driver g_virtio_serial_driver =
{
LIST_INITIAL_VALUE(g_virtio_serial_driver.node), /* node */
VIRTIO_ID_CONSOLE, /* device id */
virtio_serial_probe, /* probe */
virtio_serial_remove, /* remove */
};
static struct virtio_driver g_virtio_rprocserial_driver =
{
LIST_INITIAL_VALUE(g_virtio_rprocserial_driver.node), /* node */
VIRTIO_ID_RPROC_SERIAL, /* device id */
virtio_serial_probe, /* probe */
virtio_serial_remove, /* remove */
};
static const struct uart_ops_s g_virtio_serial_ops =
{
virtio_serial_setup, /* setup */
virtio_serial_shutdown, /* shutdown */
virtio_serial_attach, /* attach */
virtio_serial_detach, /* detach */
virtio_serial_ioctl, /* ioctl */
NULL, /* receive */
virtio_serial_rxint, /* rxint */
NULL, /* rxavailable */
#ifdef CONFIG_SERIAL_IFLOWCONTROL
NULL, /* rxflowcontrol */
#endif
virtio_serial_dmasend, /* dmasend */
virtio_serial_dmareceive, /* dmareceive */
virtio_serial_dmarxfree, /* dmarxfree */
virtio_serial_dmatxavail, /* dmatxavail */
virtio_serial_send, /* send */
virtio_serial_txint, /* txint */
virtio_serial_txready, /* txready */
virtio_serial_txempty, /* txempty */
};
static int g_virtio_serial_idx = 0;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: virtio_serial_setup
*
* Description:
* Configure the UART baud, bits, parity, fifos, etc. This
* method is called the first time that the serial port is
* opened.
*
****************************************************************************/
static int virtio_serial_setup(FAR struct uart_dev_s *dev)
{
return OK;
}
/****************************************************************************
* Name: virtio_serial_shutdown
*
* Description:
* Disable the UART. This method is called when the serial
* port is closed
*
****************************************************************************/
static void virtio_serial_shutdown(FAR struct uart_dev_s *dev)
{
/* Nothing */
}
/****************************************************************************
* Name: virtio_serial_attach
*
* Description:
* Configure the UART to operation in interrupt driven mode. This method
* is called when the serial port is opened. Normally, this is just after
* the setup() method is called, however, the serial console may operate in
* a non-interrupt driven mode during the boot phase.
*
* RX and TX interrupts are not enabled when by the attach method (unless
* the hardware supports multiple levels of interrupt enabling). The RX
* and TX interrupts are not enabled until the txint() and rxint() methods
* are called.
*
****************************************************************************/
static int virtio_serial_attach(FAR struct uart_dev_s *dev)
{
FAR struct virtio_serial_priv_s *priv = dev->priv;
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_SERIAL_RX].vq;
virtqueue_enable_cb(vq);
return 0;
}
/****************************************************************************
* Name: virtio_serial_detach
*
* Description:
* Detach UART interrupts. This method is called when the serial port is
* closed normally just before the shutdown method is called. The
* exception is the serial console which is never shutdown.
*
****************************************************************************/
static void virtio_serial_detach(FAR struct uart_dev_s *dev)
{
FAR struct virtio_serial_priv_s *priv = dev->priv;
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_SERIAL_RX].vq;
virtqueue_disable_cb(vq);
}
/****************************************************************************
* Name: virtio_serial_ioctl
*
* Description:
* All ioctl calls will be routed through this method
*
****************************************************************************/
static int virtio_serial_ioctl(FAR struct file *filep, int cmd,
unsigned long arg)
{
return -ENOTTY;
}
/****************************************************************************
* Name: virtio_serial_rxint
*
* Description:
* Call to enable or disable RX interrupts
*
****************************************************************************/
static void virtio_serial_rxint(FAR struct uart_dev_s *dev, bool enable)
{
}
/****************************************************************************
* Name: virtio_serial_send
*
* Description:
* This method will send one byte on the UART
*
****************************************************************************/
static void virtio_serial_send(FAR struct uart_dev_s *dev, int ch)
{
int nexthead;
nexthead = dev->xmit.head + 1;
if (nexthead >= dev->xmit.size)
{
nexthead = 0;
}
if (nexthead != dev->xmit.tail)
{
/* No.. not full. Add the character to the TX buffer and return. */
dev->xmit.buffer[dev->xmit.head] = ch;
dev->xmit.head = nexthead;
}
uart_dmatxavail(dev);
}
/****************************************************************************
* Name: virtio_serial_txint
*
* Description:
* Call to enable or disable TX interrupts
*
****************************************************************************/
static void virtio_serial_txint(FAR struct uart_dev_s *dev, bool enable)
{
}
/****************************************************************************
* Name: uart_txready
*
* Description:
* Return true if the tranmsit fifo is not full
*
****************************************************************************/
static bool virtio_serial_txready(FAR struct uart_dev_s *dev)
{
int nexthead = dev->xmit.head + 1;
if (nexthead >= dev->xmit.size)
{
nexthead = 0;
}
return nexthead != dev->xmit.tail;
}
/****************************************************************************
* Name: virtio_serial_txempty
*
* Description:
* Return true if the transmit fifo is empty
*
****************************************************************************/
static bool virtio_serial_txempty(FAR struct uart_dev_s *dev)
{
return true;
}
/****************************************************************************
* Name: virtio_serial_dmasend
****************************************************************************/
static void virtio_serial_dmasend(FAR struct uart_dev_s *dev)
{
FAR struct virtio_serial_priv_s *priv = dev->priv;
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_SERIAL_TX].vq;
FAR struct uart_dmaxfer_s *xfer = &dev->dmatx;
struct virtqueue_buf vb[2];
uintptr_t len;
int num = 1;
/* Get the total send length */
len = xfer->length + xfer->nlength;
/* Set the virtqueue buffer */
vb[0].buf = xfer->buffer;
vb[0].len = xfer->length;
if (xfer->nlength != 0)
{
vb[1].buf = xfer->nbuffer;
vb[1].len = xfer->nlength;
num = 2;
}
/* Add buffer to TX virtiqueue and notify the other size */
virtqueue_add_buffer(vq, vb, num, 0, (FAR void *)len);
virtqueue_kick(vq);
}
/****************************************************************************
* Name: virtio_serial_dmatxavail
****************************************************************************/
static void virtio_serial_dmatxavail(FAR struct uart_dev_s *dev)
{
if (dev->dmatx.length == 0)
{
uart_xmitchars_dma(dev);
}
}
/****************************************************************************
* Name: virtio_serial_dmareceive
****************************************************************************/
static void virtio_serial_dmareceive(FAR struct uart_dev_s *dev)
{
FAR struct virtio_serial_priv_s *priv = dev->priv;
FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_SERIAL_RX].vq;
FAR struct uart_dmaxfer_s *xfer = &dev->dmarx;
struct virtqueue_buf vb[2];
int num = 1;
vb[0].buf = xfer->buffer;
vb[0].len = xfer->length;
if (xfer->nlength != 0)
{
vb[num].buf = xfer->nbuffer;
vb[num].len = xfer->nlength;
num = 2;
}
/* Add buffer to the RX virtqueue and notify the device side */
virtqueue_add_buffer(vq, vb, 0, num, xfer);
virtqueue_kick(vq);
}
/****************************************************************************
* Name: virtio_serial_dmarxfree
****************************************************************************/
static void virtio_serial_dmarxfree(FAR struct uart_dev_s *dev)
{
if (dev->dmarx.length == 0)
{
uart_recvchars_dma(dev);
}
}
/****************************************************************************
* Name: virtio_serial_rxready
*
* Description:
* The virt serial receive virtqueue callback funtion
*
****************************************************************************/
static void virtio_serial_rxready(FAR struct virtqueue *vq)
{
FAR struct virtio_serial_priv_s *priv = vq->vq_dev->priv;
FAR struct uart_dmaxfer_s *xfer;
uint32_t len;
/* Received some data, call uart_recvchars_done() */
xfer = virtqueue_get_buffer(vq, &len, NULL);
if (xfer == NULL)
{
return;
}
xfer->nbytes = len;
uart_recvchars_done(&priv->udev);
uart_dmarxfree(&priv->udev);
}
/****************************************************************************
* Name: virtio_serial_txdone
*
* Description:
* The virt serial transimit virtqueue callback funtion
*
****************************************************************************/
static void virtio_serial_txdone(FAR struct virtqueue *vq)
{
FAR struct virtio_serial_priv_s *priv = vq->vq_dev->priv;
uintptr_t len;
/* Call uart_xmitchars_done to notify the upperhalf */
len = (uintptr_t)virtqueue_get_buffer(vq, NULL, NULL);
priv->udev.dmatx.nbytes = len;
uart_xmitchars_done(&priv->udev);
uart_dmatxavail(&priv->udev);
}
/****************************************************************************
* Name: virtio_serial_init
****************************************************************************/
static int virtio_serial_init(FAR struct virtio_serial_priv_s *priv,
FAR struct virtio_device *vdev)
{
FAR const char *vqnames[VIRTIO_SERIAL_NUM];
vq_callback callbacks[VIRTIO_SERIAL_NUM];
FAR struct uart_dev_s *udev;
int ret;
priv->vdev = vdev;
vdev->priv = priv;
/* Uart device buffer and ops init */
udev = &priv->udev;
udev->priv = priv;
udev->ops = &g_virtio_serial_ops;
udev->recv.size = CONFIG_DRIVERS_VIRTIO_SERIAL_BUFSIZE;
udev->recv.buffer = virtio_zalloc_buf(vdev, udev->recv.size, 16);
if (udev->recv.buffer == NULL)
{
vrterr("No enough memory\n");
return -ENOMEM;
}
udev->xmit.size = CONFIG_DRIVERS_VIRTIO_SERIAL_BUFSIZE;
udev->xmit.buffer = virtio_zalloc_buf(vdev, udev->xmit.size, 16);
if (udev->xmit.buffer == NULL)
{
vrterr("No enough memory\n");
ret = -ENOMEM;
goto err_with_recv;
}
/* Initialize the virtio device */
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER);
virtio_set_features(vdev, 0);
virtio_set_status(vdev, VIRTIO_CONFIG_FEATURES_OK);
vqnames[VIRTIO_SERIAL_RX] = "virtio_serial_rx";
vqnames[VIRTIO_SERIAL_TX] = "virtio_serial_tx";
callbacks[VIRTIO_SERIAL_RX] = virtio_serial_rxready;
callbacks[VIRTIO_SERIAL_TX] = virtio_serial_txdone;
ret = virtio_create_virtqueues(vdev, 0, VIRTIO_SERIAL_NUM, vqnames,
callbacks);
if (ret < 0)
{
vrterr("virtio_device_create_virtqueue failed, ret=%d\n", ret);
goto err_with_xmit;
}
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
return OK;
err_with_xmit:
virtio_free_buf(vdev, udev->xmit.buffer);
err_with_recv:
virtio_free_buf(vdev, udev->recv.buffer);
virtio_reset_device(vdev);
return ret;
}
/****************************************************************************
* Name: virtio_serial_uninit
****************************************************************************/
static void virtio_serial_uninit(FAR struct virtio_serial_priv_s *priv)
{
FAR struct virtio_device *vdev = priv->vdev;
virtio_reset_device(vdev);
virtio_delete_virtqueues(vdev);
virtio_free_buf(vdev, priv->udev.xmit.buffer);
virtio_free_buf(vdev, priv->udev.recv.buffer);
}
/****************************************************************************
* Name: virtio_serial_probe
****************************************************************************/
static int virtio_serial_probe(FAR struct virtio_device *vdev)
{
FAR struct virtio_serial_priv_s *priv;
int ret;
/* Alloc the virtio serial driver and uart buffer */
priv = kmm_zalloc(sizeof(*priv));
if (priv == NULL)
{
vrterr("No enough memory\n");
return -ENOMEM;
}
ret = virtio_serial_init(priv, vdev);
if (ret < 0)
{
vrterr("virtio_serial_init failed, ret=%d\n", ret);
goto err_with_priv;
}
/* Uart driver register */
snprintf(priv->name, NAME_MAX, "/dev/ttyV%d", g_virtio_serial_idx);
ret = uart_register(priv->name, &priv->udev);
if (ret < 0)
{
vrterr("uart_register failed, ret=%d\n", ret);
goto err_with_init;
}
g_virtio_serial_idx++;
return ret;
err_with_init:
virtio_serial_uninit(priv);
err_with_priv:
kmm_free(priv);
return ret;
}
/****************************************************************************
* Name: virtio_serial_remove
****************************************************************************/
static void virtio_serial_remove(FAR struct virtio_device *vdev)
{
FAR struct virtio_serial_priv_s *priv = vdev->priv;
unregister_driver(priv->name);
virtio_serial_uninit(priv);
kmm_free(priv);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: virtio_register_serial_driver
****************************************************************************/
int virtio_register_serial_driver(void)
{
int ret1 = virtio_register_driver(&g_virtio_serial_driver);
int ret2 = virtio_register_driver(&g_virtio_rprocserial_driver);
return ret1 < 0 ? ret1 : ret2;
}

View file

@ -1,5 +1,5 @@
/****************************************************************************
* arch/risc-v/src/qemu-rv/qemu_rv_virtio.c
* drivers/virtio/virtio-serial.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@ -18,23 +18,44 @@
*
****************************************************************************/
#ifndef __DRIVERS_VIRTIO_VIRTIO_SERIAL_H
#define __DRIVERS_VIRTIO_VIRTIO_SERIAL_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <syslog.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/virtio/virtio-mmio.h>
#ifdef CONFIG_DRIVERS_VIRTIO_SERIAL
/****************************************************************************
* Public Functions
* Pre-processor Definitions
****************************************************************************/
void riscv_netinitialize(void)
/****************************************************************************
* Public Data
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
virtio_mmio_init();
#else
#define EXTERN extern
#endif
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
int virtio_register_serial_driver(void);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_DRIVERS_VIRTIO_SERIAL */
#endif /* __DRIVERS_VIRTIO_VIRTIO_SERIAL_H */

339
drivers/virtio/virtio.c Normal file
View file

@ -0,0 +1,339 @@
/****************************************************************************
* drivers/virtio/virtio.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <debug.h>
#include <nuttx/mutex.h>
#include <nuttx/kmalloc.h>
#include <nuttx/virtio/virtio.h>
#include "virtio-blk.h"
#include "virtio-net.h"
#include "virtio-rng.h"
#include "virtio-serial.h"
/****************************************************************************
* Private Types
****************************************************************************/
struct virtio_bus_s
{
mutex_t lock; /* Lock for the list */
struct list_node device; /* Wait match virtio device list */
struct list_node driver; /* Virtio driver list */
};
struct virtio_device_item_s
{
struct list_node node; /* list node */
struct virtio_device *device; /* Pointer to the virtio device */
struct virtio_driver *driver; /* Pointer to the virtio driver that
* matched with current virtio device
*/
};
/****************************************************************************
* Private Data
****************************************************************************/
static struct virtio_bus_s g_virtio_bus =
{
NXMUTEX_INITIALIZER,
LIST_INITIAL_VALUE(g_virtio_bus.device),
LIST_INITIAL_VALUE(g_virtio_bus.driver),
};
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: virtio_alloc_buf
****************************************************************************/
FAR void *virtio_alloc_buf(FAR struct virtio_device *vdev,
size_t size, size_t align)
{
if (align == 0)
{
return kmm_malloc(size);
}
else
{
return kmm_memalign(align, size);
}
}
/****************************************************************************
* Name: virtio_zalloc_buf
****************************************************************************/
FAR void *virtio_zalloc_buf(FAR struct virtio_device *vdev,
size_t size, size_t align)
{
FAR void *ptr = virtio_alloc_buf(vdev, size, align);
if (ptr != NULL)
{
memset(ptr, 0, size);
}
return ptr;
}
/****************************************************************************
* Name: virtio_mmio_free_buf
****************************************************************************/
void virtio_free_buf(FAR struct virtio_device *vdev, FAR void *buf)
{
kmm_free(buf);
}
/****************************************************************************
* Name: virtio_register_drivers
****************************************************************************/
void virtio_register_drivers(void)
{
int ret = OK;
#ifdef CONFIG_DRIVERS_VIRTIO_BLK
ret = virtio_register_blk_driver();
if (ret < 0)
{
vrterr("virtio_register_blk_driver failed, ret=%d\n", ret);
}
#endif
#ifdef CONFIG_DRIVERS_VIRTIO_NET
ret = virtio_register_net_driver();
if (ret < 0)
{
vrterr("virtio_register_net_driver failed, ret=%d\n", ret);
}
#endif
#ifdef CONFIG_DRIVERS_VIRTIO_RNG
ret = virtio_register_rng_driver();
if (ret < 0)
{
vrterr("virtio_register_rng_driver failed, ret=%d\n", ret);
}
#endif
#ifdef CONFIG_DRIVERS_VIRTIO_SERIAL
ret = virtio_register_serial_driver();
if (ret < 0)
{
vrterr("virtio_serial_driver_init failed, ret=%d\n", ret);
}
#endif
UNUSED(ret);
}
/****************************************************************************
* Name: virtio_register_driver
****************************************************************************/
int virtio_register_driver(FAR struct virtio_driver *driver)
{
FAR struct list_node *node;
int ret;
DEBUGASSERT(driver != NULL && driver->probe != NULL &&
driver->remove != NULL);
ret = nxmutex_lock(&g_virtio_bus.lock);
if (ret < 0)
{
return ret;
}
/* Add the driver to the virtio_bus driver list */
list_add_tail(&g_virtio_bus.driver, &driver->node);
/* Match all the devices has registered in the virtio_bus */
list_for_every(&g_virtio_bus.device, node)
{
FAR struct virtio_device_item_s *item =
container_of(node, struct virtio_device_item_s, node);
FAR struct virtio_device *device = item->device;
if (driver->device == device->id.device)
{
/* If found the device in the device list, call driver probe,
* if probe success, assign item->driver to indicate the device
* matched.
*/
if (driver->probe(device) >= 0)
{
item->driver = driver;
}
}
}
nxmutex_unlock(&g_virtio_bus.lock);
return ret;
}
/****************************************************************************
* Name: virtio_unregister_driver
****************************************************************************/
int virtio_unregister_driver(FAR struct virtio_driver *driver)
{
FAR struct list_node *node;
int ret;
DEBUGASSERT(driver != NULL);
ret = nxmutex_lock(&g_virtio_bus.lock);
if (ret < 0)
{
return ret;
}
/* Find all the devices matched with driver in device list */
list_for_every(&g_virtio_bus.device, node)
{
FAR struct virtio_device_item_s *item =
container_of(node, struct virtio_device_item_s, node);
if (item->driver == driver)
{
/* 1. Call driver remove function;
* 2. Mark item->driver NULL to indicate the device unmatched;
*/
driver->remove(item->device);
item->driver = NULL;
}
}
/* Remove the driver from the driver list */
list_delete(&driver->node);
nxmutex_unlock(&g_virtio_bus.lock);
return ret;
}
/****************************************************************************
* Name: virtio_register_device
****************************************************************************/
int virtio_register_device(FAR struct virtio_device *device)
{
FAR struct virtio_device_item_s *item;
FAR struct list_node *node;
int ret;
item = kmm_zalloc(sizeof(*item));
if (item == NULL)
{
return -ENOMEM;
}
item->device = device;
ret = nxmutex_lock(&g_virtio_bus.lock);
if (ret < 0)
{
kmm_free(item);
return ret;
}
/* Add the device to the virtio_bus device list */
list_add_tail(&g_virtio_bus.device, &item->node);
/* Match the driver has registered in the virtio_bus */
list_for_every(&g_virtio_bus.driver, node)
{
FAR struct virtio_driver *driver =
container_of(node, struct virtio_driver, node);
if (driver->device == device->id.device)
{
/* If found the driver in the driver list, call driver probe,
* if probe success, assign item->driver to indicate the device
* matched.
*/
if (driver->probe(device) >= 0)
{
item->driver = driver;
}
break;
}
}
nxmutex_unlock(&g_virtio_bus.lock);
return ret;
}
/****************************************************************************
* Name: virtio_unregister_device
****************************************************************************/
int virtio_unregister_device(FAR struct virtio_device *device)
{
FAR struct list_node *node;
int ret;
ret = nxmutex_lock(&g_virtio_bus.lock);
if (ret < 0)
{
return ret;
}
/* Find the device in device list */
list_for_every(&g_virtio_bus.device, node)
{
FAR struct virtio_device_item_s *item =
container_of(node, struct virtio_device_item_s, node);
if (item->device == device)
{
/* Call driver remove and mark item->driver NULL to indicate
* the device unmatched
*/
item->driver->remove(device);
item->driver = NULL;
/* Remove the device from the device list and free memory */
list_delete(&item->node);
kmm_free(item);
break;
}
}
nxmutex_unlock(&g_virtio_bus.lock);
return ret;
}

View file

@ -25,119 +25,18 @@
* Included Files
****************************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include <arch/spinlock.h>
#ifdef CONFIG_DRIVERS_VIRTIO_MMIO
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* See virtio-v1.1-csprd01.pdf */
#define VIRTIO_MAGIC 0x74726976
#define VIRTIO_VERSION 0x2 /* NOTE: Legacy devices used 0x1 */
#define VIRTIO_DEV_NET 0x1
#define VIRTIO_DEV_BLK 0x2
#define VIRTIO_STATUS_ACKNOWLEDGE (1)
#define VIRTIO_STATUS_DRIVER (2)
#define VIRTIO_STATUS_DRIVER_OK (4)
#define VIRTIO_STATUS_FEATURES_OK (8)
#define VIRTQ_DESC_F_NEXT 1 /* marks a buffer as continuing */
#define VIRTQ_DESC_F_WRITE 2 /* marks a buffer as device write-only */
#define virtio_mb() SP_DMB()
#define virtio_getreg32(a) (FAR *(volatile FAR uint32_t *)(a))
#define virtio_putreg32(v,a) (FAR *(volatile FAR uint32_t *)(a) = (v))
/****************************************************************************
* Public Types
* Public Type Definitions
****************************************************************************/
/* Table 4.1 MMIO Device Register Layout */
struct virtio_mmio_regs
{
uint32_t magic_value; /* offset:0x000 */
uint32_t version; /* offset:0x004 */
uint32_t device_id; /* offset:0x008 */
uint32_t vendor_id; /* offset:0x00c */
uint32_t device_features; /* offset:0x010 */
uint32_t device_features_sel; /* offset:0x014 */
uint32_t _reserved0[2];
uint32_t driver_features; /* offset:0x020 */
uint32_t driver_features_sel; /* offset:0x024 */
uint32_t _reserved1[2];
uint32_t queue_sel; /* offset:0x030 */
uint32_t queue_num_max; /* offset:0x034 */
uint32_t queue_num; /* offset:0x038 */
uint32_t _reserved2[2];
uint32_t queue_ready; /* offset:0x044 */
uint32_t _reserved3[2];
uint32_t queue_notify; /* offset:0x050 */
uint32_t _reserved4[3];
uint32_t interrupt_status; /* offset:0x060 */
uint32_t interrupt_ack; /* offset:0x064 */
uint32_t _reserved5[2];
uint32_t status; /* offset:0x070 */
uint32_t _reserved6[3];
uint32_t queue_desc_low; /* offset:0x080 */
uint32_t queue_desc_high; /* offset:0x084 */
uint32_t _reserved7[2];
uint32_t queue_avail_low; /* offset:0x090 */
uint32_t queue_avail_high; /* offset:0x094 */
uint32_t _reserved8[2];
uint32_t queue_used_low; /* offset:0x0a0 */
uint32_t queue_used_high; /* offset:0x0a4 */
uint32_t _reserved9[21];
uint32_t config_generation; /* offset:0x0fc */
uint32_t config[0];
};
struct virtqueue_desc
{
uint64_t addr;
uint32_t len;
uint16_t flags;
uint16_t next;
};
struct virtqueue_avail
{
uint16_t flags;
uint16_t idx;
uint16_t ring[0];
};
struct virtqueue_used_elem
{
uint32_t id;
uint32_t len;
};
struct virtqueue_used
{
uint16_t flags;
uint16_t idx;
struct virtqueue_used_elem ring[0];
};
struct virtqueue
{
uint32_t len;
uint16_t last_used_idx;
FAR struct virtqueue_desc *desc;
FAR struct virtqueue_avail *avail;
FAR struct virtqueue_used *used;
FAR uint16_t *avail_event;
FAR void **desc_virt;
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
@ -150,21 +49,20 @@ extern "C"
#define EXTERN extern
#endif
FAR struct virtqueue *virtq_create(uint32_t len);
/****************************************************************************
* Name: virtio_register_mmio_device
*
* Description:
* Register virtio mmio device to the virtio bus
*
****************************************************************************/
uint32_t virtq_alloc_desc(FAR struct virtqueue *virtq,
uint32_t id,
FAR void *addr);
void virtq_add_to_mmio_device(FAR struct virtio_mmio_regs *regs,
FAR struct virtqueue *virtq,
uint32_t queue_sel);
void virtio_mmio_init(void);
int virtio_register_mmio_device(FAR void *regs, int irq);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_DRIVERS_VIRTIO_MMIO */
#endif /* __INCLUDE_NUTTX_VIRTIO_VIRTIO_MMIO_H */

View file

@ -0,0 +1,107 @@
/****************************************************************************
* include/nuttx/virtio/virtio.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_VIRTIO_VIRTIO_H
#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdint.h>
#include <nuttx/compiler.h>
#include <nuttx/list.h>
#ifdef CONFIG_DRIVERS_VIRTIO
#include <openamp/open_amp.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define virtio_read_config_member(vdev, structname, member, ptr) \
virtio_read_config((vdev), offsetof(structname, member), \
(ptr), sizeof(*(ptr)));
#define virtio_write_config_member(vdev, structname, member, ptr) \
virtio_write_config((vdev), offsetof(structname, member), \
(ptr), sizeof(*(ptr)));
/****************************************************************************
* Public Type Definitions
****************************************************************************/
struct virtio_driver
{
struct list_node node;
uint32_t device; /* device id */
CODE int (*probe)(FAR struct virtio_device *vdev);
CODE void (*remove)(FAR struct virtio_device *vdev);
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/* Driver and device register/unregister function */
int virtio_register_driver(FAR struct virtio_driver *driver);
int virtio_register_device(FAR struct virtio_device *device);
int virtio_unregister_driver(FAR struct virtio_driver *driver);
int virtio_unregister_device(FAR struct virtio_device *device);
/* Virtio alloc/free buffer API
* NOTE:
* For now, these three apis are implemented in NuttX, and direclty mapping
* to kmm_memalgin/kmm_free, so it's only compatible with virtio mmio
* transport for now. After the virtio remoteproc transport layer completed,
* these three apis should be moved to OpenAmp, and different transport layer
* provide different implementation.
*/
FAR void *virtio_alloc_buf(FAR struct virtio_device *vdev,
size_t size, size_t align);
FAR void *virtio_zalloc_buf(FAR struct virtio_device *vdev,
size_t size, size_t align);
void virtio_free_buf(FAR struct virtio_device *vdev, FAR void *buf);
/* Virtio driver initailied function, called in NuttX driver_intialize() */
void virtio_register_drivers(void);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_DRIVERS_VIRTIO */
#endif /* __INCLUDE_NUTTX_VIRTIO_VIRTIO_H */