From owner-svn-src-head@freebsd.org Wed May 6 23:28:52 2020 Return-Path: Delivered-To: svn-src-head@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 0FDC12E2712; Wed, 6 May 2020 23:28:52 +0000 (UTC) (envelope-from jrtc27@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 49HXpq6glFz4M51; Wed, 6 May 2020 23:28:51 +0000 (UTC) (envelope-from jrtc27@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id DBA36755D; Wed, 6 May 2020 23:28:51 +0000 (UTC) (envelope-from jrtc27@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id 046NSptc079153; Wed, 6 May 2020 23:28:51 GMT (envelope-from jrtc27@FreeBSD.org) Received: (from jrtc27@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id 046NSp8x079151; Wed, 6 May 2020 23:28:51 GMT (envelope-from jrtc27@FreeBSD.org) Message-Id: <202005062328.046NSp8x079151@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: jrtc27 set sender to jrtc27@FreeBSD.org using -f From: Jessica Clarke Date: Wed, 6 May 2020 23:28:51 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r360722 - head/sys/dev/virtio/mmio X-SVN-Group: head X-SVN-Commit-Author: jrtc27 X-SVN-Commit-Paths: head/sys/dev/virtio/mmio X-SVN-Commit-Revision: 360722 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 06 May 2020 23:28:52 -0000 Author: jrtc27 Date: Wed May 6 23:28:51 2020 New Revision: 360722 URL: https://svnweb.freebsd.org/changeset/base/360722 Log: virtio_mmio: Support non-transitional version 2 devices The non-legacy virtio MMIO specification drops the use of PFNs and replaces them with physical addresses. Whilst many implementations are so-called transitional devices, also implementing the legacy specification, TinyEMU[1] does not. Device-specific configuration registers have also changed to being little-endian, and must be accessed using a single aligned access for registers up to 32 bits, and two 32-bit aligned accesses for 64-bit registers. [1] https://bellard.org/tinyemu/ Reviewed by: br, brooks (mentor) Approved by: br, brooks (mentor) Differential Revision: https://reviews.freebsd.org/D24681 Modified: head/sys/dev/virtio/mmio/virtio_mmio.c head/sys/dev/virtio/mmio/virtio_mmio.h Modified: head/sys/dev/virtio/mmio/virtio_mmio.c ============================================================================== --- head/sys/dev/virtio/mmio/virtio_mmio.c Wed May 6 23:23:22 2020 (r360721) +++ head/sys/dev/virtio/mmio/virtio_mmio.c Wed May 6 23:28:51 2020 (r360722) @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -76,6 +77,8 @@ static int vtmmio_read_ivar(device_t, device_t, int, u static int vtmmio_write_ivar(device_t, device_t, int, uintptr_t); static uint64_t vtmmio_negotiate_features(device_t, uint64_t); static int vtmmio_with_feature(device_t, uint64_t); +static void vtmmio_set_virtqueue(struct vtmmio_softc *sc, + struct virtqueue *vq, uint32_t size); static int vtmmio_alloc_virtqueues(device_t, int, int, struct vq_alloc_info *); static int vtmmio_setup_intr(device_t, enum intr_type); @@ -223,6 +226,16 @@ vtmmio_attach(device_t dev) return (ENXIO); } + sc->vtmmio_version = vtmmio_read_config_4(sc, VIRTIO_MMIO_VERSION); + if (sc->vtmmio_version < 1 || sc->vtmmio_version > 2) { + device_printf(dev, "Unsupported version: %x\n", + sc->vtmmio_version); + bus_release_resource(dev, SYS_RES_MEMORY, 0, + sc->res[0]); + sc->res[0] = NULL; + return (ENXIO); + } + vtmmio_reset(sc); /* Tell the host we've noticed this device. */ @@ -404,6 +417,46 @@ vtmmio_with_feature(device_t dev, uint64_t feature) return ((sc->vtmmio_features & feature) != 0); } +static void +vtmmio_set_virtqueue(struct vtmmio_softc *sc, struct virtqueue *vq, + uint32_t size) +{ + vm_paddr_t paddr; + + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_NUM, size); +#if 0 + device_printf(dev, "virtqueue paddr 0x%08lx\n", + (uint64_t)paddr); +#endif + if (sc->vtmmio_version == 1) { + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_ALIGN, + VIRTIO_MMIO_VRING_ALIGN); + paddr = virtqueue_paddr(vq); + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, + paddr >> PAGE_SHIFT); + } else { + paddr = virtqueue_desc_paddr(vq); + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_DESC_LOW, + paddr); + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_DESC_HIGH, + paddr >> 32); + + paddr = virtqueue_avail_paddr(vq); + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_AVAIL_LOW, + paddr); + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_AVAIL_HIGH, + paddr >> 32); + + paddr = virtqueue_used_paddr(vq); + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_USED_LOW, + paddr); + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_USED_HIGH, + paddr >> 32); + + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_READY, 1); + } +} + static int vtmmio_alloc_virtqueues(device_t dev, int flags, int nvqs, struct vq_alloc_info *vq_info) @@ -448,15 +501,7 @@ vtmmio_alloc_virtqueues(device_t dev, int flags, int n break; } - vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_NUM, size); - vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_ALIGN, - VIRTIO_MMIO_VRING_ALIGN); -#if 0 - device_printf(dev, "virtqueue paddr 0x%08lx\n", - (uint64_t)virtqueue_paddr(vq)); -#endif - vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, - virtqueue_paddr(vq) >> PAGE_SHIFT); + vtmmio_set_virtqueue(sc, vq, size); vqx->vtv_vq = *info->vqai_vq = vq; vqx->vtv_no_intr = info->vqai_intr == NULL; @@ -568,10 +613,54 @@ vtmmio_read_dev_config(device_t dev, bus_size_t offset bus_size_t off; uint8_t *d; int size; + uint64_t low32, high32; sc = device_get_softc(dev); off = VIRTIO_MMIO_CONFIG + offset; + /* + * The non-legacy MMIO specification adds the following restriction: + * + * 4.2.2.2: For the device-specific configuration space, the driver + * MUST use 8 bit wide accesses for 8 bit wide fields, 16 bit wide + * and aligned accesses for 16 bit wide fields and 32 bit wide and + * aligned accesses for 32 and 64 bit wide fields. + * + * The endianness also varies between non-legacy and legacy: + * + * 2.4: Note: The device configuration space uses the little-endian + * format for multi-byte fields. + * + * 2.4.3: Note that for legacy interfaces, device configuration space + * is generally the guest’s native endian, rather than PCI’s + * little-endian. The correct endian-ness is documented for each + * device. + */ + if (sc->vtmmio_version > 1) { + switch (length) { + case 1: + *(uint8_t *)dst = vtmmio_read_config_1(sc, off); + break; + case 2: + *(uint16_t *)dst = + le16toh(vtmmio_read_config_2(sc, off)); + break; + case 4: + *(uint32_t *)dst = + le32toh(vtmmio_read_config_4(sc, off)); + break; + case 8: + low32 = le32toh(vtmmio_read_config_4(sc, off)); + high32 = le32toh(vtmmio_read_config_4(sc, off + 4)); + *(uint64_t *)dst = (high32 << 32) | low32; + break; + default: + panic("%s: invalid length %d\n", __func__, length); + } + + return; + } + for (d = dst; length > 0; d += size, off += size, length -= size) { #ifdef ALLOW_WORD_ALIGNED_ACCESS if (length >= 4) { @@ -601,6 +690,37 @@ vtmmio_write_dev_config(device_t dev, bus_size_t offse sc = device_get_softc(dev); off = VIRTIO_MMIO_CONFIG + offset; + /* + * The non-legacy MMIO specification adds size and alignment + * restrctions. It also changes the endianness from native-endian to + * little-endian. See vtmmio_read_dev_config. + */ + if (sc->vtmmio_version > 1) { + switch (length) { + case 1: + vtmmio_write_config_1(sc, off, *(uint8_t *)src); + break; + case 2: + vtmmio_write_config_2(sc, off, + htole16(*(uint16_t *)src)); + break; + case 4: + vtmmio_write_config_4(sc, off, + htole32(*(uint32_t *)src)); + break; + case 8: + vtmmio_write_config_4(sc, off, + htole32(*(uint64_t *)src)); + vtmmio_write_config_4(sc, off + 4, + htole32((*(uint64_t *)src) >> 32)); + break; + default: + panic("%s: invalid length %d\n", __func__, length); + } + + return; + } + for (s = src; length > 0; s += size, off += size, length -= size) { #ifdef ALLOW_WORD_ALIGNED_ACCESS if (length >= 4) { @@ -685,15 +805,7 @@ vtmmio_reinit_virtqueue(struct vtmmio_softc *sc, int i if (error) return (error); - vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_NUM, size); - vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_ALIGN, - VIRTIO_MMIO_VRING_ALIGN); -#if 0 - device_printf(sc->dev, "virtqueue paddr 0x%08lx\n", - (uint64_t)virtqueue_paddr(vq)); -#endif - vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, - virtqueue_paddr(vq) >> PAGE_SHIFT); + vtmmio_set_virtqueue(sc, vq, size); return (0); } @@ -719,7 +831,10 @@ vtmmio_free_virtqueues(struct vtmmio_softc *sc) vqx = &sc->vtmmio_vqs[idx]; vtmmio_select_virtqueue(sc, idx); - vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, 0); + if (sc->vtmmio_version == 1) + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, 0); + else + vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_READY, 0); virtqueue_free(vqx->vtv_vq); vqx->vtv_vq = NULL; Modified: head/sys/dev/virtio/mmio/virtio_mmio.h ============================================================================== --- head/sys/dev/virtio/mmio/virtio_mmio.h Wed May 6 23:23:22 2020 (r360721) +++ head/sys/dev/virtio/mmio/virtio_mmio.h Wed May 6 23:28:51 2020 (r360722) @@ -44,6 +44,7 @@ struct vtmmio_softc { uint64_t vtmmio_features; uint32_t vtmmio_flags; + uint32_t vtmmio_version; /* This "bus" will only ever have one child. */ device_t vtmmio_child_dev; @@ -64,16 +65,24 @@ int vtmmio_attach(device_t); #define VIRTIO_MMIO_HOST_FEATURES_SEL 0x014 #define VIRTIO_MMIO_GUEST_FEATURES 0x020 #define VIRTIO_MMIO_GUEST_FEATURES_SEL 0x024 -#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 +#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 /* version 1 only */ #define VIRTIO_MMIO_QUEUE_SEL 0x030 #define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 #define VIRTIO_MMIO_QUEUE_NUM 0x038 -#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c -#define VIRTIO_MMIO_QUEUE_PFN 0x040 +#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c /* version 1 only */ +#define VIRTIO_MMIO_QUEUE_PFN 0x040 /* version 1 only */ +#define VIRTIO_MMIO_QUEUE_READY 0x044 /* requires version 2 */ #define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 #define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 #define VIRTIO_MMIO_INTERRUPT_ACK 0x064 #define VIRTIO_MMIO_STATUS 0x070 +#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 /* requires version 2 */ +#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 /* requires version 2 */ +#define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090 /* requires version 2 */ +#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094 /* requires version 2 */ +#define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0 /* requires version 2 */ +#define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4 /* requires version 2 */ +#define VIRTIO_MMIO_CONFIG_GENERATION 0x100 /* requires version 2 */ #define VIRTIO_MMIO_CONFIG 0x100 #define VIRTIO_MMIO_INT_VRING (1 << 0) #define VIRTIO_MMIO_INT_CONFIG (1 << 1)