ia64/linux-2.6.18-xen.hg

annotate drivers/pci/rom.c @ 882:8dec4aa9b8b9

PCI pass through: PCIe IO space multiplexing

This is required for more than 16 HVM domain to boot from
PCIe pass through device.

Linux as dom0 exclusively assigns IO space to downstream PCI bridges
and the assignment unit of PCI bridge IO space is 4K. So the only up
to 16 PCIe device can be accessed via IO space within 64K IO ports.
PCI expansion ROM BIOS often uses IO port access to boot from the
device, so on virtualized environment, it means only up to 16 guest
domain can boot from pass-through device.

This patch allows PCIe IO space sharing of pass-through device.
- reassign IO space of PCIe devices specified by
"guestiomuldev=[<segment>:]<bus>:<dev>[,[<segment:><bus>:dev]][,...]"
to be shared.
This is implemented as Linux PCI quirk fixup.

The sharing unit is PCIe switch. Ie IO space of the end point
devices under the same switch will be shared. If there are more than
one switches, two areas of IO space will be used.

- And the driver which arbitrates the accesses to the multiplexed PCIe
IO space. Later qemu-dm will use this.

Limitation:
IO port of IO shared devices can't be accessed from dom0 Linux device
driver. But this wouldn't be a big issue because PCIe specification
discourages the use of IO space and recommends that IO space should be
used only for bootable device with ROM code. OS device driver should
work without IO space access.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
author Keir Fraser <keir.fraser@citrix.com>
date Thu May 28 09:57:49 2009 +0100 (2009-05-28)
parents 831230e53067
children
rev   line source
ian@0 1 /*
ian@0 2 * drivers/pci/rom.c
ian@0 3 *
ian@0 4 * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
ian@0 5 * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
ian@0 6 *
ian@0 7 * PCI ROM access routines
ian@0 8 */
ian@0 9 #include <linux/kernel.h>
ian@0 10 #include <linux/pci.h>
ian@0 11 #include <linux/slab.h>
ian@0 12
ian@0 13 #include "pci.h"
ian@0 14
ian@0 15 /**
ian@0 16 * pci_enable_rom - enable ROM decoding for a PCI device
ian@0 17 * @pdev: PCI device to enable
ian@0 18 *
ian@0 19 * Enable ROM decoding on @dev. This involves simply turning on the last
ian@0 20 * bit of the PCI ROM BAR. Note that some cards may share address decoders
ian@0 21 * between the ROM and other resources, so enabling it may disable access
ian@0 22 * to MMIO registers or other card memory.
ian@0 23 */
ian@0 24 static int pci_enable_rom(struct pci_dev *pdev)
ian@0 25 {
ian@0 26 struct resource *res = pdev->resource + PCI_ROM_RESOURCE;
ian@0 27 struct pci_bus_region region;
ian@0 28 u32 rom_addr;
ian@0 29
ian@0 30 if (!res->flags)
ian@0 31 return -1;
ian@0 32
ian@0 33 pcibios_resource_to_bus(pdev, &region, res);
ian@0 34 pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
ian@0 35 rom_addr &= ~PCI_ROM_ADDRESS_MASK;
ian@0 36 rom_addr |= region.start | PCI_ROM_ADDRESS_ENABLE;
ian@0 37 pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
ian@0 38 return 0;
ian@0 39 }
ian@0 40
ian@0 41 /**
ian@0 42 * pci_disable_rom - disable ROM decoding for a PCI device
ian@0 43 * @pdev: PCI device to disable
ian@0 44 *
ian@0 45 * Disable ROM decoding on a PCI device by turning off the last bit in the
ian@0 46 * ROM BAR.
ian@0 47 */
ian@0 48 static void pci_disable_rom(struct pci_dev *pdev)
ian@0 49 {
ian@0 50 u32 rom_addr;
ian@0 51 pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
ian@0 52 rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
ian@0 53 pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
ian@0 54 }
ian@0 55
ian@0 56 /**
ian@0 57 * pci_map_rom - map a PCI ROM to kernel space
ian@0 58 * @pdev: pointer to pci device struct
ian@0 59 * @size: pointer to receive size of pci window over ROM
ian@0 60 * @return: kernel virtual pointer to image of ROM
ian@0 61 *
ian@0 62 * Map a PCI ROM into kernel space. If ROM is boot video ROM,
ian@0 63 * the shadow BIOS copy will be returned instead of the
ian@0 64 * actual ROM.
ian@0 65 */
ian@0 66 void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
ian@0 67 {
ian@0 68 struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
ian@0 69 loff_t start;
ian@0 70 void __iomem *rom;
ian@0 71 void __iomem *image;
ian@0 72 int last_image;
ian@0 73
ian@0 74 /* IORESOURCE_ROM_SHADOW only set on x86 */
ian@0 75 if (res->flags & IORESOURCE_ROM_SHADOW) {
ian@0 76 /* primary video rom always starts here */
ian@0 77 start = (loff_t)0xC0000;
ian@0 78 *size = 0x20000; /* cover C000:0 through E000:0 */
ian@0 79 } else {
ian@0 80 if (res->flags & IORESOURCE_ROM_COPY) {
ian@0 81 *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
ian@0 82 return (void __iomem *)(unsigned long)
ian@0 83 pci_resource_start(pdev, PCI_ROM_RESOURCE);
ian@0 84 } else {
ian@0 85 /* assign the ROM an address if it doesn't have one */
ian@0 86 if (res->parent == NULL &&
ian@0 87 pci_assign_resource(pdev,PCI_ROM_RESOURCE))
ian@0 88 return NULL;
ian@0 89 start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
ian@0 90 *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
ian@0 91 if (*size == 0)
ian@0 92 return NULL;
ian@0 93
ian@0 94 /* Enable ROM space decodes */
ian@0 95 if (pci_enable_rom(pdev))
ian@0 96 return NULL;
ian@0 97 }
ian@0 98 }
ian@0 99
ian@0 100 rom = ioremap(start, *size);
ian@0 101 if (!rom) {
ian@0 102 /* restore enable if ioremap fails */
ian@0 103 if (!(res->flags & (IORESOURCE_ROM_ENABLE |
ian@0 104 IORESOURCE_ROM_SHADOW |
ian@0 105 IORESOURCE_ROM_COPY)))
ian@0 106 pci_disable_rom(pdev);
ian@0 107 return NULL;
ian@0 108 }
ian@0 109
ian@0 110 /*
ian@0 111 * Try to find the true size of the ROM since sometimes the PCI window
ian@0 112 * size is much larger than the actual size of the ROM.
ian@0 113 * True size is important if the ROM is going to be copied.
ian@0 114 */
ian@0 115 image = rom;
ian@0 116 do {
ian@0 117 void __iomem *pds;
ian@0 118 /* Standard PCI ROMs start out with these bytes 55 AA */
ian@0 119 if (readb(image) != 0x55)
ian@0 120 break;
ian@0 121 if (readb(image + 1) != 0xAA)
ian@0 122 break;
ian@0 123 /* get the PCI data structure and check its signature */
ian@0 124 pds = image + readw(image + 24);
ian@0 125 if (readb(pds) != 'P')
ian@0 126 break;
ian@0 127 if (readb(pds + 1) != 'C')
ian@0 128 break;
ian@0 129 if (readb(pds + 2) != 'I')
ian@0 130 break;
ian@0 131 if (readb(pds + 3) != 'R')
ian@0 132 break;
ian@0 133 last_image = readb(pds + 21) & 0x80;
ian@0 134 /* this length is reliable */
ian@0 135 image += readw(pds + 16) * 512;
ian@0 136 } while (!last_image);
ian@0 137
ian@0 138 /* never return a size larger than the PCI resource window */
ian@0 139 /* there are known ROMs that get the size wrong */
ian@0 140 *size = min((size_t)(image - rom), *size);
ian@0 141
ian@0 142 return rom;
ian@0 143 }
ian@0 144
ian@0 145 /**
ian@0 146 * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
ian@0 147 * @pdev: pointer to pci device struct
ian@0 148 * @size: pointer to receive size of pci window over ROM
ian@0 149 * @return: kernel virtual pointer to image of ROM
ian@0 150 *
ian@0 151 * Map a PCI ROM into kernel space. If ROM is boot video ROM,
ian@0 152 * the shadow BIOS copy will be returned instead of the
ian@0 153 * actual ROM.
ian@0 154 */
ian@0 155 void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
ian@0 156 {
ian@0 157 struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
ian@0 158 void __iomem *rom;
ian@0 159
ian@0 160 rom = pci_map_rom(pdev, size);
ian@0 161 if (!rom)
ian@0 162 return NULL;
ian@0 163
ian@0 164 if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
ian@0 165 return rom;
ian@0 166
ian@0 167 res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
ian@0 168 if (!res->start)
ian@0 169 return rom;
ian@0 170
ian@0 171 res->end = res->start + *size;
ian@0 172 memcpy_fromio((void*)(unsigned long)res->start, rom, *size);
ian@0 173 pci_unmap_rom(pdev, rom);
ian@0 174 res->flags |= IORESOURCE_ROM_COPY;
ian@0 175
ian@0 176 return (void __iomem *)(unsigned long)res->start;
ian@0 177 }
ian@0 178
ian@0 179 /**
ian@0 180 * pci_unmap_rom - unmap the ROM from kernel space
ian@0 181 * @pdev: pointer to pci device struct
ian@0 182 * @rom: virtual address of the previous mapping
ian@0 183 *
ian@0 184 * Remove a mapping of a previously mapped ROM
ian@0 185 */
ian@0 186 void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
ian@0 187 {
ian@0 188 struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
ian@0 189
ian@0 190 if (res->flags & IORESOURCE_ROM_COPY)
ian@0 191 return;
ian@0 192
ian@0 193 iounmap(rom);
ian@0 194
ian@0 195 /* Disable again before continuing, leave enabled if pci=rom */
ian@0 196 if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
ian@0 197 pci_disable_rom(pdev);
ian@0 198 }
ian@0 199
ian@0 200 /**
ian@0 201 * pci_remove_rom - disable the ROM and remove its sysfs attribute
ian@0 202 * @pdev: pointer to pci device struct
ian@0 203 *
ian@0 204 * Remove the rom file in sysfs and disable ROM decoding.
ian@0 205 */
ian@0 206 void pci_remove_rom(struct pci_dev *pdev)
ian@0 207 {
ian@0 208 struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
ian@0 209
ian@0 210 if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
ian@0 211 sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
ian@0 212 if (!(res->flags & (IORESOURCE_ROM_ENABLE |
ian@0 213 IORESOURCE_ROM_SHADOW |
ian@0 214 IORESOURCE_ROM_COPY)))
ian@0 215 pci_disable_rom(pdev);
ian@0 216 }
ian@0 217
ian@0 218 /**
ian@0 219 * pci_cleanup_rom - internal routine for freeing the ROM copy created
ian@0 220 * by pci_map_rom_copy called from remove.c
ian@0 221 * @pdev: pointer to pci device struct
ian@0 222 *
ian@0 223 * Free the copied ROM if we allocated one.
ian@0 224 */
ian@0 225 void pci_cleanup_rom(struct pci_dev *pdev)
ian@0 226 {
ian@0 227 struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
ian@0 228 if (res->flags & IORESOURCE_ROM_COPY) {
ian@0 229 kfree((void*)(unsigned long)res->start);
ian@0 230 res->flags &= ~IORESOURCE_ROM_COPY;
ian@0 231 res->start = 0;
ian@0 232 res->end = 0;
ian@0 233 }
ian@0 234 }
ian@0 235
ian@0 236 EXPORT_SYMBOL(pci_map_rom);
ian@0 237 EXPORT_SYMBOL(pci_map_rom_copy);
ian@0 238 EXPORT_SYMBOL(pci_unmap_rom);
ian@0 239 EXPORT_SYMBOL(pci_remove_rom);