From patchwork Thu Jan 9 14:43:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Auger X-Patchwork-Id: 1220424 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=hp59Vw25; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47tprf3cqMz9sNx for ; Fri, 10 Jan 2020 01:48:18 +1100 (AEDT) Received: from localhost ([::1]:33588 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ipZ6d-0002cd-GE for incoming@patchwork.ozlabs.org; Thu, 09 Jan 2020 09:48:15 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:37623) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ipZ2a-000771-Ta for qemu-devel@nongnu.org; Thu, 09 Jan 2020 09:44:06 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ipZ2Z-0006tx-1H for qemu-devel@nongnu.org; Thu, 09 Jan 2020 09:44:04 -0500 Received: from us-smtp-2.mimecast.com ([207.211.31.81]:52357 helo=us-smtp-delivery-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ipZ2Y-0006qz-QA for qemu-devel@nongnu.org; Thu, 09 Jan 2020 09:44:02 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1578581042; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=oCKVN25uFhbS4n6wveHRcfl8aC9n/MviXYxsR2+3NF8=; b=hp59Vw25sDs83m+G+jH5fOc+ogHTQy7SnbMoVdJWJlz3HCD7wUetWs9TWNq3TcUjJpnTTK L+Vf3SeKL03gq8MvFErPAT/bTa0nOwuTN4ALau2Ops6whdmAFNiBeH2FFoFk12V0iox+Gz VIlIkxgf31Vd3OO6QafmD3VOwGRSXWE= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-233-1b-Vc3PdNjOWv3O4NsNqMQ-1; Thu, 09 Jan 2020 09:44:01 -0500 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 8742C18CA24C; Thu, 9 Jan 2020 14:43:59 +0000 (UTC) Received: from laptop.redhat.com (ovpn-117-108.ams2.redhat.com [10.36.117.108]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9A93860C88; Thu, 9 Jan 2020 14:43:56 +0000 (UTC) From: Eric Auger To: eric.auger.pro@gmail.com, eric.auger@redhat.com, qemu-devel@nongnu.org, qemu-arm@nongnu.org, peter.maydell@linaro.org, jean-philippe@linaro.org, dgilbert@redhat.com, quintela@redhat.com, mst@redhat.com, peterx@redhat.com Subject: [PATCH v12 04/13] virtio-iommu: Add the iommu regions Date: Thu, 9 Jan 2020 15:43:10 +0100 Message-Id: <20200109144319.15912-5-eric.auger@redhat.com> In-Reply-To: <20200109144319.15912-1-eric.auger@redhat.com> References: <20200109144319.15912-1-eric.auger@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-MC-Unique: 1b-Vc3PdNjOWv3O4NsNqMQ-1 X-Mimecast-Spam-Score: 0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 207.211.31.81 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kevin.tian@intel.com, bharatb.linux@gmail.com, tnowicki@marvell.com Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Implement a callback called on PCI bus enumeration that initializes for a given device on the bus hierarchy an IOMMU memory region. The PCI bus hierarchy is stored locally in IOMMUPciBus and IOMMUDevice objects. At the time of the enumeration, the bus number may not be computed yet. So operations that will need to retrieve the IOMMUdevice and its IOMMU memory region from the bus number and devfn, once the bus number is garanteed to be frozen, use an array of IOMMUPciBus, lazily populated. virtio_iommu_mr() is the top helper that allows to retrieve the IOMMU memory region from the requester ID. Signed-off-by: Eric Auger --- v11 -> v12: - add the iommu_find_iommu_pcibus() mechanics. Without it, when attaching t device to a domain we could not check the device is effectively protected by this IOMMU v10 -> v11: - use g_hash_table_new_full for allocating as_by_busptr v9 -> v10: - remove pc/virt machine headers - virtio_iommu_find_add_as: mr_index introduced in that patch and name properly freed v6 -> v7: - use primary_bus - rebase on new translate proto featuring iommu_idx v5 -> v6: - include qapi/error.h - fix g_hash_table_lookup key in virtio_iommu_find_add_as v4 -> v5: - use PCI bus handle as a key - use get_primary_pci_bus() callback v3 -> v4: - add trace_virtio_iommu_init_iommu_mr v2 -> v3: - use IOMMUMemoryRegion - iommu mr name built with BDF - rename smmu_get_sid into virtio_iommu_get_sid and use PCI_BUILD_BDF --- hw/virtio/trace-events | 2 + hw/virtio/virtio-iommu.c | 135 +++++++++++++++++++++++++++++++ include/hw/virtio/virtio-iommu.h | 3 + 3 files changed, 140 insertions(+) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index f7141aa2f6..10a2b120f3 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -64,3 +64,5 @@ virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d" virtio_iommu_unmap(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 +virtio_iommu_translate(const char *name, uint32_t rid, uint64_t iova, int flag) "mr=%s rid=%d addr=0x%"PRIx64" flag=%d" +virtio_iommu_init_iommu_mr(char *iommu_mr) "init %s" diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 9d1b997df7..acc939f334 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -23,6 +23,8 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio.h" #include "sysemu/kvm.h" +#include "qapi/error.h" +#include "qemu/error-report.h" #include "trace.h" #include "standard-headers/linux/virtio_ids.h" @@ -34,6 +36,93 @@ /* Max size */ #define VIOMMU_DEFAULT_QUEUE_SIZE 256 +static inline uint16_t virtio_iommu_get_sid(IOMMUDevice *dev) +{ + return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); +} + +/** + * The bus number is used for lookup when SID based operations occur. + * In that case we lazily populate the IOMMUPciBus array from the bus hash + * table. At the time the IOMMUPciBus is created (iommu_find_add_as), the bus + * numbers may not be always initialized yet. + */ +static IOMMUPciBus *iommu_find_iommu_pcibus(VirtIOIOMMU *s, uint8_t bus_num) +{ + IOMMUPciBus *iommu_pci_bus = s->iommu_pcibus_by_bus_num[bus_num]; + + if (!iommu_pci_bus) { + GHashTableIter iter; + + g_hash_table_iter_init(&iter, s->as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&iommu_pci_bus)) { + if (pci_bus_num(iommu_pci_bus->bus) == bus_num) { + s->iommu_pcibus_by_bus_num[bus_num] = iommu_pci_bus; + return iommu_pci_bus; + } + } + } + return iommu_pci_bus; +} + +IOMMUMemoryRegion *virtio_iommu_mr(VirtIOIOMMU *s, uint32_t sid); +IOMMUMemoryRegion *virtio_iommu_mr(VirtIOIOMMU *s, uint32_t sid) +{ + uint8_t bus_n, devfn; + IOMMUPciBus *iommu_pci_bus; + IOMMUDevice *dev; + + bus_n = PCI_BUS_NUM(sid); + iommu_pci_bus = iommu_find_iommu_pcibus(s, bus_n); + if (iommu_pci_bus) { + devfn = sid & 0xFF; + dev = iommu_pci_bus->pbdev[devfn]; + if (dev) { + return &dev->iommu_mr; + } + } + return NULL; +} + +static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, + int devfn) +{ + VirtIOIOMMU *s = opaque; + IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus); + static uint32_t mr_index; + IOMMUDevice *sdev; + + if (!sbus) { + sbus = g_malloc0(sizeof(IOMMUPciBus) + + sizeof(IOMMUDevice *) * IOMMU_PCI_DEVFN_MAX); + sbus->bus = bus; + g_hash_table_insert(s->as_by_busptr, bus, sbus); + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + char *name = g_strdup_printf("%s-%d-%d", + TYPE_VIRTIO_IOMMU_MEMORY_REGION, + mr_index++, devfn); + sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(IOMMUDevice)); + + sdev->viommu = s; + sdev->bus = bus; + sdev->devfn = devfn; + + trace_virtio_iommu_init_iommu_mr(name); + + memory_region_init_iommu(&sdev->iommu_mr, sizeof(sdev->iommu_mr), + TYPE_VIRTIO_IOMMU_MEMORY_REGION, + OBJECT(s), name, + UINT64_MAX); + address_space_init(&sdev->as, + MEMORY_REGION(&sdev->iommu_mr), TYPE_VIRTIO_IOMMU); + g_free(name); + } + return &sdev->as; +} + static int virtio_iommu_attach(VirtIOIOMMU *s, struct virtio_iommu_req_attach *req) { @@ -172,6 +261,27 @@ out: } } +static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, + IOMMUAccessFlags flag, + int iommu_idx) +{ + IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); + uint32_t sid; + + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = addr, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + + sid = virtio_iommu_get_sid(sdev); + + trace_virtio_iommu_translate(mr->parent_obj.name, sid, addr, flag); + return entry; +} + static void virtio_iommu_get_config(VirtIODevice *vdev, uint8_t *config_data) { VirtIOIOMMU *dev = VIRTIO_IOMMU(vdev); @@ -226,6 +336,8 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) virtio_init(vdev, "virtio-iommu", VIRTIO_ID_IOMMU, sizeof(struct virtio_iommu_config)); + memset(s->iommu_pcibus_by_bus_num, 0, sizeof(s->iommu_pcibus_by_bus_num)); + s->req_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, virtio_iommu_handle_command); s->event_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, NULL); @@ -244,6 +356,14 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MMIO); qemu_mutex_init(&s->mutex); + + s->as_by_busptr = g_hash_table_new_full(NULL, NULL, NULL, g_free); + + if (s->primary_bus) { + pci_setup_iommu(s->primary_bus, virtio_iommu_find_add_as, s); + } else { + error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!"); + } } static void virtio_iommu_device_unrealize(DeviceState *dev, Error **errp) @@ -301,6 +421,14 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data) vdc->vmsd = &vmstate_virtio_iommu_device; } +static void virtio_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = virtio_iommu_translate; +} + static const TypeInfo virtio_iommu_info = { .name = TYPE_VIRTIO_IOMMU, .parent = TYPE_VIRTIO_DEVICE, @@ -309,9 +437,16 @@ static const TypeInfo virtio_iommu_info = { .class_init = virtio_iommu_class_init, }; +static const TypeInfo virtio_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_VIRTIO_IOMMU_MEMORY_REGION, + .class_init = virtio_iommu_memory_region_class_init, +}; + static void virtio_register_types(void) { type_register_static(&virtio_iommu_info); + type_register_static(&virtio_iommu_memory_region_info); } type_init(virtio_register_types) diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 4ebf4f093e..c6c33b4af0 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -28,6 +28,8 @@ #define VIRTIO_IOMMU(obj) \ OBJECT_CHECK(VirtIOIOMMU, (obj), TYPE_VIRTIO_IOMMU) +#define TYPE_VIRTIO_IOMMU_MEMORY_REGION "virtio-iommu-memory-region" + #define IOMMU_PCI_BUS_MAX 256 #define IOMMU_PCI_DEVFN_MAX 256 @@ -51,6 +53,7 @@ typedef struct VirtIOIOMMU { struct virtio_iommu_config config; uint64_t features; GHashTable *as_by_busptr; + IOMMUPciBus *iommu_pcibus_by_bus_num[IOMMU_PCI_BUS_MAX]; PCIBus *primary_bus; GTree *domains; QemuMutex mutex;