diff options
Diffstat (limited to 'drivers/pci/access.c')
| -rw-r--r-- | drivers/pci/access.c | 89 | 
1 files changed, 89 insertions, 0 deletions
| diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 24a76de49f41..2a42add7f563 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c | |||
| @@ -60,3 +60,92 @@ EXPORT_SYMBOL(pci_bus_read_config_dword); | |||
| 60 | EXPORT_SYMBOL(pci_bus_write_config_byte); | 60 | EXPORT_SYMBOL(pci_bus_write_config_byte); | 
| 61 | EXPORT_SYMBOL(pci_bus_write_config_word); | 61 | EXPORT_SYMBOL(pci_bus_write_config_word); | 
| 62 | EXPORT_SYMBOL(pci_bus_write_config_dword); | 62 | EXPORT_SYMBOL(pci_bus_write_config_dword); | 
| 63 | |||
| 64 | static u32 pci_user_cached_config(struct pci_dev *dev, int pos) | ||
| 65 | { | ||
| 66 | u32 data; | ||
| 67 | |||
| 68 | data = dev->saved_config_space[pos/sizeof(dev->saved_config_space[0])]; | ||
| 69 | data >>= (pos % sizeof(dev->saved_config_space[0])) * 8; | ||
| 70 | return data; | ||
| 71 | } | ||
| 72 | |||
| 73 | #define PCI_USER_READ_CONFIG(size,type) \ | ||
| 74 | int pci_user_read_config_##size \ | ||
| 75 | (struct pci_dev *dev, int pos, type *val) \ | ||
| 76 | { \ | ||
| 77 | unsigned long flags; \ | ||
| 78 | int ret = 0; \ | ||
| 79 | u32 data = -1; \ | ||
| 80 | if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ | ||
| 81 | spin_lock_irqsave(&pci_lock, flags); \ | ||
| 82 | if (likely(!dev->block_ucfg_access)) \ | ||
| 83 | ret = dev->bus->ops->read(dev->bus, dev->devfn, \ | ||
| 84 | pos, sizeof(type), &data); \ | ||
| 85 | else if (pos < sizeof(dev->saved_config_space)) \ | ||
| 86 | data = pci_user_cached_config(dev, pos); \ | ||
| 87 | spin_unlock_irqrestore(&pci_lock, flags); \ | ||
| 88 | *val = (type)data; \ | ||
| 89 | return ret; \ | ||
| 90 | } | ||
| 91 | |||
| 92 | #define PCI_USER_WRITE_CONFIG(size,type) \ | ||
| 93 | int pci_user_write_config_##size \ | ||
| 94 | (struct pci_dev *dev, int pos, type val) \ | ||
| 95 | { \ | ||
| 96 | unsigned long flags; \ | ||
| 97 | int ret = -EIO; \ | ||
| 98 | if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ | ||
| 99 | spin_lock_irqsave(&pci_lock, flags); \ | ||
| 100 | if (likely(!dev->block_ucfg_access)) \ | ||
| 101 | ret = dev->bus->ops->write(dev->bus, dev->devfn, \ | ||
| 102 | pos, sizeof(type), val); \ | ||
| 103 | spin_unlock_irqrestore(&pci_lock, flags); \ | ||
| 104 | return ret; \ | ||
| 105 | } | ||
| 106 | |||
| 107 | PCI_USER_READ_CONFIG(byte, u8) | ||
| 108 | PCI_USER_READ_CONFIG(word, u16) | ||
| 109 | PCI_USER_READ_CONFIG(dword, u32) | ||
| 110 | PCI_USER_WRITE_CONFIG(byte, u8) | ||
| 111 | PCI_USER_WRITE_CONFIG(word, u16) | ||
| 112 | PCI_USER_WRITE_CONFIG(dword, u32) | ||
| 113 | |||
| 114 | /** | ||
| 115 | * pci_block_user_cfg_access - Block userspace PCI config reads/writes | ||
| 116 | * @dev: pci device struct | ||
| 117 | * | ||
| 118 | * This function blocks any userspace PCI config accesses from occurring. | ||
| 119 | * When blocked, any writes will be bit bucketed and reads will return the | ||
| 120 | * data saved using pci_save_state for the first 64 bytes of config | ||
| 121 | * space and return 0xff for all other config reads. | ||
| 122 | **/ | ||
| 123 | void pci_block_user_cfg_access(struct pci_dev *dev) | ||
| 124 | { | ||
| 125 | unsigned long flags; | ||
| 126 | |||
| 127 | pci_save_state(dev); | ||
| 128 | |||
| 129 | /* spinlock to synchronize with anyone reading config space now */ | ||
| 130 | spin_lock_irqsave(&pci_lock, flags); | ||
| 131 | dev->block_ucfg_access = 1; | ||
| 132 | spin_unlock_irqrestore(&pci_lock, flags); | ||
| 133 | } | ||
| 134 | EXPORT_SYMBOL_GPL(pci_block_user_cfg_access); | ||
| 135 | |||
| 136 | /** | ||
| 137 | * pci_unblock_user_cfg_access - Unblock userspace PCI config reads/writes | ||
| 138 | * @dev: pci device struct | ||
| 139 | * | ||
| 140 | * This function allows userspace PCI config accesses to resume. | ||
| 141 | **/ | ||
| 142 | void pci_unblock_user_cfg_access(struct pci_dev *dev) | ||
| 143 | { | ||
| 144 | unsigned long flags; | ||
| 145 | |||
| 146 | /* spinlock to synchronize with anyone reading saved config space */ | ||
| 147 | spin_lock_irqsave(&pci_lock, flags); | ||
| 148 | dev->block_ucfg_access = 0; | ||
| 149 | spin_unlock_irqrestore(&pci_lock, flags); | ||
| 150 | } | ||
| 151 | EXPORT_SYMBOL_GPL(pci_unblock_user_cfg_access); | ||
