qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] Re: PATCH/RFC: PCI memory mapping


From: Brian Wheeler
Subject: Re: [Qemu-devel] Re: PATCH/RFC: PCI memory mapping
Date: Thu, 02 Apr 2009 13:23:49 -0400

On Thu, 2009-04-02 at 17:18 +0200, Tristan Gingold wrote:
> On Apr 2, 2009, at 4:57 PM, Brian Wheeler wrote:
> 
> > [first off, if there's an easier way to do this, let me know!]
> >
> > This patch adds an address mapping function to the PCI bus so the host
> > chipset can remap PCI generated addresses to the appropriate physical
> > addresses.
> 
> I have been thinking about this for a while as many 64bits machines  
> have an IOTLB or IOMMU.
> And with VTd and AMD IOMMU it is becoming common.
> 
> > It adds two parameters to pci_register_bus:
> >
> > PCIBus *pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn  
> > map_irq,
> >                         qemu_irq *pic, int devfn_min, int nirq,
> >                      pci_map_addr_fn map_addr, void *map_addr_opaque)
> 
> map_addr_opaque should be simply opaque (in case of new callbacks are  
> added).
> 

Are you sure?  pic is the opaque data passed to pci_map_irq_fn, so we're
already setting more than one callback.  I've done it differently in the
new patch.


> > The map_addr is a function which has the prototype:
> >
> > typedef target_phys_addr_t (*pci_map_addr_fn)(void *opaque, int  
> > bus_num,
> >                                           target_phys_addr_t addr);
> 
> I think this is not complete enough.
> 
> I'd replace int bus_num with PCIDevice *dev.  You can get the pci bus  
> from the device.
> The IOTLB mapping may depend on the device so we really need it.
> 

You can't actually get the bus number outside of pci.c.



> Second you need to pass and return a length as the mapping is valid  
> only for a certain length.
> 

I agree, as well as read/write access.



> In the future the returned length could be 0 to simulate a pci abort...
> 
> > and the map_addr_opaque data is a pointer to the IOTLB buffer in the
> > chipset.
> >
> > When a pci device is doing dma or needs to write to the system, it can
> > use the
> >
> > target_phys_addr_t pci_phys_addr(PCIDevice *device,
> >                              target_phys_addr_t addr);
> >
> > function to get the mapped target address.
> >
> > If map_addr is NULL the function just returns the address it was  
> > passed.
> > Otherwise the map_addr function is called and the address is  
> > translated.
> 
> I think we should do this in new functions such as pci_memory_read/ 
> pci_memory_write which will
> replace cpu_physical_memory_read/cpu_physical_memory_write.
> 

Ok, so how about this patch. 

* new iommu API created:

#define PCI_IOMMU_MAP_INVALID 1
#define PCI_IOMMU_MAP_VALID 0
typedef int (*pci_map_addr_fn)(void *opaque, PCIDevice *device, 
                               target_phys_addr_t addr, ram_addr_t len,
                               int is_write, target_phys_addr_t *physaddr);
void pci_register_iommu(PCIBus *bus, pci_map_addr_fn map_addr, void *opaque);

int pci_iommu_memory_rw(PCIDevice *device, target_phys_addr_t addr,
                           uint8_t *buf, int len, int is_write);

static inline int pci_iommu_memory_read(PCIDevice *device, 
                                           target_phys_addr_t addr,
                                           uint8_t *buf, int len); 

static inline int pci_iommu_memory_write(PCIDevice *device, 
                                            target_phys_addr_t addr,
                                            const uint8_t *buf, int len);

int pci_register_iommu_memory_offset(PCIDevice *device,
                                        target_phys_addr_t start_addr,
                                        ram_addr_t size,
                                        ram_addr_t phys_offset,
                                        ram_addr_t region_offset);

static inline int pci_register_iommu_memory(PCIDevice *device, 
                                 target_phys_addr_t start_addr,
                                 ram_addr_t size,
                                 ram_addr_t phys_offset);

int pci_get_bus_num(PCIDevice *device);


* minimal impact to the codebase, while providing new functionality:
** pci_register_bus argument list unchanged
** drivers converted to IOMMU will work ok even if the system doesn't
register an IOMMU device

What do you guys think?

Brian


Signed-off-by: Brian Wheeler <address@hidden>

--- qemu/hw/pci.c       2009-03-25 15:00:23.000000000 -0400
+++ qemu-alpha-20090330/hw/pci.c        2009-04-02 13:11:34.000000000 -0400
@@ -29,12 +29,15 @@
 #include "sysemu.h"
 
 //#define DEBUG_PCI
+//#define DEBUG_PCI_IOMMU
 
 struct PCIBus {
     int bus_num;
     int devfn_min;
     pci_set_irq_fn set_irq;
     pci_map_irq_fn map_irq;
+    pci_map_addr_fn map_addr;
+    void *map_addr_opaque;
     uint32_t config_reg; /* XXX: suppress */
     /* low level pic */
     SetIRQFunc *low_set_irq;
@@ -57,6 +60,7 @@
 static int pci_irq_index;
 static PCIBus *first_bus;
 
+
 static void pcibus_save(QEMUFile *f, void *opaque)
 {
     PCIBus *bus = (PCIBus *)opaque;
@@ -100,6 +104,8 @@
     bus->irq_opaque = pic;
     bus->devfn_min = devfn_min;
     bus->nirq = nirq;
+    bus->map_addr = NULL;
+    bus->map_addr_opaque =NULL;
     first_bus = bus;
     register_savevm("PCIBUS", nbus++, 1, pcibus_save, pcibus_load, bus);
     return bus;
@@ -656,6 +662,62 @@
 }
 
 /***********************************************************/
+/* Generic pci memory mapping support */
+
+void pci_register_iommu(PCIBus *bus, pci_map_addr_fn map_addr, void *opaque) {
+    bus->map_addr = map_addr;
+    bus->map_addr_opaque = opaque;
+}
+
+int pci_register_iommu_memory_offset(PCIDevice *device,
+                                       target_phys_addr_t start_addr,
+                                       ram_addr_t size,
+                                       ram_addr_t phys_offset,
+                                       ram_addr_t region_offset)
+{
+  target_phys_addr_t physaddr = start_addr;
+  if(device->bus->map_addr != NULL) {
+    int result = device->bus->map_addr(device->bus->map_addr_opaque,
+                                      device, start_addr, size, 1, &physaddr);
+    if(result==PCI_IOMMU_MAP_INVALID) {
+#ifdef PCI_DEBUG_IOMMU
+      printf("(register) PCI IOMMU: Mapping %x failed.\n", start_addr);
+#endif
+      return result;
+    }
+  }
+  cpu_register_physical_memory_offset(start_addr, size, phys_offset, 
+                                     region_offset);
+  return PCI_IOMMU_MAP_VALID;
+}
+
+int pci_iommu_memory_rw(PCIDevice *device, target_phys_addr_t addr,
+                          uint8_t *buf, int len, int is_write)
+{
+  target_phys_addr_t physaddr = addr + pci_mem_base;
+  if(device->bus->map_addr != NULL) {
+    int result = device->bus->map_addr(device->bus->map_addr_opaque,
+                                      device, addr, len, is_write, &physaddr);
+    if(result==PCI_IOMMU_MAP_INVALID) {
+#ifdef PCI_DEBUG_IOMMU
+      printf("(rw) PCI IOMMU: Mapping %x failed.\n", addr);
+#endif
+      return result;
+    }
+  }
+#ifdef PCI_DEBUG_IOMMU
+  printf("(rw) PCI IOMMU:  %x -> %x \n", addr, physaddr);
+#endif
+  cpu_physical_memory_rw(physaddr, buf, len, is_write);
+  return PCI_IOMMU_MAP_VALID;
+}
+
+int pci_get_bus_num(PCIDevice *device) {
+  return device->bus->bus_num;
+}
+
+
+/***********************************************************/
 /* monitor info on PCI */
 
 typedef struct {
--- qemu/hw/pci.h       2009-03-25 15:00:23.000000000 -0400
+++ qemu-alpha-20090330/hw/pci.h        2009-04-02 12:57:50.000000000 -0400
@@ -206,6 +214,48 @@
     cpu_to_le16wu((uint16_t *)&pci_config[PCI_CLASS_DEVICE], val);
 }
 
+/* PCI Memory Mapping */
+#define PCI_IOMMU_MAP_INVALID 1
+#define PCI_IOMMU_MAP_VALID 0
+typedef int (*pci_map_addr_fn)(void *opaque, PCIDevice *device, 
+                              target_phys_addr_t addr, ram_addr_t len,
+                              int is_write, target_phys_addr_t *physaddr);
+void pci_register_iommu(PCIBus *bus, pci_map_addr_fn map_addr, void *opaque);
+
+int pci_iommu_memory_rw(PCIDevice *device, target_phys_addr_t addr,
+                          uint8_t *buf, int len, int is_write);
+
+static inline int pci_iommu_memory_read(PCIDevice *device, 
+                                          target_phys_addr_t addr,
+                                          uint8_t *buf, int len) 
+{
+  return pci_iommu_memory_rw(device, addr, buf, len, 0);
+}
+
+static inline int pci_iommu_memory_write(PCIDevice *device, 
+                                           target_phys_addr_t addr,
+                                           const uint8_t *buf, int len) 
+{
+  return pci_iommu_memory_rw(device, addr, (uint8_t *)buf, len, 1);
+}
+
+int pci_register_iommu_memory_offset(PCIDevice *device,
+                                       target_phys_addr_t start_addr,
+                                       ram_addr_t size,
+                                       ram_addr_t phys_offset,
+                                       ram_addr_t region_offset);
+
+static inline int pci_register_iommu_memory(PCIDevice *device, 
+                                target_phys_addr_t start_addr,
+                                ram_addr_t size,
+                                ram_addr_t phys_offset) {
+  return pci_register_iommu_memory_offset(device, start_addr, size, 
+                                            phys_offset, 0);
+}
+
+int pci_get_bus_num(PCIDevice *device);
+
+
 /* lsi53c895a.c */
 #define LSI_MAX_DEVS 7
 void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id);






reply via email to

[Prev in Thread] Current Thread [Next in Thread]