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); | ||