diff --git a/arch/arm64/src/qemu/Make.defs b/arch/arm64/src/qemu/Make.defs index 79ea095e197..2d70132802c 100644 --- a/arch/arm64/src/qemu/Make.defs +++ b/arch/arm64/src/qemu/Make.defs @@ -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 diff --git a/arch/risc-v/src/qemu-rv/Make.defs b/arch/risc-v/src/qemu-rv/Make.defs index 15cc9271af7..3c5982a8b0a 100644 --- a/arch/risc-v/src/qemu-rv/Make.defs +++ b/arch/risc-v/src/qemu-rv/Make.defs @@ -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 diff --git a/boards/arm64/qemu/qemu-armv8a/README.txt b/boards/arm64/qemu/qemu-armv8a/README.txt index 73d0d22d9ee..1beb1396d5a 100644 --- a/boards/arm64/qemu/qemu-armv8a/README.txt +++ b/boards/arm64/qemu/qemu-armv8a/README.txt @@ -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) diff --git a/boards/arm64/qemu/qemu-armv8a/configs/netnsh/defconfig b/boards/arm64/qemu/qemu-armv8a/configs/netnsh/defconfig index fb444eec989..799034a8151 100644 --- a/boards/arm64/qemu/qemu-armv8a/configs/netnsh/defconfig +++ b/boards/arm64/qemu/qemu-armv8a/configs/netnsh/defconfig @@ -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 diff --git a/boards/arm64/qemu/qemu-armv8a/configs/netnsh_hv/defconfig b/boards/arm64/qemu/qemu-armv8a/configs/netnsh_hv/defconfig index fe6d7f488a7..3f8787235a0 100644 --- a/boards/arm64/qemu/qemu-armv8a/configs/netnsh_hv/defconfig +++ b/boards/arm64/qemu/qemu-armv8a/configs/netnsh_hv/defconfig @@ -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 diff --git a/boards/arm64/qemu/qemu-armv8a/configs/netnsh_smp/defconfig b/boards/arm64/qemu/qemu-armv8a/configs/netnsh_smp/defconfig index 70b2212bc9d..607aeca6249 100644 --- a/boards/arm64/qemu/qemu-armv8a/configs/netnsh_smp/defconfig +++ b/boards/arm64/qemu/qemu-armv8a/configs/netnsh_smp/defconfig @@ -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 diff --git a/boards/arm64/qemu/qemu-armv8a/configs/netnsh_smp_hv/defconfig b/boards/arm64/qemu/qemu-armv8a/configs/netnsh_smp_hv/defconfig index c347b4177f2..bbda07e5c65 100644 --- a/boards/arm64/qemu/qemu-armv8a/configs/netnsh_smp_hv/defconfig +++ b/boards/arm64/qemu/qemu-armv8a/configs/netnsh_smp_hv/defconfig @@ -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 diff --git a/boards/arm64/qemu/qemu-armv8a/src/qemu_bringup.c b/boards/arm64/qemu/qemu-armv8a/src/qemu_bringup.c index a7523c5a334..b3f850f2bd1 100644 --- a/boards/arm64/qemu/qemu-armv8a/src/qemu_bringup.c +++ b/boards/arm64/qemu/qemu-armv8a/src/qemu_bringup.c @@ -28,9 +28,42 @@ #include #include +#include #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; } diff --git a/boards/risc-v/qemu-rv/rv-virt/README.txt b/boards/risc-v/qemu-rv/rv-virt/README.txt index 35d2dee7672..cfc3a4168d7 100644 --- a/boards/risc-v/qemu-rv/rv-virt/README.txt +++ b/boards/risc-v/qemu-rv/rv-virt/README.txt @@ -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 \ - -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 + -global virtio-mmio.force-legacy=false \ + -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.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 \ - -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 + -global virtio-mmio.force-legacy=false \ + -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.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 diff --git a/boards/risc-v/qemu-rv/rv-virt/configs/knetnsh64/defconfig b/boards/risc-v/qemu-rv/rv-virt/configs/knetnsh64/defconfig index e089bc5ebd1..70d5a7ac713 100644 --- a/boards/risc-v/qemu-rv/rv-virt/configs/knetnsh64/defconfig +++ b/boards/risc-v/qemu-rv/rv-virt/configs/knetnsh64/defconfig @@ -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 diff --git a/boards/risc-v/qemu-rv/rv-virt/configs/knetnsh64_smp/defconfig b/boards/risc-v/qemu-rv/rv-virt/configs/knetnsh64_smp/defconfig index c9dfc6ee904..16554fa8e42 100644 --- a/boards/risc-v/qemu-rv/rv-virt/configs/knetnsh64_smp/defconfig +++ b/boards/risc-v/qemu-rv/rv-virt/configs/knetnsh64_smp/defconfig @@ -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 diff --git a/boards/risc-v/qemu-rv/rv-virt/configs/netnsh/defconfig b/boards/risc-v/qemu-rv/rv-virt/configs/netnsh/defconfig index 457349c6053..9190a2c1454 100644 --- a/boards/risc-v/qemu-rv/rv-virt/configs/netnsh/defconfig +++ b/boards/risc-v/qemu-rv/rv-virt/configs/netnsh/defconfig @@ -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 diff --git a/boards/risc-v/qemu-rv/rv-virt/configs/netnsh64/defconfig b/boards/risc-v/qemu-rv/rv-virt/configs/netnsh64/defconfig index 5e409595ff1..c5aab61c145 100644 --- a/boards/risc-v/qemu-rv/rv-virt/configs/netnsh64/defconfig +++ b/boards/risc-v/qemu-rv/rv-virt/configs/netnsh64/defconfig @@ -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 diff --git a/boards/risc-v/qemu-rv/rv-virt/configs/netnsh64_smp/defconfig b/boards/risc-v/qemu-rv/rv-virt/configs/netnsh64_smp/defconfig index fcbb37e0be7..109ef2b17c8 100644 --- a/boards/risc-v/qemu-rv/rv-virt/configs/netnsh64_smp/defconfig +++ b/boards/risc-v/qemu-rv/rv-virt/configs/netnsh64_smp/defconfig @@ -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 diff --git a/boards/risc-v/qemu-rv/rv-virt/configs/netnsh_smp/defconfig b/boards/risc-v/qemu-rv/rv-virt/configs/netnsh_smp/defconfig index 973f00952f3..56806fba3f8 100644 --- a/boards/risc-v/qemu-rv/rv-virt/configs/netnsh_smp/defconfig +++ b/boards/risc-v/qemu-rv/rv-virt/configs/netnsh_smp/defconfig @@ -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 diff --git a/boards/risc-v/qemu-rv/rv-virt/src/qemu_rv_appinit.c b/boards/risc-v/qemu-rv/rv-virt/src/qemu_rv_appinit.c index ffb00398ceb..e8599d2b194 100644 --- a/boards/risc-v/qemu-rv/rv-virt/src/qemu_rv_appinit.c +++ b/boards/risc-v/qemu-rv/rv-virt/src/qemu_rv_appinit.c @@ -30,9 +30,46 @@ #include #include +#include #include +/**************************************************************************** + * 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 } diff --git a/drivers/drivers_initialize.c b/drivers/drivers_initialize.c index 2539138e1e8..2a523648952 100644 --- a/drivers/drivers_initialize.c +++ b/drivers/drivers_initialize.c @@ -44,6 +44,7 @@ #include #include #include +#include /**************************************************************************** * 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 } diff --git a/drivers/virtio/CMakeLists.txt b/drivers/virtio/CMakeLists.txt new file mode 100644 index 00000000000..ebd9ea25c5b --- /dev/null +++ b/drivers/virtio/CMakeLists.txt @@ -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}) diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 64c31bfb445..ef0777813f4 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -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 diff --git a/drivers/virtio/Make.defs b/drivers/virtio/Make.defs index 889bdcd1385..e4051eecb72 100644 --- a/drivers/virtio/Make.defs +++ b/drivers/virtio/Make.defs @@ -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 diff --git a/drivers/virtio/virtio-blk.c b/drivers/virtio/virtio-blk.c new file mode 100644 index 00000000000..711c8ec46c9 --- /dev/null +++ b/drivers/virtio/virtio-blk.c @@ -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 +#include +#include + +#include +#include +#include +#include +#include + +#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); +} diff --git a/drivers/virtio/virtio-mmio-blk.h b/drivers/virtio/virtio-blk.h similarity index 79% rename from drivers/virtio/virtio-mmio-blk.h rename to drivers/virtio/virtio-blk.h index 7f4749f5ad7..d51ffd128aa 100644 --- a/drivers/virtio/virtio-mmio-blk.h +++ b/drivers/virtio/virtio-blk.h @@ -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 */ diff --git a/drivers/virtio/virtio-mmio-blk.c b/drivers/virtio/virtio-mmio-blk.c deleted file mode 100644 index 5fbd92c81cf..00000000000 --- a/drivers/virtio/virtio-mmio-blk.c +++ /dev/null @@ -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 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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(®s->status) | VIRTIO_STATUS_FEATURES_OK, - ®s->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(®s->status) | VIRTIO_STATUS_DRIVER_OK, - ®s->status); - virtio_mb(); - - g_virtblk_ninterfaces++; - - return ret; -} - -#endif /* CONFIG_DRIVERS_VIRTIO_BLK */ diff --git a/drivers/virtio/virtio-mmio-net.c b/drivers/virtio/virtio-mmio-net.c deleted file mode 100644 index 1d8b829d353..00000000000 --- a/drivers/virtio/virtio-mmio-net.c +++ /dev/null @@ -1,1293 +0,0 @@ -/**************************************************************************** - * drivers/virtio/virtio-mmio-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 - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "virtio-mmio-net.h" - -#ifdef CONFIG_NET_PKT -# include -#endif - -#ifdef CONFIG_DRIVERS_VIRTIO_NET - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* Work queue support is required. */ - -#if !defined(CONFIG_SCHED_WORKQUEUE) -# error Work queue support is required in this configuration (CONFIG_SCHED_WORKQUEUE) -#else - -/* The low priority work queue is preferred. If it is not enabled, LPWORK - * will be the same as HPWORK. - * - * NOTE: However, the network should NEVER run on the high priority work - * queue! That queue is intended only to service short back end interrupt - * processing that never suspends. Suspending the high priority work queue - * may bring the system to its knees! - */ - -#define ETHWORK LPWORK - -/* VIRTIO_NET_NINTERFACES determines the number of - * physical interfaces that will be supported. - */ - -#ifndef VIRTIO_NET_NINTERFACES -# define VIRTIO_NET_NINTERFACES 1 -#endif - -/* TX timeout = 1 minute */ - -#define VIRTNET_TXTIMEOUT (60*CLK_TCK) - -/* virtio net related definition */ - -#define VIRTIO_NET_HDRLEN 10 - -/* Packet buffer size */ - -#define PKTBUF_SIZE (VIRTIO_NET_HDRLEN + MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE) - -/* This is a helper pointer for accessing the contents of Ethernet header */ - -#define BUF ((struct eth_hdr_s *)priv->vnet_dev.d_buf) - -#define VIRTIO_NET_Q_RX 0 -#define VIRTIO_NET_Q_TX 1 - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct virtnet_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; -}; - -/* The virtnet_driver_s encapsulates all state information for - * a single hardware interface - */ - -struct virtnet_driver_s -{ - bool vnet_bifup; /* true:ifup false:ifdown */ - int irq; - - FAR struct virtio_mmio_regs *regs; /* virtio_mmio registers */ - FAR struct virtqueue *txq; /* TX queue */ - FAR struct virtqueue *rxq; /* RX queue */ - - struct wdog_s vnet_txtimeout; /* TX timeout timer */ - struct work_s vnet_irqwork; /* For deferring interrupt work to the work queue */ - struct work_s vnet_pollwork; /* For deferring poll work to the work queue */ - - /* This holds the information visible to the NuttX network */ - - struct net_driver_s vnet_dev; /* Interface understood by the network */ -}; - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* These statically allocated structures would mean that only a single - * instance of the device could be supported. In order to support multiple - * devices instances, this data would have to be allocated dynamically. - */ - -/* A single packet buffer per device is used in this example. There might - * be multiple packet buffers in a more complex, pipelined design. Many - * contemporary Ethernet interfaces, for example, use multiple, linked DMA - * descriptors in rings to implement such a pipeline. This example assumes - * much simpler hardware that simply handles one packet at a time. - * - * NOTE that if VIRTIO_NET_NINTERFACES were greater than 1, - * you would need a minimum on one packet buffer per instance. - * Much better to be allocated dynamically in cases where more than - * one are needed. - */ - -static uint16_t -g_pktbuf[VIRTIO_NET_NINTERFACES][(PKTBUF_SIZE + 1) / 2]; - -/* Driver state structure */ - -static struct virtnet_driver_s g_virtnet[VIRTIO_NET_NINTERFACES]; - -static uint32_t g_ninterfaces = 0; - -/* Common virtnet header */ - -static struct virtnet_hdr_s g_hdr; - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/* Common TX logic */ - -static int virtnet_transmit(FAR struct virtnet_driver_s *priv); -static int virtnet_txpoll(FAR struct net_driver_s *dev); - -/* Interrupt handling */ - -static void virtnet_reply(FAR struct virtnet_driver_s *priv); -static void virtnet_receive(FAR struct virtnet_driver_s *priv); -static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv); - -static void virtnet_interrupt_work(FAR void *arg); -static int virtnet_interrupt(int irq, FAR void *context, FAR void *arg); - -/* Watchdog timer expirations */ - -static void virtnet_txtimeout_work(FAR void *arg); -static void virtnet_txtimeout_expiry(FAR wdparm_t arg); - -/* NuttX callback functions */ - -static int virtnet_ifup(FAR struct net_driver_s *dev); -static int virtnet_ifdown(FAR struct net_driver_s *dev); - -static void virtnet_txavail_work(FAR void *arg); -static int virtnet_txavail(FAR struct net_driver_s *dev); - -#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) -static int virtnet_addmac(FAR struct net_driver_s *dev, - FAR const uint8_t *mac); -#ifdef CONFIG_NET_MCASTGROUP -static int virtnet_rmmac(FAR struct net_driver_s *dev, - FAR const uint8_t *mac); -#endif -#ifdef CONFIG_NET_ICMPv6 -static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv); -#endif -#endif -#ifdef CONFIG_NETDEV_IOCTL -static int virtnet_ioctl(FAR struct net_driver_s *dev, int cmd, - unsigned long arg); -#endif - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: virtnet_cmd_status - ****************************************************************************/ - -#ifdef DEBUG -static void virtnet_cmd_status(FAR struct virtnet_driver_s *priv) -{ - FAR struct virtio_mmio_regs *regs = priv->regs; - - vrtinfo("virtnet at %p\n", regs); - vrtinfo(" status=0x%" PRIx32 "\n", - virtio_getreg32(®s->status)); - vrtinfo(" interrupt_status=0x%" PRIx32 "\n", - virtio_getreg32(®s->interrupt_status)); - - virtio_putreg32(VIRTIO_NET_Q_TX, ®s->queue_sel); - virtio_mb(); - - vrtinfo(" txq: avail->idx=%d, used->idx=%d, ready=0x%" PRIx32 "\n", - priv->txq->avail->idx, priv->txq->used->idx, - virtio_getreg32(®s->queue_ready)); - - virtio_putreg32(VIRTIO_NET_Q_RX, ®s->queue_sel); - virtio_mb(); - - vrtinfo(" rxq: avail->idx=%d used->idx=%d ready=0x%" PRIx32 "\n", - priv->rxq->avail->idx, priv->rxq->used->idx, - virtio_getreg32(®s->queue_ready)); -} -#endif - -/**************************************************************************** - * Name: virtnet_add_packets_to_rxq - ****************************************************************************/ - -static void virtnet_add_packets_to_rxq(FAR struct virtnet_driver_s *priv) -{ - FAR struct virtqueue *rxq = priv->rxq; - FAR uint8_t *pkt; - uint16_t d1; - uint32_t i; - - vrtinfo("+++ rxq->len=%" PRId32 " rxq->avail->idx=%d \n", - rxq->len, rxq->avail->idx); - DEBUGASSERT(rxq->avail->idx == 0); - - for (i = 0; i < rxq->len / 2; i++) - { - pkt = kmm_memalign(16, PKTBUF_SIZE); - ASSERT(pkt); - - /* Allocate a new descriptor for header + packet */ - - d1 = virtq_alloc_desc(rxq, i, pkt); - - /* Set up the descriptor */ - - rxq->desc[d1].len = PKTBUF_SIZE; - rxq->desc[d1].flags = VIRTQ_DESC_F_WRITE; - - /* Set the descriptor to avail->ring */ - - rxq->avail->ring[i] = d1; - - /* Increment the avail->idx */ - - virtio_mb(); - rxq->avail->idx += 1; - } - - vrtinfo("+++ virtq->avail->idx=%d \n", rxq->avail->idx); - vrtinfo("+++ virtq->used->idx=%d \n", rxq->used->idx); -} - -/**************************************************************************** - * Name: virtnet_transmit - * - * Description: - * Start hardware transmission. Called either from the txdone interrupt - * handling or from watchdog based polling. - * - * Input Parameters: - * priv - Reference to the driver state structure - * - * Returned Value: - * OK on success; a negated errno on failure - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static int virtnet_transmit(FAR struct virtnet_driver_s *priv) -{ - FAR void *pkt = priv->vnet_dev.d_buf; - uint32_t len = priv->vnet_dev.d_len; - uint16_t idx = priv->txq->avail->idx; - uint16_t d1; - uint16_t d2; - - /* Send the packet: - * address=priv->vnet_dev.d_buf, length=priv->vnet_dev.d_len - */ - - /* Increment statistics */ - - NETDEV_TXPACKETS(priv->vnet_dev); - - vrtinfo("=== Sending packet, length: %d\n", priv->vnet_dev.d_len); - -#ifdef DEBUG - virtnet_cmd_status(priv); -#endif - - /* Allocate new descriptors for header and packet */ - - d1 = virtq_alloc_desc(priv->txq, idx, (FAR void *)&g_hdr); - d2 = virtq_alloc_desc(priv->txq, idx + 1, pkt); - DEBUGASSERT(d1 != d2); - - /* Set up the descriptor for packet */ - - priv->txq->desc[d2].len = len; - priv->txq->desc[d2].flags = 0; - - /* Set up the descriptor for header */ - - priv->txq->desc[d1].len = VIRTIO_NET_HDRLEN; - priv->txq->desc[d1].flags = VIRTQ_DESC_F_NEXT; - priv->txq->desc[d1].next = d2; - - /* Set the first descriptor to the avail->ring */ - - priv->txq->avail->ring[d1] = d1; - - /* Increment the avail->idx for each two descriptors */ - - virtio_mb(); - priv->txq->avail->idx += 1; - - vrtinfo("*** d1=%d, d2=%d, txq->avail->idx=%d\n", - d1, d2, priv->txq->avail->idx); - - virtio_putreg32(VIRTIO_NET_Q_TX, &priv->regs->queue_notify); - - vrtinfo("*** finish updating queue_notify\n"); - - /* Setup the TX timeout watchdog (perhaps restarting the timer) */ - - wd_start(&priv->vnet_txtimeout, VIRTNET_TXTIMEOUT, - virtnet_txtimeout_expiry, (wdparm_t)priv); - - /* Wait for completion */ - - while (priv->txq->avail->idx != priv->txq->used->idx) - { - virtio_mb(); - } - - return OK; -} - -/**************************************************************************** - * Name: virtnet_txpoll - * - * Description: - * The transmitter is available, check if the network has any outgoing - * packets ready to send. This is a callback from devif_poll(). - * devif_poll() may be called: - * - * 1. When the preceding TX packet send is complete, - * 2. When the preceding TX packet send timesout and the interface is reset - * 3. During normal TX polling - * - * Input Parameters: - * dev - Reference to the NuttX driver state structure - * - * Returned Value: - * Always OK - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static int virtnet_txpoll(FAR struct net_driver_s *dev) -{ - FAR struct virtnet_driver_s *priv = - (FAR struct virtnet_driver_s *)dev->d_private; - - /* If the polling resulted in data that should be sent out on the network, - * the field d_len is set to a value > 0. - */ - - vrtinfo("Poll result: d_len=%d\n", priv->vnet_dev.d_len); - - /* Send the packet */ - - virtnet_reply(priv); - - /* NOTE: Since virtnet_transmit() now waits for TX completion, - * this method should return OK to continue. - */ - - return OK; -} - -/**************************************************************************** - * Name: virtnet_reply - * - * Description: - * After a packet has been received and dispatched to the network, it - * may return return with an outgoing packet. This function checks for - * that case and performs the transmission if necessary. - * - * Input Parameters: - * priv - Reference to the driver state structure - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static void virtnet_reply(FAR struct virtnet_driver_s *priv) -{ - /* If the packet dispatch resulted in data that should be sent out on the - * network, the field d_len will set to a value > 0. - */ - - if (priv->vnet_dev.d_len > 0) - { - /* And send the packet */ - - virtnet_transmit(priv); - } -} - -/**************************************************************************** - * Function: virtnet_rxdispatch - * - * Description: - * A new Rx packet was received; dispatch that packet to the network layer - * as necessary. - * - * Input Parameters: - * priv - Reference to the driver state structure - * - * Returned Value: - * None - * - * Assumptions: - * Global interrupts are disabled by interrupt handling logic. - * - ****************************************************************************/ - -static void virtnet_rxdispatch(FAR struct virtnet_driver_s *priv) -{ - /* Update statistics */ - - NETDEV_RXPACKETS(&priv->dev); - -#ifdef CONFIG_NET_PKT - /* When packet sockets are enabled, feed the frame into the tap */ - - pkt_input(&priv->vnet_dev); -#endif - -#ifdef CONFIG_NET_IPv4 - /* Check for an IPv4 packet */ - - if (BUF->type == HTONS(ETHTYPE_IP)) - { - vrtinfo("IPv4 frame\n"); - NETDEV_RXIPV4(&priv->vnet_dev); - - /* Receive an IPv4 packet from the network device */ - - vrtinfo("+++ call ipv4_input() d_len=%d\n", priv->vnet_dev.d_len); - ipv4_input(&priv->vnet_dev); - - /* Check for a reply to the IPv4 packet */ - - virtnet_reply(priv); - } - else -#endif -#ifdef CONFIG_NET_IPv6 - /* Check for an IPv6 packet */ - - if (BUF->type == HTONS(ETHTYPE_IP6)) - { - vrtinfo("IPv6 frame\n"); - NETDEV_RXIPV6(&priv->vnet_dev); - - /* Dispatch IPv6 packet to the network layer */ - - ipv6_input(&priv->vnet_dev); - - /* Check for a reply to the IPv6 packet */ - - virtnet_reply(priv); - } - else -#endif -#ifdef CONFIG_NET_ARP - /* Check for an ARP packet */ - - if (BUF->type == HTONS(ETHTYPE_ARP)) - { - /* Dispatch ARP packet to the network layer */ - - arp_input(&priv->vnet_dev); - NETDEV_RXARP(&priv->vnet_dev); - - /* If the above function invocation resulted in data that should be - * sent out on the network, the field d_len will set to a value - * > 0. - */ - - if (priv->vnet_dev.d_len > 0) - { - virtnet_transmit(priv); - } - } - else -#endif - { - NETDEV_RXDROPPED(&priv->vnet_dev); - - vrtwarn("+++ dropped BUF->type=0x%x \n", BUF->type); - DEBUGASSERT(0 != BUF->type); - } -} - -/**************************************************************************** - * Name: virtnet_receive - * - * Description: - * An interrupt was received indicating the availability of a new RX packet - * - * Input Parameters: - * priv - Reference to the driver state structure - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static void virtnet_receive(FAR struct virtnet_driver_s *priv) -{ - FAR uint8_t *buf = priv->vnet_dev.d_buf; - uint16_t used = priv->rxq->used->idx; - uint16_t idx; - - vrtinfo("+++ rxq->last_used_idx=%d: rxq->used->idx=%d\n", - priv->rxq->last_used_idx, priv->rxq->used->idx); - - for (idx = priv->rxq->last_used_idx; idx != used; idx++) - { - uint16_t id = idx % priv->rxq->len; - uint16_t d1 = priv->rxq->used->ring[id].id; /* index for header + packet */ - uint32_t len = priv->rxq->used->ring[id].len; - uint16_t flags = priv->rxq->desc[d1].flags; - - vrtinfo("+++ idx=%d id=%d used->idx=%d: d1=%d f=%d len=%" PRId32 "\n", - idx, id, priv->rxq->used->idx, d1, flags, len); - - /* Set the packet info to d_buf and set d_len */ - - priv->vnet_dev.d_buf = priv->rxq->desc_virt[d1] + VIRTIO_NET_HDRLEN; - priv->vnet_dev.d_len = len - VIRTIO_NET_HDRLEN; - - vrtinfo("Receiving packet, pktlen: %d\n", priv->vnet_dev.d_len); - - /* Dispatch the incoming packet */ - - virtnet_rxdispatch(priv); - - /* Set the descriptor back into the avail queue */ - - id = priv->rxq->avail->idx % priv->rxq->len; - priv->rxq->avail->ring[id] = d1; - - virtio_mb(); - priv->rxq->avail->idx += 1; - - vrtinfo("+++ rxq->avail->idx=%d\n", priv->rxq->avail->idx); - } - - priv->rxq->last_used_idx = used; - vrtinfo("+++ rxq->last_used_idx=%d\n", priv->rxq->last_used_idx); - - priv->vnet_dev.d_buf = buf; -} - -/**************************************************************************** - * Name: virtnet_interrupt_work - * - * Description: - * Perform interrupt related work from the worker thread - * - * Input Parameters: - * arg - The argument passed when work_queue() was called. - * - * Returned Value: - * OK on success - * - * Assumptions: - * Runs on a worker thread. - * - ****************************************************************************/ - -static void virtnet_interrupt_work(FAR void *arg) -{ - FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg; - - /* Lock the network and serialize driver operations if necessary. - * NOTE: Serialization is only required in the case where the driver work - * is performed on an LP worker thread and where more than one LP worker - * thread has been configured. - */ - - net_lock(); - - /* Process incoming packets */ - - virtnet_receive(priv); - - net_unlock(); - - if (priv->rxq->last_used_idx == priv->rxq->used->idx) - { - /* Re-enable Ethernet interrupts */ - - priv->rxq->avail->flags = 0; - priv->txq->avail->flags = 0; - virtio_mb(); - - virtio_putreg32(VIRTIO_NET_Q_RX, &priv->regs->queue_notify); - } - else if (work_available(&priv->vnet_irqwork)) - { - work_queue(ETHWORK, &priv->vnet_irqwork, - virtnet_interrupt_work, priv, 0); - } -} - -/**************************************************************************** - * Name: virtnet_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 virtnet_interrupt(int irq, FAR void *context, FAR void *arg) -{ - FAR struct virtnet_driver_s *priv = (FAR struct virtnet_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); - vrtinfo("+++ called (stat=0x%" PRIx32 ")\n", stat); - - /* Disable further Ethernet interrupts. Because Ethernet interrupts are - * also disabled if the TX timeout event occurs, there can be no race - * condition here. - */ - - priv->rxq->avail->flags = 1; - priv->txq->avail->flags = 1; - virtio_mb(); - - /* Schedule to perform the interrupt processing on the worker thread. */ - - if (work_available(&priv->vnet_irqwork)) - { - work_queue(ETHWORK, &priv->vnet_irqwork, - virtnet_interrupt_work, priv, 0); - } - - return OK; -} - -/**************************************************************************** - * Name: virtnet_txtimeout_work - * - * Description: - * Perform TX timeout related work from the worker thread - * - * Input Parameters: - * arg - The argument passed when work_queue() as called. - * - * Returned Value: - * OK on success - * - ****************************************************************************/ - -static void virtnet_txtimeout_work(FAR void *arg) -{ - FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg; - - /* Lock the network and serialize driver operations if necessary. - * NOTE: Serialization is only required in the case where the driver work - * is performed on an LP worker thread and where more than one LP worker - * thread has been configured. - */ - - net_lock(); - - /* Increment statistics and dump debug info */ - - NETDEV_TXTIMEOUTS(priv->vnet_dev); - - /* Then reset the hardware */ - - /* Then poll the network for new XMIT data */ - - devif_poll(&priv->vnet_dev, virtnet_txpoll); - net_unlock(); -} - -/**************************************************************************** - * Name: virtnet_txtimeout_expiry - * - * Description: - * Our TX watchdog timed out. Called from the timer interrupt handler. - * The last TX never completed. Reset the hardware and start again. - * - * Input Parameters: - * arg - The argument - * - * Returned Value: - * None - * - * Assumptions: - * Runs in the context of a the timer interrupt handler. Local - * interrupts are disabled by the interrupt logic. - * - ****************************************************************************/ - -static void virtnet_txtimeout_expiry(wdparm_t arg) -{ - FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg; - -#ifdef TODO - /* Disable further Ethernet interrupts. This will prevent some race - * conditions with interrupt work. There is still a potential race - * condition with interrupt work that is already queued and in progress. - */ -#endif - - /* Schedule to perform the TX timeout processing on the worker thread. */ - - if (work_available(&priv->vnet_irqwork)) - { - work_queue(ETHWORK, &priv->vnet_irqwork, - virtnet_txtimeout_work, priv, 0); - } -} - -/**************************************************************************** - * Name: virtnet_ifup - * - * Description: - * NuttX Callback: Bring up the Ethernet interface when an IP address is - * provided - * - * Input Parameters: - * dev - Reference to the NuttX driver state structure - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static int virtnet_ifup(FAR struct net_driver_s *dev) -{ - FAR struct virtnet_driver_s *priv = - (FAR struct virtnet_driver_s *)dev->d_private; - -#ifdef CONFIG_NET_IPv4 - vrtinfo("Bringing up: %d.%d.%d.%d\n", - (int)dev->d_ipaddr & 0xff, - (int)(dev->d_ipaddr >> 8) & 0xff, - (int)(dev->d_ipaddr >> 16) & 0xff, - (int)dev->d_ipaddr >> 24); -#endif -#ifdef CONFIG_NET_IPv6 - vrtinfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", - dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2], - dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5], - dev->d_ipv6addr[6], dev->d_ipv6addr[7]); -#endif - - /* Initialize PHYs, Ethernet interface, and setup up Ethernet interrupts */ - - /* Instantiate MAC address from - * priv->vnet_dev.d_mac.ether.ether_addr_octet - */ - -#ifdef CONFIG_NET_ICMPv6 - /* Set up IPv6 multicast address filtering */ - - virtnet_ipv6multicast(priv); -#endif - - /* Enable the Ethernet interrupt */ - - priv->vnet_bifup = true; - up_enable_irq(priv->irq); - return OK; -} - -/**************************************************************************** - * Name: virtnet_ifdown - * - * Description: - * NuttX Callback: Stop the interface. - * - * Input Parameters: - * dev - Reference to the NuttX driver state structure - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static int virtnet_ifdown(FAR struct net_driver_s *dev) -{ - FAR struct virtnet_driver_s *priv = - (FAR struct virtnet_driver_s *)dev->d_private; - irqstate_t flags; - - /* Disable the Ethernet interrupt */ - - flags = enter_critical_section(); - up_disable_irq(priv->irq); - - /* Cancel the TX timeout timers */ - - wd_cancel(&priv->vnet_txtimeout); - - /* Put the EMAC in its reset, non-operational state. This should be - * a known configuration that will guarantee the virtnet_ifup() always - * successfully brings the interface back up. - */ - - /* Mark the device "down" */ - - priv->vnet_bifup = false; - leave_critical_section(flags); - return OK; -} - -/**************************************************************************** - * Name: virtnet_txavail_work - * - * Description: - * Perform an out-of-cycle poll on the worker thread. - * - * Input Parameters: - * arg - Reference to the NuttX driver state structure (cast to void*) - * - * Returned Value: - * None - * - * Assumptions: - * Runs on a work queue thread. - * - ****************************************************************************/ - -static void virtnet_txavail_work(FAR void *arg) -{ - FAR struct virtnet_driver_s *priv = (FAR struct virtnet_driver_s *)arg; - - /* Lock the network and serialize driver operations if necessary. - * NOTE: Serialization is only required in the case where the driver work - * is performed on an LP worker thread and where more than one LP worker - * thread has been configured. - */ - - net_lock(); - - /* Ignore the notification if the interface is not yet up */ - - if (priv->vnet_bifup) - { - /* Check if there is room in the hardware to hold another packet. */ - - /* If so, then poll the network for new XMIT data */ - - devif_poll(&priv->vnet_dev, virtnet_txpoll); - } - - net_unlock(); -} - -/**************************************************************************** - * Name: virtnet_txavail - * - * Description: - * Driver callback invoked when new TX data is available. This is a - * stimulus perform an out-of-cycle poll and, thereby, reduce the TX - * latency. - * - * Input Parameters: - * dev - Reference to the NuttX driver state structure - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static int virtnet_txavail(FAR struct net_driver_s *dev) -{ - FAR struct virtnet_driver_s *priv = - (FAR struct virtnet_driver_s *)dev->d_private; - - /* Is our single work structure available? It may not be if there are - * pending interrupt actions and we will have to ignore the Tx - * availability action. - */ - - if (work_available(&priv->vnet_pollwork)) - { - /* Schedule to serialize the poll on the worker thread. */ - - work_queue(ETHWORK, &priv->vnet_pollwork, - virtnet_txavail_work, priv, 0); - } - - return OK; -} - -/**************************************************************************** - * Name: virtnet_addmac - * - * Description: - * NuttX Callback: Add the specified MAC address to the hardware multicast - * address filtering - * - * Input Parameters: - * dev - Reference to the NuttX driver state structure - * mac - The MAC address to be added - * - * Returned Value: - * Zero (OK) on success; a negated errno value on failure. - * - ****************************************************************************/ - -#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) -static int virtnet_addmac(FAR struct net_driver_s *dev, - FAR const uint8_t *mac) -{ - FAR struct virtnet_driver_s *priv = - (FAR struct virtnet_driver_s *)dev->d_private; - - /* Add the MAC address to the hardware multicast routing table */ - - UNUSED(priv); - return OK; -} -#endif - -/**************************************************************************** - * Name: virtnet_rmmac - * - * Description: - * NuttX Callback: Remove the specified MAC address from the hardware - * multicast address filtering - * - * Input Parameters: - * dev - Reference to the NuttX driver state structure - * mac - The MAC address to be removed - * - * Returned Value: - * Zero (OK) on success; a negated errno value on failure. - * - ****************************************************************************/ - -#ifdef CONFIG_NET_MCASTGROUP -static int virtnet_rmmac(FAR struct net_driver_s *dev, - FAR const uint8_t *mac) -{ - FAR struct virtnet_driver_s *priv = - (FAR struct virtnet_driver_s *)dev->d_private; - - /* Add the MAC address to the hardware multicast routing table */ - - UNUSED(priv); - return OK; -} -#endif - -/**************************************************************************** - * Name: virtnet_ipv6multicast - * - * Description: - * Configure the IPv6 multicast MAC address. - * - * Input Parameters: - * priv - A reference to the private driver state structure - * - * Returned Value: - * Zero (OK) on success; a negated errno value on failure. - * - ****************************************************************************/ - -#ifdef CONFIG_NET_ICMPv6 -static void virtnet_ipv6multicast(FAR struct virtnet_driver_s *priv) -{ - FAR struct net_driver_s *dev; - uint16_t tmp16; - uint8_t mac[6]; - - /* For ICMPv6, we need to add the IPv6 multicast address - * - * For IPv6 multicast addresses, the Ethernet MAC is derived by - * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00, - * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map - * to the Ethernet MAC address 33:33:00:01:00:03. - * - * NOTES: This appears correct for the ICMPv6 Router Solicitation - * Message, but the ICMPv6 Neighbor Solicitation message seems to - * use 33:33:ff:01:00:03. - */ - - mac[0] = 0x33; - mac[1] = 0x33; - - dev = &priv->dev; - tmp16 = dev->d_ipv6addr[6]; - mac[2] = 0xff; - mac[3] = tmp16 >> 8; - - tmp16 = dev->d_ipv6addr[7]; - mac[4] = tmp16 & 0xff; - mac[5] = tmp16 >> 8; - - vrtinfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - - virtnet_addmac(dev, mac); - -#ifdef CONFIG_NET_ICMPv6_AUTOCONF - /* Add the IPv6 all link-local nodes Ethernet address. This is the - * address that we expect to receive ICMPv6 Router Advertisement - * packets. - */ - - virtnet_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet); - -#endif /* CONFIG_NET_ICMPv6_AUTOCONF */ - -#ifdef CONFIG_NET_ICMPv6_ROUTER - /* Add the IPv6 all link-local routers Ethernet address. This is the - * address that we expect to receive ICMPv6 Router Solicitation - * packets. - */ - - virtnet_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet); - -#endif /* CONFIG_NET_ICMPv6_ROUTER */ -} -#endif /* CONFIG_NET_ICMPv6 */ - -/**************************************************************************** - * Name: virtnet_ioctl - * - * Description: - * Handle network IOCTL commands directed to this device. - * - * Input Parameters: - * dev - Reference to the NuttX driver state structure - * cmd - The IOCTL command - * arg - The argument for the IOCTL command - * - * Returned Value: - * OK on success; Negated errno on failure. - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef CONFIG_NETDEV_IOCTL -static int virtnet_ioctl(FAR struct net_driver_s *dev, int cmd, - unsigned long arg) -{ - FAR struct virtnet_driver_s *priv = - (FAR struct virtnet_driver_s *)dev->d_private; - int ret; - - /* Decode and dispatch the driver-specific IOCTL command */ - - switch (cmd) - { - /* Add cases here to support the IOCTL commands */ - - default: - vrterr("ERROR: Unrecognized IOCTL command: %d\n", command); - return -ENOTTY; /* Special return value for this case */ - } - - return OK; -} -#endif - -/**************************************************************************** - * Name: virtnet_initialize - * - * Description: - * Initialize the virt-net driver - * - * Input Parameters: - * - * - * Returned Value: - * OK on success; Negated errno on failure. - * - * Assumptions: - * Called early in initialization before multi-tasking is initiated. - * - ****************************************************************************/ - -static int virtnet_initialize(FAR struct virtio_mmio_regs *regs, int irq) -{ - FAR struct virtnet_driver_s *priv; - - /* Get the interface structure associated with this interface number. */ - - DEBUGASSERT(g_ninterfaces < VIRTIO_NET_NINTERFACES); - priv = &g_virtnet[g_ninterfaces]; - - /* Initialize the driver structure */ - - memset(priv, 0, sizeof(struct virtnet_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, virtnet_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_NET_QUEUE_LEN); - priv->rxq = virtq_create(CONFIG_DRIVERS_VIRTIO_NET_QUEUE_LEN); - - virtq_add_to_mmio_device(regs, priv->rxq, VIRTIO_NET_Q_RX); - virtq_add_to_mmio_device(regs, priv->txq, VIRTIO_NET_Q_TX); - - /* Prepare packets for rxq */ - - virtnet_add_packets_to_rxq(priv); - - priv->vnet_dev.d_buf = (FAR uint8_t *)g_pktbuf[g_ninterfaces]; /* Single packet buffer */ - - priv->vnet_dev.d_ifup = virtnet_ifup; /* I/F up (new IP address) callback */ - priv->vnet_dev.d_ifdown = virtnet_ifdown; /* I/F down callback */ - priv->vnet_dev.d_txavail = virtnet_txavail; /* New TX data callback */ -#ifdef CONFIG_NET_MCASTGROUP - priv->vnet_dev.d_addmac = virtnet_addmac; /* Add multicast MAC address */ - priv->vnet_dev.d_rmmac = virtnet_rmmac; /* Remove multicast MAC address */ -#endif -#ifdef CONFIG_NETDEV_IOCTL - priv->vnet_dev.d_ioctl = virtnet_ioctl; /* Handle network IOCTL commands */ -#endif - priv->vnet_dev.d_private = g_virtnet; /* Used to recover private state from dev */ - -#ifdef TODO - /* Put the interface in the down state. This usually amounts to resetting - * the device and/or calling virtnet_ifdown(). - */ - - /* Read the MAC address from the hardware into - * priv->vnet_dev.d_mac.ether.ether_addr_octet - * Applies only if the Ethernet MAC has its own internal address. - */ -#endif - - /* Register the device with the OS so that socket IOCTLs can be performed */ - - netdev_register(&priv->vnet_dev, NET_LL_ETHERNET); - return OK; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * 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 irq) -{ - int ret = OK; - - /* TODO: feature negotiation */ - - /* Set STATUS_FEATURE_OK */ - - virtio_putreg32(virtio_getreg32(®s->status) | VIRTIO_STATUS_FEATURES_OK, - ®s->status); - virtio_mb(); - - ret = virtnet_initialize(regs, irq); - - if (OK != ret) - { - vrterr("error: virtnet_initialize() returned %d \n", ret); - return ret; - } - - /* Set STATUS_FRIVER_OK */ - - virtio_putreg32(virtio_getreg32(®s->status) | VIRTIO_STATUS_DRIVER_OK, - ®s->status); - virtio_mb(); - - g_ninterfaces++; - - return ret; -} - -#endif /* !defined(CONFIG_SCHED_WORKQUEUE) */ -#endif /* CONFIG_DRIVERS_VIRTIO_NET */ diff --git a/drivers/virtio/virtio-mmio.c b/drivers/virtio/virtio-mmio.c index f5a380f6ae1..97a23bd1026 100644 --- a/drivers/virtio/virtio-mmio.c +++ b/drivers/virtio/virtio-mmio.c @@ -24,212 +24,852 @@ #include -#include -#include #include +#include +#include +#include -#include -#include +#include #include -#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(®s->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(®s->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); - return ret; - } - - /* Reset */ - - virtio_putreg32(0, ®s->status); - virtio_mb(); - - /* Set ack */ - - val = virtio_getreg32(®s->status) | VIRTIO_STATUS_ACKNOWLEDGE; - virtio_putreg32(val, ®s->status); - virtio_mb(); - - /* Set driver */ - - val = virtio_getreg32(®s->status) | VIRTIO_STATUS_DRIVER; - virtio_putreg32(val, ®s->status); - virtio_mb(); - - /* Check the device_id */ - - val = virtio_getreg32(®s->device_id); - - switch (val) - { -#ifdef CONFIG_DRIVERS_VIRTIO_NET - case VIRTIO_DEV_NET: - ret = virtio_mmio_net_init(regs, irq); - break; -#endif -#ifdef CONFIG_DRIVERS_VIRTIO_BLK - case VIRTIO_DEV_BLK: - ret = virtio_mmio_blk_init(regs, irq); - break; -#endif - default: - vrtwarn("unsupported device_id 0x%" PRIx32 "\n", val); + vrterr("virtio_mmio_config_virtqueue failed, ret=%d\n", ret); } return ret; } +/**************************************************************************** + * Name: virtio_mmio_create_virtqueues + ****************************************************************************/ + +static int virtio_mmio_create_virtqueues(FAR struct virtio_device *vdev, + unsigned int flags, + unsigned int nvqs, + FAR const char *names[], + vq_callback callbacks[]) +{ + 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; + 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; + default: + DEBUGASSERT(0); + } +} + +/**************************************************************************** + * 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; +} + /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** - * 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) -{ - vrtinfo("==== queue_sel=%" PRId32 ", virtq->len=%" PRId32 "\n", - queue_sel, virtq->len); - - virtio_putreg32(queue_sel, ®s->queue_sel); - virtio_mb(); - - virtio_putreg32(virtq->len, ®s->queue_num); - - virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->desc)), - ®s->queue_desc_low); - virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->desc) >> 32), - ®s->queue_desc_high); - - virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->avail)), - ®s->queue_avail_low); - virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->avail) >> 32), - ®s->queue_avail_high); - - virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->used)), - ®s->queue_used_low); - virtio_putreg32((uint32_t)(ptr_to_uint64(virtq->used) >> 32), - ®s->queue_used_high); - - virtio_mb(); - virtio_putreg32(1, ®s->queue_ready); -} - -/**************************************************************************** - * Name: virtio_mmio_init - ****************************************************************************/ - -void virtio_mmio_init(void) -{ - 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++) + if (onceinit == false) { - virtio_dev_init(virtio + size * i, irq + i); + onceinit = true; + ret = metal_init(¶ms); + if (ret < 0) + { + 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; } diff --git a/drivers/virtio/virtio-net.c b/drivers/virtio/virtio-net.c new file mode 100644 index 00000000000..56d88959de3 --- /dev/null +++ b/drivers/virtio/virtio-net.c @@ -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 +#include +#include +#include + +#include +#include +#include +#include + +#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); +} diff --git a/drivers/virtio/virtio-mmio-net.h b/drivers/virtio/virtio-net.h similarity index 79% rename from drivers/virtio/virtio-mmio-net.h rename to drivers/virtio/virtio-net.h index 59510e58ba7..9d5d1cc89dd 100644 --- a/drivers/virtio/virtio-mmio-net.h +++ b/drivers/virtio/virtio-net.h @@ -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 */ diff --git a/drivers/virtio/virtio-rng.c b/drivers/virtio/virtio-rng.c new file mode 100644 index 00000000000..5459639b297 --- /dev/null +++ b/drivers/virtio/virtio-rng.c @@ -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 +#include +#include + +#include +#include +#include + +#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); +} diff --git a/arch/arm64/src/qemu/qemu_virtio.c b/drivers/virtio/virtio-rng.h similarity index 62% rename from arch/arm64/src/qemu/qemu_virtio.c rename to drivers/virtio/virtio-rng.h index 5d476d2eedc..71c9b8db9e1 100644 --- a/arch/arm64/src/qemu/qemu_virtio.c +++ b/drivers/virtio/virtio-rng.h @@ -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 -#include -#include -#include - -#include +#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 */ diff --git a/drivers/virtio/virtio-serial.c b/drivers/virtio/virtio-serial.c new file mode 100644 index 00000000000..3e3153016bc --- /dev/null +++ b/drivers/virtio/virtio-serial.c @@ -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 + +#include +#include +#include + +#include +#include + +#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; +} diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_virtio.c b/drivers/virtio/virtio-serial.h similarity index 62% rename from arch/risc-v/src/qemu-rv/qemu_rv_virtio.c rename to drivers/virtio/virtio-serial.h index bbb567873ee..07be952ab3b 100644 --- a/arch/risc-v/src/qemu-rv/qemu_rv_virtio.c +++ b/drivers/virtio/virtio-serial.h @@ -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 -#include -#include -#include - -#include +#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 */ diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c new file mode 100644 index 00000000000..ea2e07bc80d --- /dev/null +++ b/drivers/virtio/virtio.c @@ -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 + +#include +#include +#include + +#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; +} diff --git a/include/nuttx/virtio/virtio-mmio.h b/include/nuttx/virtio/virtio-mmio.h index 5bf53ab0296..d7c0d0574af 100644 --- a/include/nuttx/virtio/virtio-mmio.h +++ b/include/nuttx/virtio/virtio-mmio.h @@ -25,119 +25,18 @@ * Included Files ****************************************************************************/ -#include #include -#include + +#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 */ diff --git a/include/nuttx/virtio/virtio.h b/include/nuttx/virtio/virtio.h new file mode 100644 index 00000000000..3920d1fa973 --- /dev/null +++ b/include/nuttx/virtio/virtio.h @@ -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 + +#include +#include + +#ifdef CONFIG_DRIVERS_VIRTIO + +#include + +/**************************************************************************** + * 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 */