1*216460ecSBin Meng# SPDX-License-Identifier: GPL-2.0+ 2*216460ecSBin Meng# 3*216460ecSBin Meng# Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> 4*216460ecSBin Meng 5*216460ecSBin MengVirtIO Support 6*216460ecSBin Meng============== 7*216460ecSBin Meng 8*216460ecSBin MengThis document describes the information about U-Boot support for VirtIO [1] 9*216460ecSBin Mengdevices, including supported boards, build instructions, driver details etc. 10*216460ecSBin Meng 11*216460ecSBin MengWhat's VirtIO? 12*216460ecSBin Meng-------------- 13*216460ecSBin MengVirtIO is a virtualization standard for network and disk device drivers where 14*216460ecSBin Mengjust the guest's device driver "knows" it is running in a virtual environment, 15*216460ecSBin Mengand cooperates with the hypervisor. This enables guests to get high performance 16*216460ecSBin Mengnetwork and disk operations, and gives most of the performance benefits of 17*216460ecSBin Mengparavirtualization. In the U-Boot case, the guest is U-Boot itself, while the 18*216460ecSBin Mengvirtual environment are normally QEMU [2] targets like ARM, RISC-V and x86. 19*216460ecSBin Meng 20*216460ecSBin MengStatus 21*216460ecSBin Meng------ 22*216460ecSBin MengVirtIO can use various different buses, aka transports as described in the 23*216460ecSBin Mengspec. While VirtIO devices are commonly implemented as PCI devices on x86, 24*216460ecSBin Mengembedded devices models like ARM/RISC-V, which does not normally come with 25*216460ecSBin MengPCI support might use simple memory mapped device (MMIO) instead of the PCI 26*216460ecSBin Mengdevice. The memory mapped virtio device behaviour is based on the PCI device 27*216460ecSBin Mengspecification. Therefore most operations including device initialization, 28*216460ecSBin Mengqueues configuration and buffer transfers are nearly identical. Both MMIO 29*216460ecSBin Mengand PCI transport options are supported in U-Boot. 30*216460ecSBin Meng 31*216460ecSBin MengThe VirtIO spec defines a lots of VirtIO device types, however at present only 32*216460ecSBin Mengnetwork and block device, the most two commonly used devices, are supported. 33*216460ecSBin Meng 34*216460ecSBin MengThe following QEMU targets are supported. 35*216460ecSBin Meng 36*216460ecSBin Meng - qemu_arm_defconfig 37*216460ecSBin Meng - qemu_arm64_defconfig 38*216460ecSBin Meng - qemu-riscv32_defconfig 39*216460ecSBin Meng - qemu-riscv64_defconfig 40*216460ecSBin Meng - qemu-x86_defconfig 41*216460ecSBin Meng - qemu-x86_64_defconfig 42*216460ecSBin Meng 43*216460ecSBin MengNote ARM and RISC-V targets are configured with VirtIO MMIO transport driver, 44*216460ecSBin Mengand on x86 it's the PCI transport driver. 45*216460ecSBin Meng 46*216460ecSBin MengBuild Instructions 47*216460ecSBin Meng------------------ 48*216460ecSBin MengBuilding U-Boot for pre-configured QEMU targets is no different from others. 49*216460ecSBin MengFor example, we can do the following with the CROSS_COMPILE environment 50*216460ecSBin Mengvariable being properly set to a working toolchain for ARM: 51*216460ecSBin Meng 52*216460ecSBin Meng $ make qemu_arm_defconfig 53*216460ecSBin Meng $ make 54*216460ecSBin Meng 55*216460ecSBin MengYou can even create a QEMU ARM target with VirtIO devices showing up on both 56*216460ecSBin MengMMIO and PCI buses. In this case, you can enable the PCI transport driver 57*216460ecSBin Mengfrom 'make menuconfig': 58*216460ecSBin Meng 59*216460ecSBin MengDevice Drivers ---> 60*216460ecSBin Meng ... 61*216460ecSBin Meng VirtIO Drivers ---> 62*216460ecSBin Meng ... 63*216460ecSBin Meng [*] PCI driver for virtio devices 64*216460ecSBin Meng 65*216460ecSBin MengOther drivers are at the same location and can be tuned to suit the needs. 66*216460ecSBin Meng 67*216460ecSBin MengRequirements 68*216460ecSBin Meng------------ 69*216460ecSBin MengIt is required that QEMU v2.5.0+ should be used to test U-Boot VirtIO support 70*216460ecSBin Mengon QEMU ARM and x86, and v2.12.0+ on QEMU RISC-V. 71*216460ecSBin Meng 72*216460ecSBin MengTesting 73*216460ecSBin Meng------- 74*216460ecSBin MengThe following QEMU command line is used to get U-Boot up and running with 75*216460ecSBin MengVirtIO net and block devices on ARM. 76*216460ecSBin Meng 77*216460ecSBin Meng $ qemu-system-arm -nographic -machine virt -bios u-boot.bin \ 78*216460ecSBin Meng -netdev tap,ifname=tap0,id=net0 \ 79*216460ecSBin Meng -device virtio-net-device,netdev=net0 \ 80*216460ecSBin Meng -drive if=none,file=test.img,format=raw,id=hd0 \ 81*216460ecSBin Meng -device virtio-blk-device,drive=hd0 82*216460ecSBin Meng 83*216460ecSBin MengOn x86, command is slightly different to create PCI VirtIO devices. 84*216460ecSBin Meng 85*216460ecSBin Meng $ qemu-system-i386 -nographic -bios u-boot.rom \ 86*216460ecSBin Meng -netdev tap,ifname=tap0,id=net0 \ 87*216460ecSBin Meng -device virtio-net-pci,netdev=net0 \ 88*216460ecSBin Meng -drive if=none,file=test.img,format=raw,id=hd0 \ 89*216460ecSBin Meng -device virtio-blk-pci,drive=hd0 90*216460ecSBin Meng 91*216460ecSBin MengAdditional net and block devices can be created by appending more '-device' 92*216460ecSBin Mengparameters. It is also possible to specify both MMIO and PCI VirtIO devices. 93*216460ecSBin MengFor example, the following commnad creates 3 VirtIO devices, with 1 on MMIO 94*216460ecSBin Mengand 2 on PCI bus. 95*216460ecSBin Meng 96*216460ecSBin Meng $ qemu-system-arm -nographic -machine virt -bios u-boot.bin \ 97*216460ecSBin Meng -netdev tap,ifname=tap0,id=net0 \ 98*216460ecSBin Meng -device virtio-net-pci,netdev=net0 \ 99*216460ecSBin Meng -drive if=none,file=test0.img,format=raw,id=hd0 \ 100*216460ecSBin Meng -device virtio-blk-device,drive=hd0 \ 101*216460ecSBin Meng -drive if=none,file=test1.img,format=raw,id=hd1 \ 102*216460ecSBin Meng -device virtio-blk-pci,drive=hd1 103*216460ecSBin Meng 104*216460ecSBin MengBy default QEMU creates VirtIO legacy devices by default. To create non-legacy 105*216460ecSBin Meng(aka modern) devices, pass additional device property/value pairs like below: 106*216460ecSBin Meng 107*216460ecSBin Meng $ qemu-system-i386 -nographic -bios u-boot.rom \ 108*216460ecSBin Meng -netdev tap,ifname=tap0,id=net0 \ 109*216460ecSBin Meng -device virtio-net-pci,netdev=net0,disable-legacy=true,disable-modern=false \ 110*216460ecSBin Meng -drive if=none,file=test.img,format=raw,id=hd0 \ 111*216460ecSBin Meng -device virtio-blk-pci,drive=hd0,disable-legacy=true,disable-modern=false 112*216460ecSBin Meng 113*216460ecSBin MengA 'virtio' command is provided in U-Boot shell. 114*216460ecSBin Meng 115*216460ecSBin Meng => virtio 116*216460ecSBin Meng virtio - virtio block devices sub-system 117*216460ecSBin Meng 118*216460ecSBin Meng Usage: 119*216460ecSBin Meng virtio scan - initialize virtio bus 120*216460ecSBin Meng virtio info - show all available virtio block devices 121*216460ecSBin Meng virtio device [dev] - show or set current virtio block device 122*216460ecSBin Meng virtio part [dev] - print partition table of one or all virtio block devices 123*216460ecSBin Meng virtio read addr blk# cnt - read `cnt' blocks starting at block 124*216460ecSBin Meng `blk#' to memory address `addr' 125*216460ecSBin Meng virtio write addr blk# cnt - write `cnt' blocks starting at block 126*216460ecSBin Meng `blk#' from memory address `addr' 127*216460ecSBin Meng 128*216460ecSBin MengTo probe all the VirtIO devices, type: 129*216460ecSBin Meng 130*216460ecSBin Meng => virtio scan 131*216460ecSBin Meng 132*216460ecSBin MengThen we can show the connected block device details by: 133*216460ecSBin Meng 134*216460ecSBin Meng => virtio info 135*216460ecSBin Meng Device 0: QEMU VirtIO Block Device 136*216460ecSBin Meng Type: Hard Disk 137*216460ecSBin Meng Capacity: 4096.0 MB = 4.0 GB (8388608 x 512) 138*216460ecSBin Meng 139*216460ecSBin MengAnd list the directories and files on the disk by: 140*216460ecSBin Meng 141*216460ecSBin Meng => ls virtio 0 / 142*216460ecSBin Meng <DIR> 4096 . 143*216460ecSBin Meng <DIR> 4096 .. 144*216460ecSBin Meng <DIR> 16384 lost+found 145*216460ecSBin Meng <DIR> 4096 dev 146*216460ecSBin Meng <DIR> 4096 proc 147*216460ecSBin Meng <DIR> 4096 sys 148*216460ecSBin Meng <DIR> 4096 var 149*216460ecSBin Meng <DIR> 4096 etc 150*216460ecSBin Meng <DIR> 4096 usr 151*216460ecSBin Meng <SYM> 7 bin 152*216460ecSBin Meng <SYM> 8 sbin 153*216460ecSBin Meng <SYM> 7 lib 154*216460ecSBin Meng <SYM> 9 lib64 155*216460ecSBin Meng <DIR> 4096 run 156*216460ecSBin Meng <DIR> 4096 boot 157*216460ecSBin Meng <DIR> 4096 home 158*216460ecSBin Meng <DIR> 4096 media 159*216460ecSBin Meng <DIR> 4096 mnt 160*216460ecSBin Meng <DIR> 4096 opt 161*216460ecSBin Meng <DIR> 4096 root 162*216460ecSBin Meng <DIR> 4096 srv 163*216460ecSBin Meng <DIR> 4096 tmp 164*216460ecSBin Meng 0 .autorelabel 165*216460ecSBin Meng 166*216460ecSBin MengDriver Internals 167*216460ecSBin Meng---------------- 168*216460ecSBin MengThere are 3 level of drivers in the VirtIO driver family. 169*216460ecSBin Meng 170*216460ecSBin Meng +---------------------------------------+ 171*216460ecSBin Meng | virtio device drivers | 172*216460ecSBin Meng | +-------------+ +------------+ | 173*216460ecSBin Meng | | virtio-net | | virtio-blk | | 174*216460ecSBin Meng | +-------------+ +------------+ | 175*216460ecSBin Meng +---------------------------------------+ 176*216460ecSBin Meng +---------------------------------------+ 177*216460ecSBin Meng | virtio transport drivers | 178*216460ecSBin Meng | +-------------+ +------------+ | 179*216460ecSBin Meng | | virtio-mmio | | virtio-pci | | 180*216460ecSBin Meng | +-------------+ +------------+ | 181*216460ecSBin Meng +---------------------------------------+ 182*216460ecSBin Meng +----------------------+ 183*216460ecSBin Meng | virtio uclass driver | 184*216460ecSBin Meng +----------------------+ 185*216460ecSBin Meng 186*216460ecSBin MengThe root one is the virtio uclass driver (virtio-uclass.c), which does lots of 187*216460ecSBin Mengcommon stuff for the transport drivers (virtio_mmio.c, virtio_pci.c). The real 188*216460ecSBin Mengvirtio device is discovered in the transport driver's probe() method, and its 189*216460ecSBin Mengdevice ID is saved in the virtio uclass's private data of the transport device. 190*216460ecSBin MengThen in the virtio uclass's post_probe() method, the real virtio device driver 191*216460ecSBin Meng(virtio_net.c, virtio_blk.c) is bound if there is a match on the device ID. 192*216460ecSBin Meng 193*216460ecSBin MengThe child_post_bind(), child_pre_probe() and child_post_probe() methods of the 194*216460ecSBin Mengvirtio uclass driver help bring the virtio device driver online. They do things 195*216460ecSBin Menglike acknowledging device, feature negotiation, etc, which are really common 196*216460ecSBin Mengfor all virtio devices. 197*216460ecSBin Meng 198*216460ecSBin MengThe transport drivers provide a set of ops (struct dm_virtio_ops) for the real 199*216460ecSBin Mengvirtio device driver to call. These ops APIs's parameter is designed to remind 200*216460ecSBin Mengthe caller to pass the correct 'struct udevice' id of the virtio device, eg: 201*216460ecSBin Meng 202*216460ecSBin Mengint virtio_get_status(struct udevice *vdev, u8 *status) 203*216460ecSBin Meng 204*216460ecSBin MengSo the parameter 'vdev' indicates the device should be the real virtio device. 205*216460ecSBin MengBut we also have an API like: 206*216460ecSBin Meng 207*216460ecSBin Mengstruct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num, 208*216460ecSBin Meng unsigned int vring_align, 209*216460ecSBin Meng struct udevice *udev) 210*216460ecSBin Meng 211*216460ecSBin MengHere the parameter 'udev' indicates the device should be the transport device. 212*216460ecSBin MengSimilar naming is applied in other functions that are even not APIs, eg: 213*216460ecSBin Meng 214*216460ecSBin Mengstatic int virtio_uclass_post_probe(struct udevice *udev) 215*216460ecSBin Mengstatic int virtio_uclass_child_pre_probe(struct udevice *vdev) 216*216460ecSBin Meng 217*216460ecSBin MengSo it's easy to tell which device these functions are operating on. 218*216460ecSBin Meng 219*216460ecSBin MengDevelopment Flow 220*216460ecSBin Meng---------------- 221*216460ecSBin MengAt present only VirtIO network card (device ID 1) and block device (device 222*216460ecSBin MengID 2) are supported. If you want to develop new driver for new devices, 223*216460ecSBin Mengplease follow the guideline below. 224*216460ecSBin Meng 225*216460ecSBin Meng1. add new device ID in virtio.h 226*216460ecSBin Meng#define VIRTIO_ID_XXX X 227*216460ecSBin Meng 228*216460ecSBin Meng2. update VIRTIO_ID_MAX_NUM to be the largest device ID plus 1 229*216460ecSBin Meng 230*216460ecSBin Meng3. add new driver name string in virtio.h 231*216460ecSBin Meng#define VIRTIO_XXX_DRV_NAME "virtio-xxx" 232*216460ecSBin Meng 233*216460ecSBin Meng4. create a new driver with name set to the name string above 234*216460ecSBin MengU_BOOT_DRIVER(virtio_xxx) = { 235*216460ecSBin Meng .name = VIRTIO_XXX_DRV_NAME, 236*216460ecSBin Meng ... 237*216460ecSBin Meng .remove = virtio_reset, 238*216460ecSBin Meng .flags = DM_FLAG_ACTIVE_DMA, 239*216460ecSBin Meng} 240*216460ecSBin Meng 241*216460ecSBin MengNote the driver needs to provide the remove method and normally this can be 242*216460ecSBin Menghooked to virtio_reset(). The driver flags should contain DM_FLAG_ACTIVE_DMA 243*216460ecSBin Mengfor the remove method to be called before jumping to OS. 244*216460ecSBin Meng 245*216460ecSBin Meng5. provide bind() method in the driver, where virtio_driver_features_init() 246*216460ecSBin Meng should be called for driver to negotiate feature support with the device. 247*216460ecSBin Meng 248*216460ecSBin Meng6. do funny stuff with the driver 249*216460ecSBin Meng 250*216460ecSBin MengReferences 251*216460ecSBin Meng---------- 252*216460ecSBin Meng[1] http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.pdf 253*216460ecSBin Meng[2] https://www.qemu.org 254