From patchwork Wed Aug 4 22:32:13 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard - Gabriel Munteanu X-Patchwork-Id: 117166 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o74MXpd8013516 for ; Wed, 4 Aug 2010 22:33:51 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757951Ab0HDWds (ORCPT ); Wed, 4 Aug 2010 18:33:48 -0400 Received: from mail-bw0-f46.google.com ([209.85.214.46]:38912 "EHLO mail-bw0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757691Ab0HDWdr (ORCPT ); Wed, 4 Aug 2010 18:33:47 -0400 Received: by bwz1 with SMTP id 1so2651413bwz.19 for ; Wed, 04 Aug 2010 15:33:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:sender:from:to:cc:subject :date:message-id:x-mailer:in-reply-to:references:in-reply-to :references; bh=BaSsizBVk26wWFvRqq+p+k7stn0KZKGWwxnujB6aKbY=; b=A+O2tG1MIgfWeGnqGrD7sA777FjHLmPl6Ik0+epPmzCCvLS9tFO/ZtgAFDnr9Th/cT 8C7cXSwk2B0CbU7kEnX096itS/F8RJFRMGg9LZ/YcQPonifVG6uKRoJ1bUSXX/BPofxa vYxojn2wtwzoefcrxpkJ21o29m5yqaaNqtkR8= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; b=DUFHMxm9N1h22XinyM/8szg83yDZbQWLAYBXKuKda0svEeWRQfor5MZBeafbz8uy/3 qphTyIB8Oc6U6qAZRVitAh4FnXgbwly3vLlQ9snqZXU1mTyR3uHwcerFd8tYb/Gyis64 0nEIGasMNaZff/aQTpB9fbmFPSP8SJ6Ql5twY= Received: by 10.204.6.75 with SMTP id 11mr6680750bky.95.1280961225261; Wed, 04 Aug 2010 15:33:45 -0700 (PDT) Received: from localhost ([188.25.93.228]) by mx.google.com with ESMTPS id a11sm6305197bkc.0.2010.08.04.15.33.44 (version=SSLv3 cipher=RC4-MD5); Wed, 04 Aug 2010 15:33:44 -0700 (PDT) From: Eduard - Gabriel Munteanu To: joro@8bytes.org Cc: avi@redhat.com, paul@codesourcery.com, anthony@codemonkey.ws, kvm@vger.kernel.org, qemu-devel@nongnu.org, Eduard - Gabriel Munteanu Subject: [RFC PATCH 1/4] pci: memory access API and IOMMU support Date: Thu, 5 Aug 2010 01:32:13 +0300 Message-Id: X-Mailer: git-send-email 1.7.1 In-Reply-To: References: In-Reply-To: References: Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Wed, 04 Aug 2010 22:33:52 +0000 (UTC) diff --git a/hw/pci.c b/hw/pci.c index 6871728..ce2734b 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -58,6 +58,10 @@ struct PCIBus { Keep a count of the number of devices with raised IRQs. */ int nirq; int *irq_count; + +#ifdef CONFIG_PCI_IOMMU + PCIIOMMU *iommu; +#endif }; static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent); @@ -2029,6 +2033,147 @@ static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent) } } +#ifdef CONFIG_PCI_IOMMU + +void pci_register_iommu(PCIDevice *dev, PCIIOMMU *iommu) +{ + dev->bus->iommu = iommu; +} + +void pci_memory_rw(PCIDevice *dev, + pci_addr_t addr, + uint8_t *buf, + pci_addr_t len, + int is_write) +{ + int err, plen; + unsigned perms; + PCIIOMMU *iommu = dev->bus->iommu; + target_phys_addr_t paddr; + + if (!iommu || !iommu->translate) + return cpu_physical_memory_rw(addr, buf, len, is_write); + + perms = is_write ? IOMMU_PERM_WRITE : IOMMU_PERM_READ; + + while (len) { + err = iommu->translate(iommu, dev, addr, &paddr, &plen, perms); + if (err) + return; + + /* The translation might be valid for larger regions. */ + if (plen > len) + plen = len; + + cpu_physical_memory_rw(paddr, buf, plen, is_write); + + len -= plen; + addr += plen; + buf += plen; + } +} + +void *pci_memory_map(PCIDevice *dev, + PCIInvalidateIOTLBFunc *cb, + void *opaque, + pci_addr_t addr, + target_phys_addr_t *len, + int is_write) +{ + int err, plen; + unsigned perms; + PCIIOMMU *iommu = dev->bus->iommu; + target_phys_addr_t paddr; + + if (!iommu || !iommu->translate) + return cpu_physical_memory_map(addr, len, is_write); + + perms = is_write ? IOMMU_PERM_WRITE : IOMMU_PERM_READ; + + plen = *len; + err = iommu->translate(iommu, dev, addr, &paddr, &plen, perms); + if (err) + return NULL; + + /* + * If this is true, the virtual region is contiguous, + * but the translated physical region isn't. We just + * clamp *len, much like cpu_physical_memory_map() does. + */ + if (plen < *len) + *len = plen; + + /* We treat maps as remote TLBs to cope with stuff like AIO. */ + if (cb && iommu->register_iotlb_invalidator) + iommu->register_iotlb_invalidator(iommu, dev, addr, cb, opaque); + + return cpu_physical_memory_map(paddr, len, is_write); +} + +void pci_memory_unmap(PCIDevice *dev, + void *buffer, + target_phys_addr_t len, + int is_write, + target_phys_addr_t access_len) +{ + cpu_physical_memory_unmap(buffer, len, is_write, access_len); +} + +#define DEFINE_PCI_LD(suffix, size) \ +uint##size##_t pci_ld##suffix(PCIDevice *dev, pci_addr_t addr) \ +{ \ + PCIIOMMU *iommu = dev->bus->iommu; \ + target_phys_addr_t paddr; \ + int plen, err; \ + \ + if (!iommu || !iommu->translate) \ + return ld##suffix##_phys(addr); \ + \ + err = iommu->translate(iommu, dev, \ + addr, &paddr, &plen, IOMMU_PERM_READ); \ + if (err || (plen < size / 8)) \ + return 0; \ + \ + return ld##suffix##_phys(paddr); \ +} + +#define DEFINE_PCI_ST(suffix, size) \ +void pci_st##suffix(PCIDevice *dev, pci_addr_t addr, uint##size##_t val) \ +{ \ + PCIIOMMU *iommu = dev->bus->iommu; \ + target_phys_addr_t paddr; \ + int plen, err; \ + \ + if (!iommu || !iommu->translate) { \ + st##suffix##_phys(addr, val); \ + return; \ + } \ + \ + err = iommu->translate(iommu, dev, \ + addr, &paddr, &plen, IOMMU_PERM_WRITE); \ + if (err || (plen < size / 8)) \ + return; \ + \ + st##suffix##_phys(paddr, val); \ +} + +#else /* !defined(CONFIG_PCI_IOMMU) */ + +#define DEFINE_PCI_LD(suffix, size) +#define DEFINE_PCI_ST(suffix, size) + +#endif /* CONFIG_PCI_IOMMU */ + +DEFINE_PCI_LD(ub, 8) +DEFINE_PCI_LD(uw, 16) +DEFINE_PCI_LD(l, 32) +DEFINE_PCI_LD(q, 64) + +DEFINE_PCI_ST(b, 8) +DEFINE_PCI_ST(w, 16) +DEFINE_PCI_ST(l, 32) +DEFINE_PCI_ST(q, 64) + static PCIDeviceInfo bridge_info = { .qdev.name = "pci-bridge", .qdev.size = sizeof(PCIBridge), diff --git a/hw/pci.h b/hw/pci.h index 4bd8a1a..bd8c21b 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -430,4 +430,134 @@ static inline int ranges_overlap(uint64_t first1, uint64_t len1, return !(last2 < first1 || last1 < first2); } +/* + * Memory I/O and PCI IOMMU definitions. + */ + +typedef target_phys_addr_t pci_addr_t; + +typedef int PCIInvalidateIOTLBFunc(void *opaque); + +#ifndef CONFIG_PCI_IOMMU + +static inline void pci_memory_rw(PCIDevice *dev, + pci_addr_t addr, + uint8_t *buf, + pci_addr_t len, + int is_write) +{ + cpu_physical_memory_rw(addr, buf, len, is_write); +} + +static inline void *pci_memory_map(PCIDevice *dev, + PCIInvalidateIOTLBFunc *cb, + void *opaque, + pci_addr_t addr, + target_phys_addr_t *len, + int is_write) +{ + return cpu_physical_memory_map(addr, plen, is_write); +} + +static inline void pci_memory_unmap(PCIDevice *dev, + void *buffer, + target_phys_addr_t len, + int is_write, + target_phys_addr_t access_len) +{ + cpu_physical_memory_unmap(buffer, len, is_write, access_len); +} + +#define DECLARE_PCI_LD(suffix, size) \ +static inline uint##size##_t pci_ld##suffix(PCIDevice *dev, \ + pci_addr_t addr) \ +{ \ + return ld##suffix##_phys(addr); \ +} + +#define DECLARE_PCI_ST(suffix, size) \ +static inline void pci_st##suffix(PCIDevice *dev, \ + pci_addr_t addr, \ + uint##size##_t val) \ +{ \ + st##suffix##_phys(addr, val); \ +} + +#else /* defined(CONFIG_PCI_IOMMU) */ + +struct PCIIOMMU { + void *opaque; + + void (*register_iotlb_invalidator)(PCIIOMMU *iommu, + PCIDevice *dev, + pci_addr_t addr, + PCIInvalidateIOTLBFunc *cb, + void *opaque); + int (*translate)(PCIIOMMU *iommu, + PCIDevice *dev, + pci_addr_t addr, + target_phys_addr_t *paddr, + int *len, + unsigned perms); +}; + +#define IOMMU_PERM_READ (1 << 0) +#define IOMMU_PERM_WRITE (1 << 1) +#define IOMMU_PERM_RW (IOMMU_PERM_READ | IOMMU_PERM_WRITE) + +extern void pci_memory_rw(PCIDevice *dev, + pci_addr_t addr, + uint8_t *buf, + pci_addr_t len, + int is_write); +extern void *pci_memory_map(PCIDevice *dev, + PCIInvalidateIOTLBFunc *cb, + void *opaque, + pci_addr_t addr, + target_phys_addr_t *len, + int is_write); +extern void pci_memory_unmap(PCIDevice *dev, + void *buffer, + target_phys_addr_t len, + int is_write, + target_phys_addr_t access_len); +extern void pci_register_iommu(PCIDevice *dev, + PCIIOMMU *iommu); + +#define DECLARE_PCI_LD(suffix, size) \ +extern uint##size##_t pci_ld##suffix(PCIDevice *dev, pci_addr_t addr); + +#define DECLARE_PCI_ST(suffix, size) \ +extern void pci_st##suffix(PCIDevice *dev, \ + pci_addr_t addr, \ + uint##size##_t val); + +#endif /* CONFIG_PCI_IOMMU */ + +static inline void pci_memory_read(PCIDevice *dev, + pci_addr_t addr, + uint8_t *buf, + pci_addr_t len) +{ + pci_memory_rw(dev, addr, buf, len, 0); +} + +static inline void pci_memory_write(PCIDevice *dev, + pci_addr_t addr, + const uint8_t *buf, + pci_addr_t len) +{ + pci_memory_rw(dev, addr, (uint8_t *) buf, len, 1); +} + +DECLARE_PCI_LD(ub, 8) +DECLARE_PCI_LD(uw, 16) +DECLARE_PCI_LD(l, 32) +DECLARE_PCI_LD(q, 64) + +DECLARE_PCI_ST(b, 8) +DECLARE_PCI_ST(w, 16) +DECLARE_PCI_ST(l, 32) +DECLARE_PCI_ST(q, 64) + #endif diff --git a/qemu-common.h b/qemu-common.h index 3fb2f0b..8daf962 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -219,6 +219,7 @@ typedef struct PCIHostState PCIHostState; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIBus PCIBus; typedef struct PCIDevice PCIDevice; +typedef struct PCIIOMMU PCIIOMMU; typedef struct SerialState SerialState; typedef struct IRQState *qemu_irq; typedef struct PCMCIACardState PCMCIACardState;