[Top][All Lists]
[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);