From patchwork Sat Aug 28 14:54:53 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: 139811 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o7SEuxbe024795 for ; Sat, 28 Aug 2010 14:57:38 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751973Ab0H1O46 (ORCPT ); Sat, 28 Aug 2010 10:56:58 -0400 Received: from mail-bw0-f46.google.com ([209.85.214.46]:38181 "EHLO mail-bw0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751709Ab0H1O4t (ORCPT ); Sat, 28 Aug 2010 10:56:49 -0400 Received: by mail-bw0-f46.google.com with SMTP id 11so2651956bwz.19 for ; Sat, 28 Aug 2010 07:56:49 -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; bh=IdbKneAnYiN5wbcN5St3yf49Atnj/Mm3znG/qYvPUcE=; b=cvT5REwrrMTiZh4GP4hapSV07xjTJUIFNjOWJy4RN5YbgQ1mBbTavJzWKkRit5l/Gs ZirOFQzKUnDAe/KWhpKIJF6iTXB5buhKz7wlIe6iu8A1oxatEvhe0lC9ukbO/Jdji/BS HOdUzLTnHLyJpdMDE0hdlX6hGDkIsWeX0AWIA= 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=MU+TE/6qzblgXuLf/H9xe8DEjFr+xN0Z/D3Wgzt0mqbWgZB4CKyI8ggM4OT5QWp0RT +FDVsETyw0SBXasuyBYkGQQPWlRic7enhPcceRXP3s6k5p1kePTDyDsDBbFbvQRCGRws o9v3BNVgS8yR8uK7euaCqWdBOECvdJOJXrheA= Received: by 10.204.115.212 with SMTP id j20mr1561610bkq.5.1283007408928; Sat, 28 Aug 2010 07:56:48 -0700 (PDT) Received: from localhost ([188.25.93.165]) by mx.google.com with ESMTPS id y2sm3511678bkx.20.2010.08.28.07.56.47 (version=SSLv3 cipher=RC4-MD5); Sat, 28 Aug 2010 07:56:47 -0700 (PDT) From: Eduard - Gabriel Munteanu To: mst@redhat.com Cc: joro@8bytes.org, blauwirbel@gmail.com, paul@codesourcery.com, avi@redhat.com, anthony@codemonkey.ws, av1474@comtv.ru, yamahata@valinux.co.jp, kvm@vger.kernel.org, qemu-devel@nongnu.org, Eduard - Gabriel Munteanu Subject: [PATCH 2/7] pci: memory access API and IOMMU support Date: Sat, 28 Aug 2010 17:54:53 +0300 Message-Id: <1283007298-10942-3-git-send-email-eduard.munteanu@linux360.ro> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1283007298-10942-1-git-send-email-eduard.munteanu@linux360.ro> References: <1283007298-10942-1-git-send-email-eduard.munteanu@linux360.ro> 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 (demeter1.kernel.org [140.211.167.41]); Sat, 28 Aug 2010 14:57:38 +0000 (UTC) diff --git a/hw/pci.c b/hw/pci.c index 2dc1577..b460905 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -158,6 +158,19 @@ static void pci_device_reset(PCIDevice *dev) pci_update_mappings(dev); } +static int pci_no_translate(PCIDevice *iommu, + PCIDevice *dev, + pcibus_t addr, + target_phys_addr_t *paddr, + target_phys_addr_t *len, + unsigned perms) +{ + *paddr = addr; + *len = -1; + + return 0; +} + static void pci_bus_reset(void *opaque) { PCIBus *bus = opaque; @@ -220,7 +233,10 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, { qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name); assert(PCI_FUNC(devfn_min) == 0); - bus->devfn_min = devfn_min; + + bus->devfn_min = devfn_min; + bus->iommu = NULL; + bus->translate = pci_no_translate; /* host bridge */ QLIST_INIT(&bus->child); @@ -1789,3 +1805,170 @@ static char *pcibus_get_dev_path(DeviceState *dev) return strdup(path); } +void pci_register_iommu(PCIDevice *iommu, + PCITranslateFunc *translate) +{ + iommu->bus->iommu = iommu; + iommu->bus->translate = translate; +} + +void pci_memory_rw(PCIDevice *dev, + pcibus_t addr, + uint8_t *buf, + pcibus_t len, + int is_write) +{ + int err; + unsigned perms; + PCIDevice *iommu = dev->bus->iommu; + target_phys_addr_t paddr, plen; + + perms = is_write ? IOMMU_PERM_WRITE : IOMMU_PERM_READ; + + while (len) { + err = dev->bus->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; + } +} + +static void pci_memory_register_map(PCIDevice *dev, + pcibus_t addr, + pcibus_t len, + target_phys_addr_t paddr, + PCIInvalidateMapFunc *invalidate, + void *invalidate_opaque) +{ + PCIMemoryMap *map; + + map = qemu_malloc(sizeof(PCIMemoryMap)); + map->addr = addr; + map->len = len; + map->paddr = paddr; + map->invalidate = invalidate; + map->invalidate_opaque = invalidate_opaque; + + QLIST_INSERT_HEAD(&dev->memory_maps, map, list); +} + +static void pci_memory_unregister_map(PCIDevice *dev, + target_phys_addr_t paddr, + target_phys_addr_t len) +{ + PCIMemoryMap *map; + + QLIST_FOREACH(map, &dev->memory_maps, list) { + if (map->paddr == paddr && map->len == len) { + QLIST_REMOVE(map, list); + free(map); + } + } +} + +void pci_memory_invalidate_range(PCIDevice *dev, + pcibus_t addr, + pcibus_t len) +{ + PCIMemoryMap *map; + + QLIST_FOREACH(map, &dev->memory_maps, list) { + if (ranges_overlap(addr, len, map->addr, map->len)) { + map->invalidate(map->invalidate_opaque); + QLIST_REMOVE(map, list); + free(map); + } + } +} + +void *pci_memory_map(PCIDevice *dev, + PCIInvalidateMapFunc *cb, + void *opaque, + pcibus_t addr, + target_phys_addr_t *len, + int is_write) +{ + int err; + unsigned perms; + PCIDevice *iommu = dev->bus->iommu; + target_phys_addr_t paddr, plen; + + perms = is_write ? IOMMU_PERM_WRITE : IOMMU_PERM_READ; + + plen = *len; + err = dev->bus->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) + pci_memory_register_map(dev, addr, *len, paddr, 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); + pci_memory_unregister_map(dev, (target_phys_addr_t) buffer, len); +} + +#define DEFINE_PCI_LD(suffix, size) \ +uint##size##_t pci_ld##suffix(PCIDevice *dev, pcibus_t addr) \ +{ \ + int err; \ + target_phys_addr_t paddr, plen; \ + \ + err = dev->bus->translate(dev->bus->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, pcibus_t addr, uint##size##_t val) \ +{ \ + int err; \ + target_phys_addr_t paddr, plen; \ + \ + err = dev->bus->translate(dev->bus->iommu, dev, \ + addr, &paddr, &plen, IOMMU_PERM_WRITE); \ + if (err || (plen < size / 8)) \ + return; \ + \ + st##suffix##_phys(paddr, val); \ +} + +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) + diff --git a/hw/pci.h b/hw/pci.h index c551f96..3131016 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -172,6 +172,8 @@ struct PCIDevice { char *romfile; ram_addr_t rom_offset; uint32_t rom_bar; + + QLIST_HEAD(, PCIMemoryMap) memory_maps; }; PCIDevice *pci_register_device(PCIBus *bus, const char *name, @@ -391,4 +393,76 @@ static inline int ranges_overlap(uint64_t first1, uint64_t len1, return !(last2 < first1 || last1 < first2); } +/* + * Memory I/O and PCI IOMMU definitions. + */ + +#define IOMMU_PERM_READ (1 << 0) +#define IOMMU_PERM_WRITE (1 << 1) +#define IOMMU_PERM_RW (IOMMU_PERM_READ | IOMMU_PERM_WRITE) + +typedef int PCIInvalidateMapFunc(void *opaque); +typedef int PCITranslateFunc(PCIDevice *iommu, + PCIDevice *dev, + pcibus_t addr, + target_phys_addr_t *paddr, + target_phys_addr_t *len, + unsigned perms); + +extern void pci_memory_rw(PCIDevice *dev, + pcibus_t addr, + uint8_t *buf, + pcibus_t len, + int is_write); +extern void *pci_memory_map(PCIDevice *dev, + PCIInvalidateMapFunc *cb, + void *opaque, + pcibus_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, + PCITranslateFunc *translate); +extern void pci_memory_invalidate_range(PCIDevice *dev, + pcibus_t addr, + pcibus_t len); + +#define DECLARE_PCI_LD(suffix, size) \ +extern uint##size##_t pci_ld##suffix(PCIDevice *dev, pcibus_t addr); + +#define DECLARE_PCI_ST(suffix, size) \ +extern void pci_st##suffix(PCIDevice *dev, \ + pcibus_t addr, \ + uint##size##_t val); + +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) + +static inline void pci_memory_read(PCIDevice *dev, + pcibus_t addr, + uint8_t *buf, + pcibus_t len) +{ + pci_memory_rw(dev, addr, buf, len, 0); +} + +static inline void pci_memory_write(PCIDevice *dev, + pcibus_t addr, + const uint8_t *buf, + pcibus_t len) +{ + pci_memory_rw(dev, addr, (uint8_t *) buf, len, 1); +} + #endif diff --git a/hw/pci_internals.h b/hw/pci_internals.h index e3c93a3..fb134b9 100644 --- a/hw/pci_internals.h +++ b/hw/pci_internals.h @@ -33,6 +33,9 @@ struct PCIBus { Keep a count of the number of devices with raised IRQs. */ int nirq; int *irq_count; + + PCIDevice *iommu; + PCITranslateFunc *translate; }; struct PCIBridge { @@ -44,4 +47,13 @@ struct PCIBridge { const char *bus_name; }; +struct PCIMemoryMap { + pcibus_t addr; + pcibus_t len; + target_phys_addr_t paddr; + PCIInvalidateMapFunc *invalidate; + void *invalidate_opaque; + QLIST_ENTRY(PCIMemoryMap) list; +}; + #endif /* QEMU_PCI_INTERNALS_H */ diff --git a/qemu-common.h b/qemu-common.h index d735235..8b060e8 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -218,6 +218,7 @@ typedef struct SMBusDevice SMBusDevice; typedef struct PCIHostState PCIHostState; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIBus PCIBus; +typedef struct PCIMemoryMap PCIMemoryMap; typedef struct PCIDevice PCIDevice; typedef struct PCIBridge PCIBridge; typedef struct SerialState SerialState;