From patchwork Tue May 21 10:57:21 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 245278 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id CB3752C00BC for ; Tue, 21 May 2013 21:09:35 +1000 (EST) Received: from localhost ([::1]:37179 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UekRg-0003YX-AJ for incoming@patchwork.ozlabs.org; Tue, 21 May 2013 07:09:32 -0400 Received: from eggs.gnu.org ([208.118.235.92]:45135) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UekGu-0004to-Qu for qemu-devel@nongnu.org; Tue, 21 May 2013 06:58:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UekGt-0005WH-5R for qemu-devel@nongnu.org; Tue, 21 May 2013 06:58:24 -0400 Received: from mail-ea0-x235.google.com ([2a00:1450:4013:c01::235]:51225) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UekGs-0005W6-Sm for qemu-devel@nongnu.org; Tue, 21 May 2013 06:58:23 -0400 Received: by mail-ea0-f181.google.com with SMTP id a11so289201eae.26 for ; Tue, 21 May 2013 03:58:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=oVy5IRXfu/Y1mQQBmAOVG+3q8ly0C+4FzuInbd/0oOg=; b=fhmNgB+4VX7pQYasRc1/lBiWu8HU3fxxvu7uCyQyGTEUxWKgM+KKNuwPOKY8f5SXo7 lfKyDqWyip0Q2DtVKmtfZejnM7pxlZXHZlu6OV/+Flu1S7pBTUSYSmkUI60Mxta5wq9e dWqR61bu6ePeAv9DqKf9DRC1VTEvQAZ/B2aLN0wYxPCS9uPSCpcF+dH/FH1WPBpZZFc/ tgYyzND1cr4G9SoGMZnmHXXF+e/0v9Ah/tC4IgJWr16zIEIQXyIWuw40rhXqElDVJRux YpQClIrq5IWrt/0Ou+DLHbOJldLkoMs3o21JNCJRLKaKtZl1UGkvwxxnY3m4Oq5n7iMW tkDQ== X-Received: by 10.14.246.137 with SMTP id q9mr5100911eer.39.1369133902098; Tue, 21 May 2013 03:58:22 -0700 (PDT) Received: from playground.lan (net-37-116-223-193.cust.dsl.vodafone.it. [37.116.223.193]) by mx.google.com with ESMTPSA id a5sm2754398ees.6.2013.05.21.03.58.19 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Tue, 21 May 2013 03:58:21 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Tue, 21 May 2013 12:57:21 +0200 Message-Id: <1369133851-1894-21-git-send-email-pbonzini@redhat.com> X-Mailer: git-send-email 1.8.1.4 In-Reply-To: <1369133851-1894-1-git-send-email-pbonzini@redhat.com> References: <1369133851-1894-1-git-send-email-pbonzini@redhat.com> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:4013:c01::235 Cc: peter.maydell@linaro.org, jan.kiszka@gmail.com, Avi Kivity , David Gibson Subject: [Qemu-devel] [PATCH 20/30] memory: iommu support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Avi Kivity Add a new memory region type that translates addresses it is given, then forwards them to a target address space. This is similar to an alias, except that the mapping is more flexible than a linear translation and trucation, and also less efficient since the translation happens at runtime. The implementation uses an AddressSpace mapping the target region to avoid hierarchical dispatch all the way to the resolved region; only iommu regions are looked up dynamically. Signed-off-by: Avi Kivity [Modified to put translation in address_space_translate - Paolo] Signed-off-by: Paolo Bonzini --- exec.c | 35 ++++++++++++++++++++++----- include/exec/memory.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++---- memory.c | 21 ++++++++++++++++ 3 files changed, 112 insertions(+), 10 deletions(-) diff --git a/exec.c b/exec.c index a29dec5..b7266c8 100644 --- a/exec.c +++ b/exec.c @@ -208,15 +208,38 @@ MemoryRegionSection *address_space_translate(AddressSpace *as, hwaddr addr, hwaddr *xlat, hwaddr *plen, bool is_write) { + IOMMUTLBEntry iotlb; MemoryRegionSection *section; + hwaddr len = *plen; - section = address_space_lookup_region(as, addr); - /* Compute offset within MemoryRegionSection */ - addr -= section->offset_within_address_space; - *plen = MIN(section->size - addr, *plen); + for (;;) { + section = address_space_lookup_region(as, addr); + + /* Compute offset within MemoryRegionSection */ + addr -= section->offset_within_address_space; + len = MIN(section->size - addr, len); + + /* Compute offset within MemoryRegion */ + addr += section->offset_within_region; + + if (!section->mr->iommu_ops) { + break; + } + + iotlb = section->mr->iommu_ops->translate(section->mr, addr); + addr = ((iotlb.translated_addr & ~iotlb.addr_mask) + | (addr & iotlb.addr_mask)); + len = MIN(len, (addr | iotlb.addr_mask) - addr + 1); + if (!(iotlb.perm & (1 << is_write))) { + section = &phys_sections[phys_section_unassigned]; + break; + } + + as = section->mr->iommu_target_as; + } - /* Compute offset within MemoryRegion */ - *xlat = addr + section->offset_within_region; + *plen = len; + *xlat = addr; return section; } diff --git a/include/exec/memory.h b/include/exec/memory.h index 94709d1..e5cad03 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -115,12 +115,36 @@ struct MemoryRegionOps { const MemoryRegionMmio old_mmio; }; +typedef struct IOMMUTLBEntry IOMMUTLBEntry; +typedef struct MemoryRegionIOMMUOps MemoryRegionIOMMUOps; + +/* See address_space_translate: bit 0 is read, bit 1 is write. */ +typedef enum { + IOMMU_NONE = 0, + IOMMU_RO = 1, + IOMMU_WO = 2, + IOMMU_RW = 3, +} IOMMUAccessFlags; + +struct IOMMUTLBEntry { + hwaddr iova; + hwaddr translated_addr; + hwaddr addr_mask; /* 0xfff = 4k translation */ + IOMMUAccessFlags perm; +}; + +struct MemoryRegionIOMMUOps { + /* Returns a TLB entry that contains a given address. */ + IOMMUTLBEntry (*translate)(MemoryRegion *iommu, hwaddr addr); +}; + typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; struct MemoryRegion { /* All fields are private - violators will be prosecuted */ const MemoryRegionOps *ops; + const MemoryRegionIOMMUOps *iommu_ops; void *opaque; MemoryRegion *parent; Int128 size; @@ -147,6 +171,7 @@ struct MemoryRegion { uint8_t dirty_log_mask; unsigned ioeventfd_nb; MemoryRegionIoeventfd *ioeventfds; + struct AddressSpace *iommu_target_as; }; struct MemoryRegionPortio { @@ -332,6 +357,25 @@ void memory_region_init_rom_device(MemoryRegion *mr, void memory_region_init_reservation(MemoryRegion *mr, const char *name, uint64_t size); + +/** + * memory_region_init_iommu: Initialize a memory region that translates addresses + * + * An IOMMU region translates addresses and forwards accesses to a target memory region. + * + * @mr: the #MemoryRegion to be initialized + * @ops: a function that translates addresses into the @target region + * @target_as: the #AddressSpace that will be used to satisfy accesses to translated + * addresses + * @name: used for debugging; not visible to the user or ABI + * @size: size of the region. + */ +void memory_region_init_iommu(MemoryRegion *mr, + MemoryRegionIOMMUOps *ops, + AddressSpace *target_as, + const char *name, + uint64_t size); + /** * memory_region_destroy: Destroy a memory region and reclaim all resources. * @@ -371,6 +415,15 @@ static inline bool memory_region_is_romd(MemoryRegion *mr) } /** + * memory_region_is_iommu: check whether a memory region is an iommu + * + * Returns %true is a memory region is an iommu. + * + * @mr: the memory region being queried + */ +bool memory_region_is_iommu(MemoryRegion *mr); + +/** * memory_region_name: get a memory region's name * * Returns the string that was used to initialize the memory region. @@ -816,7 +869,8 @@ void address_space_destroy(AddressSpace *as); /** * address_space_rw: read from or write to an address space. * - * Return true if the operation hit any unassigned memory. + * Return true if the operation hit any unassigned memory or encountered an + * IOMMU fault. * * @as: #AddressSpace to be accessed * @addr: address within that address space @@ -829,7 +883,8 @@ bool address_space_rw(AddressSpace *as, hwaddr addr, uint8_t *buf, /** * address_space_write: write to address space. * - * Return true if the operation hit any unassigned memory. + * Return true if the operation hit any unassigned memory or encountered an + * IOMMU fault. * * @as: #AddressSpace to be accessed * @addr: address within that address space @@ -841,7 +896,8 @@ bool address_space_write(AddressSpace *as, hwaddr addr, /** * address_space_read: read from an address space. * - * Return true if the operation hit any unassigned memory. + * Return true if the operation hit any unassigned memory or encountered an + * IOMMU fault. * * @as: #AddressSpace to be accessed * @addr: address within that address space @@ -865,7 +921,9 @@ MemoryRegionSection *address_space_translate(AddressSpace *as, hwaddr addr, /* address_space_valid: check for validity of an address space range * - * Check whether memory is assigned to the given address space range. + * Check whether memory is assigned to the given address space range, and + * access is permitted by any IOMMU regions that are active for the address + * space. * * For now, addr and len should be aligned to a page size. This limitation * will be lifted in the future. diff --git a/memory.c b/memory.c index 99f046d..b25d21d 100644 --- a/memory.c +++ b/memory.c @@ -787,6 +787,7 @@ void memory_region_init(MemoryRegion *mr, uint64_t size) { mr->ops = NULL; + mr->iommu_ops = NULL; mr->parent = NULL; mr->size = int128_make64(size); if (size == UINT64_MAX) { @@ -977,6 +978,21 @@ void memory_region_init_rom_device(MemoryRegion *mr, mr->ram_addr = qemu_ram_alloc(size, mr); } +void memory_region_init_iommu(MemoryRegion *mr, + MemoryRegionIOMMUOps *ops, + AddressSpace *target_as, + const char *name, + uint64_t size) +{ + memory_region_init(mr, name, size); + mr->ops = NULL; + mr->iommu_ops = ops, + mr->opaque = mr; + mr->terminates = true; /* then re-forwards */ + mr->destructor = memory_region_destructor_none; + mr->iommu_target_as = target_as; +} + static uint64_t invalid_read(void *opaque, hwaddr addr, unsigned size) { @@ -1051,6 +1067,11 @@ bool memory_region_is_rom(MemoryRegion *mr) return mr->ram && mr->readonly; } +bool memory_region_is_iommu(MemoryRegion *mr) +{ + return mr->iommu_ops; +} + void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) { uint8_t mask = 1 << client;